Skip to content

Commit ccaba49

Browse files
committed
prevent nested monitoring
1 parent 85f2245 commit ccaba49

File tree

1 file changed

+45
-20
lines changed

1 file changed

+45
-20
lines changed

reflex/utils/monitoring.py

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import contextlib
55
import functools
66
import inspect
7+
import threading
78
from collections.abc import AsyncGenerator, Awaitable, Callable, Generator
89
from typing import TypeVar, overload
910

@@ -20,6 +21,10 @@
2021
LeakAction = None # pyright: ignore[reportAssignmentType]
2122

2223

24+
# Thread-local storage to track if monitoring is already active
25+
_thread_local = threading.local()
26+
27+
2328
def is_pyleak_enabled() -> bool:
2429
"""Check if PyLeak monitoring is enabled and available.
2530
@@ -43,18 +48,28 @@ def monitor_sync():
4348
yield
4449
return
4550

51+
# Check if monitoring is already active in this thread
52+
if getattr(_thread_local, "monitoring_active", False):
53+
yield
54+
return
55+
4656
config = get_config()
4757
action = config.pyleak_action or LeakAction.WARN # pyright: ignore[reportOptionalMemberAccess]
4858

49-
with contextlib.ExitStack() as stack:
50-
# Thread leak detection has issues with background tasks (no_thread_leaks)
51-
stack.enter_context(
52-
no_event_loop_blocking( # pyright: ignore[reportOptionalCall]
53-
action=action,
54-
threshold=config.pyleak_blocking_threshold,
59+
# Mark monitoring as active
60+
_thread_local.monitoring_active = True
61+
try:
62+
with contextlib.ExitStack() as stack:
63+
# Thread leak detection has issues with background tasks (no_thread_leaks)
64+
stack.enter_context(
65+
no_event_loop_blocking( # pyright: ignore[reportOptionalCall]
66+
action=action,
67+
threshold=config.pyleak_blocking_threshold,
68+
)
5569
)
56-
)
57-
yield
70+
yield
71+
finally:
72+
_thread_local.monitoring_active = False
5873

5974

6075
@contextlib.asynccontextmanager
@@ -68,23 +83,33 @@ async def monitor_async():
6883
yield
6984
return
7085

86+
# Check if monitoring is already active in this thread
87+
if getattr(_thread_local, "monitoring_active", False):
88+
yield
89+
return
90+
7191
config = get_config()
7292
action = config.pyleak_action or LeakAction.WARN # pyright: ignore[reportOptionalMemberAccess]
7393

74-
async with contextlib.AsyncExitStack() as stack:
75-
# Thread leak detection has issues with background tasks (no_thread_leaks)
76-
# Re-add thread leak later.
77-
78-
# Block detection for event loops
79-
stack.enter_context(
80-
no_event_loop_blocking( # pyright: ignore[reportOptionalCall]
81-
action=action,
82-
threshold=config.pyleak_blocking_threshold,
94+
# Mark monitoring as active
95+
_thread_local.monitoring_active = True
96+
try:
97+
async with contextlib.AsyncExitStack() as stack:
98+
# Thread leak detection has issues with background tasks (no_thread_leaks)
99+
# Re-add thread leak later.
100+
101+
# Block detection for event loops
102+
stack.enter_context(
103+
no_event_loop_blocking( # pyright: ignore[reportOptionalCall]
104+
action=action,
105+
threshold=config.pyleak_blocking_threshold,
106+
)
83107
)
84-
)
85-
# Task leak detection has issues with background tasks (no_task_leaks)
108+
# Task leak detection has issues with background tasks (no_task_leaks)
86109

87-
yield
110+
yield
111+
finally:
112+
_thread_local.monitoring_active = False
88113

89114

90115
YieldType = TypeVar("YieldType")

0 commit comments

Comments
 (0)