From 5431e7c0a4ab14daf7c23947f1d9712c8abaab17 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 13 Nov 2025 13:04:21 +0100 Subject: [PATCH 1/5] feat: Preserve metadata on wrapped coroutines --- sentry_sdk/integrations/asyncio.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sentry_sdk/integrations/asyncio.py b/sentry_sdk/integrations/asyncio.py index 66742fe6e4..3668e012ce 100644 --- a/sentry_sdk/integrations/asyncio.py +++ b/sentry_sdk/integrations/asyncio.py @@ -1,4 +1,5 @@ import sys +import functools import sentry_sdk from sentry_sdk.consts import OP @@ -39,6 +40,7 @@ def patch_asyncio(): def _sentry_task_factory(loop, coro, **kwargs): # type: (asyncio.AbstractEventLoop, Coroutine[Any, Any, Any], Any) -> asyncio.Future[Any] + @functools.wraps(coro) async def _task_with_sentry_span_creation(): # type: () -> Any result = None From ac6f6311a843008cf0ee258ed463ce90251f00b0 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Fri, 21 Nov 2025 13:35:44 +0100 Subject: [PATCH 2/5] target fields copied to coroutine --- sentry_sdk/integrations/asyncio.py | 13 ++++++++++++- tests/integrations/asyncio/test_asyncio.py | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/asyncio.py b/sentry_sdk/integrations/asyncio.py index 3668e012ce..33a729aeed 100644 --- a/sentry_sdk/integrations/asyncio.py +++ b/sentry_sdk/integrations/asyncio.py @@ -30,6 +30,17 @@ def get_name(coro): ) +def _wrap_coroutine(wrapped): + # type: Coroutine[Any, Any, Any] -> Coroutine[Any, Any, Any] + # Only __name__ and __qualname__ are copied from function to coroutine in CPython + return functools.partial( + functools.update_wrapper, + wrapped=wrapped, + assigned=("__name__", "__qualname__"), + updated=(), + ) + + def patch_asyncio(): # type: () -> None orig_task_factory = None @@ -40,7 +51,7 @@ def patch_asyncio(): def _sentry_task_factory(loop, coro, **kwargs): # type: (asyncio.AbstractEventLoop, Coroutine[Any, Any, Any], Any) -> asyncio.Future[Any] - @functools.wraps(coro) + @_wrap_coroutine(coro) async def _task_with_sentry_span_creation(): # type: () -> Any result = None diff --git a/tests/integrations/asyncio/test_asyncio.py b/tests/integrations/asyncio/test_asyncio.py index 66113746bf..11b60fb0e1 100644 --- a/tests/integrations/asyncio/test_asyncio.py +++ b/tests/integrations/asyncio/test_asyncio.py @@ -67,7 +67,16 @@ async def test_create_task( with sentry_sdk.start_transaction(name="test_transaction_for_create_task"): with sentry_sdk.start_span(op="root", name="not so important"): - tasks = [asyncio.create_task(foo()), asyncio.create_task(bar())] + foo_task = asyncio.create_task(foo()) + bar_task = asyncio.create_task(bar()) + + if hasattr(foo_task.get_coro(), "__name__"): + assert foo_task.get_coro().__name__ == "foo" + if hasattr(bar_task.get_coro(), "__name__"): + assert bar_task.get_coro().__name__ == "bar" + + tasks = [foo_task, bar_task] + await asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION) sentry_sdk.flush() From 5ec677d2d2d4e8aa5f7f9c0a2f74932704ce628b Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Fri, 21 Nov 2025 13:55:35 +0100 Subject: [PATCH 3/5] mypy --- sentry_sdk/integrations/asyncio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/asyncio.py b/sentry_sdk/integrations/asyncio.py index 33a729aeed..8109b7c4f9 100644 --- a/sentry_sdk/integrations/asyncio.py +++ b/sentry_sdk/integrations/asyncio.py @@ -31,7 +31,7 @@ def get_name(coro): def _wrap_coroutine(wrapped): - # type: Coroutine[Any, Any, Any] -> Coroutine[Any, Any, Any] + # type: (Coroutine[Any, Any, Any]) -> Coroutine[Any, Any, Any] # Only __name__ and __qualname__ are copied from function to coroutine in CPython return functools.partial( functools.update_wrapper, From b5ee9afc3f9dde59725faad94c238fb2c1605254 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Fri, 21 Nov 2025 14:13:29 +0100 Subject: [PATCH 4/5] . --- sentry_sdk/integrations/asyncio.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/asyncio.py b/sentry_sdk/integrations/asyncio.py index 8109b7c4f9..e327b3d5ac 100644 --- a/sentry_sdk/integrations/asyncio.py +++ b/sentry_sdk/integrations/asyncio.py @@ -15,11 +15,13 @@ from typing import cast, TYPE_CHECKING if TYPE_CHECKING: - from typing import Any + from typing import Any, Callable, TypeVar from collections.abc import Coroutine from sentry_sdk._types import ExcInfo + F = TypeVar("T", bound=Callable[..., Any]) + def get_name(coro): # type: (Any) -> str @@ -31,11 +33,11 @@ def get_name(coro): def _wrap_coroutine(wrapped): - # type: (Coroutine[Any, Any, Any]) -> Coroutine[Any, Any, Any] + # type: (Coroutine[Any, Any, Any]) -> Callable[[T], T] # Only __name__ and __qualname__ are copied from function to coroutine in CPython return functools.partial( functools.update_wrapper, - wrapped=wrapped, + wrapped=wrapped, # type: ignore assigned=("__name__", "__qualname__"), updated=(), ) From 1111a21c1023bbf036d29edd10a989b59e5b2e0b Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Fri, 21 Nov 2025 14:14:01 +0100 Subject: [PATCH 5/5] . --- sentry_sdk/integrations/asyncio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/asyncio.py b/sentry_sdk/integrations/asyncio.py index e327b3d5ac..a652edc280 100644 --- a/sentry_sdk/integrations/asyncio.py +++ b/sentry_sdk/integrations/asyncio.py @@ -20,7 +20,7 @@ from sentry_sdk._types import ExcInfo - F = TypeVar("T", bound=Callable[..., Any]) + T = TypeVar("T", bound=Callable[..., Any]) def get_name(coro):