Skip to content

Commit fedc9b6

Browse files
committed
Fix decorator exception bug.
Change-Id: I9c389fe66d63f8e65233595f5c354d97957b03a1
1 parent 9b0b797 commit fedc9b6

File tree

3 files changed

+121
-6
lines changed

3 files changed

+121
-6
lines changed

cozeloop/decorator/decorator.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ def sync_wrapper(*args: Any, **kwargs: Any):
101101
span.set_tags(tags)
102102
span.finish()
103103

104-
return res
104+
if res is not None:
105+
return res
105106

106107
@wraps(func)
107108
async def async_wrapper(*args: Any, **kwargs: Any):
@@ -139,7 +140,8 @@ async def async_wrapper(*args: Any, **kwargs: Any):
139140
span.set_tags(tags)
140141
span.finish()
141142

142-
return res
143+
if res is not None:
144+
return res
143145

144146
@wraps(func)
145147
def gen_wrapper(*args: Any, **kwargs: Any):
@@ -247,7 +249,7 @@ def sync_stream_wrapper(*args: Any, **kwargs: Any):
247249
span.set_input(input)
248250
span.set_tags(tags)
249251

250-
if not hasattr(res, "__iter__"):
252+
if not hasattr(res, "__iter__") and res is not None:
251253
return res
252254

253255
@wraps(func)
@@ -286,7 +288,7 @@ async def async_stream_wrapper(*args: Any, **kwargs: Any):
286288
span.set_input(input)
287289
span.set_tags(tags)
288290

289-
if not hasattr(res, "__aiter__"):
291+
if not hasattr(res, "__aiter__") and res is not None:
290292
return res
291293

292294
if is_async_gen_func(func):
@@ -421,7 +423,7 @@ async def __aiter__(self) -> AsyncIterator[S]:
421423
await self._aend()
422424
raise
423425
except Exception as e:
424-
await self._aend()
426+
await self._aend(e)
425427
raise e
426428
else:
427429
await self._aend()

cozeloop/internal/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
22
# SPDX-License-Identifier: MIT
33

4-
VERSION = 'v0.1.22'
4+
VERSION = 'v0.1.23'

tests/decorator/test_decorator.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,116 @@ async def collect():
368368
assert result == [1, 2, 3]
369369
assert span.output == '[2, 4, 6]'
370370
assert span.finished is True
371+
372+
373+
class TestCozeLoopDecoratorExceptionHandling:
374+
"""测试 CozeLoopDecorator 是否正确处理并重新抛出异常,而不是将其掩盖"""
375+
376+
def test_sync_func_exception(self, monkeypatch):
377+
span = SpanMock()
378+
monkeypatch.setattr(decorator_module, "start_span", lambda *args, **kwargs: span)
379+
380+
@decorator_module.CozeLoopDecorator().observe()
381+
def risky_func():
382+
raise ValueError("Sync error")
383+
384+
with pytest.raises(ValueError, match="Sync error"):
385+
risky_func()
386+
387+
assert span.error == "Sync error"
388+
assert span.finished is True
389+
390+
def test_async_func_exception(self, monkeypatch):
391+
span = SpanMock()
392+
monkeypatch.setattr(decorator_module, "start_span", lambda *args, **kwargs: span)
393+
394+
@decorator_module.CozeLoopDecorator().observe()
395+
async def risky_async_func():
396+
raise ValueError("Async error")
397+
398+
with pytest.raises(ValueError, match="Async error"):
399+
asyncio.run(risky_async_func())
400+
401+
assert span.error == "Async error"
402+
assert span.finished is True
403+
404+
def test_generator_func_exception(self, monkeypatch):
405+
span = SpanMock()
406+
monkeypatch.setattr(decorator_module, "start_span", lambda *args, **kwargs: span)
407+
408+
@decorator_module.CozeLoopDecorator().observe()
409+
def risky_gen():
410+
yield 1
411+
raise ValueError("Gen error")
412+
413+
gen = risky_gen()
414+
assert next(gen) == 1
415+
with pytest.raises(ValueError, match="Gen error"):
416+
next(gen)
417+
418+
assert span.error == "Gen error"
419+
assert span.finished is True
420+
421+
def test_async_generator_func_exception(self, monkeypatch):
422+
span = SpanMock()
423+
monkeypatch.setattr(decorator_module, "start_span", lambda *args, **kwargs: span)
424+
425+
@decorator_module.CozeLoopDecorator().observe()
426+
async def risky_agen():
427+
yield 1
428+
raise ValueError("Async gen error")
429+
430+
async def collect():
431+
agen = risky_agen()
432+
assert (await agen.__anext__()) == 1
433+
with pytest.raises(ValueError, match="Async gen error"):
434+
await agen.__anext__()
435+
436+
asyncio.run(collect())
437+
438+
assert span.error == "Async gen error"
439+
assert span.finished is True
440+
441+
def test_sync_stream_wrapper_exception(self, monkeypatch):
442+
span = SpanMock()
443+
monkeypatch.setattr(decorator_module, "start_span", lambda *args, **kwargs: span)
444+
445+
def risky_iter_func():
446+
class RiskyIter:
447+
def __iter__(self):
448+
return self
449+
def __next__(self):
450+
raise ValueError("Stream error")
451+
return RiskyIter()
452+
453+
decorated = decorator_module.CozeLoopDecorator().observe(process_iterator_outputs=lambda x: x)(risky_iter_func)
454+
stream = decorated()
455+
with pytest.raises(ValueError, match="Stream error"):
456+
list(stream)
457+
458+
assert span.error == "Stream error"
459+
assert span.finished is True
460+
461+
def test_async_stream_wrapper_exception(self, monkeypatch):
462+
span = SpanMock()
463+
monkeypatch.setattr(decorator_module, "start_span", lambda *args, **kwargs: span)
464+
465+
async def risky_aiter_func():
466+
class RiskyAIter:
467+
def __aiter__(self):
468+
return self
469+
async def __anext__(self):
470+
raise ValueError("Async stream error")
471+
return RiskyAIter()
472+
473+
async def collect():
474+
decorated = decorator_module.CozeLoopDecorator().observe(process_iterator_outputs=lambda x: x)(risky_aiter_func)
475+
stream = await decorated()
476+
with pytest.raises(ValueError, match="Async stream error"):
477+
async for _ in stream:
478+
pass
479+
480+
asyncio.run(collect())
481+
482+
assert span.error == "Async stream error"
483+
assert span.finished is True

0 commit comments

Comments
 (0)