Skip to content

Commit 63be16e

Browse files
committed
Fixes and tests
line_profiler/_line_profiler.pyx _ThreadState - Renamed from `ThreadState` - Made `._wrap_trace` unaccessible on the Python level LineProfiler wrap_trace.__set__() Now syncing between `_ThreadState`s in `._all_thread_states` _thread_state.__get__() If another thread is already initialized, get the `wrap_trace` therefrom tests/test_sys_trace.py New test module (mostly cherry-picked from before we merged pyutils#347): - `test_callback_preservation()` - `test_callback_wrapping()` - `test_wrapping_throwing_callback()` - `test_wrapping_line_event_disabling_callback()` - `test_wrapping_thread_local_callbacks()`
1 parent c0a0fbc commit 63be16e

2 files changed

Lines changed: 546 additions & 15 deletions

File tree

line_profiler/_line_profiler.pyx

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ list[tuple[int, int, int]]]):
301301
self.unit = unit
302302

303303

304-
cdef class ThreadState:
304+
cdef class _ThreadState:
305305
"""
306306
Helper object for holding the thread-local state; documentations are
307307
for reference only, and all APIs are to be considered private and
@@ -310,7 +310,7 @@ cdef class ThreadState:
310310
cdef TraceCallback *legacy_callback
311311
cdef dict mon_callbacks # type: dict[int, Callable | None]
312312
cdef public object active_instances # type: set[LineProfiler]
313-
cdef public int _wrap_trace
313+
cdef int _wrap_trace
314314

315315
def __init__(self, instances=(), wrap_trace=False):
316316
self.active_instances = set(instances)
@@ -534,7 +534,7 @@ cdef class LineProfiler:
534534
cdef public object threaddata
535535

536536
# These are shared between instances and threads
537-
# type: dict[int, set[LineProfiler]], int = thread id
537+
# type: dict[int, _ThreadState], int = thread id
538538
_all_thread_states = {}
539539
# type: dict[bytes, int], bytes = bytecode
540540
_all_paddings = {}
@@ -694,12 +694,17 @@ cdef class LineProfiler:
694694
self.threaddata.enable_count = value
695695

696696
# These two are shared between instances, but thread-local
697+
# (Ideally speaking they could've been class attributes...)
697698

698699
property wrap_trace:
699700
def __get__(self):
700701
return self._thread_state.wrap_trace
701702
def __set__(self, wrap_trace):
702-
self._thread_state.wrap_trace = wrap_trace
703+
# Make sure we have a thread state
704+
state = self._thread_state
705+
# Sync values between all thread states
706+
for state in self._all_thread_states.values():
707+
state.wrap_trace = wrap_trace
703708

704709
property _thread_state:
705710
def __get__(self):
@@ -708,16 +713,23 @@ cdef class LineProfiler:
708713
return self._all_thread_states[thread_id]
709714
except KeyError:
710715
pass
711-
712-
# First instance, load default `wrap_trace` value from the
713-
# environment
714-
# (TODO: migrate to `line_profiler.cli_utils.boolean()`
715-
# after merging #335)
716-
from os import environ
717-
718-
env = environ.get('LINE_PROFILE_WRAP_TRACE', '').lower()
719-
wrap_trace = env not in {'', '0', 'off', 'false', 'no'}
720-
self._all_thread_states[thread_id] = state = ThreadState(
716+
# First profiler instance on the thread, get the correct
717+
# `wrap_trace` value and set up a `_ThreadState`
718+
try:
719+
state, *_ = self._all_thread_states.values()
720+
except ValueError:
721+
# First thread in the interpretor: load default
722+
# `wrap_trace` value from the environment
723+
# (TODO: migrate to `line_profiler.cli_utils.boolean()`
724+
# after merging #335)
725+
from os import environ
726+
727+
env = environ.get('LINE_PROFILE_WRAP_TRACE', '').lower()
728+
wrap_trace = env not in {'', '0', 'off', 'false', 'no'}
729+
else:
730+
# Fetch the `.wrap_trace` value from an existing state
731+
wrap_trace = state.wrap_trace
732+
self._all_thread_states[thread_id] = state = _ThreadState(
721733
wrap_trace=wrap_trace)
722734
return state
723735

@@ -909,7 +921,7 @@ cdef extern int legacy_trace_callback(
909921
https://github.com/python/cpython/blob/de2a4036/Include/cpython/\
910922
pystate.h#L16
911923
"""
912-
cdef ThreadState state_ = <ThreadState>state
924+
cdef _ThreadState state_ = <_ThreadState>state
913925
if what == PyTrace_LINE or what == PyTrace_RETURN:
914926
# Normally we'd need to DECREF the return from
915927
# `get_frame_code()`, but Cython does that for us

0 commit comments

Comments
 (0)