Skip to content

Commit 3452ff6

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 #347): - `test_callback_preservation()` - `test_callback_wrapping()` - `test_wrapping_throwing_callback()` - `test_wrapping_line_event_disabling_callback()` - `test_wrapping_thread_local_callbacks()`
1 parent 102f02b commit 3452ff6

2 files changed

Lines changed: 547 additions & 16 deletions

File tree

line_profiler/_line_profiler.pyx

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,15 @@ list[tuple[int, int, int]]]):
235235
self.unit = unit
236236

237237

238-
cdef class ThreadState:
238+
cdef class _ThreadState:
239239
"""
240240
Helper object for holding the thread-local state; documentations are
241241
for reference only, and all APIs are to be considered private and
242242
subject to change.
243243
"""
244244
cdef TraceCallback *callback
245245
cdef public object active_instances # type: set[LineProfiler]
246-
cdef public int _wrap_trace
246+
cdef int _wrap_trace
247247

248248
def __init__(self, instances=(), wrap_trace=False):
249249
self.active_instances = set(instances)
@@ -382,7 +382,7 @@ cdef class LineProfiler:
382382
cdef public object threaddata
383383

384384
# This is shared between instances and threads
385-
_all_thread_states = {} # type: dict[int, ThreadState]
385+
_all_thread_states = {} # type: dict[int, _ThreadState]
386386

387387
def __init__(self, *functions, wrap_trace=None):
388388
self.functions = []
@@ -469,12 +469,17 @@ cdef class LineProfiler:
469469
self.threaddata.enable_count = value
470470

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

473474
property wrap_trace:
474475
def __get__(self):
475476
return self._thread_state.wrap_trace
476477
def __set__(self, wrap_trace):
477-
self._thread_state.wrap_trace = wrap_trace
478+
# Make sure we have a thread state
479+
state = self._thread_state
480+
# Sync values between all thread states
481+
for state in self._all_thread_states.values():
482+
state.wrap_trace = wrap_trace
478483

479484
property _thread_state:
480485
def __get__(self):
@@ -483,16 +488,23 @@ cdef class LineProfiler:
483488
return self._all_thread_states[thread_id]
484489
except KeyError:
485490
pass
486-
487-
# First instance, load default `wrap_trace` value from the
488-
# environment
489-
# (TODO: migrate to `line_profiler.cli_utils.boolean()`
490-
# after merging #335)
491-
from os import environ
492-
493-
env = environ.get('LINE_PROFILE_WRAP_TRACE', '').lower()
494-
wrap_trace = env not in {'', '0', 'off', 'false', 'no'}
495-
self._all_thread_states[thread_id] = state = ThreadState(
491+
# First profiler instance on the thread, get the correct
492+
# `wrap_trace` value and set up a `_ThreadState`
493+
try:
494+
state, *_ = self._all_thread_states.values()
495+
except ValueError:
496+
# First thread in the interpretor: load default
497+
# `wrap_trace` value from the environment
498+
# (TODO: migrate to `line_profiler.cli_utils.boolean()`
499+
# after merging #335)
500+
from os import environ
501+
502+
env = environ.get('LINE_PROFILE_WRAP_TRACE', '').lower()
503+
wrap_trace = env not in {'', '0', 'off', 'false', 'no'}
504+
else:
505+
# Fetch the `.wrap_trace` value from an existing state
506+
wrap_trace = state.wrap_trace
507+
self._all_thread_states[thread_id] = state = _ThreadState(
496508
wrap_trace=wrap_trace)
497509
return state
498510

@@ -633,7 +645,7 @@ cdef extern int python_trace_callback(object state_,
633645
https://github.com/python/cpython/blob/de2a4036/Include/cpython/\
634646
pystate.h#L16
635647
"""
636-
cdef ThreadState state
648+
cdef _ThreadState state
637649
cdef object prof_
638650
cdef LineProfiler prof
639651
cdef LastTime old
@@ -645,7 +657,7 @@ pystate.h#L16
645657
cdef unordered_map[int64, LineTime] line_entries
646658
cdef uint64 linenum
647659

648-
state = <ThreadState>state_
660+
state = <_ThreadState>state_
649661

650662
if what == PyTrace_LINE or what == PyTrace_RETURN:
651663
# Normally we'd need to DECREF the return from get_frame_code,

0 commit comments

Comments
 (0)