Skip to content

Commit 623078e

Browse files
authored
Merge pull request #184 from Theelx/main
Fix #183
2 parents d5fbcfb + 70a5002 commit 623078e

File tree

5 files changed

+42
-3
lines changed

5 files changed

+42
-3
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
Changes
22
=======
3+
4.0.1
4+
~~~~~
5+
* FIX: Profiling classmethods works again. #183
36

47
4.0.0
58
~~~~~

kernprof.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
# NOTE: This version needs to be manually maintained with the line_profiler
1515
# __version__ for now.
16-
__version__ = '4.0.0'
16+
__version__ = '4.0.1'
1717

1818
# Guard the import of cProfile such that 3.x people
1919
# without lsprof can still use this script.

line_profiler/_line_profiler.pyx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ cdef class LineProfiler:
204204
"instead." % (func.__name__,)
205205
)
206206
try:
207+
if isinstance(func, classmethod):
208+
func = func.__func__
207209
code = func.__code__
208210
except AttributeError:
209211
import warnings

line_profiler/line_profiler.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
f'Has it been compiled? Underlying error is ex={ex!r}'
1717
)
1818

19-
__version__ = '4.0.0'
19+
__version__ = '4.0.1'
2020

2121

2222
def load_ipython_extension(ip):
@@ -39,6 +39,8 @@ def is_generator(f):
3939
isgen = (f.__code__.co_flags & CO_GENERATOR) != 0
4040
return isgen
4141

42+
def is_classmethod(f):
43+
return isinstance(f, classmethod)
4244

4345
class LineProfiler(CLineProfiler):
4446
""" A profiler that records the execution times of individual lines.
@@ -49,14 +51,30 @@ def __call__(self, func):
4951
it on function exit.
5052
"""
5153
self.add_function(func)
52-
if is_coroutine(func):
54+
if is_classmethod(func):
55+
wrapper = self.wrap_classmethod(func)
56+
elif is_coroutine(func):
5357
wrapper = self.wrap_coroutine(func)
5458
elif is_generator(func):
5559
wrapper = self.wrap_generator(func)
5660
else:
5761
wrapper = self.wrap_function(func)
5862
return wrapper
5963

64+
def wrap_classmethod(self, func):
65+
"""
66+
Wrap a classmethod to profile it.
67+
"""
68+
@functools.wraps(func)
69+
def wrapper(*args, **kwds):
70+
self.enable_by_count()
71+
try:
72+
result = func.__func__(func.__class__, *args, **kwds)
73+
finally:
74+
self.disable_by_count()
75+
return result
76+
return wrapper
77+
6078
def wrap_coroutine(self, func):
6179
"""
6280
Wrap a Python 3.5 coroutine to profile it.

tests/test_line_profiler.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ def g(x):
1212
y = yield x + 10
1313
yield y + 20
1414

15+
class C:
16+
@classmethod
17+
def c(self, value):
18+
print(value)
19+
return 0
20+
1521

1622
def test_init():
1723
lp = LineProfiler()
@@ -86,3 +92,13 @@ def test_gen_decorator():
8692
with pytest.raises(StopIteration):
8793
next(i)
8894
assert profile.enable_count == 0
95+
96+
def test_classmethod_decorator():
97+
profile = LineProfiler()
98+
c_wrapped = profile(C.c)
99+
assert c_wrapped.__name__ == 'c'
100+
assert profile.enable_count == 0
101+
val = c_wrapped('test')
102+
assert profile.enable_count == 0
103+
assert val == C.c('test')
104+
assert profile.enable_count == 0

0 commit comments

Comments
 (0)