Skip to content

Commit 0193dea

Browse files
committed
Check if event loop is unpatched
1 parent 36ef8cb commit 0193dea

3 files changed

Lines changed: 29 additions & 35 deletions

File tree

sentry_sdk/integrations/__init__.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -280,36 +280,6 @@ def setup_integrations(
280280
return integrations
281281

282282

283-
def _enable_integration(integration: "Integration") -> None:
284-
identifier = integration.identifier
285-
286-
with _installer_lock:
287-
client = sentry_sdk.get_client()
288-
if not client.is_active():
289-
return
290-
291-
if identifier in client.integrations:
292-
logger.debug("Integration already enabled: %s", identifier)
293-
return
294-
295-
if identifier not in _installed_integrations or identifier == "asyncio":
296-
# Asyncio is special because it patches the currently running event
297-
# loop. _installed_integrations, on the other hand, prevents
298-
# re-patching on the process level.
299-
logger.debug("Setting up integration %s", identifier)
300-
_processed_integrations.add(identifier)
301-
try:
302-
type(integration).setup_once()
303-
integration.setup_once_with_options(client.options)
304-
except DidNotEnable as e:
305-
logger.debug("Did not enable integration %s: %s", identifier, e)
306-
return
307-
308-
_installed_integrations.add(identifier)
309-
310-
client.integrations[integration.identifier] = integration
311-
312-
313283
def _check_minimum_version(
314284
integration: "type[Integration]",
315285
version: "Optional[tuple[int, ...]]",

sentry_sdk/integrations/asyncio.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import sentry_sdk
55
from sentry_sdk.consts import OP
6-
from sentry_sdk.integrations import Integration, DidNotEnable, _enable_integration
6+
from sentry_sdk.integrations import Integration, DidNotEnable
77
from sentry_sdk.utils import event_from_exception, logger, reraise
88

99
try:
@@ -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:
@@ -167,4 +172,11 @@ async def async_entrypoint():
167172
sentry_sdk.init(disabled_integrations=[...]), this function will ignore that
168173
and the integration will be enabled.
169174
"""
170-
_enable_integration(AsyncioIntegration(*args, **kwargs))
175+
client = sentry_sdk.get_client()
176+
if not client.is_active():
177+
return
178+
179+
logger.debug("Setting up integration asyncio")
180+
integration = AsyncioIntegration(*args, **kwargs)
181+
integration.setup_once()
182+
client.integrations["asyncio"] = integration

tests/integrations/asyncio/test_asyncio.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ def test_patch_asyncio(mock_get_running_loop):
233233
Test that the patch_asyncio function will patch the task factory.
234234
"""
235235
mock_loop = mock_get_running_loop.return_value
236+
mock_loop.get_task_factory.return_value._is_sentry_task_factory = False
236237

237238
patch_asyncio()
238239

@@ -282,6 +283,7 @@ def test_sentry_task_factory_with_factory(mock_get_running_loop):
282283

283284
# The original task factory will be mocked out here, let's retrieve the value for later
284285
orig_task_factory = mock_loop.get_task_factory.return_value
286+
orig_task_factory._is_sentry_task_factory = False
285287

286288
# Retieve sentry task factory (since it is an inner function within patch_asyncio)
287289
sentry_task_factory = get_sentry_task_factory(mock_get_running_loop)
@@ -344,6 +346,7 @@ def test_sentry_task_factory_context_with_factory(mock_get_running_loop):
344346

345347
# The original task factory will be mocked out here, let's retrieve the value for later
346348
orig_task_factory = mock_loop.get_task_factory.return_value
349+
orig_task_factory._is_sentry_task_factory = False
347350

348351
# Retieve sentry task factory (since it is an inner function within patch_asyncio)
349352
sentry_task_factory = get_sentry_task_factory(mock_get_running_loop)
@@ -448,18 +451,27 @@ async def test_delayed_enable_integration_with_options(sentry_init, capture_even
448451

449452
@minimum_python_38
450453
@pytest.mark.asyncio
451-
async def test_delayed_enable_enabled_integration(sentry_init):
454+
async def test_delayed_enable_enabled_integration(sentry_init, uninstall_integration):
455+
# Ensure asyncio integration is not already installed from previous tests
456+
uninstall_integration("asyncio")
457+
452458
integration = AsyncioIntegration()
453459
sentry_init(integrations=[integration], traces_sample_rate=1.0)
454460

455461
assert "asyncio" in sentry_sdk.get_client().integrations
456462

463+
# Get the task factory after initial setup - it should be Sentry's
464+
loop = asyncio.get_running_loop()
465+
task_factory_before = loop.get_task_factory()
466+
assert getattr(task_factory_before, "_is_sentry_task_factory", False) is True
467+
457468
enable_asyncio_integration()
458469

459470
assert "asyncio" in sentry_sdk.get_client().integrations
460471

461-
# The new asyncio integration should not override the old one
462-
assert sentry_sdk.get_client().integrations["asyncio"] == integration
472+
# The task factory should be the same (loop not re-patched)
473+
task_factory_after = loop.get_task_factory()
474+
assert task_factory_before is task_factory_after
463475

464476

465477
@minimum_python_38

0 commit comments

Comments
 (0)