Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 1 | # |
| 2 | # Module providing various facilities to other parts of the package |
| 3 | # |
| 4 | # multiprocessing/util.py |
| 5 | # |
R. David Murray | 3fc969a | 2010-12-14 01:38:16 +0000 | [diff] [blame] | 6 | # Copyright (c) 2006-2008, R Oudkerk |
Richard Oudkerk | 3e268aa | 2012-04-30 12:13:55 +0100 | [diff] [blame] | 7 | # Licensed to PSF under a Contributor Agreement. |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 8 | # |
| 9 | |
Richard Oudkerk | 739ae56 | 2012-05-25 13:54:53 +0100 | [diff] [blame] | 10 | import os |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 11 | import itertools |
Victor Stinner | a6d865c | 2016-03-25 09:29:50 +0100 | [diff] [blame] | 12 | import sys |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 13 | import weakref |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 14 | import atexit |
| 15 | import threading # we want threading to install it's |
| 16 | # cleanup function before multiprocessing does |
Antoine Pitrou | ebdcd85 | 2012-05-18 18:33:07 +0200 | [diff] [blame] | 17 | from subprocess import _args_from_interpreter_flags |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 18 | |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 19 | from . import process |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 20 | |
| 21 | __all__ = [ |
| 22 | 'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger', |
| 23 | 'log_to_stderr', 'get_temp_dir', 'register_after_fork', |
Jesse Noller | 41faa54 | 2009-01-25 03:45:53 +0000 | [diff] [blame] | 24 | 'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal', |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 25 | 'close_all_fds_except', 'SUBDEBUG', 'SUBWARNING', |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 26 | ] |
| 27 | |
| 28 | # |
| 29 | # Logging |
| 30 | # |
| 31 | |
| 32 | NOTSET = 0 |
| 33 | SUBDEBUG = 5 |
| 34 | DEBUG = 10 |
| 35 | INFO = 20 |
| 36 | SUBWARNING = 25 |
| 37 | |
| 38 | LOGGER_NAME = 'multiprocessing' |
| 39 | DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s' |
| 40 | |
| 41 | _logger = None |
| 42 | _log_to_stderr = False |
| 43 | |
| 44 | def sub_debug(msg, *args): |
| 45 | if _logger: |
| 46 | _logger.log(SUBDEBUG, msg, *args) |
| 47 | |
| 48 | def debug(msg, *args): |
| 49 | if _logger: |
| 50 | _logger.log(DEBUG, msg, *args) |
| 51 | |
| 52 | def info(msg, *args): |
| 53 | if _logger: |
| 54 | _logger.log(INFO, msg, *args) |
| 55 | |
| 56 | def sub_warning(msg, *args): |
| 57 | if _logger: |
| 58 | _logger.log(SUBWARNING, msg, *args) |
| 59 | |
| 60 | def get_logger(): |
| 61 | ''' |
| 62 | Returns logger used by multiprocessing |
| 63 | ''' |
| 64 | global _logger |
Florent Xicluna | 04842a8 | 2011-11-11 20:05:50 +0100 | [diff] [blame] | 65 | import logging |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 66 | |
Jesse Noller | 41faa54 | 2009-01-25 03:45:53 +0000 | [diff] [blame] | 67 | logging._acquireLock() |
| 68 | try: |
| 69 | if not _logger: |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 70 | |
Jesse Noller | 41faa54 | 2009-01-25 03:45:53 +0000 | [diff] [blame] | 71 | _logger = logging.getLogger(LOGGER_NAME) |
| 72 | _logger.propagate = 0 |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 73 | |
Jesse Noller | 41faa54 | 2009-01-25 03:45:53 +0000 | [diff] [blame] | 74 | # XXX multiprocessing should cleanup before logging |
| 75 | if hasattr(atexit, 'unregister'): |
| 76 | atexit.unregister(_exit_function) |
| 77 | atexit.register(_exit_function) |
| 78 | else: |
| 79 | atexit._exithandlers.remove((_exit_function, (), {})) |
| 80 | atexit._exithandlers.append((_exit_function, (), {})) |
| 81 | |
| 82 | finally: |
| 83 | logging._releaseLock() |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 84 | |
| 85 | return _logger |
| 86 | |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 87 | def log_to_stderr(level=None): |
| 88 | ''' |
| 89 | Turn on logging and add a handler which prints to stderr |
| 90 | ''' |
| 91 | global _log_to_stderr |
| 92 | import logging |
Jesse Noller | 41faa54 | 2009-01-25 03:45:53 +0000 | [diff] [blame] | 93 | |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 94 | logger = get_logger() |
| 95 | formatter = logging.Formatter(DEFAULT_LOGGING_FORMAT) |
| 96 | handler = logging.StreamHandler() |
| 97 | handler.setFormatter(formatter) |
| 98 | logger.addHandler(handler) |
Jesse Noller | 41faa54 | 2009-01-25 03:45:53 +0000 | [diff] [blame] | 99 | |
| 100 | if level: |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 101 | logger.setLevel(level) |
| 102 | _log_to_stderr = True |
Jesse Noller | 41faa54 | 2009-01-25 03:45:53 +0000 | [diff] [blame] | 103 | return _logger |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 104 | |
| 105 | # |
| 106 | # Function returning a temp directory which will be removed on exit |
| 107 | # |
| 108 | |
Miss Islington (bot) | 957656e | 2019-07-04 04:34:31 -0700 | [diff] [blame] | 109 | def _remove_temp_dir(rmtree, tempdir): |
| 110 | rmtree(tempdir) |
| 111 | |
| 112 | current_process = process.current_process() |
| 113 | # current_process() can be None if the finalizer is called |
| 114 | # late during Python finalization |
| 115 | if current_process is not None: |
| 116 | current_process._config['tempdir'] = None |
| 117 | |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 118 | def get_temp_dir(): |
| 119 | # get name of a temp directory which will be automatically cleaned up |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 120 | tempdir = process.current_process()._config.get('tempdir') |
| 121 | if tempdir is None: |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 122 | import shutil, tempfile |
| 123 | tempdir = tempfile.mkdtemp(prefix='pymp-') |
| 124 | info('created temp directory %s', tempdir) |
Miss Islington (bot) | 957656e | 2019-07-04 04:34:31 -0700 | [diff] [blame] | 125 | # keep a strong reference to shutil.rmtree(), since the finalizer |
| 126 | # can be called late during Python shutdown |
| 127 | Finalize(None, _remove_temp_dir, args=(shutil.rmtree, tempdir), |
| 128 | exitpriority=-100) |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 129 | process.current_process()._config['tempdir'] = tempdir |
| 130 | return tempdir |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 131 | |
| 132 | # |
| 133 | # Support for reinitialization of objects when bootstrapping a child process |
| 134 | # |
| 135 | |
| 136 | _afterfork_registry = weakref.WeakValueDictionary() |
| 137 | _afterfork_counter = itertools.count() |
| 138 | |
| 139 | def _run_after_forkers(): |
| 140 | items = list(_afterfork_registry.items()) |
| 141 | items.sort() |
| 142 | for (index, ident, func), obj in items: |
| 143 | try: |
| 144 | func(obj) |
| 145 | except Exception as e: |
| 146 | info('after forker raised exception %s', e) |
| 147 | |
| 148 | def register_after_fork(obj, func): |
| 149 | _afterfork_registry[(next(_afterfork_counter), id(obj), func)] = obj |
| 150 | |
| 151 | # |
| 152 | # Finalization using weakrefs |
| 153 | # |
| 154 | |
| 155 | _finalizer_registry = {} |
| 156 | _finalizer_counter = itertools.count() |
| 157 | |
| 158 | |
| 159 | class Finalize(object): |
| 160 | ''' |
| 161 | Class which supports object finalization using weakrefs |
| 162 | ''' |
| 163 | def __init__(self, obj, callback, args=(), kwargs=None, exitpriority=None): |
Allen W. Smith, Ph.D | bd73e72 | 2017-08-29 17:52:18 -0500 | [diff] [blame] | 164 | if (exitpriority is not None) and not isinstance(exitpriority,int): |
| 165 | raise TypeError( |
| 166 | "Exitpriority ({0!r}) must be None or int, not {1!s}".format( |
| 167 | exitpriority, type(exitpriority))) |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 168 | |
| 169 | if obj is not None: |
| 170 | self._weakref = weakref.ref(obj, self) |
Allen W. Smith, Ph.D | bd73e72 | 2017-08-29 17:52:18 -0500 | [diff] [blame] | 171 | elif exitpriority is None: |
| 172 | raise ValueError("Without object, exitpriority cannot be None") |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 173 | |
| 174 | self._callback = callback |
| 175 | self._args = args |
| 176 | self._kwargs = kwargs or {} |
| 177 | self._key = (exitpriority, next(_finalizer_counter)) |
Richard Oudkerk | 739ae56 | 2012-05-25 13:54:53 +0100 | [diff] [blame] | 178 | self._pid = os.getpid() |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 179 | |
| 180 | _finalizer_registry[self._key] = self |
| 181 | |
Antoine Pitrou | 71a28a9 | 2011-07-09 01:03:00 +0200 | [diff] [blame] | 182 | def __call__(self, wr=None, |
| 183 | # Need to bind these locally because the globals can have |
| 184 | # been cleared at shutdown |
| 185 | _finalizer_registry=_finalizer_registry, |
Richard Oudkerk | ad06444 | 2012-06-04 18:58:59 +0100 | [diff] [blame] | 186 | sub_debug=sub_debug, getpid=os.getpid): |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 187 | ''' |
| 188 | Run the callback unless it has already been called or cancelled |
| 189 | ''' |
| 190 | try: |
| 191 | del _finalizer_registry[self._key] |
| 192 | except KeyError: |
| 193 | sub_debug('finalizer no longer registered') |
| 194 | else: |
Richard Oudkerk | ad06444 | 2012-06-04 18:58:59 +0100 | [diff] [blame] | 195 | if self._pid != getpid(): |
Richard Oudkerk | 739ae56 | 2012-05-25 13:54:53 +0100 | [diff] [blame] | 196 | sub_debug('finalizer ignored because different process') |
| 197 | res = None |
| 198 | else: |
| 199 | sub_debug('finalizer calling %s with args %s and kwargs %s', |
| 200 | self._callback, self._args, self._kwargs) |
| 201 | res = self._callback(*self._args, **self._kwargs) |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 202 | self._weakref = self._callback = self._args = \ |
| 203 | self._kwargs = self._key = None |
| 204 | return res |
| 205 | |
| 206 | def cancel(self): |
| 207 | ''' |
| 208 | Cancel finalization of the object |
| 209 | ''' |
| 210 | try: |
| 211 | del _finalizer_registry[self._key] |
| 212 | except KeyError: |
| 213 | pass |
| 214 | else: |
| 215 | self._weakref = self._callback = self._args = \ |
| 216 | self._kwargs = self._key = None |
| 217 | |
| 218 | def still_active(self): |
| 219 | ''' |
| 220 | Return whether this finalizer is still waiting to invoke callback |
| 221 | ''' |
| 222 | return self._key in _finalizer_registry |
| 223 | |
| 224 | def __repr__(self): |
| 225 | try: |
| 226 | obj = self._weakref() |
| 227 | except (AttributeError, TypeError): |
| 228 | obj = None |
| 229 | |
| 230 | if obj is None: |
Serhiy Storchaka | 465e60e | 2014-07-25 23:36:00 +0300 | [diff] [blame] | 231 | return '<%s object, dead>' % self.__class__.__name__ |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 232 | |
Serhiy Storchaka | 465e60e | 2014-07-25 23:36:00 +0300 | [diff] [blame] | 233 | x = '<%s object, callback=%s' % ( |
| 234 | self.__class__.__name__, |
| 235 | getattr(self._callback, '__name__', self._callback)) |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 236 | if self._args: |
| 237 | x += ', args=' + str(self._args) |
| 238 | if self._kwargs: |
| 239 | x += ', kwargs=' + str(self._kwargs) |
| 240 | if self._key[0] is not None: |
Miss Islington (bot) | 4bd1d05 | 2019-08-30 13:42:54 -0700 | [diff] [blame] | 241 | x += ', exitpriority=' + str(self._key[0]) |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 242 | return x + '>' |
| 243 | |
| 244 | |
| 245 | def _run_finalizers(minpriority=None): |
| 246 | ''' |
| 247 | Run all finalizers whose exit priority is not None and at least minpriority |
| 248 | |
| 249 | Finalizers with highest priority are called first; finalizers with |
| 250 | the same priority will be called in reverse order of creation. |
| 251 | ''' |
Alexander Belopolsky | f36c49d | 2012-09-09 13:20:58 -0400 | [diff] [blame] | 252 | if _finalizer_registry is None: |
| 253 | # This function may be called after this module's globals are |
| 254 | # destroyed. See the _exit_function function in this module for more |
| 255 | # notes. |
| 256 | return |
Alexander Belopolsky | 7f704c1 | 2012-09-09 13:25:06 -0400 | [diff] [blame] | 257 | |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 258 | if minpriority is None: |
Antoine Pitrou | 1eb6c00 | 2017-06-13 17:10:39 +0200 | [diff] [blame] | 259 | f = lambda p : p[0] is not None |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 260 | else: |
Antoine Pitrou | 1eb6c00 | 2017-06-13 17:10:39 +0200 | [diff] [blame] | 261 | f = lambda p : p[0] is not None and p[0] >= minpriority |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 262 | |
Antoine Pitrou | 1eb6c00 | 2017-06-13 17:10:39 +0200 | [diff] [blame] | 263 | # Careful: _finalizer_registry may be mutated while this function |
| 264 | # is running (either by a GC run or by another thread). |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 265 | |
Antoine Pitrou | 1eb6c00 | 2017-06-13 17:10:39 +0200 | [diff] [blame] | 266 | # list(_finalizer_registry) should be atomic, while |
| 267 | # list(_finalizer_registry.items()) is not. |
| 268 | keys = [key for key in list(_finalizer_registry) if f(key)] |
| 269 | keys.sort(reverse=True) |
| 270 | |
| 271 | for key in keys: |
| 272 | finalizer = _finalizer_registry.get(key) |
| 273 | # key may have been removed from the registry |
| 274 | if finalizer is not None: |
| 275 | sub_debug('calling %s', finalizer) |
| 276 | try: |
| 277 | finalizer() |
| 278 | except Exception: |
| 279 | import traceback |
| 280 | traceback.print_exc() |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 281 | |
| 282 | if minpriority is None: |
| 283 | _finalizer_registry.clear() |
| 284 | |
| 285 | # |
| 286 | # Clean up on exit |
| 287 | # |
| 288 | |
| 289 | def is_exiting(): |
| 290 | ''' |
| 291 | Returns true if the process is shutting down |
| 292 | ''' |
| 293 | return _exiting or _exiting is None |
| 294 | |
| 295 | _exiting = False |
| 296 | |
Alexander Belopolsky | f36c49d | 2012-09-09 13:20:58 -0400 | [diff] [blame] | 297 | def _exit_function(info=info, debug=debug, _run_finalizers=_run_finalizers, |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 298 | active_children=process.active_children, |
| 299 | current_process=process.current_process): |
Alexander Belopolsky | f36c49d | 2012-09-09 13:20:58 -0400 | [diff] [blame] | 300 | # We hold on to references to functions in the arglist due to the |
| 301 | # situation described below, where this function is called after this |
| 302 | # module's globals are destroyed. |
| 303 | |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 304 | global _exiting |
| 305 | |
Richard Oudkerk | 73d9a29 | 2012-06-14 15:30:10 +0100 | [diff] [blame] | 306 | if not _exiting: |
| 307 | _exiting = True |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 308 | |
Richard Oudkerk | 73d9a29 | 2012-06-14 15:30:10 +0100 | [diff] [blame] | 309 | info('process shutting down') |
| 310 | debug('running all "atexit" finalizers with priority >= 0') |
| 311 | _run_finalizers(0) |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 312 | |
Alexander Belopolsky | f36c49d | 2012-09-09 13:20:58 -0400 | [diff] [blame] | 313 | if current_process() is not None: |
| 314 | # We check if the current process is None here because if |
Andrew Svetlov | 5b89840 | 2012-12-18 21:26:36 +0200 | [diff] [blame] | 315 | # it's None, any call to ``active_children()`` will raise |
Richard Oudkerk | e8cd6bb | 2012-09-13 17:27:15 +0100 | [diff] [blame] | 316 | # an AttributeError (active_children winds up trying to |
| 317 | # get attributes from util._current_process). One |
| 318 | # situation where this can happen is if someone has |
| 319 | # manipulated sys.modules, causing this module to be |
| 320 | # garbage collected. The destructor for the module type |
| 321 | # then replaces all values in the module dict with None. |
| 322 | # For instance, after setuptools runs a test it replaces |
| 323 | # sys.modules with a copy created earlier. See issues |
| 324 | # #9775 and #15881. Also related: #4106, #9205, and |
| 325 | # #9207. |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 326 | |
Alexander Belopolsky | f36c49d | 2012-09-09 13:20:58 -0400 | [diff] [blame] | 327 | for p in active_children(): |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 328 | if p.daemon: |
Alexander Belopolsky | f36c49d | 2012-09-09 13:20:58 -0400 | [diff] [blame] | 329 | info('calling terminate() for daemon %s', p.name) |
| 330 | p._popen.terminate() |
| 331 | |
| 332 | for p in active_children(): |
| 333 | info('calling join() for process %s', p.name) |
| 334 | p.join() |
Richard Oudkerk | 73d9a29 | 2012-06-14 15:30:10 +0100 | [diff] [blame] | 335 | |
| 336 | debug('running the remaining "atexit" finalizers') |
| 337 | _run_finalizers() |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 338 | |
| 339 | atexit.register(_exit_function) |
| 340 | |
| 341 | # |
| 342 | # Some fork aware types |
| 343 | # |
| 344 | |
| 345 | class ForkAwareThreadLock(object): |
| 346 | def __init__(self): |
Richard Oudkerk | 409c313 | 2013-04-17 20:58:00 +0100 | [diff] [blame] | 347 | self._reset() |
| 348 | register_after_fork(self, ForkAwareThreadLock._reset) |
| 349 | |
| 350 | def _reset(self): |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 351 | self._lock = threading.Lock() |
| 352 | self.acquire = self._lock.acquire |
| 353 | self.release = self._lock.release |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 354 | |
Charles-François Natali | a924fc7 | 2014-05-25 14:12:12 +0100 | [diff] [blame] | 355 | def __enter__(self): |
| 356 | return self._lock.__enter__() |
| 357 | |
| 358 | def __exit__(self, *args): |
| 359 | return self._lock.__exit__(*args) |
| 360 | |
| 361 | |
Benjamin Peterson | e711caf | 2008-06-11 16:44:04 +0000 | [diff] [blame] | 362 | class ForkAwareLocal(threading.local): |
| 363 | def __init__(self): |
| 364 | register_after_fork(self, lambda obj : obj.__dict__.clear()) |
| 365 | def __reduce__(self): |
| 366 | return type(self), () |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 367 | |
| 368 | # |
| 369 | # Close fds except those specified |
| 370 | # |
| 371 | |
| 372 | try: |
| 373 | MAXFD = os.sysconf("SC_OPEN_MAX") |
| 374 | except Exception: |
| 375 | MAXFD = 256 |
| 376 | |
| 377 | def close_all_fds_except(fds): |
| 378 | fds = list(fds) + [-1, MAXFD] |
| 379 | fds.sort() |
| 380 | assert fds[-1] == MAXFD, 'fd too large' |
| 381 | for i in range(len(fds) - 1): |
| 382 | os.closerange(fds[i]+1, fds[i+1]) |
Victor Stinner | a6d865c | 2016-03-25 09:29:50 +0100 | [diff] [blame] | 383 | # |
| 384 | # Close sys.stdin and replace stdin with os.devnull |
| 385 | # |
| 386 | |
| 387 | def _close_stdin(): |
| 388 | if sys.stdin is None: |
| 389 | return |
| 390 | |
| 391 | try: |
| 392 | sys.stdin.close() |
| 393 | except (OSError, ValueError): |
| 394 | pass |
| 395 | |
| 396 | try: |
| 397 | fd = os.open(os.devnull, os.O_RDONLY) |
| 398 | try: |
| 399 | sys.stdin = open(fd, closefd=False) |
| 400 | except: |
| 401 | os.close(fd) |
| 402 | raise |
| 403 | except (OSError, ValueError): |
| 404 | pass |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 405 | |
| 406 | # |
Antoine Pitrou | e756f66 | 2018-03-11 19:21:38 +0100 | [diff] [blame] | 407 | # Flush standard streams, if any |
| 408 | # |
| 409 | |
| 410 | def _flush_std_streams(): |
| 411 | try: |
| 412 | sys.stdout.flush() |
| 413 | except (AttributeError, ValueError): |
| 414 | pass |
| 415 | try: |
| 416 | sys.stderr.flush() |
| 417 | except (AttributeError, ValueError): |
| 418 | pass |
| 419 | |
| 420 | # |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 421 | # Start a program with only specified fds kept open |
| 422 | # |
| 423 | |
| 424 | def spawnv_passfds(path, args, passfds): |
Victor Stinner | 67973c0 | 2013-08-28 12:21:47 +0200 | [diff] [blame] | 425 | import _posixsubprocess |
Serhiy Storchaka | 66bffd1 | 2017-04-19 21:12:46 +0300 | [diff] [blame] | 426 | passfds = tuple(sorted(map(int, passfds))) |
Victor Stinner | daf4555 | 2013-08-28 00:53:59 +0200 | [diff] [blame] | 427 | errpipe_read, errpipe_write = os.pipe() |
Richard Oudkerk | 84ed9a6 | 2013-08-14 15:35:41 +0100 | [diff] [blame] | 428 | try: |
| 429 | return _posixsubprocess.fork_exec( |
| 430 | args, [os.fsencode(path)], True, passfds, None, None, |
| 431 | -1, -1, -1, -1, -1, -1, errpipe_read, errpipe_write, |
| 432 | False, False, None) |
| 433 | finally: |
| 434 | os.close(errpipe_read) |
| 435 | os.close(errpipe_write) |
Thomas Moreau | c09a9f5 | 2019-05-20 21:37:05 +0200 | [diff] [blame] | 436 | |
| 437 | |
| 438 | def close_fds(*fds): |
| 439 | """Close each file descriptor given as an argument""" |
| 440 | for fd in fds: |
| 441 | os.close(fd) |