diff --git a/external/devlib/devlib/utils/asyn.py b/external/devlib/devlib/utils/asyn.py index 7b209f7de333b3a5929fcca15d01fcf258c82351..5879a598dd39187f3139cb0780826a5703fa11b4 100644 --- a/external/devlib/devlib/utils/asyn.py +++ b/external/devlib/devlib/utils/asyn.py @@ -392,7 +392,7 @@ class _AwaitableGenlet: def __await__(self): coro = self._coro - is_started = coro.cr_running + is_started = inspect.iscoroutine(coro) and coro.cr_running def genf(): gen = _Genlet.from_coro(coro) @@ -535,7 +535,7 @@ def run(coro): # Ensure we have a fresh coroutine. inspect.getcoroutinestate() does not # work on all objects that asyncio creates on some version of Python, such # as iterable_coroutine - assert not coro.cr_running + assert not (inspect.iscoroutine(coro) and coro.cr_running) try: loop = asyncio.get_running_loop() diff --git a/external/devlib/devlib/utils/misc.py b/external/devlib/devlib/utils/misc.py index d5210e41c043b9cc460011fd1b9567f59593a37d..1c49d0d0b1b1e16e704fb61f31e28c11a36e8fff 100644 --- a/external/devlib/devlib/utils/misc.py +++ b/external/devlib/devlib/utils/misc.py @@ -842,8 +842,13 @@ class tls_property: def __delete__(self, instance): tls, values = self._get_tls(instance) with self.lock: - values.discard(tls.value) - del tls.value + try: + value = tls.value + except AttributeError: + pass + else: + values.discard(value) + del tls.value def _get_tls(self, instance): dct = instance.__dict__ diff --git a/external/devlib/tests/test_asyn.py b/external/devlib/tests/test_asyn.py index 9e10941e2902f46c21f25ac74f168bcbdcd297f6..b743084bbe1d0225b742b4bbe1bb5af53cca55e0 100644 --- a/external/devlib/tests/test_asyn.py +++ b/external/devlib/tests/test_asyn.py @@ -470,6 +470,28 @@ def _do_test_run(top_run): test_async_cm4() + def test_async_cm5(): + @asynccontextmanager + async def cm_f(): + yield 42 + + cm = cm_f() + assert top_run(cm.__aenter__()) == 42 + assert not top_run(cm.__aexit__(None, None, None)) + + test_async_cm5() + + def test_async_gen1(): + async def agen_f(): + for i in range(2): + yield i + + agen = agen_f() + assert top_run(anext(agen)) == 0 + assert top_run(anext(agen)) == 1 + + test_async_gen1() + def _test_in_thread(setup, test): def f(): @@ -491,13 +513,32 @@ def _test_run_with_setup(setup): def run_with_existing_loop2(coro): # This is similar to how things are executed on IPython/jupyterlab loop = asyncio.new_event_loop() - return loop.run_until_complete(coro) + x = loop.run_until_complete(coro) + loop.close() + return x + + def run_with_to_thread(top_run, coro): + # Add a layer of asyncio.to_thread(), to simulate a case where users + # would be using the blocking API along with asyncio.to_thread() (code + # written before devlib gained async capabilities or wishing to + # preserve compat with older devlib versions) + async def wrapper(): + return await asyncio.to_thread( + top_run, coro + ) + return top_run(wrapper()) + runners = [ run, asyncio.run, run_with_existing_loop, run_with_existing_loop2, + + partial(run_with_to_thread, run), + partial(run_with_to_thread, asyncio.run), + partial(run_with_to_thread, run_with_existing_loop), + partial(run_with_to_thread, run_with_existing_loop2), ] for top_run in runners: