145 lines
5.2 KiB
Diff
145 lines
5.2 KiB
Diff
|
|
From 41bd027d617de09a2c58972312b36f7be2779db6 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Victor Stinner <vstinner@redhat.com>
|
||
|
|
Date: Wed, 16 Jan 2019 00:02:35 +0100
|
||
|
|
Subject: [PATCH] bpo-35537: subprocess uses os.posix_spawn in some cases
|
||
|
|
(GH-11452)
|
||
|
|
|
||
|
|
The subprocess module can now use the os.posix_spawn() function
|
||
|
|
in some cases for better performance. Currently, it is only used on macOS
|
||
|
|
and Linux (using glibc 2.24 or newer) if all these conditions are met:
|
||
|
|
|
||
|
|
* executable path contains a directory
|
||
|
|
* close_fds=False
|
||
|
|
* preexec_fn, pass_fds, cwd, stdin, stdout, stderr
|
||
|
|
and start_new_session parameters are not set
|
||
|
|
|
||
|
|
Conflict:NA
|
||
|
|
Reference:https://github.com/python/cpython/commit/9daecf37a571e98aaf43a387bcc9e41a7132f477
|
||
|
|
|
||
|
|
Co-authored-by: Joannah Nanjekye <nanjekyejoannah@gmail.com>
|
||
|
|
Signed-off-by: hanxinke <hanxinke@huawei.com>
|
||
|
|
---
|
||
|
|
Lib/subprocess.py | 82 +++++++++++++++++++
|
||
|
|
.../2018-12-20-16-24-51.bpo-35537.z4E7aA.rst | 2 +
|
||
|
|
2 files changed, 84 insertions(+)
|
||
|
|
create mode 100644 Misc/NEWS.d/next/Library/2018-12-20-16-24-51.bpo-35537.z4E7aA.rst
|
||
|
|
|
||
|
|
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
|
||
|
|
index 3f99be5..d711ddb 100644
|
||
|
|
--- a/Lib/subprocess.py
|
||
|
|
+++ b/Lib/subprocess.py
|
||
|
|
@@ -630,6 +630,57 @@ def getoutput(cmd):
|
||
|
|
return getstatusoutput(cmd)[1]
|
||
|
|
|
||
|
|
|
||
|
|
+def _use_posix_spawn():
|
||
|
|
+ """Check is posix_spawn() can be used for subprocess.
|
||
|
|
+
|
||
|
|
+ subprocess requires a posix_spawn() implementation that reports properly
|
||
|
|
+ errors to the parent process, set errno on the following failures:
|
||
|
|
+
|
||
|
|
+ * process attribute actions failed
|
||
|
|
+ * file actions failed
|
||
|
|
+ * exec() failed
|
||
|
|
+
|
||
|
|
+ Prefer an implementation which can use vfork in some cases for best
|
||
|
|
+ performances.
|
||
|
|
+ """
|
||
|
|
+ if _mswindows or not hasattr(os, 'posix_spawn'):
|
||
|
|
+ # os.posix_spawn() is not available
|
||
|
|
+ return False
|
||
|
|
+
|
||
|
|
+ if sys.platform == 'darwin':
|
||
|
|
+ # posix_spawn() is a syscall on macOS and properly reports errors
|
||
|
|
+ return True
|
||
|
|
+
|
||
|
|
+ # Check libc name and runtime libc version
|
||
|
|
+ try:
|
||
|
|
+ ver = os.confstr('CS_GNU_LIBC_VERSION')
|
||
|
|
+ # parse 'glibc 2.28' as ('glibc', (2, 28))
|
||
|
|
+ parts = ver.split(maxsplit=1)
|
||
|
|
+ if len(parts) != 2:
|
||
|
|
+ # reject unknown format
|
||
|
|
+ raise ValueError
|
||
|
|
+ libc = parts[0]
|
||
|
|
+ version = tuple(map(int, parts[1].split('.')))
|
||
|
|
+
|
||
|
|
+ if sys.platform == 'linux' and libc == 'glibc' and version >= (2, 24):
|
||
|
|
+ # glibc 2.24 has a new Linux posix_spawn implementation using vfork
|
||
|
|
+ # which properly reports errors to the parent process.
|
||
|
|
+ return True
|
||
|
|
+ # Note: Don't use the POSIX implementation of glibc because it doesn't
|
||
|
|
+ # use vfork (even if glibc 2.26 added a pipe to properly report errors
|
||
|
|
+ # to the parent process).
|
||
|
|
+ except (AttributeError, ValueError, OSError):
|
||
|
|
+ # os.confstr() or CS_GNU_LIBC_VERSION value not available
|
||
|
|
+ pass
|
||
|
|
+
|
||
|
|
+ # By default, consider that the implementation does not properly report
|
||
|
|
+ # errors.
|
||
|
|
+ return False
|
||
|
|
+
|
||
|
|
+
|
||
|
|
+_USE_POSIX_SPAWN = _use_posix_spawn()
|
||
|
|
+
|
||
|
|
+
|
||
|
|
class Popen(object):
|
||
|
|
""" Execute a child program in a new process.
|
||
|
|
|
||
|
|
@@ -1409,6 +1460,23 @@ class Popen(object):
|
||
|
|
errread, errwrite)
|
||
|
|
|
||
|
|
|
||
|
|
+ def _posix_spawn(self, args, executable, env, restore_signals):
|
||
|
|
+ """Execute program using os.posix_spawn()."""
|
||
|
|
+ if env is None:
|
||
|
|
+ env = os.environ
|
||
|
|
+
|
||
|
|
+ kwargs = {}
|
||
|
|
+ if restore_signals:
|
||
|
|
+ # See _Py_RestoreSignals() in Python/pylifecycle.c
|
||
|
|
+ sigset = []
|
||
|
|
+ for signame in ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ'):
|
||
|
|
+ signum = getattr(signal, signame, None)
|
||
|
|
+ if signum is not None:
|
||
|
|
+ sigset.append(signum)
|
||
|
|
+ kwargs['setsigdef'] = sigset
|
||
|
|
+
|
||
|
|
+ self.pid = os.posix_spawn(executable, args, env, **kwargs)
|
||
|
|
+
|
||
|
|
def _execute_child(self, args, executable, preexec_fn, close_fds,
|
||
|
|
pass_fds, cwd, env,
|
||
|
|
startupinfo, creationflags, shell,
|
||
|
|
@@ -1433,6 +1501,20 @@ class Popen(object):
|
||
|
|
|
||
|
|
if executable is None:
|
||
|
|
executable = args[0]
|
||
|
|
+
|
||
|
|
+ if (_USE_POSIX_SPAWN
|
||
|
|
+ and os.path.dirname(executable)
|
||
|
|
+ and preexec_fn is None
|
||
|
|
+ and not close_fds
|
||
|
|
+ and not pass_fds
|
||
|
|
+ and cwd is None
|
||
|
|
+ and p2cread == p2cwrite == -1
|
||
|
|
+ and c2pread == c2pwrite == -1
|
||
|
|
+ and errread == errwrite == -1
|
||
|
|
+ and not start_new_session):
|
||
|
|
+ self._posix_spawn(args, executable, env, restore_signals)
|
||
|
|
+ return
|
||
|
|
+
|
||
|
|
orig_executable = executable
|
||
|
|
|
||
|
|
# For transferring possible exec failure from child to parent.
|
||
|
|
diff --git a/Misc/NEWS.d/next/Library/2018-12-20-16-24-51.bpo-35537.z4E7aA.rst b/Misc/NEWS.d/next/Library/2018-12-20-16-24-51.bpo-35537.z4E7aA.rst
|
||
|
|
new file mode 100644
|
||
|
|
index 0000000..b14d749
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/Misc/NEWS.d/next/Library/2018-12-20-16-24-51.bpo-35537.z4E7aA.rst
|
||
|
|
@@ -0,0 +1,2 @@
|
||
|
|
+The :mod:`subprocess` module can now use the :func:`os.posix_spawn` function in
|
||
|
|
+some cases for better performance.
|
||
|
|
--
|
||
|
|
2.23.0
|
||
|
|
|