Skip to content

Commit

Permalink
Patch for duplicate functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Erotemic committed Aug 16, 2023
1 parent 1b7c619 commit fe5a0b5
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 64 deletions.
37 changes: 21 additions & 16 deletions line_profiler/_line_profiler.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ cdef inline int64 compute_line_hash(uint64 block_hash, uint64 linenum):
return block_hash ^ linenum

def label(code):
""" Return a (filename, first_lineno, func_name) tuple for a given code
object.
"""
Return a (filename, first_lineno, func_name) tuple for a given code object.
This is the same labelling as used by the cProfile module in Python 2.5.
"""
Expand Down Expand Up @@ -142,17 +142,19 @@ cpdef _code_replace(func, co_code):

# Note: this is a regular Python class to allow easy pickling.
class LineStats(object):
""" Object to encapsulate line-profile statistics.
Attributes
----------
timings : dict
Mapping from (filename, first_lineno, function_name) of the profiled
function to a list of (lineno, nhits, total_time) tuples for each
profiled line. total_time is an integer in the native units of the
timer.
unit : float
The number of seconds per timer unit.
"""
Object to encapsulate line-profile statistics.
Attributes:
timings (dict):
Mapping from (filename, first_lineno, function_name) of the
profiled function to a list of (lineno, nhits, total_time) tuples
for each profiled line. total_time is an integer in the native
units of the timer.
unit (float):
The number of seconds per timer unit.
"""
def __init__(self, timings, unit):
self.timings = timings
Expand Down Expand Up @@ -225,7 +227,8 @@ cdef class LineProfiler:
if code.co_code in self.dupes_map:
self.dupes_map[code.co_code] += [code]
# code hash already exists, so there must be a duplicate function. add no-op
co_code = code.co_code + (9).to_bytes(1, byteorder=byteorder) * (len(self.dupes_map[code.co_code]))
# co_code = code.co_code + (9).to_bytes(1, byteorder=byteorder) * (len(self.dupes_map[code.co_code]))
co_code = code.co_code + (9).to_bytes(1, byteorder=byteorder) * (len(self.dupes_map[code.co_code]) * 2)
CodeType = type(code)
code = _code_replace(func, co_code=co_code)
try:
Expand Down Expand Up @@ -333,7 +336,8 @@ cdef class LineProfiler:
unset_trace()

def get_stats(self):
""" Return a LineStats object containing the timings.
"""
Return a LineStats object containing the timings.
"""
cdef dict cmap

Expand Down Expand Up @@ -373,7 +377,8 @@ cdef class LineProfiler:
@cython.wraparound(False)
cdef int python_trace_callback(object self_, PyFrameObject *py_frame, int what,
PyObject *arg):
""" The PyEval_SetTrace() callback.
"""
The PyEval_SetTrace() callback.
"""
cdef LineProfiler self
cdef object code
Expand Down
57 changes: 53 additions & 4 deletions tests/test_autoprofile.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,55 @@ def func4(a):
temp_dpath.delete()


def test_duplicate_function_autoprofile():
"""
Test that every function in a file is profiled when autoprofile is enabled.
"""
temp_dpath = ub.Path(tempfile.mkdtemp())

code = ub.codeblock(
'''
def func1(a):
return a + 1
def func2(a):
return a + 1
def func3(a):
return a + 1
def func4(a):
return a + 1
func1(1)
func2(1)
func3(1)
''')
with ub.ChDir(temp_dpath):

script_fpath = ub.Path('script.py')
script_fpath.write_text(code)

args = [sys.executable, '-m', 'kernprof', '-p', 'script.py', '-l', os.fspath(script_fpath)]
proc = ub.cmd(args)
print(proc.stdout)
print(proc.stderr)
proc.check_returncode()

args = [sys.executable, '-m', 'line_profiler', os.fspath(script_fpath) + '.lprof']
proc = ub.cmd(args)
raw_output = proc.stdout
print(raw_output)
proc.check_returncode()

assert 'Function: func1' in raw_output
assert 'Function: func2' in raw_output
assert 'Function: func3' in raw_output
assert 'Function: func4' in raw_output

temp_dpath.delete()


def _write_demo_module(temp_dpath):
"""
Make a dummy test module structure
Expand Down Expand Up @@ -258,10 +307,10 @@ def test_autoprofile_script_with_prof_imports():
temp_dpath = ub.Path(tempfile.mkdtemp())
script_fpath = _write_demo_module(temp_dpath)

import sys
if sys.version_info[0:2] >= (3, 11):
import pytest
pytest.skip('Failing due to the noop bug')
# import sys
# if sys.version_info[0:2] >= (3, 11):
# import pytest
# pytest.skip('Failing due to the noop bug')

args = [sys.executable, '-m', 'kernprof', '--prof-imports', '-p', 'script.py', '-l', os.fspath(script_fpath)]
proc = ub.cmd(args, cwd=temp_dpath, verbose=2)
Expand Down
20 changes: 20 additions & 0 deletions tests/test_duplicate_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
def test_duplicate_function():
"""
Test from https://github.com/pyutils/line_profiler/issues/232
"""
import line_profiler

class C:
def f1(self):
pass

def f2(self):
pass

def f3(self):
pass

profile = line_profiler.LineProfiler()
profile(C.f1)
profile(C.f2)
profile(C.f3)
Loading

0 comments on commit fe5a0b5

Please sign in to comment.