| 
									
										
										
										
											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', | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |     'DefaultEventLoopPolicy', | 
					
						
							| 
									
										
										
										
											2023-10-12 07:13:57 -07:00
										 |  |  |     'EventLoop', | 
					
						
							| 
									
										
										
										
											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 = {} | 
					
						
							| 
									
										
										
										
											2023-11-08 17:10:10 +01:00
										 |  |  |         self._unix_server_sockets = {} | 
					
						
							| 
									
										
										
										
											2024-06-23 18:38:50 +05:30
										 |  |  |         if can_use_pidfd(): | 
					
						
							|  |  |  |             self._watcher = _PidfdChildWatcher() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._watcher = _ThreadedChildWatcher() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2024-08-12 00:35:51 +08:00
										 |  |  |                 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): | 
					
						
							| 
									
										
										
										
											2024-06-23 18:38:50 +05:30
										 |  |  |         watcher = self._watcher | 
					
						
							|  |  |  |         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): | 
					
						
							| 
									
										
										
										
											2023-09-20 15:54:19 +02:00
										 |  |  |         self.call_soon_threadsafe(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, | 
					
						
							| 
									
										
										
										
											2023-11-08 17:10:10 +01:00
										 |  |  |             start_serving=True, cleanup_socket=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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-08 17:10:10 +01:00
										 |  |  |         if cleanup_socket: | 
					
						
							|  |  |  |             path = sock.getsockname() | 
					
						
							|  |  |  |             # Check for abstract socket. `str` and `bytes` paths are supported. | 
					
						
							|  |  |  |             if path[0] not in (0, '\x00'): | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     self._unix_server_sockets[sock] = os.stat(path).st_ino | 
					
						
							|  |  |  |                 except FileNotFoundError: | 
					
						
							|  |  |  |                     pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-22 23:07:14 -05:00
										 |  |  |         # On 32-bit architectures truncate to 1GiB to avoid OverflowError | 
					
						
							|  |  |  |         blocksize = min(blocksize, sys.maxsize//2 + 1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-16 19:59:34 +02:00
										 |  |  |         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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-08 17:10:10 +01:00
										 |  |  |     def _stop_serving(self, sock): | 
					
						
							|  |  |  |         # Is this a unix socket that needs cleanup? | 
					
						
							|  |  |  |         if sock in self._unix_server_sockets: | 
					
						
							|  |  |  |             path = sock.getsockname() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             path = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         super()._stop_serving(sock) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if path is not None: | 
					
						
							|  |  |  |             prev_ino = self._unix_server_sockets[sock] | 
					
						
							|  |  |  |             del self._unix_server_sockets[sock] | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if os.stat(path).st_ino == prev_ino: | 
					
						
							|  |  |  |                     os.unlink(path) | 
					
						
							|  |  |  |             except FileNotFoundError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             except OSError as err: | 
					
						
							|  |  |  |                 logger.error('Unable to clean up listening UNIX socket ' | 
					
						
							|  |  |  |                              '%r: %r', path, err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2023-04-27 13:03:29 +03:00
										 |  |  |         self._loop.call_soon(self._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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-27 13:03:29 +03:00
										 |  |  |     def _add_reader(self, fd, callback): | 
					
						
							|  |  |  |         if not self.is_reading(): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self._loop._add_reader(fd, callback) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_reading(self): | 
					
						
							|  |  |  |         return not self._paused and not self._closing | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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): | 
					
						
							| 
									
										
										
										
											2023-04-27 13:03:29 +03:00
										 |  |  |         if not self.is_reading(): | 
					
						
							| 
									
										
										
										
											2019-09-29 15:00:35 +03:00
										 |  |  |             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
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-23 18:38:50 +05:30
										 |  |  | class _PidfdChildWatcher: | 
					
						
							| 
									
										
										
										
											2019-11-13 19:08:50 -08:00
										 |  |  |     """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 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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-23 18:38:50 +05:30
										 |  |  | class _ThreadedChildWatcher: | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |     """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 __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, | 
					
						
							| 
									
										
										
										
											2023-10-27 15:44:12 -07:00
										 |  |  |                                   name=f"asyncio-waitpid-{next(self._pid_counter)}", | 
					
						
							| 
									
										
										
										
											2019-06-30 12:54:59 +03:00
										 |  |  |                                   args=(loop, pid, callback, args), | 
					
						
							|  |  |  |                                   daemon=True) | 
					
						
							|  |  |  |         self._threads[pid] = thread | 
					
						
							|  |  |  |         thread.start() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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): | 
					
						
							| 
									
										
										
										
											2024-06-23 18:38:50 +05:30
										 |  |  |     """UNIX event loop policy""" | 
					
						
							| 
									
										
										
										
											2013-11-04 15:50:46 -08:00
										 |  |  |     _loop_factory = _UnixSelectorEventLoop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SelectorEventLoop = _UnixSelectorEventLoop | 
					
						
							|  |  |  | DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy | 
					
						
							| 
									
										
										
										
											2023-10-12 07:13:57 -07:00
										 |  |  | EventLoop = SelectorEventLoop |