Permalink
Browse files

Get support for new 3.5 Python coroutines

This commit implement the proper stuff to decorate
the new coroutine style implemented by Python3.5 and their new
statments, making the canonical `profile` decorator compatible.

To hide incompatible 3.5 statements between versions. The idea is be able
to run all Python versions, since 2.X to 3.5 without break the current behaviour.
The Python 3.5 specific code is hidden into private files that will be imported only in
3.5 environments.
  • Loading branch information...
pfreixes committed Oct 4, 2016
1 parent 1960340 commit bd02febfa0a811802b652d6f22c7ecac6ed2ff32
Showing with 65 additions and 1 deletion.
  1. +16 −1 line_profiler.py
  2. +15 −0 line_profiler_py35.py
  3. +23 −0 tests/_test_kernprof_py35.py
  4. +11 −0 tests/test_kernprof.py
View
@@ -22,6 +22,7 @@
# Python 2/3 compatibility utils
# ===========================================================
PY3 = sys.version_info[0] == 3
PY35 = PY3 and sys.version_info[1] >= 5
# exec (from https://bitbucket.org/gutworth/six/):
if PY3:
@@ -41,6 +42,14 @@ def exec_(_code_, _globs_=None, _locs_=None):
_locs_ = _globs_
exec("""exec _code_ in _globs_, _locs_""")
if PY35:
import inspect
def is_coroutine(f):
return inspect.iscoroutinefunction(f)
else:
def is_coroutine(f):
return False
# ============================================================
CO_GENERATOR = 0x0020
@@ -60,7 +69,9 @@ def __call__(self, func):
it on function exit.
"""
self.add_function(func)
if is_generator(func):
if is_coroutine(func):
wrapper = self.wrap_coroutine(func)
elif is_generator(func):
wrapper = self.wrap_generator(func)
else:
wrapper = self.wrap_function(func)
@@ -102,6 +113,10 @@ def wrapper(*args, **kwds):
return result
return wrapper
if PY35:
import line_profiler_py35
wrap_coroutine = line_profiler_py35.wrap_coroutine
def dump_stats(self, filename):
""" Dump a representation of the data to a file as a pickled LineStats
object from `get_stats()`.
View
@@ -0,0 +1,15 @@
""" This file is only imported in python 3.5 environments """
def wrap_coroutine(self, func):
"""
Wrap a Python 3.5 coroutine to profile it.
"""
@functools.wraps(func)
async def wrapper(*args, **kwds):
self.enable_by_count()
try:
result = await func(*args, **kwds)
finally:
self.disable_by_count()
return result
return wrapper
@@ -0,0 +1,23 @@
from kernprof import ContextualProfile
def test_coroutine_decorator(self):
async def _():
async def c(x):
""" A coroutine. """
y = x + 10
return y
profile = ContextualProfile()
c_wrapped = profile(c)
self.assertEqual(c_wrapped.__name__, c.__name__)
self.assertEqual(c_wrapped.__doc__, c.__doc__)
self.assertEqual(profile.enable_count, 0)
value = await c_wrapped(10)
self.assertEqual(profile.enable_count, 0)
self.assertEqual(value, await c(10))
import asyncio
loop = asyncio.get_event_loop()
loop.run_until_complete(_())
loop.close()
View
@@ -1,7 +1,11 @@
import unittest
import sys
from kernprof import ContextualProfile
PY3 = sys.version_info[0] == 3
PY35 = PY3 and sys.version_info[1] >= 5
def f(x):
""" A function. """
@@ -72,3 +76,10 @@ def test_gen_decorator(self):
with self.assertRaises(StopIteration):
next(i)
self.assertEqual(profile.enable_count, 0)
if PY35:
import _test_kernprof_py35
test_coroutine_decorator = _test_kernprof_py35.test_coroutine_decorator
if __name__ == '__main__':
unittest.main()

0 comments on commit bd02feb

Please sign in to comment.