Skip to content

Commit d43a6fc

Browse files
committed
Fixed event-set updating
line_profiler/_line_profiler.pyx _patch_events() New helper function (w/doctest) for patching `_SysMonitoringState.events` _SysMonitoringState.call_callback() Fixed bug where LINE, PY_RETURN, and PY_YIELD events become enabled after `.deregister()`-ing, regardless of whether they are enabled when `.register()`-ing tests/test_sys_monitoring.py SysMonHelper.get_current_callback() New helper method disable_line_events() Fixed bug where local line events are enabled instead of disabled test_standalone_callback_usage() test_wrapping_trace() Added checks for the restoration of the callback after profiling test_callback_switching() New test combining `test_standalone_callback_switching()` and `test_wrapping_switching_callback()` test_callback_update_global_events() New test for callbacks which call `sys.monitoring.set_events()` test_callback_toggle_local_events() New test for callbacks which return `sys.monitoring.DISABLE` and call `sys.monitoring.restart_events()`
1 parent 52c5603 commit d43a6fc

File tree

2 files changed

+284
-22
lines changed

2 files changed

+284
-22
lines changed

line_profiler/_line_profiler.pyx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,24 @@ cpdef _code_replace(func, co_code):
258258
return code
259259

260260

261+
cpdef int _patch_events(int events, int before, int after):
262+
"""
263+
Patch ``events`` based on the differences between ``before`` and
264+
``after``.
265+
266+
Example:
267+
>>> events = 0b110000
268+
>>> before = 0b101101
269+
>>> after = 0b_001011 # Additions: 0b10, deletions: 0b100100
270+
>>> assert _patch_events(events, before, after) == 0b010010
271+
"""
272+
cdef int all_set_bits, plus, minus
273+
all_set_bits = before | after
274+
plus = all_set_bits - before
275+
minus = all_set_bits - after
276+
return ((events | minus) - minus) | plus
277+
278+
261279
# Note: this is a regular Python class to allow easy pickling.
262280
class LineStats(object):
263281
"""
@@ -388,11 +406,10 @@ cdef class _SysMonitoringState:
388406
cdef PyObject *result
389407
cdef object callback # type: Callable | None
390408
cdef object callback_after # type: Callable | None
391-
cdef object callback_wrapped # type: Callable | None
392409
cdef object code_location # type: tuple[code, Unpack[tuple]]
393410
cdef object arg_tuple # type: tuple[code, Unpack[tuple]]
394411
cdef object disabled # type: set[tuple[code, Unpack[tuple]]]
395-
cdef int ev_id
412+
cdef int ev_id, events_before
396413
cdef Py_uintptr_t version = monitoring_restart_version()
397414
cdef dict callbacks_before = {}
398415

@@ -402,8 +419,8 @@ cdef class _SysMonitoringState:
402419
self.disabled.clear()
403420

404421
# Call the wrapped callback where suitable
405-
callback_wrapped = self.callbacks.get(event_id)
406-
if callback_wrapped is None: # No cached callback
422+
callback = self.callbacks.get(event_id)
423+
if callback is None: # No cached callback
407424
return
408425
code_location = (code,) + loc_args
409426
disabled = self.disabled.setdefault(event_id, set())
@@ -418,8 +435,9 @@ cdef class _SysMonitoringState:
418435

419436
arg_tuple = code_location + other_args
420437
try:
438+
events_before = mon.get_events(self.tool_id)
421439
result = PyObject_Call(
422-
<PyObject *>callback_wrapped, <PyObject *>arg_tuple, NULL)
440+
<PyObject *>callback, <PyObject *>arg_tuple, NULL)
423441
else:
424442
# Since we can't actually disable the event (or line
425443
# profiling will be interrupted), just mark the location so
@@ -428,9 +446,11 @@ cdef class _SysMonitoringState:
428446
if result == <PyObject *>(mon.DISABLE):
429447
disabled.add(code_location)
430448
finally:
431-
self.events = mon.get_events(self.tool_id)
432-
register = mon.register_callback
449+
# Update the events
450+
self.events = _patch_events(
451+
self.events, events_before, mon.get_events(self.tool_id))
433452
# If the wrapped callback has changed:
453+
register = mon.register_callback
434454
for ev_id, callback in callbacks_before.items():
435455
# - Restore the `sys.monitoring` callback
436456
callback_after = register(self.tool_id, ev_id, callback)

0 commit comments

Comments
 (0)