@@ -47,6 +47,10 @@ def patch_asyncio() -> None:
4747 loop = asyncio .get_running_loop ()
4848 orig_task_factory = loop .get_task_factory ()
4949
50+ # Check if already patched
51+ if getattr (orig_task_factory , "_is_sentry_task_factory" , False ):
52+ return
53+
5054 def _sentry_task_factory (
5155 loop : "asyncio.AbstractEventLoop" ,
5256 coro : "Coroutine[Any, Any, Any]" ,
@@ -102,6 +106,7 @@ async def _task_with_sentry_span_creation() -> "Any":
102106
103107 return task
104108
109+ _sentry_task_factory ._is_sentry_task_factory = True # type: ignore
105110 loop .set_task_factory (_sentry_task_factory ) # type: ignore
106111
107112 except RuntimeError :
@@ -138,3 +143,48 @@ class AsyncioIntegration(Integration):
138143 @staticmethod
139144 def setup_once () -> None :
140145 patch_asyncio ()
146+
147+
148+ def enable_asyncio_integration (* args : "Any" , ** kwargs : "Any" ) -> None :
149+ """
150+ Enable AsyncioIntegration with the provided options.
151+
152+ This is useful in scenarios where Sentry needs to be initialized before
153+ an event loop is set up, but you still want to instrument asyncio once there
154+ is an event loop. In that case, you can sentry_sdk.init() early on without
155+ the AsyncioIntegration and then, once the event loop has been set up,
156+ execute:
157+
158+ ```python
159+ from sentry_sdk.integrations.asyncio import enable_asyncio_integration
160+
161+ async def async_entrypoint():
162+ enable_asyncio_integration()
163+ ```
164+
165+ Any arguments provided will be passed to AsyncioIntegration() as is.
166+
167+ If AsyncioIntegration has already patched the current event loop, this
168+ function won't have any effect.
169+
170+ If AsyncioIntegration was provided in
171+ sentry_sdk.init(disabled_integrations=[...]), this function will ignore that
172+ and the integration will be enabled.
173+ """
174+ client = sentry_sdk .get_client ()
175+ if not client .is_active ():
176+ return
177+
178+ # This function purposefully bypasses the integration machinery in
179+ # integrations/__init__.py. _installed_integrations/_processed_integrations
180+ # is used to prevent double patching the same module, but in the case of
181+ # the AsyncioIntegration, we don't monkeypatch the standard library directly,
182+ # we patch the currently running event loop, and we keep the record of doing
183+ # that on the loop itself.
184+ logger .debug ("Setting up integration asyncio" )
185+
186+ integration = AsyncioIntegration (* args , ** kwargs )
187+ integration .setup_once ()
188+
189+ if "asyncio" not in client .integrations :
190+ client .integrations ["asyncio" ] = integration
0 commit comments