11import sys
2+ import signal
23
34import sentry_sdk
45from sentry_sdk .consts import OP
@@ -36,10 +37,25 @@ def patch_asyncio():
3637 loop = asyncio .get_running_loop ()
3738 orig_task_factory = loop .get_task_factory ()
3839
40+ # Add a shutdown handler to log a helpful message
41+ def shutdown_handler ():
42+ logger .info (
43+ "AsyncIO is shutting down. If you see 'Task was destroyed but it is pending!' "
44+ "errors with '_task_with_sentry_span_creation', these are normal during shutdown "
45+ "and not a problem with your code or Sentry."
46+ )
47+
48+ try :
49+ loop .add_signal_handler (signal .SIGINT , shutdown_handler )
50+ loop .add_signal_handler (signal .SIGTERM , shutdown_handler )
51+ except (NotImplementedError , AttributeError ):
52+ # Signal handlers might not be supported on all platforms
53+ pass
54+
3955 def _sentry_task_factory (loop , coro , ** kwargs ):
4056 # type: (asyncio.AbstractEventLoop, Coroutine[Any, Any, Any], Any) -> asyncio.Future[Any]
4157
42- async def _coro_creating_hub_and_span ():
58+ async def _task_with_sentry_span_creation ():
4359 # type: () -> Any
4460 result = None
4561
@@ -56,20 +72,32 @@ async def _coro_creating_hub_and_span():
5672
5773 return result
5874
75+ task = None
76+
5977 # Trying to use user set task factory (if there is one)
6078 if orig_task_factory :
61- return orig_task_factory (loop , _coro_creating_hub_and_span (), ** kwargs )
62-
63- # The default task factory in `asyncio` does not have its own function
64- # but is just a couple of lines in `asyncio.base_events.create_task()`
65- # Those lines are copied here.
66-
67- # WARNING:
68- # If the default behavior of the task creation in asyncio changes,
69- # this will break!
70- task = Task (_coro_creating_hub_and_span (), loop = loop , ** kwargs )
71- if task ._source_traceback : # type: ignore
72- del task ._source_traceback [- 1 ] # type: ignore
79+ task = orig_task_factory (
80+ loop , _task_with_sentry_span_creation (), ** kwargs
81+ )
82+
83+ if task is None :
84+ # The default task factory in `asyncio` does not have its own function
85+ # but is just a couple of lines in `asyncio.base_events.create_task()`
86+ # Those lines are copied here.
87+
88+ # WARNING:
89+ # If the default behavior of the task creation in asyncio changes,
90+ # this will break!
91+ task = Task (_task_with_sentry_span_creation (), loop = loop , ** kwargs )
92+ if task ._source_traceback : # type: ignore
93+ del task ._source_traceback [- 1 ] # type: ignore
94+
95+ # Set the task name to include the original coroutine's name
96+ try :
97+ task .set_name (f"{ get_name (coro )} (Sentry-wrapped)" )
98+ except AttributeError :
99+ # set_name might not be available in all Python versions
100+ pass
73101
74102 return task
75103
0 commit comments