Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions line_profiler/_line_profiler.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,19 @@ cdef class LineProfiler:

@property
def c_last_time(self):
return (<dict>self._c_last_time)[threading.get_ident()]
"""
Raises:
KeyError
If no profiling data is available on the current thread.
"""
try:
return (<dict>self._c_last_time)[threading.get_ident()]
except KeyError as e:
# We haven't actually profiled anything yet
raise (KeyError('No profiling data on the current thread '
'(`threading.get_ident()` = '
f'{threading.get_ident()})')
.with_traceback(e.__traceback__)) from None

@property
def code_map(self):
Expand Down Expand Up @@ -500,13 +512,12 @@ cdef class LineProfiler:
line_profiler 4.0 no longer directly maintains last_time, but this will
construct something similar for backwards compatibility.
"""
c_last_time = (<dict>self._c_last_time)[threading.get_ident()]
code_hash_map = self.code_hash_map
c_last_time = self.c_last_time
py_last_time = {}
for code, code_hashes in code_hash_map.items():
for code_hash in code_hashes:
if code_hash in c_last_time:
py_last_time[code] = c_last_time[code_hash]
for code in self.code_hash_map:
block_hash = hash(code.co_code)
if block_hash in c_last_time:
py_last_time[code] = c_last_time[block_hash]
return py_last_time


Expand Down
33 changes: 33 additions & 0 deletions tests/test_line_profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,39 @@ def test_init():
}


def test_last_time():
"""
Test that `LineProfiler.c_last_time` and `LineProfiler.last_time`
are consistent.
"""
prof = LineProfiler()
with pytest.raises(KeyError, match='[Nn]o profiling data'):
prof.c_last_time

def get_last_time(prof, *, c=False):
try:
return getattr(prof, 'c_last_time' if c else 'last_time')
except KeyError:
return {}

@prof
def func():
return (get_last_time(prof, c=True).copy(),
get_last_time(prof).copy())

# These are always empty outside a profiling context
# (hence the need of the above function to capture the transient
# values)
assert not get_last_time(prof, c=True)
assert not get_last_time(prof)
# Inside `func()`, both should get an entry therefor
clt, lt = func()
assert not get_last_time(prof, c=True)
assert not get_last_time(prof)
assert set(clt) == {hash(func.__wrapped__.__code__.co_code)}
assert set(lt) == {func.__wrapped__.__code__}


def test_enable_disable():
lp = LineProfiler()
assert lp.enable_count == 0
Expand Down
Loading