From 7a7cf6a1ee8d7244da0aad01578327272b9a7407 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 30 May 2024 03:56:18 +0800 Subject: [PATCH] nest_asyncio --- pdm.lock | 12 +-------- pyproject.toml | 1 - tests/conftest.py | 68 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 16 deletions(-) diff --git a/pdm.lock b/pdm.lock index 4277c01..3f5f431 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["cross_platform"] lock_version = "4.4" -content_hash = "sha256:24c0c9f544f05e7b6e88f90539413e19951fecb6db9e899d05f62e4547132997" +content_hash = "sha256:0a2519b9c40dec5ae5e9fe918210c67119b400822c5306d236dd2215ea5c7519" [[package]] name = "appnope" @@ -300,16 +300,6 @@ files = [ {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, ] -[[package]] -name = "nest-asyncio" -version = "1.6.0" -requires_python = ">=3.5" -summary = "Patch asyncio to allow nested event loops" -files = [ - {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, - {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, -] - [[package]] name = "packaging" version = "23.2" diff --git a/pyproject.toml b/pyproject.toml index 6b884c9..97b3322 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,6 @@ distribution = true dev = [ "pytest>=7.4.0", "ipython>=8.12.2", - "nest-asyncio>=1.6.0", ] [tool.pdm.scripts] diff --git a/tests/conftest.py b/tests/conftest.py index 59fd67a..7a30ce5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,13 +1,73 @@ import asyncio +import asyncio.events as events +from contextlib import contextmanager +import os +import threading -import nest_asyncio import pytest_asyncio +def _patch_loop(loop): # nest_asyncio + """Patch loop to make it reentrant.""" + + def run_until_complete(self, future): + with manage_run(self): + f = asyncio.ensure_future(future, loop=self) + if f is not future: + f._log_destroy_pending = False + while not f.done(): + self._run_once() + if self._stopping: + break + if not f.done(): + raise RuntimeError('Event loop stopped before Future completed.') # noqa: TRY003 + return f.result() + + @contextmanager + def manage_run(self): + """Set up the loop for running.""" + self._check_closed() + old_thread_id = self._thread_id + old_running_loop = events._get_running_loop() + try: + self._thread_id = threading.get_ident() + events._set_running_loop(self) + self._num_runs_pending += 1 + if self._is_proactorloop: + if self._self_reading_future is None: + self.call_soon(self._loop_self_reading) + yield + finally: + self._thread_id = old_thread_id + events._set_running_loop(old_running_loop) + self._num_runs_pending -= 1 + if self._is_proactorloop: + if self._num_runs_pending == 0 and self._self_reading_future is not None: + ov = self._self_reading_future._ov + self._self_reading_future.cancel() + if ov is not None: + self._proactor._unregister(ov) + self._self_reading_future = None + + def _check_running(self): + """Do not throw exception if loop is already running.""" + pass + + if hasattr(loop, '_nest_patched'): + return + if not isinstance(loop, asyncio.BaseEventLoop): + raise ValueError("Can't patch loop of type %s" % type(loop)) # noqa: TRY004 + cls = loop.__class__ + cls.run_until_complete = run_until_complete + cls._check_running = _check_running + cls._num_runs_pending = 1 if loop.is_running() else 0 + cls._is_proactorloop = os.name == 'nt' and issubclass(cls, asyncio.ProactorEventLoop) + cls._nest_patched = True + + @pytest_asyncio.fixture(scope='session') def event_loop(): # https://pytest-asyncio.readthedocs.io/en/latest/reference/fixtures.html#fixtures - policy = asyncio.get_event_loop_policy() - loop = policy.new_event_loop() - nest_asyncio.apply(loop) + loop = asyncio.get_event_loop_policy().new_event_loop() + _patch_loop(loop) yield loop loop.close()