| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- from __future__ import annotations
- import asyncio
- import sys
- from collections.abc import Callable, Coroutine
- from typing import Any, TypeVar
- __all__ = ["asyncio_run", "iscoroutinefunction"]
- if sys.version_info >= (3, 14):
- from inspect import iscoroutinefunction
- else:
- from asyncio import iscoroutinefunction
- _T = TypeVar("_T")
- if sys.version_info >= (3, 12):
- asyncio_run = asyncio.run
- elif sys.version_info >= (3, 11):
- def asyncio_run(
- main: Coroutine[Any, Any, _T],
- *,
- debug: bool = False,
- loop_factory: Callable[[], asyncio.AbstractEventLoop] | None = None,
- ) -> _T:
- # asyncio.run from Python 3.12
- # https://docs.python.org/3/license.html#psf-license
- with asyncio.Runner(debug=debug, loop_factory=loop_factory) as runner:
- return runner.run(main)
- else:
- # modified version of asyncio.run from Python 3.10 to add loop_factory kwarg
- # https://docs.python.org/3/license.html#psf-license
- def asyncio_run(
- main: Coroutine[Any, Any, _T],
- *,
- debug: bool = False,
- loop_factory: Callable[[], asyncio.AbstractEventLoop] | None = None,
- ) -> _T:
- try:
- asyncio.get_running_loop()
- except RuntimeError:
- pass
- else:
- raise RuntimeError("asyncio.run() cannot be called from a running event loop")
- if not asyncio.iscoroutine(main):
- raise ValueError(f"a coroutine was expected, got {main!r}")
- if loop_factory is None:
- loop = asyncio.new_event_loop()
- else:
- loop = loop_factory()
- try:
- if loop_factory is None:
- asyncio.set_event_loop(loop)
- if debug is not None:
- loop.set_debug(debug)
- return loop.run_until_complete(main)
- finally:
- try:
- _cancel_all_tasks(loop)
- loop.run_until_complete(loop.shutdown_asyncgens())
- loop.run_until_complete(loop.shutdown_default_executor())
- finally:
- if loop_factory is None:
- asyncio.set_event_loop(None)
- loop.close()
- def _cancel_all_tasks(loop: asyncio.AbstractEventLoop) -> None:
- to_cancel = asyncio.all_tasks(loop)
- if not to_cancel:
- return
- for task in to_cancel:
- task.cancel()
- loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True))
- for task in to_cancel:
- if task.cancelled():
- continue
- if task.exception() is not None:
- loop.call_exception_handler(
- {
- "message": "unhandled exception during asyncio.run() shutdown",
- "exception": task.exception(),
- "task": task,
- }
- )
|