@@ -524,6 +524,7 @@ sys.monitoring.html#monitoring-event-RERAISE
524524 cdef public object active_instances # type: set[LineProfiler]
525525 cdef int _wrap_trace
526526 cdef int _set_frame_local_trace
527+ cdef int recursion_guard
527528
528529 def __init__ (
529530 self , tool_id: int , wrap_trace: bool , set_frame_local_trace: bool ):
@@ -548,6 +549,7 @@ sys.monitoring.html#monitoring-event-RERAISE
548549 self .active_instances = set ()
549550 self .wrap_trace = wrap_trace
550551 self .set_frame_local_trace = set_frame_local_trace
552+ self .recursion_guard = 0
551553
552554 @ cython.profile (False )
553555 def __call__ (self , frame: types.FrameType , event: str , arg ):
@@ -574,8 +576,11 @@ main/Python/sysmodule.c
574576 ' line' : PyTrace_LINE,
575577 ' return' : PyTrace_RETURN,
576578 ' opcode' : PyTrace_OPCODE}[event]
577- legacy_trace_callback(self , < PyFrameObject * > frame,
578- what, < PyObject * > arg)
579+ if not self .recursion_guard:
580+ # Prevent recursion (e.g. when `.wrap_trace` and
581+ # `.set_frame_local_trace` are both true)
582+ legacy_trace_callback(self , < PyFrameObject * > frame,
583+ what, < PyObject * > arg)
579584 # Set the C-level trace callback back to
580585 # `legacy_trace_callback()` where appropriate, so that future
581586 # calls can bypass this `.__call__()` method
@@ -632,7 +637,7 @@ main/Python/sysmodule.c
632637 Line-event (|LINE|_) callback passed to
633638 :py:func:`sys.monitoring.register_callback`.
634639
635- .. |LINE| replace:: :py:attr:`sys.monitoring.events.LINE`
640+ .. |LINE| replace:: :py:attr:`! sys.monitoring.events.LINE`
636641 .. _LINE: https://docs.python.org/3/library/\
637642sys.monitoring.html#monitoring-event-LINE
638643 """
@@ -647,7 +652,7 @@ sys.monitoring.html#monitoring-event-LINE
647652 :py:func:`sys.monitoring.register_callback`.
648653
649654 .. |PY_RETURN| replace:: \
650- :py:attr:`sys.monitoring.events.PY_RETURN`
655+ :py:attr:`! sys.monitoring.events.PY_RETURN`
651656 .. _PY_RETURN: https://docs.python.org/3/library/\
652657sys.monitoring.html#monitoring-event-PY_RETURN
653658 """
@@ -662,7 +667,7 @@ sys.monitoring.html#monitoring-event-PY_RETURN
662667 :py:func:`sys.monitoring.register_callback`.
663668
664669 .. |PY_YIELD| replace:: \
665- :py:attr:`sys.monitoring.events.PY_YIELD`
670+ :py:attr:`! sys.monitoring.events.PY_YIELD`
666671 .. _PY_YIELD: https://docs.python.org/3/library/\
667672sys.monitoring.html#monitoring-event-PY_YIELD
668673 """
@@ -676,8 +681,7 @@ sys.monitoring.html#monitoring-event-PY_YIELD
676681 Raise-event (|RAISE|_) callback passed to
677682 :py:func:`sys.monitoring.register_callback`.
678683
679- .. |RAISE| replace:: \
680- :py:attr:`sys.monitoring.events.RAISE`
684+ .. |RAISE| replace:: :py:attr:`!sys.monitoring.events.RAISE`
681685 .. _RAISE: https://docs.python.org/3/library/\
682686sys.monitoring.html#monitoring-event-RAISE
683687 """
@@ -691,8 +695,7 @@ sys.monitoring.html#monitoring-event-RAISE
691695 Re-raise-event (|RERAISE|_) callback passed to
692696 :py:func:`sys.monitoring.register_callback`.
693697
694- .. |RERAISE| replace:: \
695- :py:attr:`sys.monitoring.events.RERAISE`
698+ .. |RERAISE| replace:: :py:attr:`!sys.monitoring.events.RERAISE`
696699 .. _RERAISE: https://docs.python.org/3/library/\
697700sys.monitoring.html#monitoring-event-RERAISE
698701 """
@@ -1392,6 +1395,7 @@ pystate.h#L16
13921395 """
13931396 cdef _LineProfilerManager manager_ = < _LineProfilerManager> manager
13941397 cdef int result
1398+ cdef int recursion_guard = manager_.recursion_guard
13951399
13961400 if what == PyTrace_CALL:
13971401 # Any code using the `sys.gettrace()`-`sys.settrace()` paradigm
@@ -1424,9 +1428,17 @@ pystate.h#L16
14241428 # Call the trace callback that we're wrapping around where
14251429 # appropriate
14261430 if manager_._wrap_trace:
1427- result = call_callback(
1428- < PyObject * > disable_line_events, manager_.legacy_callback,
1429- py_frame, what, arg)
1431+ # Due to how the frame-local callback could be set to the active
1432+ # `_LineProfilerManager` or a wrapper object (see
1433+ # `set_local_trace()`), wrap the callback call to make sure that
1434+ # we don't recurse back here
1435+ manager_.recursion_guard = 1
1436+ try :
1437+ result = call_callback(
1438+ < PyObject * > disable_line_events, manager_.legacy_callback,
1439+ py_frame, what, arg)
1440+ finally :
1441+ manager_.recursion_guard = recursion_guard
14301442 else :
14311443 result = 0
14321444
0 commit comments