28
28
29
29
__all__ = (
30
30
'SelectorEventLoop' ,
31
- 'AbstractChildWatcher' ,
32
- 'PidfdChildWatcher' ,
33
- 'ThreadedChildWatcher' ,
34
31
'DefaultEventLoopPolicy' ,
35
32
'EventLoop' ,
36
33
)
@@ -65,6 +62,10 @@ def __init__(self, selector=None):
65
62
super ().__init__ (selector )
66
63
self ._signal_handlers = {}
67
64
self ._unix_server_sockets = {}
65
+ if can_use_pidfd ():
66
+ self ._watcher = _PidfdChildWatcher ()
67
+ else :
68
+ self ._watcher = _ThreadedChildWatcher ()
68
69
69
70
def close (self ):
70
71
super ().close ()
@@ -197,33 +198,22 @@ def _make_write_pipe_transport(self, pipe, protocol, waiter=None,
197
198
async def _make_subprocess_transport (self , protocol , args , shell ,
198
199
stdin , stdout , stderr , bufsize ,
199
200
extra = None , ** kwargs ):
200
- with warnings .catch_warnings ():
201
- warnings .simplefilter ('ignore' , DeprecationWarning )
202
- watcher = events .get_event_loop_policy ()._watcher
203
-
204
- with watcher :
205
- if not watcher .is_active ():
206
- # Check early.
207
- # Raising exception before process creation
208
- # prevents subprocess execution if the watcher
209
- # is not ready to handle it.
210
- raise RuntimeError ("asyncio.get_child_watcher() is not activated, "
211
- "subprocess support is not installed." )
212
- waiter = self .create_future ()
213
- transp = _UnixSubprocessTransport (self , protocol , args , shell ,
214
- stdin , stdout , stderr , bufsize ,
215
- waiter = waiter , extra = extra ,
216
- ** kwargs )
217
- watcher .add_child_handler (transp .get_pid (),
218
- self ._child_watcher_callback , transp )
219
- try :
220
- await waiter
221
- except (SystemExit , KeyboardInterrupt ):
222
- raise
223
- except BaseException :
224
- transp .close ()
225
- await transp ._wait ()
226
- raise
201
+ watcher = self ._watcher
202
+ waiter = self .create_future ()
203
+ transp = _UnixSubprocessTransport (self , protocol , args , shell ,
204
+ stdin , stdout , stderr , bufsize ,
205
+ waiter = waiter , extra = extra ,
206
+ ** kwargs )
207
+ watcher .add_child_handler (transp .get_pid (),
208
+ self ._child_watcher_callback , transp )
209
+ try :
210
+ await waiter
211
+ except (SystemExit , KeyboardInterrupt ):
212
+ raise
213
+ except BaseException :
214
+ transp .close ()
215
+ await transp ._wait ()
216
+ raise
227
217
228
218
return transp
229
219
@@ -865,93 +855,7 @@ def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
865
855
stdin_w .close ()
866
856
867
857
868
- class AbstractChildWatcher :
869
- """Abstract base class for monitoring child processes.
870
-
871
- Objects derived from this class monitor a collection of subprocesses and
872
- report their termination or interruption by a signal.
873
-
874
- New callbacks are registered with .add_child_handler(). Starting a new
875
- process must be done within a 'with' block to allow the watcher to suspend
876
- its activity until the new process if fully registered (this is needed to
877
- prevent a race condition in some implementations).
878
-
879
- Example:
880
- with watcher:
881
- proc = subprocess.Popen("sleep 1")
882
- watcher.add_child_handler(proc.pid, callback)
883
-
884
- Notes:
885
- Implementations of this class must be thread-safe.
886
-
887
- Since child watcher objects may catch the SIGCHLD signal and call
888
- waitpid(-1), there should be only one active object per process.
889
- """
890
-
891
- def __init_subclass__ (cls ) -> None :
892
- if cls .__module__ != __name__ :
893
- warnings ._deprecated ("AbstractChildWatcher" ,
894
- "{name!r} is deprecated as of Python 3.12 and will be "
895
- "removed in Python {remove}." ,
896
- remove = (3 , 14 ))
897
-
898
- def add_child_handler (self , pid , callback , * args ):
899
- """Register a new child handler.
900
-
901
- Arrange for callback(pid, returncode, *args) to be called when
902
- process 'pid' terminates. Specifying another callback for the same
903
- process replaces the previous handler.
904
-
905
- Note: callback() must be thread-safe.
906
- """
907
- raise NotImplementedError ()
908
-
909
- def remove_child_handler (self , pid ):
910
- """Removes the handler for process 'pid'.
911
-
912
- The function returns True if the handler was successfully removed,
913
- False if there was nothing to remove."""
914
-
915
- raise NotImplementedError ()
916
-
917
- def attach_loop (self , loop ):
918
- """Attach the watcher to an event loop.
919
-
920
- If the watcher was previously attached to an event loop, then it is
921
- first detached before attaching to the new loop.
922
-
923
- Note: loop may be None.
924
- """
925
- raise NotImplementedError ()
926
-
927
- def close (self ):
928
- """Close the watcher.
929
-
930
- This must be called to make sure that any underlying resource is freed.
931
- """
932
- raise NotImplementedError ()
933
-
934
- def is_active (self ):
935
- """Return ``True`` if the watcher is active and is used by the event loop.
936
-
937
- Return True if the watcher is installed and ready to handle process exit
938
- notifications.
939
-
940
- """
941
- raise NotImplementedError ()
942
-
943
- def __enter__ (self ):
944
- """Enter the watcher's context and allow starting new processes
945
-
946
- This function must return self"""
947
- raise NotImplementedError ()
948
-
949
- def __exit__ (self , a , b , c ):
950
- """Exit the watcher's context"""
951
- raise NotImplementedError ()
952
-
953
-
954
- class PidfdChildWatcher (AbstractChildWatcher ):
858
+ class _PidfdChildWatcher :
955
859
"""Child watcher implementation using Linux's pid file descriptors.
956
860
957
861
This child watcher polls process file descriptors (pidfds) to await child
@@ -963,21 +867,9 @@ class PidfdChildWatcher(AbstractChildWatcher):
963
867
recent (5.3+) kernels.
964
868
"""
965
869
966
- def __enter__ (self ):
967
- return self
968
-
969
- def __exit__ (self , exc_type , exc_value , exc_traceback ):
970
- pass
971
-
972
870
def is_active (self ):
973
871
return True
974
872
975
- def close (self ):
976
- pass
977
-
978
- def attach_loop (self , loop ):
979
- pass
980
-
981
873
def add_child_handler (self , pid , callback , * args ):
982
874
loop = events .get_running_loop ()
983
875
pidfd = os .pidfd_open (pid )
@@ -1002,14 +894,7 @@ def _do_wait(self, pid, pidfd, callback, args):
1002
894
os .close (pidfd )
1003
895
callback (pid , returncode , * args )
1004
896
1005
- def remove_child_handler (self , pid ):
1006
- # asyncio never calls remove_child_handler() !!!
1007
- # The method is no-op but is implemented because
1008
- # abstract base classes require it.
1009
- return True
1010
-
1011
-
1012
- class ThreadedChildWatcher (AbstractChildWatcher ):
897
+ class _ThreadedChildWatcher :
1013
898
"""Threaded child watcher implementation.
1014
899
1015
900
The watcher uses a thread per process
@@ -1029,15 +914,6 @@ def __init__(self):
1029
914
def is_active (self ):
1030
915
return True
1031
916
1032
- def close (self ):
1033
- pass
1034
-
1035
- def __enter__ (self ):
1036
- return self
1037
-
1038
- def __exit__ (self , exc_type , exc_val , exc_tb ):
1039
- pass
1040
-
1041
917
def __del__ (self , _warn = warnings .warn ):
1042
918
threads = [thread for thread in list (self ._threads .values ())
1043
919
if thread .is_alive ()]
@@ -1055,15 +931,6 @@ def add_child_handler(self, pid, callback, *args):
1055
931
self ._threads [pid ] = thread
1056
932
thread .start ()
1057
933
1058
- def remove_child_handler (self , pid ):
1059
- # asyncio never calls remove_child_handler() !!!
1060
- # The method is no-op but is implemented because
1061
- # abstract base classes require it.
1062
- return True
1063
-
1064
- def attach_loop (self , loop ):
1065
- pass
1066
-
1067
934
def _do_waitpid (self , loop , expected_pid , callback , args ):
1068
935
assert expected_pid > 0
1069
936
@@ -1103,29 +970,9 @@ def can_use_pidfd():
1103
970
1104
971
1105
972
class _UnixDefaultEventLoopPolicy (events .BaseDefaultEventLoopPolicy ):
1106
- """UNIX event loop policy with a watcher for child processes. """
973
+ """UNIX event loop policy"""
1107
974
_loop_factory = _UnixSelectorEventLoop
1108
975
1109
- def __init__ (self ):
1110
- super ().__init__ ()
1111
- if can_use_pidfd ():
1112
- self ._watcher = PidfdChildWatcher ()
1113
- else :
1114
- self ._watcher = ThreadedChildWatcher ()
1115
-
1116
- def set_event_loop (self , loop ):
1117
- """Set the event loop.
1118
-
1119
- As a side effect, if a child watcher was set before, then calling
1120
- .set_event_loop() from the main thread will call .attach_loop(loop) on
1121
- the child watcher.
1122
- """
1123
-
1124
- super ().set_event_loop (loop )
1125
-
1126
- if (self ._watcher is not None and
1127
- threading .current_thread () is threading .main_thread ()):
1128
- self ._watcher .attach_loop (loop )
1129
976
1130
977
SelectorEventLoop = _UnixSelectorEventLoop
1131
978
DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
0 commit comments