From bf5cb748ffc3d228593a388b2286e35529f059d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=BCmer=20Cip?= Date: Thu, 18 Aug 2022 15:23:47 +0300 Subject: [PATCH 09/11] Use codeobject->co_varnames for Py versions < 11 --- run_tests.py | 4 +-- setup.py | 9 +++-- tests/test_gevent.py | 71 ++++++++++++++++++++++++-------------- yappi/_yappi.c | 9 +++-- 5 files changed, 62 insertions(+), 33 deletions(-) diff --git a/run_tests.py b/run_tests.py index e632cbf..df36f74 100644 --- a/run_tests.py +++ b/run_tests.py @@ -19,9 +19,9 @@ if __name__ == '__main__': 'test_functionality', 'test_hooks', 'test_tags', + 'test_gevent', ] - if sys.version_info < (3, 10): - tests += ['test_gevent'] + # TODO: make these auto-skip if cannot be imported if sys.version_info >= (3, 4): tests += ['test_asyncio'] if sys.version_info >= (3, 7): diff --git a/setup.py b/setup.py index 5e4fe66..f241810 100644 --- a/setup.py +++ b/setup.py @@ -62,6 +62,11 @@ CLASSIFIERS = [ 'Topic :: Software Development :: Libraries :: Python Modules', ] +test_deps = [] +if sys.version_info <= (3, 10): + # TODO: fix this when gevent supports 3.11 + test_deps += ['gevent>=20.6.2'] + setup( name=NAME, version=VERSION, @@ -91,11 +96,11 @@ setup( description="Yet Another Python Profiler", long_description=long_description, long_description_content_type='text/markdown', - keywords="python thread multithread profiler", + keywords="python thread multithread asyncio gevent profiler", classifiers=CLASSIFIERS, license="MIT", url=HOMEPAGE, extras_require={ - 'test': ['gevent>=20.6.2'], + 'test': test_deps, } ) diff --git a/tests/test_gevent.py b/tests/test_gevent.py index 8569712..0b105a2 100644 --- a/tests/test_gevent.py +++ b/tests/test_gevent.py @@ -1,15 +1,21 @@ import unittest import _yappi import yappi -import gevent -from gevent.event import Event import threading from utils import ( - YappiUnitTestCase, find_stat_by_name, burn_cpu, burn_io, - burn_io_gevent + YappiUnitTestCase, find_stat_by_name, burn_cpu, burn_io, burn_io_gevent ) +GEVENT_AVAIL = True +try: + import gevent + from gevent.event import Event +except ImportError: + GEVENT_AVAIL = False + + class GeventTestThread(threading.Thread): + def __init__(self, name, *args, **kwargs): super(GeventTestThread, self).__init__(*args, **kwargs) self.name = name @@ -19,6 +25,10 @@ class GeventTestThread(threading.Thread): gevent.get_hub().name = "Hub" super(GeventTestThread, self).run() + +@unittest.skipIf( + not GEVENT_AVAIL, "Gevent is not installed on the target system" +) class GeventTest(YappiUnitTestCase): def setUp(self): @@ -51,6 +61,7 @@ class GeventTest(YappiUnitTestCase): t.start() return t + class TestAPI(GeventTest): def test_start_flags(self): @@ -68,7 +79,9 @@ class TestAPI(GeventTest): yappi.stop() yappi.clear_stats() - yappi.start(builtins=True, profile_greenlets=True, profile_threads=False) + yappi.start( + builtins=True, profile_greenlets=True, profile_threads=False + ) self.assertEqual(_yappi._get_start_flags()["profile_builtins"], 1) self.assertEqual(_yappi._get_start_flags()["profile_multicontext"], 1) self.assertEqual(len(yappi.get_greenlet_stats()), 1) @@ -76,16 +89,21 @@ class TestAPI(GeventTest): def test_context_change_exception(self): yappi.start() + def a(): pass a() # Setting to same backend should succeed # Changing backend should fail - self.assertRaises(_yappi.error, yappi.set_context_backend, "native_thread") + self.assertRaises( + _yappi.error, yappi.set_context_backend, "native_thread" + ) yappi.stop() # Still fail, stats need to be cleared - self.assertRaises(_yappi.error, yappi.set_context_backend, "native_thread") + self.assertRaises( + _yappi.error, yappi.set_context_backend, "native_thread" + ) yappi.clear_stats() # Should succeed now yappi.set_context_backend("native_thread") @@ -93,6 +111,7 @@ class TestAPI(GeventTest): def test_get_context_stat_exception(self): yappi.start() + def a(): pass @@ -106,6 +125,7 @@ class TestAPI(GeventTest): yappi.set_context_backend("native_thread") class ThreadA(threading.Thread): + def run(self): burn_cpu(0.05) @@ -127,7 +147,9 @@ class TestAPI(GeventTest): tstats = yappi.get_thread_stats() - self.assertEqual(len(tstats), 2, "Incorrect number of contexts captured") + self.assertEqual( + len(tstats), 2, "Incorrect number of contexts captured" + ) # First stat should be of threadA since it is sorted by ttot statsA = tstats[0] @@ -139,6 +161,7 @@ class TestAPI(GeventTest): self.assertEqual(statsMain.tid, main_thread.ident) self.assertEqual(statsMain.name, main_thread.__class__.__name__) + class SingleThreadTests(GeventTest): def test_recursive_greenlet(self): @@ -148,14 +171,14 @@ class SingleThreadTests(GeventTest): return burn_io_gevent(0.1) burn_cpu(0.1) - g1 = self.spawn_greenlet("a_%d" % (n-1), a, n - 1) + g1 = self.spawn_greenlet("a_%d" % (n - 1), a, n - 1) g1.get() - g2 = self.spawn_greenlet("a_%d" % (n-2), a, n - 2) + g2 = self.spawn_greenlet("a_%d" % (n - 2), a, n - 2) g2.get() yappi.start() g = self.spawn_greenlet("a", a, 3) - g.get() # run until complete, report exception (if any) + g.get() # run until complete, report exception (if any) yappi.stop() r1 = ''' @@ -239,6 +262,7 @@ class SingleThreadTests(GeventTest): self.assert_ctx_stats_almost_equal(r2, gstats) def test_recursive_function(self): + def a(n): if (n <= 0): return @@ -280,6 +304,7 @@ class SingleThreadTests(GeventTest): self.assert_ctx_stats_almost_equal(r2, gstats) def test_exception_raised(self): + def a(n): burn_cpu(0.1) burn_io_gevent(0.1) @@ -287,7 +312,7 @@ class SingleThreadTests(GeventTest): if (n == 0): raise Exception - a(n-1) + a(n - 1) yappi.set_clock_type("cpu") yappi.start() @@ -325,7 +350,6 @@ class SingleThreadTests(GeventTest): burn_cpu(0.1) burn_io_gevent(0.1) - ev1 = Event() ev2 = Event() gl = self.spawn_greenlet("a", a, ev1, ev2) @@ -353,7 +377,6 @@ class SingleThreadTests(GeventTest): ''' self.assert_ctx_stats_almost_equal(r2, gstats) - def test_many_context_switches(self): def common(): @@ -455,15 +478,14 @@ class MultiThreadTests(GeventTest): def driver(): to_run = [ - (a, ()), - (b, ()), - (recursive_a, (5,)), - (recursive_a, (5,)) + (a, ()), (b, ()), (recursive_a, (5, )), (recursive_a, (5, )) ] ts = [] for idx, (func, args) in enumerate(to_run): - t = self.spawn_thread("%s-%d" % (func.__name__, idx), func, *args) + t = self.spawn_thread( + "%s-%d" % (func.__name__, idx), func, *args + ) ts.append(t) for t in ts: @@ -520,14 +542,13 @@ class MultiThreadTests(GeventTest): def driver(): - to_run = [ - (recursive_a, (5,)), - (recursive_a, (5,)) - ] + to_run = [(recursive_a, (5, )), (recursive_a, (5, ))] ts = [] for idx, (func, args) in enumerate(to_run): - t = self.spawn_thread("%s_%d" % (func.__name__, idx), func, *args) + t = self.spawn_thread( + "%s_%d" % (func.__name__, idx), func, *args + ) ts.append(t) recursive_a(6) @@ -558,7 +579,6 @@ class MultiThreadTests(GeventTest): # Set context backend to confgiure default callbacks yappi.set_context_backend("greenlet") - class GreenletA(gevent.Greenlet): pass @@ -598,5 +618,6 @@ class MultiThreadTests(GeventTest): ''' self.assert_ctx_stats_almost_equal(r2, gstats) + if __name__ == '__main__': unittest.main() diff --git a/yappi/_yappi.c b/yappi/_yappi.c index 88236ee..41ff517 100644 --- a/yappi/_yappi.c +++ b/yappi/_yappi.c @@ -662,6 +662,7 @@ _code2pit(PyFrameObject *fobj, uintptr_t current_tag) PyCodeObject *cobj; _pit *pit; _htab *pits; + PyObject *co_varnames; pits = _get_pits_tbl(current_tag); if (!pits) { @@ -690,7 +691,11 @@ _code2pit(PyFrameObject *fobj, uintptr_t current_tag) if (cobj->co_argcount) { // There has been a lot going on with `co_varnames`, but finally in // 3.11.0rc1, it is added as a public API - PyObject *co_varnames = PyCode_GetVarnames(cobj); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION == 11 + co_varnames = PyCode_GetVarnames(cobj); +#else + co_varnames = cobj->co_varnames; +#endif const char *firstarg = PyStr_AS_CSTRING(PyTuple_GET_ITEM(co_varnames, 0)); if (!strcmp(firstarg, "self")) { @@ -1503,8 +1508,6 @@ _start(void) return 0; } - //flags.multicontext = 0; - if (flags.multicontext) { _enum_threads(&_bootstrap_thread); } else { -- 2.34.1