././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1638387900.5022218 nest_asyncio-1.5.4/0000775000175000017500000000000000000000000013174 5ustar00ewaldewald././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1585492464.0 nest_asyncio-1.5.4/LICENSE0000664000175000017500000000245200000000000014204 0ustar00ewaldewaldBSD 2-Clause License Copyright (c) 2018-2020, Ewald de Wit All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1580213584.0 nest_asyncio-1.5.4/MANIFEST.in0000664000175000017500000000005500000000000014732 0ustar00ewaldewaldinclude LICENSE recursive-include tests *.py ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1638387900.5022218 nest_asyncio-1.5.4/PKG-INFO0000664000175000017500000000521300000000000014272 0ustar00ewaldewaldMetadata-Version: 2.1 Name: nest_asyncio Version: 1.5.4 Summary: Patch asyncio to allow nested event loops Home-page: https://github.com/erdewit/nest_asyncio Author: Ewald R. de Wit Author-email: ewald.de.wit@gmail.com License: BSD Keywords: asyncio,nested,eventloop Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Framework :: AsyncIO Requires-Python: >=3.5 Description-Content-Type: text/x-rst License-File: LICENSE |Build| |Status| |PyPiVersion| |License| |Downloads| Introduction ------------ By design asyncio `does not allow `_ its event loop to be nested. This presents a practical problem: When in an environment where the event loop is already running it's impossible to run tasks and wait for the result. Trying to do so will give the error "``RuntimeError: This event loop is already running``". The issue pops up in various environments, such as web servers, GUI applications and in Jupyter notebooks. This module patches asyncio to allow nested use of ``asyncio.run`` and ``loop.run_until_complete``. Installation ------------ .. code-block:: pip3 install nest_asyncio Python 3.5 or higher is required. Usage ----- .. code-block:: python import nest_asyncio nest_asyncio.apply() Optionally the specific loop that needs patching can be given as argument to ``apply``, otherwise the current event loop is used. An event loop can be patched whether it is already running or not. Only event loops from asyncio can be patched; Loops from other projects, such as uvloop or quamash, generally can't be patched. .. |Build| image:: https://github.com/erdewit/nest_asyncio/workflows/Test/badge.svg?branche=master :alt: Build :target: https://github.com/erdewit/nest_asyncio/actions .. |PyPiVersion| image:: https://img.shields.io/pypi/v/nest_asyncio.svg :alt: PyPi :target: https://pypi.python.org/pypi/nest_asyncio .. |Status| image:: https://img.shields.io/badge/status-stable-green.svg :alt: .. |License| image:: https://img.shields.io/badge/license-BSD-blue.svg :alt: .. |Downloads| image:: https://pepy.tech/badge/nest-asyncio/month :alt: Number of downloads :target: https://pepy.tech/project/nest-asyncio ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611509563.0 nest_asyncio-1.5.4/README.rst0000664000175000017500000000337400000000000014672 0ustar00ewaldewald|Build| |Status| |PyPiVersion| |License| |Downloads| Introduction ------------ By design asyncio `does not allow `_ its event loop to be nested. This presents a practical problem: When in an environment where the event loop is already running it's impossible to run tasks and wait for the result. Trying to do so will give the error "``RuntimeError: This event loop is already running``". The issue pops up in various environments, such as web servers, GUI applications and in Jupyter notebooks. This module patches asyncio to allow nested use of ``asyncio.run`` and ``loop.run_until_complete``. Installation ------------ .. code-block:: pip3 install nest_asyncio Python 3.5 or higher is required. Usage ----- .. code-block:: python import nest_asyncio nest_asyncio.apply() Optionally the specific loop that needs patching can be given as argument to ``apply``, otherwise the current event loop is used. An event loop can be patched whether it is already running or not. Only event loops from asyncio can be patched; Loops from other projects, such as uvloop or quamash, generally can't be patched. .. |Build| image:: https://github.com/erdewit/nest_asyncio/workflows/Test/badge.svg?branche=master :alt: Build :target: https://github.com/erdewit/nest_asyncio/actions .. |PyPiVersion| image:: https://img.shields.io/pypi/v/nest_asyncio.svg :alt: PyPi :target: https://pypi.python.org/pypi/nest_asyncio .. |Status| image:: https://img.shields.io/badge/status-stable-green.svg :alt: .. |License| image:: https://img.shields.io/badge/license-BSD-blue.svg :alt: .. |Downloads| image:: https://pepy.tech/badge/nest-asyncio/month :alt: Number of downloads :target: https://pepy.tech/project/nest-asyncio ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1638387900.501222 nest_asyncio-1.5.4/nest_asyncio.egg-info/0000775000175000017500000000000000000000000017364 5ustar00ewaldewald././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1638387900.0 nest_asyncio-1.5.4/nest_asyncio.egg-info/PKG-INFO0000664000175000017500000000521300000000000020462 0ustar00ewaldewaldMetadata-Version: 2.1 Name: nest-asyncio Version: 1.5.4 Summary: Patch asyncio to allow nested event loops Home-page: https://github.com/erdewit/nest_asyncio Author: Ewald R. de Wit Author-email: ewald.de.wit@gmail.com License: BSD Keywords: asyncio,nested,eventloop Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Framework :: AsyncIO Requires-Python: >=3.5 Description-Content-Type: text/x-rst License-File: LICENSE |Build| |Status| |PyPiVersion| |License| |Downloads| Introduction ------------ By design asyncio `does not allow `_ its event loop to be nested. This presents a practical problem: When in an environment where the event loop is already running it's impossible to run tasks and wait for the result. Trying to do so will give the error "``RuntimeError: This event loop is already running``". The issue pops up in various environments, such as web servers, GUI applications and in Jupyter notebooks. This module patches asyncio to allow nested use of ``asyncio.run`` and ``loop.run_until_complete``. Installation ------------ .. code-block:: pip3 install nest_asyncio Python 3.5 or higher is required. Usage ----- .. code-block:: python import nest_asyncio nest_asyncio.apply() Optionally the specific loop that needs patching can be given as argument to ``apply``, otherwise the current event loop is used. An event loop can be patched whether it is already running or not. Only event loops from asyncio can be patched; Loops from other projects, such as uvloop or quamash, generally can't be patched. .. |Build| image:: https://github.com/erdewit/nest_asyncio/workflows/Test/badge.svg?branche=master :alt: Build :target: https://github.com/erdewit/nest_asyncio/actions .. |PyPiVersion| image:: https://img.shields.io/pypi/v/nest_asyncio.svg :alt: PyPi :target: https://pypi.python.org/pypi/nest_asyncio .. |Status| image:: https://img.shields.io/badge/status-stable-green.svg :alt: .. |License| image:: https://img.shields.io/badge/license-BSD-blue.svg :alt: .. |Downloads| image:: https://pepy.tech/badge/nest-asyncio/month :alt: Number of downloads :target: https://pepy.tech/project/nest-asyncio ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1638387900.0 nest_asyncio-1.5.4/nest_asyncio.egg-info/SOURCES.txt0000664000175000017500000000040500000000000021247 0ustar00ewaldewaldLICENSE MANIFEST.in README.rst nest_asyncio.py pyproject.toml setup.cfg setup.py nest_asyncio.egg-info/PKG-INFO nest_asyncio.egg-info/SOURCES.txt nest_asyncio.egg-info/dependency_links.txt nest_asyncio.egg-info/top_level.txt tests/__init__.py tests/nest_test.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1638387900.0 nest_asyncio-1.5.4/nest_asyncio.egg-info/dependency_links.txt0000664000175000017500000000000100000000000023432 0ustar00ewaldewald ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1638387900.0 nest_asyncio-1.5.4/nest_asyncio.egg-info/top_level.txt0000664000175000017500000000001500000000000022112 0ustar00ewaldewaldnest_asyncio ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1638387404.0 nest_asyncio-1.5.4/nest_asyncio.py0000644000175000017500000001624000000000000016245 0ustar00ewaldewaldimport asyncio import asyncio.events as events import os import sys import threading from contextlib import contextmanager, suppress from heapq import heappop def apply(loop=None): """Patch asyncio to make its event loop reentrant.""" loop = loop or asyncio.get_event_loop() if not isinstance(loop, asyncio.BaseEventLoop): raise ValueError('Can\'t patch loop of type %s' % type(loop)) if getattr(loop, '_nest_patched', None): # already patched return _patch_asyncio() _patch_loop(loop) _patch_task() _patch_tornado() def _patch_asyncio(): """ Patch asyncio module to use pure Python tasks and futures, use module level _current_tasks, all_tasks and patch run method. """ def run(main, *, debug=False): loop = events._get_running_loop() if not loop: loop = events.new_event_loop() events.set_event_loop(loop) _patch_loop(loop) loop.set_debug(debug) task = asyncio.ensure_future(main) try: return loop.run_until_complete(task) finally: if not task.done(): task.cancel() with suppress(asyncio.CancelledError): loop.run_until_complete(task) if sys.version_info >= (3, 6, 0): asyncio.Task = asyncio.tasks._CTask = asyncio.tasks.Task = \ asyncio.tasks._PyTask asyncio.Future = asyncio.futures._CFuture = asyncio.futures.Future = \ asyncio.futures._PyFuture if sys.version_info < (3, 7, 0): asyncio.tasks._current_tasks = asyncio.tasks.Task._current_tasks asyncio.all_tasks = asyncio.tasks.Task.all_tasks if not hasattr(asyncio, '_run_orig'): asyncio._run_orig = getattr(asyncio, 'run', None) asyncio.run = run def _patch_loop(loop): """Patch loop to make it reentrant.""" def run_forever(self): with manage_run(self), manage_asyncgens(self): while True: self._run_once() if self._stopping: break self._stopping = False 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.') return f.result() def _run_once(self): """ Simplified re-implementation of asyncio's _run_once that runs handles as they become ready. """ ready = self._ready scheduled = self._scheduled while scheduled and scheduled[0]._cancelled: heappop(scheduled) timeout = ( 0 if ready or self._stopping else min(max( scheduled[0]._when - self.time(), 0), 86400) if scheduled else None) event_list = self._selector.select(timeout) self._process_events(event_list) end_time = self.time() + self._clock_resolution while scheduled and scheduled[0]._when < end_time: handle = heappop(scheduled) ready.append(handle) for _ in range(len(ready)): if not ready: break handle = ready.popleft() if not handle._cancelled: handle._run() handle = None @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 @contextmanager def manage_asyncgens(self): if not hasattr(sys, 'get_asyncgen_hooks'): # Python version is too old. return old_agen_hooks = sys.get_asyncgen_hooks() try: self._set_coroutine_origin_tracking(self._debug) if self._asyncgens is not None: sys.set_asyncgen_hooks( firstiter=self._asyncgen_firstiter_hook, finalizer=self._asyncgen_finalizer_hook) yield finally: self._set_coroutine_origin_tracking(False) if self._asyncgens is not None: sys.set_asyncgen_hooks(*old_agen_hooks) def _check_running(self): """Do not throw exception if loop is already running.""" pass cls = loop.__class__ cls.run_forever = run_forever cls.run_until_complete = run_until_complete cls._run_once = _run_once cls._check_running = _check_running cls._check_runnung = _check_running # typo in Python 3.7 source cls._nest_patched = True cls._num_runs_pending = 0 cls._is_proactorloop = ( os.name == 'nt' and issubclass(cls, asyncio.ProactorEventLoop)) if sys.version_info < (3, 7, 0): cls._set_coroutine_origin_tracking = cls._set_coroutine_wrapper def _patch_task(): """Patch the Task's step and enter/leave methods to make it reentrant.""" def step(task, exc=None): curr_task = curr_tasks.get(task._loop) try: step_orig(task, exc) finally: if curr_task is None: curr_tasks.pop(task._loop, None) else: curr_tasks[task._loop] = curr_task Task = asyncio.Task if sys.version_info >= (3, 7, 0): def enter_task(loop, task): curr_tasks[loop] = task def leave_task(loop, task): curr_tasks.pop(loop, None) asyncio.tasks._enter_task = enter_task asyncio.tasks._leave_task = leave_task curr_tasks = asyncio.tasks._current_tasks step_orig = Task._Task__step Task._Task__step = step else: curr_tasks = Task._current_tasks step_orig = Task._step Task._step = step def _patch_tornado(): """ If tornado is imported before nest_asyncio, make tornado aware of the pure-Python asyncio Future. """ if 'tornado' in sys.modules: import tornado.concurrent as tc tc.Future = asyncio.Future if asyncio.Future not in tc.FUTURES: tc.FUTURES += (asyncio.Future,) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1594895983.0 nest_asyncio-1.5.4/pyproject.toml0000664000175000017500000000022400000000000016106 0ustar00ewaldewald[build-system] requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4.3"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1638387900.5022218 nest_asyncio-1.5.4/setup.cfg0000664000175000017500000000160600000000000015020 0ustar00ewaldewald[metadata] name = nest_asyncio version = 1.5.4 author = Ewald R. de Wit author_email = ewald.de.wit@gmail.com license = BSD license_files = LICENSE description = Patch asyncio to allow nested event loops keywords = asyncio, nested, eventloop url = https://github.com/erdewit/nest_asyncio long_description = file: README.rst long_description_content_type = text/x-rst classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: BSD License Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3 :: Only Framework :: AsyncIO [options] py_modules = nest_asyncio python_requires = >=3.5 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1594895996.0 nest_asyncio-1.5.4/setup.py0000664000175000017500000000010600000000000014703 0ustar00ewaldewaldfrom setuptools import setup if __name__ == "__main__": setup() ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1638387900.501222 nest_asyncio-1.5.4/tests/0000775000175000017500000000000000000000000014336 5ustar00ewaldewald././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1551821325.0 nest_asyncio-1.5.4/tests/__init__.py0000664000175000017500000000000000000000000016435 0ustar00ewaldewald././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1629454196.0 nest_asyncio-1.5.4/tests/nest_test.py0000644000175000017500000000563600000000000016730 0ustar00ewaldewaldimport asyncio import sys import unittest import nest_asyncio def exception_handler(loop, context): print('Exception:', context) class NestTest(unittest.TestCase): def setUp(self): self.loop = asyncio.new_event_loop() nest_asyncio.apply(self.loop) asyncio.set_event_loop(self.loop) self.loop.set_debug(True) self.loop.set_exception_handler(exception_handler) def tearDown(self): self.assertIsNone(asyncio._get_running_loop()) self.loop.close() del self.loop async def coro(self): await asyncio.sleep(0.01) return 42 def test_nesting(self): async def f1(): result = self.loop.run_until_complete(self.coro()) self.assertEqual(result, await self.coro()) return result async def f2(): result = self.loop.run_until_complete(f1()) self.assertEqual(result, await f1()) return result result = self.loop.run_until_complete(f2()) self.assertEqual(result, 42) def test_ensure_future_with_run_until_complete(self): async def f(): task = asyncio.ensure_future(self.coro()) return self.loop.run_until_complete(task) result = self.loop.run_until_complete(f()) self.assertEqual(result, 42) def test_ensure_future_with_run_until_complete_with_wait(self): async def f(): task = asyncio.ensure_future(self.coro()) done, pending = self.loop.run_until_complete( asyncio.wait([task], return_when=asyncio.ALL_COMPLETED)) task = done.pop() return task.result() result = self.loop.run_until_complete(f()) self.assertEqual(result, 42) def test_timeout(self): async def f1(): await asyncio.sleep(0.1) async def f2(): asyncio.run(asyncio.wait_for(f1(), 0.01)) with self.assertRaises(asyncio.TimeoutError): self.loop.run_until_complete(f2()) def test_two_run_until_completes_in_one_outer_loop(self): async def f1(): self.loop.run_until_complete(asyncio.sleep(0.02)) return 4 async def f2(): self.loop.run_until_complete(asyncio.sleep(0.01)) return 2 result = self.loop.run_until_complete( asyncio.gather(f1(), f2())) self.assertEqual(result, [4, 2]) @unittest.skipIf(sys.version_info < (3, 7, 0), 'No contextvars module') def test_contextvars(self): from contextvars import ContextVar var = ContextVar('var') var.set(0) async def set_val(): var.set(42) async def coro(): await set_val() await asyncio.sleep(0.01) return var.get() result = self.loop.run_until_complete(coro()) self.assertEqual(result, 42) if __name__ == '__main__': unittest.main()