Skip to content

Commit

Permalink
Replace .f_lineno with PyFrame_GetLineNumber to support 3.12
Browse files Browse the repository at this point in the history
  • Loading branch information
Erotemic committed Nov 2, 2023
1 parent aafba02 commit 3a163af
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 2 deletions.
11 changes: 9 additions & 2 deletions line_profiler/_line_profiler.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ cdef extern from "Python.h":
cdef int PyTrace_C_EXCEPTION
cdef int PyTrace_C_RETURN

cdef int PyFrame_GetLineNumber(PyFrameObject *frame)


cdef extern from "timers.c":
PY_LONG_LONG hpTimer()
double hpTimerUnit()
Expand Down Expand Up @@ -419,13 +422,17 @@ cdef extern int python_trace_callback(object self_, PyFrameObject *py_frame,
cdef int64 code_hash
cdef int64 block_hash
cdef unordered_map[int64, LineTime] line_entries
cdef uint64 linenum

self = <LineProfiler>self_

if what == PyTrace_LINE or what == PyTrace_RETURN:
# Normally we'd need to DECREF the return from get_frame_code, but Cython does that for us
block_hash = hash(get_frame_code(py_frame))
code_hash = compute_line_hash(block_hash, py_frame.f_lineno)

linenum = PyFrame_GetLineNumber(py_frame)
code_hash = compute_line_hash(block_hash, linenum)

if self._c_code_map.count(code_hash):
time = hpTimer()
ident = threading.get_ident()
Expand All @@ -440,7 +447,7 @@ cdef extern int python_trace_callback(object self_, PyFrameObject *py_frame,
if what == PyTrace_LINE:
# Get the time again. This way, we don't record much time wasted
# in this function.
self._c_last_time[ident][block_hash] = LastTime(py_frame.f_lineno, hpTimer())
self._c_last_time[ident][block_hash] = LastTime(linenum, hpTimer())
elif self._c_last_time[ident].count(block_hash):
# We are returning from a function, not executing a line. Delete
# the last_time record. It may have already been deleted if we
Expand Down
45 changes: 45 additions & 0 deletions tests/test_explicit_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@
import ubelt as ub


def test_simple_explicit_nonglobal_usage():
"""
python -c "from test_explicit_profile import *; test_simple_explicit_nonglobal_usage()"
"""
from line_profiler import LineProfiler
profiler = LineProfiler()

def func(a):
return a + 1

profiled_func = profiler(func)

# Run Once
profiled_func(1)

lstats = profiler.get_stats()
print(f'lstats.timings={lstats.timings}')
print(f'lstats.unit={lstats.unit}')
print(f'profiler.code_hash_map={profiler.code_hash_map}')
profiler.print_stats()


def _demo_explicit_profile_script():
return ub.codeblock(
'''
Expand Down Expand Up @@ -140,23 +162,40 @@ def test_explicit_profile_with_in_code_enable():
"""
Test that the user can enable the profiler explicitly from within their
code.
CommandLine:
pytest tests/test_explicit_profile.py -s -k test_explicit_profile_with_in_code_enable
"""
temp_dpath = ub.Path(tempfile.mkdtemp())

code = ub.codeblock(
'''
from line_profiler import profile
import ubelt as ub
print('')
print('')
print('start test')
print('profile = {}'.format(ub.urepr(profile, nl=1)))
print(f'profile._profile={profile._profile}')
print(f'profile.enabled={profile.enabled}')
@profile
def func1(a):
return a + 1
profile.enable(output_prefix='custom_output')
print('profile = {}'.format(ub.urepr(profile, nl=1)))
print(f'profile._profile={profile._profile}')
print(f'profile.enabled={profile.enabled}')
@profile
def func2(a):
return a + 1
print('func2 = {}'.format(ub.urepr(func2, nl=1)))
profile.disable()
@profile
Expand All @@ -173,6 +212,8 @@ def func4(a):
func2(1)
func3(1)
func4(1)
profile._profile
''')
with ub.ChDir(temp_dpath):

Expand Down Expand Up @@ -259,3 +300,7 @@ def func4(a):
assert output_fpath.exists()
assert (temp_dpath / 'profile_output.lprof').exists()
temp_dpath.delete()

if __name__ == '__main__':
...
test_simple_explicit_nonglobal_usage()

0 comments on commit 3a163af

Please sign in to comment.