Skip to content

Commit a88efba

Browse files
committed
feat: Python 3.12 loop_factory
1 parent 86635bb commit a88efba

File tree

5 files changed

+86
-5
lines changed

5 files changed

+86
-5
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,10 @@ An event loop can be patched whether it is already running
4141
or not. Only event loops from asyncio can be patched;
4242
Loops from other projects, such as uvloop or quamash,
4343
generally can't be patched.
44+
45+
## Comparison with `nest_asyncio`
46+
`nest-asyncio2` is a fork of the unmaintained [`nest_asyncio`](https://github.com/erdewit/nest_asyncio), with the following changes:
47+
- Python 3.12 `loop_factory` parameter support
48+
- Python 3.14 support (`asyncio.current_task()` and others are broken in `nest_asyncio`)
49+
50+
All interfaces are kept as they are. To migrate, you just need to change the package and module name to `nest_asyncio2`.

nest_asyncio2.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ def apply(loop=None):
1818
loop = loop or asyncio.get_event_loop()
1919
_patch_loop(loop)
2020

21-
22-
def _patch_asyncio():
23-
"""Patch asyncio module to use pure Python tasks and futures."""
24-
21+
if sys.version_info < (3, 12, 0):
2522
def run(main, *, debug=False):
2623
loop = asyncio.get_event_loop()
2724
loop.set_debug(debug)
@@ -33,6 +30,35 @@ def run(main, *, debug=False):
3330
task.cancel()
3431
with suppress(asyncio.CancelledError):
3532
loop.run_until_complete(task)
33+
else:
34+
def run(main, *, debug=False, loop_factory=None):
35+
try:
36+
loop = asyncio.get_running_loop()
37+
except RuntimeError:
38+
if loop_factory is None:
39+
loop_factory = asyncio.new_event_loop
40+
# if sys.version_info < (3, 16, 0):
41+
# policy = asyncio.events._get_event_loop_policy()
42+
# try:
43+
# loop = policy.get_event_loop()
44+
# except RuntimeError:
45+
# loop = loop_factory()
46+
# else:
47+
# loop = loop_factory()
48+
loop = loop_factory()
49+
50+
loop.set_debug(debug)
51+
task = asyncio.ensure_future(main, loop=loop)
52+
try:
53+
return loop.run_until_complete(task)
54+
finally:
55+
if not task.done():
56+
task.cancel()
57+
with suppress(asyncio.CancelledError):
58+
loop.run_until_complete(task)
59+
60+
def _patch_asyncio():
61+
"""Patch asyncio module to use pure Python tasks and futures."""
3662

3763
def _get_event_loop(stacklevel=3):
3864
loop = events._get_running_loop()

setup.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = nest-asyncio2
3-
version = 1.6.0
3+
version = 1.6.1
44
author = Ewald R. de Wit
55
author_email = [email protected]
66
license = BSD
@@ -22,6 +22,8 @@ classifiers =
2222
Programming Language :: Python :: 3.10
2323
Programming Language :: Python :: 3.11
2424
Programming Language :: Python :: 3.12
25+
Programming Language :: Python :: 3.13
26+
Programming Language :: Python :: 3.14
2527
Programming Language :: Python :: 3 :: Only
2628
Framework :: AsyncIO
2729

tests/312_loop_factory.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# /// script
2+
# requires-python = ">=3.12"
3+
# dependencies = [
4+
# "nest-asyncio2",
5+
# ]
6+
#
7+
# [tool.uv.sources]
8+
# nest-asyncio2 = { path = "../", editable = true }
9+
# ///
10+
import asyncio
11+
import nest_asyncio2
12+
13+
nest_asyncio2.apply()
14+
15+
async def f():
16+
print(asyncio.get_running_loop())
17+
assert asyncio.get_running_loop() is not None
18+
19+
print(asyncio.tasks._current_tasks)
20+
# assert asyncio.tasks._current_tasks == {}
21+
22+
print(asyncio.tasks.current_task())
23+
assert asyncio.tasks.current_task() is not None
24+
25+
print(asyncio.tasks._py_current_task())
26+
# assert asyncio.tasks._py_current_task() is None
27+
28+
print(asyncio.tasks._current_tasks)
29+
# assert asyncio.tasks._current_tasks == {}
30+
31+
hit = False
32+
def loop_factory():
33+
global hit
34+
hit = True
35+
return asyncio.new_event_loop()
36+
asyncio.run(f(), loop_factory=loop_factory)
37+
assert hit

tests/test.ps1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ if (!$?) {
3030
throw "3.14"
3131
}
3232

33+
uv run --python 3.12 312_loop_factory.py
34+
if (!$?) {
35+
throw "312_loop_factory"
36+
}
37+
uv run --python 3.14 312_loop_factory.py
38+
if (!$?) {
39+
throw "314_loop_factory"
40+
}
41+
3342
uv run --python 3.14 314_task.py
3443
if (!$?) {
3544
throw "314_task"

0 commit comments

Comments
 (0)