11"""PEP 3156 event loop based on CoreFoundation."""
22
33import contextvars
4+ import inspect
45import sys
56import threading
7+ import warnings
68from asyncio import (
7- DefaultEventLoopPolicy ,
89 coroutines ,
910 events ,
1011 tasks ,
1718from .types import CFIndex
1819
1920if sys .version_info < (3 , 14 ):
20- from asyncio import SafeChildWatcher
21+ from asyncio import (
22+ AbstractEventLoopPolicy ,
23+ DefaultEventLoopPolicy ,
24+ SafeChildWatcher ,
25+ set_event_loop_policy ,
26+ )
27+ elif sys .version_info < (3 , 16 ):
28+ # Python 3.14 finalized the deprecation of SafeChildWatcher. There's no
29+ # replacement API; the feature can be removed.
30+ #
31+ # Python 3.14 also started the deprecation of event loop policies, to be
32+ # finalized in Python 3.16; there was some symbol renaming to assist in
33+ # making the deprecation visible. See
34+ # https://github.com/python/cpython/issues/127949 for details.
35+ from asyncio import (
36+ _AbstractEventLoopPolicy as AbstractEventLoopPolicy ,
37+ _DefaultEventLoopPolicy as DefaultEventLoopPolicy ,
38+ )
2139
2240__all__ = [
2341 "EventLoopPolicy" ,
2442 "CocoaLifecycle" ,
43+ "RubiconEventLoop" ,
2544 "iOSLifecycle" ,
2645]
2746
47+
2848###########################################################################
2949# CoreFoundation types and constants needed for async handlers
3050###########################################################################
@@ -421,7 +441,7 @@ def remove_writer(self, fd):
421441 ######################################################################
422442 def _check_not_coroutine (self , callback , name ):
423443 """Check whether the given callback is a coroutine or not."""
424- if coroutines .iscoroutine (callback ) or coroutines .iscoroutinefunction (callback ):
444+ if coroutines .iscoroutine (callback ) or inspect .iscoroutinefunction (callback ):
425445 raise TypeError (f"coroutines cannot be used with { name } ()" )
426446
427447 def is_running (self ):
@@ -637,7 +657,8 @@ def _set_lifecycle(self, lifecycle):
637657 "You can't set a lifecycle on a loop that's already running."
638658 )
639659 self ._lifecycle = lifecycle
640- self ._policy ._lifecycle = lifecycle
660+ if sys .version_info < (3 , 14 ):
661+ self ._policy ._lifecycle = lifecycle
641662
642663 def _add_callback (self , handle ):
643664 """Add a callback to be invoked ASAP.
@@ -656,81 +677,115 @@ def _add_callback(self, handle):
656677 self .call_soon (handle ._callback , * handle ._args )
657678
658679
659- class EventLoopPolicy (events .AbstractEventLoopPolicy ):
660- """Rubicon event loop policy.
680+ if sys .version_info < (3 , 16 ):
661681
662- In this policy, each thread has its own event loop. However, we only
663- automatically create an event loop by default for the main thread;
664- other threads by default have no event loop.
665- """
682+ class EventLoopPolicy (AbstractEventLoopPolicy ):
683+ """Rubicon event loop policy.
666684
667- def __init__ (self ):
668- self ._lifecycle = None
669- self ._default_loop = None
670- self ._watcher_lock = threading .Lock ()
671- self ._watcher = None
672- self ._policy = DefaultEventLoopPolicy ()
673- self ._policy .new_event_loop = self .new_event_loop
674- self .get_event_loop = self ._policy .get_event_loop
675- self .set_event_loop = self ._policy .set_event_loop
685+ In this policy, each thread has its own event loop. However, we only
686+ automatically create an event loop by default for the main thread; other
687+ threads by default have no event loop.
676688
677- def new_event_loop (self ):
678- """Create a new event loop and return it."""
679- if (
680- not self ._default_loop
681- and threading .current_thread () == threading .main_thread ()
682- ):
683- loop = self .get_default_loop ()
684- else :
689+ **DEPRECATED** - Python 3.14 deprecated the concept of manually creating
690+ EventLoopPolicies. Create and use a ``RubiconEventLoop`` instance instead of
691+ installing an event loop policy and calling ``asyncio.new_event_loop()``.
692+ """
693+
694+ def __init__ (self ):
695+ warnings .warn (
696+ "Custom EventLoopPolicy instances have been deprecated by Python 3.14. "
697+ "Create and use a `RubiconEventLoop` instance directly instead of "
698+ "installing an event loop policy and calling `asyncio.new_event_loop()`." ,
699+ DeprecationWarning ,
700+ stacklevel = 2 ,
701+ )
702+
703+ self ._lifecycle = None
704+ self ._default_loop = None
705+ if sys .version_info < (3 , 14 ):
706+ self ._watcher_lock = threading .Lock ()
707+ self ._watcher = None
708+ self ._policy = DefaultEventLoopPolicy ()
709+ self ._policy .new_event_loop = self .new_event_loop
710+ self .get_event_loop = self ._policy .get_event_loop
711+ self .set_event_loop = self ._policy .set_event_loop
712+
713+ def new_event_loop (self ):
714+ """Create a new event loop and return it."""
715+ if (
716+ not self ._default_loop
717+ and threading .current_thread () == threading .main_thread ()
718+ ):
719+ loop = self .get_default_loop ()
720+ else :
721+ loop = CFEventLoop (self ._lifecycle )
722+ loop ._policy = self
723+
724+ return loop
725+
726+ def get_default_loop (self ):
727+ """Get the default event loop."""
728+ if not self ._default_loop :
729+ self ._default_loop = self ._new_default_loop ()
730+ return self ._default_loop
731+
732+ def _new_default_loop (self ):
685733 loop = CFEventLoop (self ._lifecycle )
686- loop ._policy = self
734+ loop ._policy = self
735+ return loop
687736
688- return loop
737+ if sys . version_info < ( 3 , 14 ):
689738
690- def get_default_loop (self ):
691- """Get the default event loop."""
692- if not self ._default_loop :
693- self ._default_loop = self ._new_default_loop ()
694- return self ._default_loop
739+ def _init_watcher (self ):
740+ with events ._lock :
741+ if self ._watcher is None : # pragma: no branch
742+ self ._watcher = SafeChildWatcher ()
743+ if threading .current_thread () == threading .main_thread ():
744+ self ._watcher .attach_loop (self ._default_loop )
695745
696- def _new_default_loop (self ):
697- loop = CFEventLoop (self ._lifecycle )
698- loop ._policy = self
699- return loop
746+ def get_child_watcher (self ):
747+ """Get the watcher for child processes.
700748
701- if sys .version_info < (3 , 14 ):
749+ If not yet set, a :class:`~asyncio.SafeChildWatcher` object is
750+ automatically created.
702751
703- def _init_watcher (self ):
704- with events ._lock :
705- if self ._watcher is None : # pragma: no branch
706- self ._watcher = SafeChildWatcher ()
707- if threading .current_thread () == threading .main_thread ():
708- self ._watcher .attach_loop (self ._default_loop )
752+ .. note::
753+ Child watcher support was removed in Python 3.14
754+ """
755+ if self ._watcher is None :
756+ self ._init_watcher ()
709757
710- def get_child_watcher (self ):
711- """Get the watcher for child processes.
758+ return self ._watcher
712759
713- If not yet set, a :class:`~asyncio.SafeChildWatcher` object is
714- automatically created .
760+ def set_child_watcher ( self , watcher ):
761+ """Set the watcher for child processes .
715762
716- .. note::
717- Child watcher support was removed in Python 3.14
718- """
719- if self ._watcher is None :
720- self ._init_watcher ()
763+ .. note::
764+ Child watcher support was removed in Python 3.14
765+ """
766+ if self ._watcher is not None :
767+ self ._watcher . close ()
721768
722- return self ._watcher
769+ self ._watcher = watcher
723770
724- def set_child_watcher (self , watcher ):
725- """Set the watcher for child processes.
726771
727- .. note::
728- Child watcher support was removed in Python 3.14
729- """
730- if self ._watcher is not None :
731- self ._watcher .close ()
772+ if sys .version_info < (3 , 14 ):
773+
774+ def RubiconEventLoop ():
775+ """Create a new Rubicon CFEventLoop instance."""
776+ # If they're using RubiconEventLoop(), they've done the necessary adaptation.
777+ with warnings .catch_warnings ():
778+ warnings .filterwarnings (
779+ "ignore" ,
780+ message = r"^Custom EventLoopPolicy instances have been deprecated by Python 3.14" ,
781+ category = DeprecationWarning ,
782+ )
783+ policy = EventLoopPolicy ()
784+ set_event_loop_policy (policy )
785+ return policy .new_event_loop ()
732786
733- self ._watcher = watcher
787+ else :
788+ RubiconEventLoop = CFEventLoop
734789
735790
736791class CFLifecycle :
0 commit comments