@@ -606,6 +606,57 @@ def getoutput(cmd):
606
606
return getstatusoutput (cmd )[1 ]
607
607
608
608
609
+ def _use_posix_spawn ():
610
+ """Check is posix_spawn() can be used for subprocess.
611
+
612
+ subprocess requires a posix_spawn() implementation that reports properly
613
+ errors to the parent process, set errno on the following failures:
614
+
615
+ * process attribute actions failed
616
+ * file actions failed
617
+ * exec() failed
618
+
619
+ Prefer an implementation which can use vfork in some cases for best
620
+ performances.
621
+ """
622
+ if _mswindows or not hasattr (os , 'posix_spawn' ):
623
+ # os.posix_spawn() is not available
624
+ return False
625
+
626
+ if sys .platform == 'darwin' :
627
+ # posix_spawn() is a syscall on macOS and properly reports errors
628
+ return True
629
+
630
+ # Check libc name and runtime libc version
631
+ try :
632
+ ver = os .confstr ('CS_GNU_LIBC_VERSION' )
633
+ # parse 'glibc 2.28' as ('glibc', (2, 28))
634
+ parts = ver .split (maxsplit = 1 )
635
+ if len (parts ) != 2 :
636
+ # reject unknown format
637
+ raise ValueError
638
+ libc = parts [0 ]
639
+ version = tuple (map (int , parts [1 ].split ('.' )))
640
+
641
+ if sys .platform == 'linux' and libc == 'glibc' and version >= (2 , 24 ):
642
+ # glibc 2.24 has a new Linux posix_spawn implementation using vfork
643
+ # which properly reports errors to the parent process.
644
+ return True
645
+ # Note: Don't use the POSIX implementation of glibc because it doesn't
646
+ # use vfork (even if glibc 2.26 added a pipe to properly report errors
647
+ # to the parent process).
648
+ except (AttributeError , ValueError , OSError ):
649
+ # os.confstr() or CS_GNU_LIBC_VERSION value not available
650
+ pass
651
+
652
+ # By default, consider that the implementation does not properly report
653
+ # errors.
654
+ return False
655
+
656
+
657
+ _USE_POSIX_SPAWN = _use_posix_spawn ()
658
+
659
+
609
660
class Popen (object ):
610
661
""" Execute a child program in a new process.
611
662
@@ -1390,6 +1441,23 @@ def _get_handles(self, stdin, stdout, stderr):
1390
1441
errread , errwrite )
1391
1442
1392
1443
1444
+ def _posix_spawn (self , args , executable , env , restore_signals ):
1445
+ """Execute program using os.posix_spawn()."""
1446
+ if env is None :
1447
+ env = os .environ
1448
+
1449
+ kwargs = {}
1450
+ if restore_signals :
1451
+ # See _Py_RestoreSignals() in Python/pylifecycle.c
1452
+ sigset = []
1453
+ for signame in ('SIGPIPE' , 'SIGXFZ' , 'SIGXFSZ' ):
1454
+ signum = getattr (signal , signame , None )
1455
+ if signum is not None :
1456
+ sigset .append (signum )
1457
+ kwargs ['setsigdef' ] = sigset
1458
+
1459
+ self .pid = os .posix_spawn (executable , args , env , ** kwargs )
1460
+
1393
1461
def _execute_child (self , args , executable , preexec_fn , close_fds ,
1394
1462
pass_fds , cwd , env ,
1395
1463
startupinfo , creationflags , shell ,
@@ -1414,6 +1482,20 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
1414
1482
1415
1483
if executable is None :
1416
1484
executable = args [0 ]
1485
+
1486
+ if (_USE_POSIX_SPAWN
1487
+ and os .path .dirname (executable )
1488
+ and preexec_fn is None
1489
+ and not close_fds
1490
+ and not pass_fds
1491
+ and cwd is None
1492
+ and p2cread == p2cwrite == - 1
1493
+ and c2pread == c2pwrite == - 1
1494
+ and errread == errwrite == - 1
1495
+ and not start_new_session ):
1496
+ self ._posix_spawn (args , executable , env , restore_signals )
1497
+ return
1498
+
1417
1499
orig_executable = executable
1418
1500
1419
1501
# For transferring possible exec failure from child to parent.
0 commit comments