mode-streaming 0.5.2__tar.gz → 0.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/.github/workflows/tests.yml +1 -3
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/PKG-INFO +2 -1
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/loops.py +28 -2
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode_streaming.egg-info/PKG-INFO +2 -1
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode_streaming.egg-info/scm_version.json +2 -2
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/pyproject.toml +1 -0
- mode_streaming-0.5.3/tests/unit/utils/test_loops.py +94 -0
- mode_streaming-0.5.2/tests/unit/utils/test_loops.py +0 -48
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/.editorconfig +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/.github/ISSUE_TEMPLATE.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/.github/workflows/deploy-docs.yml +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/.github/workflows/publish.yml +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/.gitignore +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/.pre-commit-config.yaml +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/AUTHORS +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/CHANGELOG.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/CODE_OF_CONDUCT.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/CONTRIBUTING.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/LICENSE +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/README.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/changelog.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/contributing.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/creating-service.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/example-service.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/example-webapp.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/index.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.debug.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.exceptions.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.locals.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.loop.eventlet.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.loop.gevent.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.loop.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.loop.uvloop.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.proxy.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.services.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.signals.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.supervisors.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.threads.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.timers.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.types.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.types.services.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.types.signals.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.types.supervisors.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.aiter.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.collections.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.compat.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.contexts.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.cron.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.futures.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.graphs.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.imports.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.locals.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.locks.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.logging.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.loops.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.mocks.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.objects.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.queues.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.text.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.times.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.tracebacks.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.trees.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.types.graphs.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.utils.types.trees.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/docs/references/mode.worker.md +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/examples/service.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/examples/webapp.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mkdocs.yml +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/debug.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/exceptions.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/locals.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/loop/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/loop/_gevent_loop.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/loop/eventlet.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/loop/gevent.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/loop/uvloop.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/proxy.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/py.typed +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/services.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/signals.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/supervisors.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/threads.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/timers.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/types/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/types/services.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/types/signals.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/types/supervisors.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/aiter.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/collections.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/compat.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/contexts.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/cron.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/futures.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/graphs/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/graphs/formatter.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/graphs/graph.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/imports.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/locals.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/locks.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/logging.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/mocks.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/objects.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/queues.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/text.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/times.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/tracebacks.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/trees.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/types/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/types/graphs.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/utils/types/trees.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode/worker.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode_streaming.egg-info/SOURCES.txt +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode_streaming.egg-info/dependency_links.txt +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode_streaming.egg-info/requires.txt +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode_streaming.egg-info/scm_file_list.json +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/mode_streaming.egg-info/top_level.txt +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/requirements-docs.txt +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/requirements-tests.txt +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/requirements.txt +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/scripts/build-docs.sh +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/scripts/build.sh +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/scripts/bump.sh +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/scripts/clean.sh +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/scripts/format.sh +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/scripts/lint.sh +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/scripts/tests.sh +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/setup.cfg +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/conftest.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/test_mode.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/test_proxy.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/test_service.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/test_signals.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/test_supervisors.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/test_timers.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_aiter.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_collections.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_compat.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_contexts.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_futures.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_locals.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_queues.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_text.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_times.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_tracebacks.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/functional/utils/test_trees.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/test_debug.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/test_locals.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/test_services.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/test_supervisors.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/test_threads.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/test_worker.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/utils/__init__.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/utils/test_cron.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/utils/test_imports.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/utils/test_locks.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/utils/test_logging.py +0 -0
- {mode_streaming-0.5.2 → mode_streaming-0.5.3}/tests/unit/utils/test_objects.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mode-streaming
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: AsyncIO Service-based programming
|
|
5
5
|
Author-email: Ask Solem Hoel <ask@robinhood.com>
|
|
6
6
|
Maintainer-email: William Barnhart <williambbarnhart@gmail.com>
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
22
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
23
|
Classifier: Operating System :: POSIX
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
"""Event loop utilities."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import threading
|
|
4
5
|
from typing import Any, Callable, Optional
|
|
5
6
|
|
|
6
7
|
__all__ = ["call_asap", "clone_loop", "get_event_loop"]
|
|
7
8
|
|
|
9
|
+
#: Per-thread cache of the loop resolved by :func:`get_event_loop` for when
|
|
10
|
+
#: there is no *running* loop. ``threading.local`` gives every thread its
|
|
11
|
+
#: own attribute storage for free (no cross-thread leakage, no explicit
|
|
12
|
+
#: cleanup required when a thread exits) -- required for correctness, not
|
|
13
|
+
#: just performance, since Mode creates and runs a dedicated event loop per
|
|
14
|
+
#: :class:`~mode.threads.ServiceThread` worker thread.
|
|
15
|
+
_current_loop = threading.local()
|
|
16
|
+
|
|
8
17
|
|
|
9
18
|
def get_event_loop() -> asyncio.AbstractEventLoop:
|
|
10
19
|
"""Return the current event loop, creating one if necessary.
|
|
@@ -20,17 +29,34 @@ def get_event_loop() -> asyncio.AbstractEventLoop:
|
|
|
20
29
|
loop -- e.g. at import time, when agents/services are declared at module
|
|
21
30
|
level -- so it needs the historical "get or create" semantics. This
|
|
22
31
|
restores them in a way that works across Python 3.9-3.14.
|
|
32
|
+
|
|
33
|
+
Whether a loop is currently *running* can change on every call (that's
|
|
34
|
+
the whole point of an event loop), so :func:`asyncio.get_running_loop`
|
|
35
|
+
must always be re-checked and can't be cached. When nothing is running,
|
|
36
|
+
though, the resolved loop is stable for the lifetime of the thread that
|
|
37
|
+
resolved it, so it's cached in thread-local storage: once a thread has
|
|
38
|
+
created (or found) its loop, later calls on that thread return the
|
|
39
|
+
cached reference directly instead of re-running the
|
|
40
|
+
``asyncio.get_event_loop()``-raises-then-create dance every time.
|
|
23
41
|
"""
|
|
24
42
|
try:
|
|
25
43
|
return asyncio.get_running_loop()
|
|
26
44
|
except RuntimeError:
|
|
27
45
|
pass
|
|
46
|
+
|
|
47
|
+
loop: Optional[asyncio.AbstractEventLoop] = getattr(
|
|
48
|
+
_current_loop, "loop", None
|
|
49
|
+
)
|
|
50
|
+
if loop is not None and not loop.is_closed():
|
|
51
|
+
return loop
|
|
52
|
+
|
|
28
53
|
try:
|
|
29
|
-
|
|
54
|
+
loop = asyncio.get_event_loop()
|
|
30
55
|
except RuntimeError:
|
|
31
56
|
loop = asyncio.new_event_loop()
|
|
32
57
|
asyncio.set_event_loop(loop)
|
|
33
|
-
|
|
58
|
+
_current_loop.loop = loop
|
|
59
|
+
return loop
|
|
34
60
|
|
|
35
61
|
|
|
36
62
|
def _is_unix_loop(loop: asyncio.AbstractEventLoop) -> bool:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mode-streaming
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: AsyncIO Service-based programming
|
|
5
5
|
Author-email: Ask Solem Hoel <ask@robinhood.com>
|
|
6
6
|
Maintainer-email: William Barnhart <williambbarnhart@gmail.com>
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
22
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
23
|
Classifier: Operating System :: POSIX
|
|
@@ -40,6 +40,7 @@ classifiers = [
|
|
|
40
40
|
"Programming Language :: Python :: 3.11",
|
|
41
41
|
"Programming Language :: Python :: 3.12",
|
|
42
42
|
"Programming Language :: Python :: 3.13",
|
|
43
|
+
"Programming Language :: Python :: 3.14",
|
|
43
44
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
44
45
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
45
46
|
"Operating System :: POSIX",
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import threading
|
|
3
|
+
from unittest.mock import patch
|
|
4
|
+
|
|
5
|
+
from mode.utils.loops import get_event_loop
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_get_event_loop__returns_running_loop_when_running():
|
|
9
|
+
async def main():
|
|
10
|
+
return get_event_loop()
|
|
11
|
+
|
|
12
|
+
running = asyncio.new_event_loop()
|
|
13
|
+
try:
|
|
14
|
+
got = running.run_until_complete(main())
|
|
15
|
+
assert got is running
|
|
16
|
+
finally:
|
|
17
|
+
running.close()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_get_event_loop__reuses_current_loop_when_set():
|
|
21
|
+
loop = asyncio.new_event_loop()
|
|
22
|
+
asyncio.set_event_loop(loop)
|
|
23
|
+
try:
|
|
24
|
+
assert get_event_loop() is loop
|
|
25
|
+
finally:
|
|
26
|
+
asyncio.set_event_loop(None)
|
|
27
|
+
loop.close()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_get_event_loop__creates_loop_when_none_set():
|
|
31
|
+
# Emulates Python 3.14, where asyncio.get_event_loop() no longer creates
|
|
32
|
+
# a loop implicitly and raises RuntimeError instead.
|
|
33
|
+
asyncio.set_event_loop(None)
|
|
34
|
+
loop = get_event_loop()
|
|
35
|
+
try:
|
|
36
|
+
assert isinstance(loop, asyncio.AbstractEventLoop)
|
|
37
|
+
assert asyncio.get_event_loop() is loop
|
|
38
|
+
finally:
|
|
39
|
+
asyncio.set_event_loop(None)
|
|
40
|
+
loop.close()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_get_event_loop__is_idempotent_without_running_loop():
|
|
44
|
+
asyncio.set_event_loop(None)
|
|
45
|
+
first = get_event_loop()
|
|
46
|
+
try:
|
|
47
|
+
assert get_event_loop() is first
|
|
48
|
+
finally:
|
|
49
|
+
asyncio.set_event_loop(None)
|
|
50
|
+
first.close()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_get_event_loop__caches_loop_without_repeated_lookup():
|
|
54
|
+
# The whole point of the thread-local cache: once a loop has been
|
|
55
|
+
# resolved for a thread, later calls on that thread must not re-invoke
|
|
56
|
+
# (and potentially re-raise/re-catch RuntimeError from)
|
|
57
|
+
# asyncio.get_event_loop() again.
|
|
58
|
+
asyncio.set_event_loop(None)
|
|
59
|
+
first = get_event_loop()
|
|
60
|
+
try:
|
|
61
|
+
with patch(
|
|
62
|
+
"mode.utils.loops.asyncio.get_event_loop"
|
|
63
|
+
) as get_event_loop_mock:
|
|
64
|
+
second = get_event_loop()
|
|
65
|
+
assert second is first
|
|
66
|
+
get_event_loop_mock.assert_not_called()
|
|
67
|
+
finally:
|
|
68
|
+
asyncio.set_event_loop(None)
|
|
69
|
+
first.close()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_get_event_loop__caches_per_thread_not_globally():
|
|
73
|
+
# Mode runs a dedicated event loop per ServiceThread worker thread
|
|
74
|
+
# (see mode.threads.ServiceThread), so the cache must be thread-local:
|
|
75
|
+
# one thread's cached loop must never leak into another thread's call.
|
|
76
|
+
asyncio.set_event_loop(None)
|
|
77
|
+
main_loop = get_event_loop()
|
|
78
|
+
other_loop_holder: dict = {}
|
|
79
|
+
|
|
80
|
+
def other_thread() -> None:
|
|
81
|
+
other_loop_holder["loop"] = get_event_loop()
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
thread = threading.Thread(target=other_thread)
|
|
85
|
+
thread.start()
|
|
86
|
+
thread.join()
|
|
87
|
+
|
|
88
|
+
other_loop = other_loop_holder["loop"]
|
|
89
|
+
assert other_loop is not main_loop
|
|
90
|
+
assert get_event_loop() is main_loop
|
|
91
|
+
finally:
|
|
92
|
+
asyncio.set_event_loop(None)
|
|
93
|
+
main_loop.close()
|
|
94
|
+
other_loop_holder["loop"].close()
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
|
|
3
|
-
from mode.utils.loops import get_event_loop
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_get_event_loop__returns_running_loop_when_running():
|
|
7
|
-
async def main():
|
|
8
|
-
return get_event_loop()
|
|
9
|
-
|
|
10
|
-
running = asyncio.new_event_loop()
|
|
11
|
-
try:
|
|
12
|
-
got = running.run_until_complete(main())
|
|
13
|
-
assert got is running
|
|
14
|
-
finally:
|
|
15
|
-
running.close()
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_get_event_loop__reuses_current_loop_when_set():
|
|
19
|
-
loop = asyncio.new_event_loop()
|
|
20
|
-
asyncio.set_event_loop(loop)
|
|
21
|
-
try:
|
|
22
|
-
assert get_event_loop() is loop
|
|
23
|
-
finally:
|
|
24
|
-
asyncio.set_event_loop(None)
|
|
25
|
-
loop.close()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def test_get_event_loop__creates_loop_when_none_set():
|
|
29
|
-
# Emulates Python 3.14, where asyncio.get_event_loop() no longer creates
|
|
30
|
-
# a loop implicitly and raises RuntimeError instead.
|
|
31
|
-
asyncio.set_event_loop(None)
|
|
32
|
-
loop = get_event_loop()
|
|
33
|
-
try:
|
|
34
|
-
assert isinstance(loop, asyncio.AbstractEventLoop)
|
|
35
|
-
assert asyncio.get_event_loop() is loop
|
|
36
|
-
finally:
|
|
37
|
-
asyncio.set_event_loop(None)
|
|
38
|
-
loop.close()
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_get_event_loop__is_idempotent_without_running_loop():
|
|
42
|
-
asyncio.set_event_loop(None)
|
|
43
|
-
first = get_event_loop()
|
|
44
|
-
try:
|
|
45
|
-
assert get_event_loop() is first
|
|
46
|
-
finally:
|
|
47
|
-
asyncio.set_event_loop(None)
|
|
48
|
-
first.close()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|