Skip to content

Commit d62c880

Browse files
authored
Merge pull request #345 from Erotemic/use-qualname
Use co_qualname in reporting when possible
2 parents ad949aa + a2e46f9 commit d62c880

3 files changed

Lines changed: 22 additions & 11 deletions

File tree

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Changes
1212
* FIX: Fixed namespace bug when running ``kernprof -m`` on certain modules (e.g. ``calendar`` on Python 3.12+).
1313
* FIX: Fixed ``@contextlib.contextmanager`` bug where the cleanup code (e.g. restoration of ``sys`` attributes) is not run if exceptions occurred inside the context
1414
* ENH: Added CLI arguments ``-c`` to ``kernprof`` for (auto-)profiling module/package/inline-script execution instead of that of script files; passing ``'-'`` as the script-file name now also reads from and profiles ``stdin``
15+
* ENH: In Python >=3.11, profiled objects are reported using their qualified name.
1516
* ENH: Highlight final summary using rich if enabled
1617

1718
4.2.0

line_profiler/_line_profiler.pyx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ from libc.stdint cimport int64_t
2424
from libcpp.unordered_map cimport unordered_map
2525
import threading
2626
import opcode
27+
import types
2728

2829
NOP_VALUE: int = opcode.opmap['NOP']
2930

@@ -32,6 +33,9 @@ NOP_VALUE: int = opcode.opmap['NOP']
3233
# if sys.version_info[0:2] >= (3, 11):
3334
NOP_BYTES: bytes = NOP_VALUE.to_bytes(2, byteorder=byteorder)
3435

36+
# This should be true for Python >=3.11a1
37+
HAS_CO_QUALNAME: bool = hasattr(types.CodeType, 'co_qualname')
38+
3539
# long long int is at least 64 bytes assuming c99
3640
ctypedef unsigned long long int uint64
3741
ctypedef long long int int64
@@ -155,14 +159,21 @@ else:
155159

156160
def label(code):
157161
"""
158-
Return a (filename, first_lineno, func_name) tuple for a given code object.
162+
Return a (filename, first_lineno, _name) tuple for a given code object.
163+
164+
This is the similar labelling as used by the cProfile module in Python 2.5.
159165
160-
This is the same labelling as used by the cProfile module in Python 2.5.
166+
Note:
167+
In Python >=3.11 we use we return qualname for _name.
168+
In older versions of Python we just return name.
161169
"""
162170
if isinstance(code, str):
163171
return ('~', 0, code) # built-in functions ('~' sorts at the end)
164172
else:
165-
return (code.co_filename, code.co_firstlineno, code.co_name)
173+
if HAS_CO_QUALNAME:
174+
return (code.co_filename, code.co_firstlineno, code.co_qualname)
175+
else:
176+
return (code.co_filename, code.co_firstlineno, code.co_name)
166177

167178

168179
cpdef _code_replace(func, co_code):

tests/test_line_profiler.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def foo(cls) -> str:
257257
output = strip(sio.getvalue())
258258
print(output)
259259
# Check that we have profiled `Object.foo()`
260-
assert output.endswith('- foo')
260+
assert output.endswith('foo')
261261
line, = (line for line in output.splitlines() if line.endswith('* 2'))
262262
# Check that it has been run twice
263263
assert int(line.split()[1]) == 2
@@ -291,7 +291,7 @@ def foo(x: int) -> int:
291291
output = strip(sio.getvalue())
292292
print(output)
293293
# Check that we have profiled `Object.foo()`
294-
assert output.endswith('- foo')
294+
assert output.endswith('foo')
295295
line, = (line for line in output.splitlines() if line.endswith('* 2'))
296296
# Check that it has been run twice
297297
assert int(line.split()[1]) == 2
@@ -332,7 +332,7 @@ def foo(self, x: int) -> int:
332332
output = strip(sio.getvalue())
333333
print(output)
334334
# Check that we have profiled `Object.foo()`
335-
assert output.endswith('- foo')
335+
assert output.endswith('foo')
336336
line, = (line for line in output.splitlines() if line.endswith('* x'))
337337
# Check that the wrapped methods has been run twice in total
338338
assert int(line.split()[1]) == 2
@@ -368,7 +368,7 @@ def foo(self, x: int) -> int:
368368
output = strip(sio.getvalue())
369369
print(output)
370370
# Check that we have profiled `Object.foo()` (via `.bar()`)
371-
assert output.endswith('- foo')
371+
assert output.endswith('foo')
372372
line, = (line for line in output.splitlines() if line.endswith('* x'))
373373
# Check that the wrapped method has been run once
374374
assert int(line.split()[1]) == 1
@@ -408,7 +408,7 @@ def foo(x: int, y: int) -> int:
408408
output = strip(sio.getvalue())
409409
print(output)
410410
# Check that we have profiled `foo()`
411-
assert output.endswith('- foo')
411+
assert output.endswith('foo')
412412
line, = (line for line in output.splitlines() if line.endswith('x + y'))
413413
# Check that the wrapped partials has been run twice in total
414414
assert int(line.split()[1]) == 2
@@ -457,7 +457,7 @@ def foo(self, foo) -> None:
457457
output = strip(sio.getvalue())
458458
print(output)
459459
# Check that we have profiled `Object.foo`
460-
assert output.endswith('- foo')
460+
assert output.endswith('foo')
461461
getter_line, = (line for line in output.splitlines()
462462
if line.endswith('* 2'))
463463
setter_line, = (line for line in output.splitlines()
@@ -498,9 +498,8 @@ def foo(self) -> int:
498498
with io.StringIO() as sio:
499499
profile.print_stats(stream=sio, summarize=True)
500500
output = strip(sio.getvalue())
501-
print(output)
502501
# Check that we have profiled `Object.foo`
503-
assert output.endswith('- foo')
502+
assert output.endswith('foo')
504503
line, = (line for line in output.splitlines() if line.endswith('* 2'))
505504
# Check that the getter has been run once
506505
assert int(line.split()[1]) == 1

0 commit comments

Comments
 (0)