| 
									
										
										
										
											2014-02-18 22:27:48 -05:00
										 |  |  | """Selector event loop for Unix with signal handling.""" | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import errno | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  | import io | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  | import itertools | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2017-11-28 15:19:56 +01:00
										 |  |  | import selectors | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | import signal | 
					
						
							|  |  |  | import socket | 
					
						
							|  |  |  | import stat | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | import threading | 
					
						
							| 
									
										
										
										
											2015-01-29 17:50:58 +01:00
										 |  |  | import warnings | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  | from . import base_events | 
					
						
							| 
									
										
										
										
											2013-10-30 14:52:03 -07:00
										 |  |  | from . import base_subprocess | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | from . import constants | 
					
						
							| 
									
										
										
										
											2014-11-14 11:45:47 -08:00
										 |  |  | from . import coroutines | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | from . import events | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  | from . import exceptions | 
					
						
							| 
									
										
										
										
											2015-01-30 00:05:19 +01:00
										 |  |  | from . import futures | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | from . import selector_events | 
					
						
							| 
									
										
										
										
											2018-05-28 14:31:28 -04:00
										 |  |  | from . import tasks | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | from . import transports | 
					
						
							| 
									
										
										
										
											2013-10-17 15:39:45 -07:00
										 |  |  | from .log import logger | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | __all__ = ( | 
					
						
							|  |  |  |     'SelectorEventLoop', | 
					
						
							|  |  |  |     'AbstractChildWatcher', 'SafeChildWatcher', | 
					
						
							| 
									
										
										
										
											2019-11-14 21:47:56 -05:00
										 |  |  |     'FastChildWatcher', 'PidfdChildWatcher', | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |     'MultiLoopChildWatcher', 'ThreadedChildWatcher', | 
					
						
							|  |  |  |     'DefaultEventLoopPolicy', | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | if sys.platform == 'win32':  # pragma: no cover | 
					
						
							|  |  |  |     raise ImportError('Signals are not really supported on Windows') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-17 22:43:40 +02:00
										 |  |  | def _sighandler_noop(signum, frame): | 
					
						
							|  |  |  |     """Dummy signal handler.""" | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 12:11:24 +01:00
										 |  |  | def waitstatus_to_exitcode(status): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         return os.waitstatus_to_exitcode(status) | 
					
						
							|  |  |  |     except ValueError: | 
					
						
							|  |  |  |         # The child exited, but we don't understand its status. | 
					
						
							|  |  |  |         # This shouldn't happen, but if it does, let's just | 
					
						
							|  |  |  |         # return that status; perhaps that helps debug it. | 
					
						
							|  |  |  |         return status | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |     """Unix event loop.
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |     Adds signal handling and UNIX Domain Socket support to SelectorEventLoop. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, selector=None): | 
					
						
							|  |  |  |         super().__init__(selector) | 
					
						
							|  |  |  |         self._signal_handlers = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-06 20:25:50 -08:00
										 |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2022-09-17 14:12:45 -07:00
										 |  |  |         super().close() | 
					
						
							| 
									
										
										
										
											2017-12-21 17:06:46 +02:00
										 |  |  |         if not sys.is_finalizing(): | 
					
						
							|  |  |  |             for sig in list(self._signal_handlers): | 
					
						
							|  |  |  |                 self.remove_signal_handler(sig) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2017-12-24 13:50:03 +02:00
										 |  |  |             if self._signal_handlers: | 
					
						
							| 
									
										
										
										
											2017-12-26 11:53:38 +02:00
										 |  |  |                 warnings.warn(f"Closing the loop {self!r} " | 
					
						
							| 
									
										
										
										
											2017-12-24 13:50:03 +02:00
										 |  |  |                               f"on interpreter shutdown " | 
					
						
							| 
									
										
										
										
											2017-12-26 11:53:38 +02:00
										 |  |  |                               f"stage, skipping signal handlers removal", | 
					
						
							| 
									
										
										
										
											2017-12-24 13:50:03 +02:00
										 |  |  |                               ResourceWarning, | 
					
						
							|  |  |  |                               source=self) | 
					
						
							|  |  |  |                 self._signal_handlers.clear() | 
					
						
							| 
									
										
										
										
											2013-11-06 20:25:50 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-17 22:43:40 +02:00
										 |  |  |     def _process_self_data(self, data): | 
					
						
							|  |  |  |         for signum in data: | 
					
						
							|  |  |  |             if not signum: | 
					
						
							|  |  |  |                 # ignore null bytes written by _write_to_self() | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             self._handle_signal(signum) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def add_signal_handler(self, sig, callback, *args): | 
					
						
							|  |  |  |         """Add a handler for a signal.  UNIX only.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Raise ValueError if the signal number is invalid or uncatchable. | 
					
						
							|  |  |  |         Raise RuntimeError if there is a problem setting up the handler. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         if (coroutines.iscoroutine(callback) or | 
					
						
							|  |  |  |                 coroutines.iscoroutinefunction(callback)): | 
					
						
							| 
									
										
										
										
											2015-01-09 00:09:10 +01:00
										 |  |  |             raise TypeError("coroutines cannot be used " | 
					
						
							|  |  |  |                             "with add_signal_handler()") | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._check_signal(sig) | 
					
						
							| 
									
										
										
										
											2014-12-04 23:07:47 +01:00
										 |  |  |         self._check_closed() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         try: | 
					
						
							|  |  |  |             # set_wakeup_fd() raises ValueError if this is not the | 
					
						
							|  |  |  |             # main thread.  By calling it early we ensure that an | 
					
						
							|  |  |  |             # event loop running in another thread cannot add a signal | 
					
						
							|  |  |  |             # handler. | 
					
						
							| 
									
										
										
										
											2022-09-17 14:12:45 -07:00
										 |  |  |             signal.set_wakeup_fd(self._csock.fileno()) | 
					
						
							| 
									
										
										
										
											2014-07-23 18:21:45 +02:00
										 |  |  |         except (ValueError, OSError) as exc: | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             raise RuntimeError(str(exc)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |         handle = events.Handle(callback, args, self, None) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._signal_handlers[sig] = handle | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2014-07-17 22:43:40 +02:00
										 |  |  |             # Register a dummy signal handler to ask Python to write the signal | 
					
						
							| 
									
										
										
										
											2020-05-20 13:12:37 -04:00
										 |  |  |             # number in the wakeup file descriptor. _process_self_data() will | 
					
						
							| 
									
										
										
										
											2014-07-17 22:43:40 +02:00
										 |  |  |             # read signal numbers from this file descriptor to handle signals. | 
					
						
							|  |  |  |             signal.signal(sig, _sighandler_noop) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-05 22:47:19 +01:00
										 |  |  |             # Set SA_RESTART to limit EINTR occurrences. | 
					
						
							|  |  |  |             signal.siginterrupt(sig, False) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         except OSError as exc: | 
					
						
							|  |  |  |             del self._signal_handlers[sig] | 
					
						
							|  |  |  |             if not self._signal_handlers: | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     signal.set_wakeup_fd(-1) | 
					
						
							| 
									
										
										
										
											2014-07-23 18:21:45 +02:00
										 |  |  |                 except (ValueError, OSError) as nexc: | 
					
						
							| 
									
										
										
										
											2013-10-17 15:39:45 -07:00
										 |  |  |                     logger.info('set_wakeup_fd(-1) failed: %s', nexc) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if exc.errno == errno.EINVAL: | 
					
						
							| 
									
										
										
										
											2021-06-18 13:15:46 -07:00
										 |  |  |                 raise RuntimeError(f'sig {sig} cannot be caught') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-17 22:43:40 +02:00
										 |  |  |     def _handle_signal(self, sig): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """Internal helper that is the actual signal handler.""" | 
					
						
							|  |  |  |         handle = self._signal_handlers.get(sig) | 
					
						
							|  |  |  |         if handle is None: | 
					
						
							|  |  |  |             return  # Assume it's some race condition. | 
					
						
							|  |  |  |         if handle._cancelled: | 
					
						
							|  |  |  |             self.remove_signal_handler(sig)  # Remove it properly. | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._add_callback_signalsafe(handle) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_signal_handler(self, sig): | 
					
						
							|  |  |  |         """Remove a handler for a signal.  UNIX only.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Return True if a signal handler was removed, False if not. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._check_signal(sig) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             del self._signal_handlers[sig] | 
					
						
							|  |  |  |         except KeyError: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if sig == signal.SIGINT: | 
					
						
							|  |  |  |             handler = signal.default_int_handler | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             handler = signal.SIG_DFL | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             signal.signal(sig, handler) | 
					
						
							|  |  |  |         except OSError as exc: | 
					
						
							|  |  |  |             if exc.errno == errno.EINVAL: | 
					
						
							| 
									
										
										
										
											2021-06-18 13:15:46 -07:00
										 |  |  |                 raise RuntimeError(f'sig {sig} cannot be caught') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not self._signal_handlers: | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2022-09-17 14:12:45 -07:00
										 |  |  |                 signal.set_wakeup_fd(-1) | 
					
						
							| 
									
										
										
										
											2014-07-23 18:21:45 +02:00
										 |  |  |             except (ValueError, OSError) as exc: | 
					
						
							| 
									
										
										
										
											2013-10-17 15:39:45 -07:00
										 |  |  |                 logger.info('set_wakeup_fd(-1) failed: %s', exc) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _check_signal(self, sig): | 
					
						
							|  |  |  |         """Internal helper to validate a signal.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Raise ValueError if the signal number is invalid or uncatchable. | 
					
						
							|  |  |  |         Raise RuntimeError if there is a problem setting up the handler. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if not isinstance(sig, int): | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             raise TypeError(f'sig must be an int, not {sig!r}') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 13:00:50 +02:00
										 |  |  |         if sig not in signal.valid_signals(): | 
					
						
							|  |  |  |             raise ValueError(f'invalid signal number {sig}') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _make_read_pipe_transport(self, pipe, protocol, waiter=None, | 
					
						
							|  |  |  |                                   extra=None): | 
					
						
							|  |  |  |         return _UnixReadPipeTransport(self, pipe, protocol, waiter, extra) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_write_pipe_transport(self, pipe, protocol, waiter=None, | 
					
						
							|  |  |  |                                    extra=None): | 
					
						
							|  |  |  |         return _UnixWritePipeTransport(self, pipe, protocol, waiter, extra) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     async def _make_subprocess_transport(self, protocol, args, shell, | 
					
						
							|  |  |  |                                          stdin, stdout, stderr, bufsize, | 
					
						
							|  |  |  |                                          extra=None, **kwargs): | 
					
						
							| 
									
										
										
										
											2022-10-16 04:39:30 +05:30
										 |  |  |         with warnings.catch_warnings(): | 
					
						
							|  |  |  |             warnings.simplefilter('ignore', DeprecationWarning) | 
					
						
							| 
									
										
										
										
											2022-10-17 20:57:02 +05:30
										 |  |  |             watcher = events.get_child_watcher() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with watcher: | 
					
						
							|  |  |  |             if not watcher.is_active(): | 
					
						
							|  |  |  |                 # Check early. | 
					
						
							|  |  |  |                 # Raising exception before process creation | 
					
						
							|  |  |  |                 # prevents subprocess execution if the watcher | 
					
						
							|  |  |  |                 # is not ready to handle it. | 
					
						
							|  |  |  |                 raise RuntimeError("asyncio.get_child_watcher() is not activated, " | 
					
						
							|  |  |  |                                 "subprocess support is not installed.") | 
					
						
							|  |  |  |             waiter = self.create_future() | 
					
						
							|  |  |  |             transp = _UnixSubprocessTransport(self, protocol, args, shell, | 
					
						
							|  |  |  |                                             stdin, stdout, stderr, bufsize, | 
					
						
							|  |  |  |                                             waiter=waiter, extra=extra, | 
					
						
							|  |  |  |                                             **kwargs) | 
					
						
							|  |  |  |             watcher.add_child_handler(transp.get_pid(), | 
					
						
							|  |  |  |                                     self._child_watcher_callback, transp) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 await waiter | 
					
						
							|  |  |  |             except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |             except BaseException: | 
					
						
							|  |  |  |                 transp.close() | 
					
						
							|  |  |  |                 await transp._wait() | 
					
						
							|  |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2014-01-10 13:28:59 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         return transp | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     def _child_watcher_callback(self, pid, returncode, transp): | 
					
						
							| 
									
										
										
										
											2022-09-22 22:13:47 +05:30
										 |  |  |         # Skip one iteration for callbacks to be executed | 
					
						
							|  |  |  |         self.call_soon_threadsafe(self.call_soon, transp._process_exited, returncode) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-19 19:45:42 +00:00
										 |  |  |     async def create_unix_connection( | 
					
						
							|  |  |  |             self, protocol_factory, path=None, *, | 
					
						
							|  |  |  |             ssl=None, sock=None, | 
					
						
							|  |  |  |             server_hostname=None, | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             ssl_handshake_timeout=None, | 
					
						
							|  |  |  |             ssl_shutdown_timeout=None): | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |         assert server_hostname is None or isinstance(server_hostname, str) | 
					
						
							|  |  |  |         if ssl: | 
					
						
							|  |  |  |             if server_hostname is None: | 
					
						
							|  |  |  |                 raise ValueError( | 
					
						
							|  |  |  |                     'you have to pass server_hostname when using ssl') | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if server_hostname is not None: | 
					
						
							|  |  |  |                 raise ValueError('server_hostname is only meaningful with ssl') | 
					
						
							| 
									
										
										
										
											2017-12-20 20:24:43 +02:00
										 |  |  |             if ssl_handshake_timeout is not None: | 
					
						
							|  |  |  |                 raise ValueError( | 
					
						
							|  |  |  |                     'ssl_handshake_timeout is only meaningful with ssl') | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             if ssl_shutdown_timeout is not None: | 
					
						
							|  |  |  |                 raise ValueError( | 
					
						
							|  |  |  |                     'ssl_shutdown_timeout is only meaningful with ssl') | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if path is not None: | 
					
						
							|  |  |  |             if sock is not None: | 
					
						
							|  |  |  |                 raise ValueError( | 
					
						
							|  |  |  |                     'path and sock can not be specified at the same time') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 18:23:43 +02:00
										 |  |  |             path = os.fspath(path) | 
					
						
							| 
									
										
										
										
											2014-02-19 01:45:59 +01:00
										 |  |  |             sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 sock.setblocking(False) | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |                 await self.sock_connect(sock, path) | 
					
						
							| 
									
										
										
										
											2014-02-19 01:45:59 +01:00
										 |  |  |             except: | 
					
						
							|  |  |  |                 sock.close() | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |                 raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if sock is None: | 
					
						
							|  |  |  |                 raise ValueError('no path and sock were specified') | 
					
						
							| 
									
										
										
										
											2016-10-07 12:39:57 -04:00
										 |  |  |             if (sock.family != socket.AF_UNIX or | 
					
						
							| 
									
										
										
										
											2017-12-19 06:44:37 -05:00
										 |  |  |                     sock.type != socket.SOCK_STREAM): | 
					
						
							| 
									
										
										
										
											2016-10-07 12:39:57 -04:00
										 |  |  |                 raise ValueError( | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                     f'A UNIX Domain Stream Socket was expected, got {sock!r}') | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |             sock.setblocking(False) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         transport, protocol = await self._create_connection_transport( | 
					
						
							| 
									
										
										
										
											2017-12-19 19:45:42 +00:00
										 |  |  |             sock, protocol_factory, ssl, server_hostname, | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             ssl_handshake_timeout=ssl_handshake_timeout, | 
					
						
							|  |  |  |             ssl_shutdown_timeout=ssl_shutdown_timeout) | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |         return transport, protocol | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-19 19:45:42 +00:00
										 |  |  |     async def create_unix_server( | 
					
						
							|  |  |  |             self, protocol_factory, path=None, *, | 
					
						
							|  |  |  |             sock=None, backlog=100, ssl=None, | 
					
						
							| 
									
										
										
										
											2018-01-25 18:08:09 -05:00
										 |  |  |             ssl_handshake_timeout=None, | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             ssl_shutdown_timeout=None, | 
					
						
							| 
									
										
										
										
											2018-01-25 18:08:09 -05:00
										 |  |  |             start_serving=True): | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |         if isinstance(ssl, bool): | 
					
						
							|  |  |  |             raise TypeError('ssl argument must be an SSLContext or None') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-20 20:24:43 +02:00
										 |  |  |         if ssl_handshake_timeout is not None and not ssl: | 
					
						
							|  |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 'ssl_handshake_timeout is only meaningful with ssl') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         if ssl_shutdown_timeout is not None and not ssl: | 
					
						
							|  |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 'ssl_shutdown_timeout is only meaningful with ssl') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |         if path is not None: | 
					
						
							| 
									
										
										
										
											2014-04-07 11:18:54 +02:00
										 |  |  |             if sock is not None: | 
					
						
							|  |  |  |                 raise ValueError( | 
					
						
							|  |  |  |                     'path and sock can not be specified at the same time') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 18:23:43 +02:00
										 |  |  |             path = os.fspath(path) | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |             sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-09 12:15:08 -04:00
										 |  |  |             # Check for abstract socket. `str` and `bytes` paths are supported. | 
					
						
							|  |  |  |             if path[0] not in (0, '\x00'): | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     if stat.S_ISSOCK(os.stat(path).st_mode): | 
					
						
							|  |  |  |                         os.remove(path) | 
					
						
							|  |  |  |                 except FileNotFoundError: | 
					
						
							|  |  |  |                     pass | 
					
						
							|  |  |  |                 except OSError as err: | 
					
						
							|  |  |  |                     # Directory may have permissions only to create socket. | 
					
						
							| 
									
										
										
										
											2017-11-29 18:23:43 +02:00
										 |  |  |                     logger.error('Unable to check or remove stale UNIX socket ' | 
					
						
							|  |  |  |                                  '%r: %r', path, err) | 
					
						
							| 
									
										
										
										
											2016-10-09 12:15:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 sock.bind(path) | 
					
						
							|  |  |  |             except OSError as exc: | 
					
						
							| 
									
										
										
										
											2014-02-19 01:45:59 +01:00
										 |  |  |                 sock.close() | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |                 if exc.errno == errno.EADDRINUSE: | 
					
						
							|  |  |  |                     # Let's improve the error message by adding | 
					
						
							|  |  |  |                     # with what exact address it occurs. | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                     msg = f'Address {path!r} is already in use' | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |                     raise OSError(errno.EADDRINUSE, msg) from None | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     raise | 
					
						
							| 
									
										
										
										
											2014-06-04 00:11:52 +02:00
										 |  |  |             except: | 
					
						
							|  |  |  |                 sock.close() | 
					
						
							|  |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |         else: | 
					
						
							|  |  |  |             if sock is None: | 
					
						
							|  |  |  |                 raise ValueError( | 
					
						
							|  |  |  |                     'path was not specified, and no sock specified') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-07 12:39:57 -04:00
										 |  |  |             if (sock.family != socket.AF_UNIX or | 
					
						
							| 
									
										
										
										
											2017-12-19 06:44:37 -05:00
										 |  |  |                     sock.type != socket.SOCK_STREAM): | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |                 raise ValueError( | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                     f'A UNIX Domain Stream Socket was expected, got {sock!r}') | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         sock.setblocking(False) | 
					
						
							| 
									
										
										
										
											2018-01-25 18:08:09 -05:00
										 |  |  |         server = base_events.Server(self, [sock], protocol_factory, | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |                                     ssl, backlog, ssl_handshake_timeout, | 
					
						
							|  |  |  |                                     ssl_shutdown_timeout) | 
					
						
							| 
									
										
										
										
											2018-01-25 18:08:09 -05:00
										 |  |  |         if start_serving: | 
					
						
							|  |  |  |             server._start_serving() | 
					
						
							| 
									
										
										
										
											2018-05-28 14:31:28 -04:00
										 |  |  |             # Skip one loop iteration so that all 'loop.add_reader' | 
					
						
							|  |  |  |             # go through. | 
					
						
							| 
									
										
										
										
											2020-11-28 10:21:17 +02:00
										 |  |  |             await tasks.sleep(0) | 
					
						
							| 
									
										
										
										
											2018-01-25 18:08:09 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 12:15:06 -05:00
										 |  |  |         return server | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  |     async def _sock_sendfile_native(self, sock, file, offset, count): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             os.sendfile | 
					
						
							| 
									
										
										
										
											2019-11-19 21:34:03 +00:00
										 |  |  |         except AttributeError: | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  |             raise exceptions.SendfileNotAvailableError( | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  |                 "os.sendfile() is not available") | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             fileno = file.fileno() | 
					
						
							|  |  |  |         except (AttributeError, io.UnsupportedOperation) as err: | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  |             raise exceptions.SendfileNotAvailableError("not a regular file") | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             fsize = os.fstat(fileno).st_size | 
					
						
							| 
									
										
										
										
											2019-11-19 21:34:03 +00:00
										 |  |  |         except OSError: | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  |             raise exceptions.SendfileNotAvailableError("not a regular file") | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  |         blocksize = count if count else fsize | 
					
						
							|  |  |  |         if not blocksize: | 
					
						
							|  |  |  |             return 0  # empty file | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         fut = self.create_future() | 
					
						
							|  |  |  |         self._sock_sendfile_native_impl(fut, None, sock, fileno, | 
					
						
							|  |  |  |                                         offset, count, blocksize, 0) | 
					
						
							|  |  |  |         return await fut | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno, | 
					
						
							|  |  |  |                                    offset, count, blocksize, total_sent): | 
					
						
							|  |  |  |         fd = sock.fileno() | 
					
						
							|  |  |  |         if registered_fd is not None: | 
					
						
							|  |  |  |             # Remove the callback early.  It should be rare that the | 
					
						
							|  |  |  |             # selector says the fd is ready but the call still returns | 
					
						
							|  |  |  |             # EAGAIN, and I am willing to take a hit in that case in | 
					
						
							|  |  |  |             # order to simplify the common case. | 
					
						
							|  |  |  |             self.remove_writer(registered_fd) | 
					
						
							|  |  |  |         if fut.cancelled(): | 
					
						
							|  |  |  |             self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if count: | 
					
						
							|  |  |  |             blocksize = count - total_sent | 
					
						
							|  |  |  |             if blocksize <= 0: | 
					
						
							|  |  |  |                 self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
					
						
							|  |  |  |                 fut.set_result(total_sent) | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             sent = os.sendfile(fd, fileno, offset, blocksize) | 
					
						
							|  |  |  |         except (BlockingIOError, InterruptedError): | 
					
						
							|  |  |  |             if registered_fd is None: | 
					
						
							|  |  |  |                 self._sock_add_cancellation_callback(fut, sock) | 
					
						
							|  |  |  |             self.add_writer(fd, self._sock_sendfile_native_impl, fut, | 
					
						
							|  |  |  |                             fd, sock, fileno, | 
					
						
							|  |  |  |                             offset, count, blocksize, total_sent) | 
					
						
							|  |  |  |         except OSError as exc: | 
					
						
							| 
									
										
										
										
											2018-01-27 17:22:01 -05:00
										 |  |  |             if (registered_fd is not None and | 
					
						
							|  |  |  |                     exc.errno == errno.ENOTCONN and | 
					
						
							|  |  |  |                     type(exc) is not ConnectionError): | 
					
						
							|  |  |  |                 # If we have an ENOTCONN and this isn't a first call to | 
					
						
							|  |  |  |                 # sendfile(), i.e. the connection was closed in the middle | 
					
						
							|  |  |  |                 # of the operation, normalize the error to ConnectionError | 
					
						
							|  |  |  |                 # to make it consistent across all Posix systems. | 
					
						
							|  |  |  |                 new_exc = ConnectionError( | 
					
						
							|  |  |  |                     "socket is not connected", errno.ENOTCONN) | 
					
						
							|  |  |  |                 new_exc.__cause__ = exc | 
					
						
							|  |  |  |                 exc = new_exc | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  |             if total_sent == 0: | 
					
						
							|  |  |  |                 # We can get here for different reasons, the main | 
					
						
							|  |  |  |                 # one being 'file' is not a regular mmap(2)-like | 
					
						
							|  |  |  |                 # file, in which case we'll fall back on using | 
					
						
							|  |  |  |                 # plain send(). | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  |                 err = exceptions.SendfileNotAvailableError( | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  |                     "os.sendfile call failed") | 
					
						
							|  |  |  |                 self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
					
						
							|  |  |  |                 fut.set_exception(err) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
					
						
							|  |  |  |                 fut.set_exception(exc) | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |         except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  |             self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
					
						
							|  |  |  |             fut.set_exception(exc) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if sent == 0: | 
					
						
							|  |  |  |                 # EOF | 
					
						
							|  |  |  |                 self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
					
						
							|  |  |  |                 fut.set_result(total_sent) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 offset += sent | 
					
						
							|  |  |  |                 total_sent += sent | 
					
						
							|  |  |  |                 if registered_fd is None: | 
					
						
							|  |  |  |                     self._sock_add_cancellation_callback(fut, sock) | 
					
						
							|  |  |  |                 self.add_writer(fd, self._sock_sendfile_native_impl, fut, | 
					
						
							|  |  |  |                                 fd, sock, fileno, | 
					
						
							|  |  |  |                                 offset, count, blocksize, total_sent) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _sock_sendfile_update_filepos(self, fileno, offset, total_sent): | 
					
						
							|  |  |  |         if total_sent > 0: | 
					
						
							|  |  |  |             os.lseek(fileno, offset, os.SEEK_SET) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _sock_add_cancellation_callback(self, fut, sock): | 
					
						
							|  |  |  |         def cb(fut): | 
					
						
							|  |  |  |             if fut.cancelled(): | 
					
						
							|  |  |  |                 fd = sock.fileno() | 
					
						
							|  |  |  |                 if fd != -1: | 
					
						
							|  |  |  |                     self.remove_writer(fd) | 
					
						
							|  |  |  |         fut.add_done_callback(cb) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | class _UnixReadPipeTransport(transports.ReadTransport): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 22:27:48 -05:00
										 |  |  |     max_size = 256 * 1024  # max bytes we read in one event loop iteration | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, loop, pipe, protocol, waiter=None, extra=None): | 
					
						
							|  |  |  |         super().__init__(extra) | 
					
						
							|  |  |  |         self._extra['pipe'] = pipe | 
					
						
							|  |  |  |         self._loop = loop | 
					
						
							|  |  |  |         self._pipe = pipe | 
					
						
							|  |  |  |         self._fileno = pipe.fileno() | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  |         self._protocol = protocol | 
					
						
							|  |  |  |         self._closing = False | 
					
						
							| 
									
										
										
										
											2019-09-29 15:00:35 +03:00
										 |  |  |         self._paused = False | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-21 20:37:14 -07:00
										 |  |  |         mode = os.fstat(self._fileno).st_mode | 
					
						
							| 
									
										
										
										
											2014-01-10 13:30:04 -08:00
										 |  |  |         if not (stat.S_ISFIFO(mode) or | 
					
						
							|  |  |  |                 stat.S_ISSOCK(mode) or | 
					
						
							|  |  |  |                 stat.S_ISCHR(mode)): | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  |             self._pipe = None | 
					
						
							|  |  |  |             self._fileno = None | 
					
						
							|  |  |  |             self._protocol = None | 
					
						
							| 
									
										
										
										
											2013-10-21 20:37:14 -07:00
										 |  |  |             raise ValueError("Pipe transport is for pipes/sockets only.") | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 18:23:43 +02:00
										 |  |  |         os.set_blocking(self._fileno, False) | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._loop.call_soon(self._protocol.connection_made, self) | 
					
						
							| 
									
										
										
										
											2015-01-29 14:15:19 +01:00
										 |  |  |         # only start reading when connection_made() has been called | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |         self._loop.call_soon(self._loop._add_reader, | 
					
						
							| 
									
										
										
										
											2015-01-29 14:15:19 +01:00
										 |  |  |                              self._fileno, self._read_ready) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if waiter is not None: | 
					
						
							| 
									
										
										
										
											2015-01-29 00:36:35 +01:00
										 |  |  |             # only wake up the waiter when connection_made() has been called | 
					
						
							| 
									
										
										
										
											2015-11-17 12:19:41 -05:00
										 |  |  |             self._loop.call_soon(futures._set_result_unless_cancelled, | 
					
						
							|  |  |  |                                  waiter, None) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2015-01-15 00:04:21 +01:00
										 |  |  |         info = [self.__class__.__name__] | 
					
						
							|  |  |  |         if self._pipe is None: | 
					
						
							|  |  |  |             info.append('closed') | 
					
						
							|  |  |  |         elif self._closing: | 
					
						
							|  |  |  |             info.append('closing') | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         info.append(f'fd={self._fileno}') | 
					
						
							| 
									
										
										
										
											2016-05-13 16:04:43 -04:00
										 |  |  |         selector = getattr(self._loop, '_selector', None) | 
					
						
							|  |  |  |         if self._pipe is not None and selector is not None: | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |             polling = selector_events._test_selector_event( | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                 selector, self._fileno, selectors.EVENT_READ) | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |             if polling: | 
					
						
							|  |  |  |                 info.append('polling') | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 info.append('idle') | 
					
						
							| 
									
										
										
										
											2016-05-13 16:04:43 -04:00
										 |  |  |         elif self._pipe is not None: | 
					
						
							|  |  |  |             info.append('open') | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             info.append('closed') | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         return '<{}>'.format(' '.join(info)) | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def _read_ready(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             data = os.read(self._fileno, self.max_size) | 
					
						
							|  |  |  |         except (BlockingIOError, InterruptedError): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         except OSError as exc: | 
					
						
							| 
									
										
										
										
											2014-02-19 01:40:41 +01:00
										 |  |  |             self._fatal_error(exc, 'Fatal read error on pipe transport') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         else: | 
					
						
							|  |  |  |             if data: | 
					
						
							|  |  |  |                 self._protocol.data_received(data) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |                 if self._loop.get_debug(): | 
					
						
							|  |  |  |                     logger.info("%r was closed by peer", self) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 self._closing = True | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |                 self._loop._remove_reader(self._fileno) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 self._loop.call_soon(self._protocol.eof_received) | 
					
						
							|  |  |  |                 self._loop.call_soon(self._call_connection_lost, None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-18 07:58:20 -07:00
										 |  |  |     def pause_reading(self): | 
					
						
							| 
									
										
										
										
											2019-09-29 15:00:35 +03:00
										 |  |  |         if self._closing or self._paused: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self._paused = True | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |         self._loop._remove_reader(self._fileno) | 
					
						
							| 
									
										
										
										
											2019-09-29 15:00:35 +03:00
										 |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             logger.debug("%r pauses reading", self) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-18 07:58:20 -07:00
										 |  |  |     def resume_reading(self): | 
					
						
							| 
									
										
										
										
											2019-09-29 15:00:35 +03:00
										 |  |  |         if self._closing or not self._paused: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self._paused = False | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |         self._loop._add_reader(self._fileno, self._read_ready) | 
					
						
							| 
									
										
										
										
											2019-09-29 15:00:35 +03:00
										 |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             logger.debug("%r resumes reading", self) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 21:11:02 -04:00
										 |  |  |     def set_protocol(self, protocol): | 
					
						
							|  |  |  |         self._protocol = protocol | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_protocol(self): | 
					
						
							|  |  |  |         return self._protocol | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-16 12:43:21 -05:00
										 |  |  |     def is_closing(self): | 
					
						
							|  |  |  |         return self._closing | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def close(self): | 
					
						
							|  |  |  |         if not self._closing: | 
					
						
							|  |  |  |             self._close(None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-10 11:24:40 +01:00
										 |  |  |     def __del__(self, _warn=warnings.warn): | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |         if self._pipe is not None: | 
					
						
							| 
									
										
										
										
											2019-01-10 11:24:40 +01:00
										 |  |  |             _warn(f"unclosed transport {self!r}", ResourceWarning, source=self) | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |             self._pipe.close() | 
					
						
							| 
									
										
										
										
											2015-01-29 17:50:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 01:40:41 +01:00
										 |  |  |     def _fatal_error(self, exc, message='Fatal error on pipe transport'): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         # should be called by exception handler only | 
					
						
							| 
									
										
										
										
											2014-08-25 23:20:52 +02:00
										 |  |  |         if (isinstance(exc, OSError) and exc.errno == errno.EIO): | 
					
						
							|  |  |  |             if self._loop.get_debug(): | 
					
						
							|  |  |  |                 logger.debug("%r: %s", self, message, exc_info=True) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2014-02-18 18:02:19 -05:00
										 |  |  |             self._loop.call_exception_handler({ | 
					
						
							| 
									
										
										
										
											2014-02-19 01:40:41 +01:00
										 |  |  |                 'message': message, | 
					
						
							| 
									
										
										
										
											2014-02-18 18:02:19 -05:00
										 |  |  |                 'exception': exc, | 
					
						
							|  |  |  |                 'transport': self, | 
					
						
							|  |  |  |                 'protocol': self._protocol, | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._close(exc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _close(self, exc): | 
					
						
							|  |  |  |         self._closing = True | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |         self._loop._remove_reader(self._fileno) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._loop.call_soon(self._call_connection_lost, exc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _call_connection_lost(self, exc): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self._protocol.connection_lost(exc) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self._pipe.close() | 
					
						
							|  |  |  |             self._pipe = None | 
					
						
							|  |  |  |             self._protocol = None | 
					
						
							|  |  |  |             self._loop = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  | class _UnixWritePipeTransport(transports._FlowControlMixin, | 
					
						
							| 
									
										
										
										
											2014-01-29 13:20:39 -08:00
										 |  |  |                               transports.WriteTransport): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, loop, pipe, protocol, waiter=None, extra=None): | 
					
						
							| 
									
										
										
										
											2014-11-05 15:27:41 +01:00
										 |  |  |         super().__init__(extra, loop) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._extra['pipe'] = pipe | 
					
						
							|  |  |  |         self._pipe = pipe | 
					
						
							|  |  |  |         self._fileno = pipe.fileno() | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  |         self._protocol = protocol | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |         self._buffer = bytearray() | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  |         self._conn_lost = 0 | 
					
						
							|  |  |  |         self._closing = False  # Set when close() or write_eof() called. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-21 20:37:14 -07:00
										 |  |  |         mode = os.fstat(self._fileno).st_mode | 
					
						
							| 
									
										
										
										
											2016-08-31 09:40:18 -07:00
										 |  |  |         is_char = stat.S_ISCHR(mode) | 
					
						
							|  |  |  |         is_fifo = stat.S_ISFIFO(mode) | 
					
						
							| 
									
										
										
										
											2013-10-21 20:37:14 -07:00
										 |  |  |         is_socket = stat.S_ISSOCK(mode) | 
					
						
							| 
									
										
										
										
											2016-08-31 09:40:18 -07:00
										 |  |  |         if not (is_char or is_fifo or is_socket): | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  |             self._pipe = None | 
					
						
							|  |  |  |             self._fileno = None | 
					
						
							|  |  |  |             self._protocol = None | 
					
						
							| 
									
										
										
										
											2014-01-25 15:32:06 +01:00
										 |  |  |             raise ValueError("Pipe transport is only for " | 
					
						
							|  |  |  |                              "pipes, sockets and character devices") | 
					
						
							| 
									
										
										
										
											2016-08-31 09:42:38 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 18:23:43 +02:00
										 |  |  |         os.set_blocking(self._fileno, False) | 
					
						
							| 
									
										
										
										
											2015-01-29 14:15:19 +01:00
										 |  |  |         self._loop.call_soon(self._protocol.connection_made, self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # On AIX, the reader trick (to be notified when the read end of the | 
					
						
							|  |  |  |         # socket is closed) only works for sockets. On other platforms it | 
					
						
							|  |  |  |         # works for pipes and sockets. (Exception: OS X 10.4?  Issue #19294.) | 
					
						
							| 
									
										
										
										
											2016-08-31 09:40:18 -07:00
										 |  |  |         if is_socket or (is_fifo and not sys.platform.startswith("aix")): | 
					
						
							| 
									
										
										
										
											2015-01-29 14:15:19 +01:00
										 |  |  |             # only start reading when connection_made() has been called | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |             self._loop.call_soon(self._loop._add_reader, | 
					
						
							| 
									
										
										
										
											2015-01-29 14:15:19 +01:00
										 |  |  |                                  self._fileno, self._read_ready) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if waiter is not None: | 
					
						
							| 
									
										
										
										
											2015-01-29 00:36:35 +01:00
										 |  |  |             # only wake up the waiter when connection_made() has been called | 
					
						
							| 
									
										
										
										
											2015-11-17 12:19:41 -05:00
										 |  |  |             self._loop.call_soon(futures._set_result_unless_cancelled, | 
					
						
							|  |  |  |                                  waiter, None) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2015-01-15 00:04:21 +01:00
										 |  |  |         info = [self.__class__.__name__] | 
					
						
							|  |  |  |         if self._pipe is None: | 
					
						
							|  |  |  |             info.append('closed') | 
					
						
							|  |  |  |         elif self._closing: | 
					
						
							|  |  |  |             info.append('closing') | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         info.append(f'fd={self._fileno}') | 
					
						
							| 
									
										
										
										
											2016-05-13 16:04:43 -04:00
										 |  |  |         selector = getattr(self._loop, '_selector', None) | 
					
						
							|  |  |  |         if self._pipe is not None and selector is not None: | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |             polling = selector_events._test_selector_event( | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                 selector, self._fileno, selectors.EVENT_WRITE) | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |             if polling: | 
					
						
							|  |  |  |                 info.append('polling') | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 info.append('idle') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             bufsize = self.get_write_buffer_size() | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             info.append(f'bufsize={bufsize}') | 
					
						
							| 
									
										
										
										
											2016-05-13 16:04:43 -04:00
										 |  |  |         elif self._pipe is not None: | 
					
						
							|  |  |  |             info.append('open') | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             info.append('closed') | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         return '<{}>'.format(' '.join(info)) | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-29 13:20:39 -08:00
										 |  |  |     def get_write_buffer_size(self): | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |         return len(self._buffer) | 
					
						
							| 
									
										
										
										
											2014-01-29 13:20:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def _read_ready(self): | 
					
						
							| 
									
										
										
										
											2013-10-21 20:37:14 -07:00
										 |  |  |         # Pipe was closed by peer. | 
					
						
							| 
									
										
										
										
											2014-07-12 03:11:53 +02:00
										 |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             logger.info("%r was closed by peer", self) | 
					
						
							| 
									
										
										
										
											2014-01-31 13:04:28 +01:00
										 |  |  |         if self._buffer: | 
					
						
							|  |  |  |             self._close(BrokenPipeError()) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._close() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def write(self, data): | 
					
						
							| 
									
										
										
										
											2014-01-29 13:20:39 -08:00
										 |  |  |         assert isinstance(data, (bytes, bytearray, memoryview)), repr(data) | 
					
						
							|  |  |  |         if isinstance(data, bytearray): | 
					
						
							|  |  |  |             data = memoryview(data) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if not data: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self._conn_lost or self._closing: | 
					
						
							|  |  |  |             if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES: | 
					
						
							| 
									
										
										
										
											2013-10-17 15:39:45 -07:00
										 |  |  |                 logger.warning('pipe closed by peer or ' | 
					
						
							|  |  |  |                                'os.write(pipe, data) raised exception.') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             self._conn_lost += 1 | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not self._buffer: | 
					
						
							|  |  |  |             # Attempt to send it right away first. | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 n = os.write(self._fileno, data) | 
					
						
							|  |  |  |             except (BlockingIOError, InterruptedError): | 
					
						
							|  |  |  |                 n = 0 | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |             except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |             except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 self._conn_lost += 1 | 
					
						
							| 
									
										
										
										
											2014-02-19 01:40:41 +01:00
										 |  |  |                 self._fatal_error(exc, 'Fatal write error on pipe transport') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 return | 
					
						
							|  |  |  |             if n == len(data): | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             elif n > 0: | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |                 data = memoryview(data)[n:] | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |             self._loop._add_writer(self._fileno, self._write_ready) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |         self._buffer += data | 
					
						
							| 
									
										
										
										
											2014-01-29 13:20:39 -08:00
										 |  |  |         self._maybe_pause_protocol() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _write_ready(self): | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |         assert self._buffer, 'Data should not be empty' | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |             n = os.write(self._fileno, self._buffer) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         except (BlockingIOError, InterruptedError): | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |             pass | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |         except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |             self._buffer.clear() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             self._conn_lost += 1 | 
					
						
							|  |  |  |             # Remove writer here, _fatal_error() doesn't it | 
					
						
							|  |  |  |             # because _buffer is empty. | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |             self._loop._remove_writer(self._fileno) | 
					
						
							| 
									
										
										
										
											2014-02-19 01:40:41 +01:00
										 |  |  |             self._fatal_error(exc, 'Fatal write error on pipe transport') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |             if n == len(self._buffer): | 
					
						
							|  |  |  |                 self._buffer.clear() | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |                 self._loop._remove_writer(self._fileno) | 
					
						
							| 
									
										
										
										
											2014-01-29 13:20:39 -08:00
										 |  |  |                 self._maybe_resume_protocol()  # May append to buffer. | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |                 if self._closing: | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |                     self._loop._remove_reader(self._fileno) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                     self._call_connection_lost(None) | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             elif n > 0: | 
					
						
							| 
									
										
										
										
											2016-09-15 16:51:48 -04:00
										 |  |  |                 del self._buffer[:n] | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def can_write_eof(self): | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def write_eof(self): | 
					
						
							|  |  |  |         if self._closing: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         assert self._pipe | 
					
						
							|  |  |  |         self._closing = True | 
					
						
							|  |  |  |         if not self._buffer: | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |             self._loop._remove_reader(self._fileno) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             self._loop.call_soon(self._call_connection_lost, None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 21:11:02 -04:00
										 |  |  |     def set_protocol(self, protocol): | 
					
						
							|  |  |  |         self._protocol = protocol | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_protocol(self): | 
					
						
							|  |  |  |         return self._protocol | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-16 12:43:21 -05:00
										 |  |  |     def is_closing(self): | 
					
						
							|  |  |  |         return self._closing | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2015-01-15 13:16:50 +01:00
										 |  |  |         if self._pipe is not None and not self._closing: | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             # write_eof is all what we needed to close the write pipe | 
					
						
							|  |  |  |             self.write_eof() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-10 11:24:40 +01:00
										 |  |  |     def __del__(self, _warn=warnings.warn): | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |         if self._pipe is not None: | 
					
						
							| 
									
										
										
										
											2019-01-10 11:24:40 +01:00
										 |  |  |             _warn(f"unclosed transport {self!r}", ResourceWarning, source=self) | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |             self._pipe.close() | 
					
						
							| 
									
										
										
										
											2015-01-29 17:50:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def abort(self): | 
					
						
							|  |  |  |         self._close(None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 01:40:41 +01:00
										 |  |  |     def _fatal_error(self, exc, message='Fatal error on pipe transport'): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         # should be called by exception handler only | 
					
						
							| 
									
										
										
										
											2019-05-27 16:28:34 +03:00
										 |  |  |         if isinstance(exc, OSError): | 
					
						
							| 
									
										
										
										
											2014-08-25 23:20:52 +02:00
										 |  |  |             if self._loop.get_debug(): | 
					
						
							|  |  |  |                 logger.debug("%r: %s", self, message, exc_info=True) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2014-02-18 18:02:19 -05:00
										 |  |  |             self._loop.call_exception_handler({ | 
					
						
							| 
									
										
										
										
											2014-02-19 01:40:41 +01:00
										 |  |  |                 'message': message, | 
					
						
							| 
									
										
										
										
											2014-02-18 18:02:19 -05:00
										 |  |  |                 'exception': exc, | 
					
						
							|  |  |  |                 'transport': self, | 
					
						
							|  |  |  |                 'protocol': self._protocol, | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._close(exc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _close(self, exc=None): | 
					
						
							|  |  |  |         self._closing = True | 
					
						
							|  |  |  |         if self._buffer: | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |             self._loop._remove_writer(self._fileno) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._buffer.clear() | 
					
						
							| 
									
										
										
										
											2016-10-05 17:48:59 -04:00
										 |  |  |         self._loop._remove_reader(self._fileno) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._loop.call_soon(self._call_connection_lost, exc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _call_connection_lost(self, exc): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self._protocol.connection_lost(exc) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self._pipe.close() | 
					
						
							|  |  |  |             self._pipe = None | 
					
						
							|  |  |  |             self._protocol = None | 
					
						
							|  |  |  |             self._loop = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-30 14:52:03 -07:00
										 |  |  | class _UnixSubprocessTransport(base_subprocess.BaseSubprocessTransport): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-30 14:52:03 -07:00
										 |  |  |     def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs): | 
					
						
							| 
									
										
										
										
											2013-10-21 20:37:14 -07:00
										 |  |  |         stdin_w = None | 
					
						
							| 
									
										
										
										
											2022-10-13 18:11:15 +02:00
										 |  |  |         if stdin == subprocess.PIPE and sys.platform.startswith('aix'): | 
					
						
							|  |  |  |             # Use a socket pair for stdin on AIX, since it does not | 
					
						
							| 
									
										
										
										
											2013-10-21 20:37:14 -07:00
										 |  |  |             # support selecting read events on the write end of a | 
					
						
							|  |  |  |             # socket (which we use in order to detect closing of the | 
					
						
							| 
									
										
										
										
											2022-10-13 18:11:15 +02:00
										 |  |  |             # other end). | 
					
						
							| 
									
										
										
										
											2017-11-28 11:15:26 +01:00
										 |  |  |             stdin, stdin_w = socket.socketpair() | 
					
						
							| 
									
										
										
										
											2019-05-20 14:02:17 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self._proc = subprocess.Popen( | 
					
						
							|  |  |  |                 args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr, | 
					
						
							|  |  |  |                 universal_newlines=False, bufsize=bufsize, **kwargs) | 
					
						
							|  |  |  |             if stdin_w is not None: | 
					
						
							|  |  |  |                 stdin.close() | 
					
						
							|  |  |  |                 self._proc.stdin = open(stdin_w.detach(), 'wb', buffering=bufsize) | 
					
						
							|  |  |  |                 stdin_w = None | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             if stdin_w is not None: | 
					
						
							|  |  |  |                 stdin.close() | 
					
						
							|  |  |  |                 stdin_w.close() | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AbstractChildWatcher: | 
					
						
							|  |  |  |     """Abstract base class for monitoring child processes.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Objects derived from this class monitor a collection of subprocesses and | 
					
						
							|  |  |  |     report their termination or interruption by a signal. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     New callbacks are registered with .add_child_handler(). Starting a new | 
					
						
							|  |  |  |     process must be done within a 'with' block to allow the watcher to suspend | 
					
						
							|  |  |  |     its activity until the new process if fully registered (this is needed to | 
					
						
							|  |  |  |     prevent a race condition in some implementations). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Example: | 
					
						
							|  |  |  |         with watcher: | 
					
						
							|  |  |  |             proc = subprocess.Popen("sleep 1") | 
					
						
							|  |  |  |             watcher.add_child_handler(proc.pid, callback) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Notes: | 
					
						
							|  |  |  |         Implementations of this class must be thread-safe. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Since child watcher objects may catch the SIGCHLD signal and call | 
					
						
							|  |  |  |         waitpid(-1), there should be only one active object per process. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-12 12:47:53 +05:30
										 |  |  |     def __init_subclass__(cls) -> None: | 
					
						
							|  |  |  |         if cls.__module__ != __name__: | 
					
						
							|  |  |  |             warnings._deprecated("AbstractChildWatcher", | 
					
						
							|  |  |  |                              "{name!r} is deprecated as of Python 3.12 and will be " | 
					
						
							|  |  |  |                              "removed in Python {remove}.", | 
					
						
							|  |  |  |                               remove=(3, 14)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     def add_child_handler(self, pid, callback, *args): | 
					
						
							|  |  |  |         """Register a new child handler.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Arrange for callback(pid, returncode, *args) to be called when | 
					
						
							|  |  |  |         process 'pid' terminates. Specifying another callback for the same | 
					
						
							|  |  |  |         process replaces the previous handler. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         Note: callback() must be thread-safe. | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_child_handler(self, pid): | 
					
						
							|  |  |  |         """Removes the handler for process 'pid'.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The function returns True if the handler was successfully removed, | 
					
						
							|  |  |  |         False if there was nothing to remove."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |     def attach_loop(self, loop): | 
					
						
							|  |  |  |         """Attach the watcher to an event loop.
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |         If the watcher was previously attached to an event loop, then it is | 
					
						
							|  |  |  |         first detached before attaching to the new loop. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Note: loop may be None. | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         """Close the watcher.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This must be called to make sure that any underlying resource is freed. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |     def is_active(self): | 
					
						
							|  |  |  |         """Return ``True`` if the watcher is active and is used by the event loop.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Return True if the watcher is installed and ready to handle process exit | 
					
						
							|  |  |  |         notifications. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         """Enter the watcher's context and allow starting new processes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This function must return self"""
 | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, a, b, c): | 
					
						
							|  |  |  |         """Exit the watcher's context""" | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 19:08:50 -08:00
										 |  |  | class PidfdChildWatcher(AbstractChildWatcher): | 
					
						
							|  |  |  |     """Child watcher implementation using Linux's pid file descriptors.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This child watcher polls process file descriptors (pidfds) to await child | 
					
						
							|  |  |  |     process termination. In some respects, PidfdChildWatcher is a "Goldilocks" | 
					
						
							|  |  |  |     child watcher implementation. It doesn't require signals or threads, doesn't | 
					
						
							|  |  |  |     interfere with any processes launched outside the event loop, and scales | 
					
						
							|  |  |  |     linearly with the number of subprocesses launched by the event loop. The | 
					
						
							|  |  |  |     main disadvantage is that pidfds are specific to Linux, and only work on | 
					
						
							|  |  |  |     recent (5.3+) kernels. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, exc_type, exc_value, exc_traceback): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_active(self): | 
					
						
							| 
									
										
										
										
											2022-10-08 01:24:01 +01:00
										 |  |  |         return True | 
					
						
							| 
									
										
										
										
											2019-11-13 19:08:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2022-10-08 01:24:01 +01:00
										 |  |  |         pass | 
					
						
							| 
									
										
										
										
											2019-11-13 19:08:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def attach_loop(self, loop): | 
					
						
							| 
									
										
										
										
											2022-10-08 01:24:01 +01:00
										 |  |  |         pass | 
					
						
							| 
									
										
										
										
											2019-11-13 19:08:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def add_child_handler(self, pid, callback, *args): | 
					
						
							| 
									
										
										
										
											2022-10-08 01:24:01 +01:00
										 |  |  |         loop = events.get_running_loop() | 
					
						
							|  |  |  |         pidfd = os.pidfd_open(pid) | 
					
						
							|  |  |  |         loop._add_reader(pidfd, self._do_wait, pid, pidfd, callback, args) | 
					
						
							| 
									
										
										
										
											2019-11-13 19:08:50 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 01:24:01 +01:00
										 |  |  |     def _do_wait(self, pid, pidfd, callback, args): | 
					
						
							|  |  |  |         loop = events.get_running_loop() | 
					
						
							|  |  |  |         loop._remove_reader(pidfd) | 
					
						
							| 
									
										
										
										
											2019-12-06 16:32:41 +01:00
										 |  |  |         try: | 
					
						
							|  |  |  |             _, status = os.waitpid(pid, 0) | 
					
						
							|  |  |  |         except ChildProcessError: | 
					
						
							|  |  |  |             # The child process is already reaped | 
					
						
							|  |  |  |             # (may happen if waitpid() is called elsewhere). | 
					
						
							|  |  |  |             returncode = 255 | 
					
						
							|  |  |  |             logger.warning( | 
					
						
							|  |  |  |                 "child process pid %d exit status already read: " | 
					
						
							|  |  |  |                 " will report returncode 255", | 
					
						
							|  |  |  |                 pid) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2020-12-16 12:11:24 +01:00
										 |  |  |             returncode = waitstatus_to_exitcode(status) | 
					
						
							| 
									
										
										
										
											2019-12-06 16:32:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 19:08:50 -08:00
										 |  |  |         os.close(pidfd) | 
					
						
							|  |  |  |         callback(pid, returncode, *args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_child_handler(self, pid): | 
					
						
							| 
									
										
										
										
											2022-10-08 01:24:01 +01:00
										 |  |  |         # asyncio never calls remove_child_handler() !!! | 
					
						
							|  |  |  |         # The method is no-op but is implemented because | 
					
						
							|  |  |  |         # abstract base classes require it. | 
					
						
							| 
									
										
										
										
											2019-11-13 19:08:50 -08:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | class BaseChildWatcher(AbstractChildWatcher): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |     def __init__(self): | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         self._loop = None | 
					
						
							| 
									
										
										
										
											2016-10-05 16:57:12 -04:00
										 |  |  |         self._callbacks = {} | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |         self.attach_loop(None) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |     def is_active(self): | 
					
						
							|  |  |  |         return self._loop is not None and self._loop.is_running() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     def _do_waitpid(self, expected_pid): | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_waitpid_all(self): | 
					
						
							|  |  |  |         raise NotImplementedError() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |     def attach_loop(self, loop): | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         assert loop is None or isinstance(loop, events.AbstractEventLoop) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-05 16:57:12 -04:00
										 |  |  |         if self._loop is not None and loop is None and self._callbacks: | 
					
						
							|  |  |  |             warnings.warn( | 
					
						
							|  |  |  |                 'A loop is being detached ' | 
					
						
							|  |  |  |                 'from a child watcher with pending handlers', | 
					
						
							|  |  |  |                 RuntimeWarning) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         if self._loop is not None: | 
					
						
							|  |  |  |             self._loop.remove_signal_handler(signal.SIGCHLD) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._loop = loop | 
					
						
							|  |  |  |         if loop is not None: | 
					
						
							|  |  |  |             loop.add_signal_handler(signal.SIGCHLD, self._sig_chld) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Prevent a race condition in case a child terminated | 
					
						
							|  |  |  |             # during the switch. | 
					
						
							|  |  |  |             self._do_waitpid_all() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _sig_chld(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self._do_waitpid_all() | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |         except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2014-02-18 18:02:19 -05:00
										 |  |  |             # self._loop should always be available here | 
					
						
							|  |  |  |             # as '_sig_chld' is added as a signal handler | 
					
						
							|  |  |  |             # in 'attach_loop' | 
					
						
							|  |  |  |             self._loop.call_exception_handler({ | 
					
						
							|  |  |  |                 'message': 'Unknown exception in SIGCHLD handler', | 
					
						
							|  |  |  |                 'exception': exc, | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SafeChildWatcher(BaseChildWatcher): | 
					
						
							|  |  |  |     """'Safe' child watcher implementation.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This implementation avoids disrupting other code spawning processes by | 
					
						
							|  |  |  |     polling explicitly each process in the SIGCHLD handler instead of calling | 
					
						
							|  |  |  |     os.waitpid(-1). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This is a safe solution but it has a significant overhead when handling a | 
					
						
							|  |  |  |     big number of children (O(n) each time SIGCHLD is raised) | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-09 02:22:19 +05:30
										 |  |  |     def __init__(self): | 
					
						
							|  |  |  |         super().__init__() | 
					
						
							|  |  |  |         warnings._deprecated("SafeChildWatcher", | 
					
						
							|  |  |  |                              "{name!r} is deprecated as of Python 3.12 and will be " | 
					
						
							|  |  |  |                              "removed in Python {remove}.", | 
					
						
							|  |  |  |                               remove=(3, 14)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |     def close(self): | 
					
						
							|  |  |  |         self._callbacks.clear() | 
					
						
							|  |  |  |         super().close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, a, b, c): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_child_handler(self, pid, callback, *args): | 
					
						
							| 
									
										
										
										
											2015-01-30 00:05:19 +01:00
										 |  |  |         self._callbacks[pid] = (callback, args) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Prevent a race condition in case the child is already terminated. | 
					
						
							|  |  |  |         self._do_waitpid(pid) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |     def remove_child_handler(self, pid): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             del self._callbacks[pid] | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         except KeyError: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     def _do_waitpid_all(self): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for pid in list(self._callbacks): | 
					
						
							|  |  |  |             self._do_waitpid(pid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_waitpid(self, expected_pid): | 
					
						
							|  |  |  |         assert expected_pid > 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             pid, status = os.waitpid(expected_pid, os.WNOHANG) | 
					
						
							|  |  |  |         except ChildProcessError: | 
					
						
							|  |  |  |             # The child process is already reaped | 
					
						
							|  |  |  |             # (may happen if waitpid() is called elsewhere). | 
					
						
							|  |  |  |             pid = expected_pid | 
					
						
							|  |  |  |             returncode = 255 | 
					
						
							|  |  |  |             logger.warning( | 
					
						
							|  |  |  |                 "Unknown child process pid %d, will report returncode 255", | 
					
						
							|  |  |  |                 pid) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if pid == 0: | 
					
						
							|  |  |  |                 # The child process is still alive. | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 12:11:24 +01:00
										 |  |  |             returncode = waitstatus_to_exitcode(status) | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |             if self._loop.get_debug(): | 
					
						
							|  |  |  |                 logger.debug('process %s exited with returncode %s', | 
					
						
							|  |  |  |                              expected_pid, returncode) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             callback, args = self._callbacks.pop(pid) | 
					
						
							|  |  |  |         except KeyError:  # pragma: no cover | 
					
						
							|  |  |  |             # May happen if .remove_child_handler() is called | 
					
						
							|  |  |  |             # after os.waitpid() returns. | 
					
						
							| 
									
										
										
										
											2014-08-25 23:20:52 +02:00
										 |  |  |             if self._loop.get_debug(): | 
					
						
							|  |  |  |                 logger.warning("Child watcher got an unexpected pid: %r", | 
					
						
							|  |  |  |                                pid, exc_info=True) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         else: | 
					
						
							|  |  |  |             callback(pid, returncode, *args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FastChildWatcher(BaseChildWatcher): | 
					
						
							|  |  |  |     """'Fast' child watcher implementation.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This implementation reaps every terminated processes by calling | 
					
						
							|  |  |  |     os.waitpid(-1) directly, possibly breaking other code spawning processes | 
					
						
							|  |  |  |     and waiting for their termination. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     There is no noticeable overhead when handling a big number of children | 
					
						
							|  |  |  |     (O(1) each time a child terminates). | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |     def __init__(self): | 
					
						
							|  |  |  |         super().__init__() | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         self._lock = threading.Lock() | 
					
						
							|  |  |  |         self._zombies = {} | 
					
						
							|  |  |  |         self._forks = 0 | 
					
						
							| 
									
										
										
										
											2022-10-09 02:22:19 +05:30
										 |  |  |         warnings._deprecated("FastChildWatcher", | 
					
						
							|  |  |  |                              "{name!r} is deprecated as of Python 3.12 and will be " | 
					
						
							|  |  |  |                              "removed in Python {remove}.", | 
					
						
							|  |  |  |                               remove=(3, 14)) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |         self._callbacks.clear() | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         self._zombies.clear() | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |         super().close() | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         with self._lock: | 
					
						
							|  |  |  |             self._forks += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, a, b, c): | 
					
						
							|  |  |  |         with self._lock: | 
					
						
							|  |  |  |             self._forks -= 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if self._forks or not self._zombies: | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             collateral_victims = str(self._zombies) | 
					
						
							|  |  |  |             self._zombies.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         logger.warning( | 
					
						
							|  |  |  |             "Caught subprocesses termination from unknown pids: %s", | 
					
						
							|  |  |  |             collateral_victims) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_child_handler(self, pid, callback, *args): | 
					
						
							|  |  |  |         assert self._forks, "Must use the context manager" | 
					
						
							| 
									
										
										
										
											2016-10-05 16:57:12 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-25 16:32:17 -08:00
										 |  |  |         with self._lock: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 returncode = self._zombies.pop(pid) | 
					
						
							|  |  |  |             except KeyError: | 
					
						
							|  |  |  |                 # The child is running. | 
					
						
							|  |  |  |                 self._callbacks[pid] = callback, args | 
					
						
							|  |  |  |                 return | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-25 16:32:17 -08:00
										 |  |  |         # The child is dead already. We can fire the callback. | 
					
						
							|  |  |  |         callback(pid, returncode, *args) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |     def remove_child_handler(self, pid): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             del self._callbacks[pid] | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         except KeyError: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     def _do_waitpid_all(self): | 
					
						
							|  |  |  |         # Because of signal coalescing, we must keep calling waitpid() as | 
					
						
							|  |  |  |         # long as we're able to reap a child. | 
					
						
							|  |  |  |         while True: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 pid, status = os.waitpid(-1, os.WNOHANG) | 
					
						
							|  |  |  |             except ChildProcessError: | 
					
						
							|  |  |  |                 # No more child processes exist. | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if pid == 0: | 
					
						
							|  |  |  |                     # A child process is still alive. | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 12:11:24 +01:00
										 |  |  |                 returncode = waitstatus_to_exitcode(status) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-25 16:32:17 -08:00
										 |  |  |             with self._lock: | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     callback, args = self._callbacks.pop(pid) | 
					
						
							|  |  |  |                 except KeyError: | 
					
						
							|  |  |  |                     # unknown child | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |                     if self._forks: | 
					
						
							|  |  |  |                         # It may not be registered yet. | 
					
						
							|  |  |  |                         self._zombies[pid] = returncode | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |                         if self._loop.get_debug(): | 
					
						
							|  |  |  |                             logger.debug('unknown process %s exited ' | 
					
						
							|  |  |  |                                          'with returncode %s', | 
					
						
							|  |  |  |                                          pid, returncode) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |                         continue | 
					
						
							| 
									
										
										
										
											2014-01-25 16:32:17 -08:00
										 |  |  |                     callback = None | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     if self._loop.get_debug(): | 
					
						
							|  |  |  |                         logger.debug('process %s exited with returncode %s', | 
					
						
							|  |  |  |                                      pid, returncode) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-25 16:32:17 -08:00
										 |  |  |             if callback is None: | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |                 logger.warning( | 
					
						
							|  |  |  |                     "Caught subprocess termination from unknown pid: " | 
					
						
							|  |  |  |                     "%d -> %d", pid, returncode) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 callback(pid, returncode, *args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  | class MultiLoopChildWatcher(AbstractChildWatcher): | 
					
						
							|  |  |  |     """A watcher that doesn't require running loop in the main thread.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This implementation registers a SIGCHLD signal handler on | 
					
						
							|  |  |  |     instantiation (which may conflict with other code that | 
					
						
							|  |  |  |     install own handler for this signal). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The solution is safe but it has a significant overhead when | 
					
						
							|  |  |  |     handling a big number of processes (*O(n)* each time a | 
					
						
							|  |  |  |     SIGCHLD is received). | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Implementation note: | 
					
						
							|  |  |  |     # The class keeps compatibility with AbstractChildWatcher ABC | 
					
						
							|  |  |  |     # To achieve this it has empty attach_loop() method | 
					
						
							|  |  |  |     # and doesn't accept explicit loop argument | 
					
						
							|  |  |  |     # for add_child_handler()/remove_child_handler() | 
					
						
							|  |  |  |     # but retrieves the current loop by get_running_loop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self._callbacks = {} | 
					
						
							|  |  |  |         self._saved_sighandler = None | 
					
						
							| 
									
										
										
										
											2022-10-09 02:22:19 +05:30
										 |  |  |         warnings._deprecated("MultiLoopChildWatcher", | 
					
						
							|  |  |  |                              "{name!r} is deprecated as of Python 3.12 and will be " | 
					
						
							|  |  |  |                              "removed in Python {remove}.", | 
					
						
							|  |  |  |                               remove=(3, 14)) | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_active(self): | 
					
						
							|  |  |  |         return self._saved_sighandler is not None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         self._callbacks.clear() | 
					
						
							| 
									
										
										
										
											2020-12-16 09:50:25 -08:00
										 |  |  |         if self._saved_sighandler is None: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         handler = signal.getsignal(signal.SIGCHLD) | 
					
						
							|  |  |  |         if handler != self._sig_chld: | 
					
						
							|  |  |  |             logger.warning("SIGCHLD handler was changed by outside code") | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             signal.signal(signal.SIGCHLD, self._saved_sighandler) | 
					
						
							|  |  |  |         self._saved_sighandler = None | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, exc_type, exc_val, exc_tb): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_child_handler(self, pid, callback, *args): | 
					
						
							|  |  |  |         loop = events.get_running_loop() | 
					
						
							|  |  |  |         self._callbacks[pid] = (loop, callback, args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Prevent a race condition in case the child is already terminated. | 
					
						
							|  |  |  |         self._do_waitpid(pid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_child_handler(self, pid): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             del self._callbacks[pid] | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         except KeyError: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def attach_loop(self, loop): | 
					
						
							|  |  |  |         # Don't save the loop but initialize itself if called first time | 
					
						
							|  |  |  |         # The reason to do it here is that attach_loop() is called from | 
					
						
							|  |  |  |         # unix policy only for the main thread. | 
					
						
							|  |  |  |         # Main thread is required for subscription on SIGCHLD signal | 
					
						
							| 
									
										
										
										
											2020-12-16 09:50:25 -08:00
										 |  |  |         if self._saved_sighandler is not None: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld) | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |         if self._saved_sighandler is None: | 
					
						
							| 
									
										
										
										
											2020-12-16 09:50:25 -08:00
										 |  |  |             logger.warning("Previous SIGCHLD handler was set by non-Python code, " | 
					
						
							|  |  |  |                            "restore to default handler on watcher close.") | 
					
						
							|  |  |  |             self._saved_sighandler = signal.SIG_DFL | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 09:50:25 -08:00
										 |  |  |         # Set SA_RESTART to limit EINTR occurrences. | 
					
						
							|  |  |  |         signal.siginterrupt(signal.SIGCHLD, False) | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _do_waitpid_all(self): | 
					
						
							|  |  |  |         for pid in list(self._callbacks): | 
					
						
							|  |  |  |             self._do_waitpid(pid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_waitpid(self, expected_pid): | 
					
						
							|  |  |  |         assert expected_pid > 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             pid, status = os.waitpid(expected_pid, os.WNOHANG) | 
					
						
							|  |  |  |         except ChildProcessError: | 
					
						
							|  |  |  |             # The child process is already reaped | 
					
						
							|  |  |  |             # (may happen if waitpid() is called elsewhere). | 
					
						
							|  |  |  |             pid = expected_pid | 
					
						
							|  |  |  |             returncode = 255 | 
					
						
							|  |  |  |             logger.warning( | 
					
						
							|  |  |  |                 "Unknown child process pid %d, will report returncode 255", | 
					
						
							|  |  |  |                 pid) | 
					
						
							|  |  |  |             debug_log = False | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if pid == 0: | 
					
						
							|  |  |  |                 # The child process is still alive. | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 12:11:24 +01:00
										 |  |  |             returncode = waitstatus_to_exitcode(status) | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |             debug_log = True | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             loop, callback, args = self._callbacks.pop(pid) | 
					
						
							|  |  |  |         except KeyError:  # pragma: no cover | 
					
						
							|  |  |  |             # May happen if .remove_child_handler() is called | 
					
						
							|  |  |  |             # after os.waitpid() returns. | 
					
						
							|  |  |  |             logger.warning("Child watcher got an unexpected pid: %r", | 
					
						
							|  |  |  |                            pid, exc_info=True) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if loop.is_closed(): | 
					
						
							|  |  |  |                 logger.warning("Loop %r that handles pid %r is closed", loop, pid) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if debug_log and loop.get_debug(): | 
					
						
							|  |  |  |                     logger.debug('process %s exited with returncode %s', | 
					
						
							|  |  |  |                                  expected_pid, returncode) | 
					
						
							|  |  |  |                 loop.call_soon_threadsafe(callback, pid, returncode, *args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _sig_chld(self, signum, frame): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self._do_waitpid_all() | 
					
						
							|  |  |  |         except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         except BaseException: | 
					
						
							|  |  |  |             logger.warning('Unknown exception in SIGCHLD handler', exc_info=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ThreadedChildWatcher(AbstractChildWatcher): | 
					
						
							|  |  |  |     """Threaded child watcher implementation.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The watcher uses a thread per process | 
					
						
							|  |  |  |     for waiting for the process finish. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     It doesn't require subscription on POSIX signal | 
					
						
							|  |  |  |     but a thread creation is not free. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 06:12:33 +10:00
										 |  |  |     The watcher has O(1) complexity, its performance doesn't depend | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |     on amount of spawn processes. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self._pid_counter = itertools.count(0) | 
					
						
							|  |  |  |         self._threads = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_active(self): | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2020-01-12 06:02:50 -05:00
										 |  |  |         self._join_threads() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _join_threads(self): | 
					
						
							|  |  |  |         """Internal: Join all non-daemon threads""" | 
					
						
							|  |  |  |         threads = [thread for thread in list(self._threads.values()) | 
					
						
							|  |  |  |                    if thread.is_alive() and not thread.daemon] | 
					
						
							|  |  |  |         for thread in threads: | 
					
						
							|  |  |  |             thread.join() | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, exc_type, exc_val, exc_tb): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __del__(self, _warn=warnings.warn): | 
					
						
							|  |  |  |         threads = [thread for thread in list(self._threads.values()) | 
					
						
							|  |  |  |                    if thread.is_alive()] | 
					
						
							|  |  |  |         if threads: | 
					
						
							|  |  |  |             _warn(f"{self.__class__} has registered but not finished child processes", | 
					
						
							|  |  |  |                   ResourceWarning, | 
					
						
							|  |  |  |                   source=self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_child_handler(self, pid, callback, *args): | 
					
						
							|  |  |  |         loop = events.get_running_loop() | 
					
						
							|  |  |  |         thread = threading.Thread(target=self._do_waitpid, | 
					
						
							|  |  |  |                                   name=f"waitpid-{next(self._pid_counter)}", | 
					
						
							|  |  |  |                                   args=(loop, pid, callback, args), | 
					
						
							|  |  |  |                                   daemon=True) | 
					
						
							|  |  |  |         self._threads[pid] = thread | 
					
						
							|  |  |  |         thread.start() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_child_handler(self, pid): | 
					
						
							|  |  |  |         # asyncio never calls remove_child_handler() !!! | 
					
						
							|  |  |  |         # The method is no-op but is implemented because | 
					
						
							| 
									
										
										
										
											2021-10-20 09:17:52 -07:00
										 |  |  |         # abstract base classes require it. | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def attach_loop(self, loop): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_waitpid(self, loop, expected_pid, callback, args): | 
					
						
							|  |  |  |         assert expected_pid > 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             pid, status = os.waitpid(expected_pid, 0) | 
					
						
							|  |  |  |         except ChildProcessError: | 
					
						
							|  |  |  |             # The child process is already reaped | 
					
						
							|  |  |  |             # (may happen if waitpid() is called elsewhere). | 
					
						
							|  |  |  |             pid = expected_pid | 
					
						
							|  |  |  |             returncode = 255 | 
					
						
							|  |  |  |             logger.warning( | 
					
						
							|  |  |  |                 "Unknown child process pid %d, will report returncode 255", | 
					
						
							|  |  |  |                 pid) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2020-12-16 12:11:24 +01:00
										 |  |  |             returncode = waitstatus_to_exitcode(status) | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |             if loop.get_debug(): | 
					
						
							|  |  |  |                 logger.debug('process %s exited with returncode %s', | 
					
						
							|  |  |  |                              expected_pid, returncode) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if loop.is_closed(): | 
					
						
							|  |  |  |             logger.warning("Loop %r that handles pid %r is closed", loop, pid) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             loop.call_soon_threadsafe(callback, pid, returncode, *args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._threads.pop(expected_pid) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 05:59:09 +05:30
										 |  |  | def can_use_pidfd(): | 
					
						
							|  |  |  |     if not hasattr(os, 'pidfd_open'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         pid = os.getpid() | 
					
						
							|  |  |  |         os.close(os.pidfd_open(pid, 0)) | 
					
						
							|  |  |  |     except OSError: | 
					
						
							|  |  |  |         # blocked by security policy like SECCOMP | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy): | 
					
						
							| 
									
										
										
										
											2015-01-09 21:32:05 +01:00
										 |  |  |     """UNIX event loop policy with a watcher for child processes.""" | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     _loop_factory = _UnixSelectorEventLoop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         super().__init__() | 
					
						
							|  |  |  |         self._watcher = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _init_watcher(self): | 
					
						
							|  |  |  |         with events._lock: | 
					
						
							|  |  |  |             if self._watcher is None:  # pragma: no branch | 
					
						
							| 
									
										
										
										
											2022-10-08 05:59:09 +05:30
										 |  |  |                 if can_use_pidfd(): | 
					
						
							|  |  |  |                     self._watcher = PidfdChildWatcher() | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     self._watcher = ThreadedChildWatcher() | 
					
						
							| 
									
										
										
										
											2019-12-05 04:40:12 -08:00
										 |  |  |                 if threading.current_thread() is threading.main_thread(): | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |                     self._watcher.attach_loop(self._local._loop) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_event_loop(self, loop): | 
					
						
							|  |  |  |         """Set the event loop.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         As a side effect, if a child watcher was set before, then calling | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |         .set_event_loop() from the main thread will call .attach_loop(loop) on | 
					
						
							|  |  |  |         the child watcher. | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         super().set_event_loop(loop) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 18:23:43 +02:00
										 |  |  |         if (self._watcher is not None and | 
					
						
							| 
									
										
										
										
											2019-12-05 04:40:12 -08:00
										 |  |  |                 threading.current_thread() is threading.main_thread()): | 
					
						
							| 
									
										
										
										
											2013-11-13 15:50:08 -08:00
										 |  |  |             self._watcher.attach_loop(loop) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_child_watcher(self): | 
					
						
							| 
									
										
										
										
											2014-06-05 12:06:44 +02:00
										 |  |  |         """Get the watcher for child processes.
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |         If not yet set, a ThreadedChildWatcher object is automatically created. | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         if self._watcher is None: | 
					
						
							|  |  |  |             self._init_watcher() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-16 04:39:30 +05:30
										 |  |  |         warnings._deprecated("get_child_watcher", | 
					
						
							|  |  |  |                             "{name!r} is deprecated as of Python 3.12 and will be " | 
					
						
							|  |  |  |                             "removed in Python {remove}.", remove=(3, 14)) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |         return self._watcher | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_child_watcher(self, watcher): | 
					
						
							| 
									
										
										
										
											2014-06-05 12:06:44 +02:00
										 |  |  |         """Set the watcher for child processes.""" | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         assert watcher is None or isinstance(watcher, AbstractChildWatcher) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self._watcher is not None: | 
					
						
							|  |  |  |             self._watcher.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._watcher = watcher | 
					
						
							| 
									
										
										
										
											2022-10-16 04:39:30 +05:30
										 |  |  |         warnings._deprecated("set_child_watcher", | 
					
						
							|  |  |  |                             "{name!r} is deprecated as of Python 3.12 and will be " | 
					
						
							|  |  |  |                             "removed in Python {remove}.", remove=(3, 14)) | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  | SelectorEventLoop = _UnixSelectorEventLoop | 
					
						
							|  |  |  | DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy |