11import asyncio
22import contextlib
3- from collections .abc import Generator
3+ import sys
4+ from collections .abc import Iterator
5+ from typing import Optional
46
57
68@contextlib .contextmanager
7- def proper_loop (suggested_loop : asyncio .AbstractEventLoop | None = None ) -> Generator [ None , None , None ]:
9+ def proper_loop (suggested_loop : Optional [ asyncio .AbstractEventLoop ] = None ) -> Iterator [ asyncio . AbstractEventLoop | None ]:
810 """
911 Ensure that we have the proper loop, either suggested or properly managed.
1012
@@ -15,22 +17,43 @@ def proper_loop(suggested_loop: asyncio.AbstractEventLoop | None = None) -> Gene
1517 This loop manager is usually used in CLI only, not deeper than that;
1618 i.e. not even in ``kopf.run()``, since uvloop is only auto-managed for CLI.
1719 """
18- original_policy = asyncio .get_event_loop_policy ()
19- if suggested_loop is None : # the pure CLI use, not a KopfRunner or other code
20+ # Event loop policies were deprecated in 3.14 entirely. Yet they still exist in older versions.
21+ # However, the asyncio.Runner was introduced in Python 3.11, so we can use the logic from there.
22+ if suggested_loop is not None :
23+ yield suggested_loop
24+
25+ elif sys .version_info >= (3 , 11 ): # optional in 3.11-3.13, mandatory in >=3.14
26+ # Use uvloop if available by injecting it as the selected loop.
2027 try :
2128 import uvloop
2229 except ImportError :
2330 pass
2431 else :
25- asyncio .set_event_loop_policy (uvloop .EventLoopPolicy ())
32+ with asyncio .Runner (loop_factory = uvloop .new_event_loop ) as runner :
33+ yield runner .get_loop ()
34+ return
35+
36+ # Use the default loop/runner in place, do not inject anything.
37+ yield None
2638
27- try :
28- yield
39+ # For Python<=3.10, use the event-loop-policy-based injection.
40+ else :
41+ original_policy = asyncio .get_event_loop_policy ()
42+ if suggested_loop is None : # the pure CLI use, not a KopfRunner or other code
43+ try :
44+ import uvloop
45+ except ImportError :
46+ pass
47+ else :
48+ asyncio .set_event_loop_policy (uvloop .EventLoopPolicy ())
2949
30- finally :
3150 try :
32- import uvloop
33- except ImportError :
34- pass
35- else :
36- asyncio .set_event_loop_policy (original_policy )
51+ yield
52+
53+ finally :
54+ try :
55+ import uvloop
56+ except ImportError :
57+ pass
58+ else :
59+ asyncio .set_event_loop_policy (original_policy )
0 commit comments