@@ -1489,10 +1489,10 @@ def test_setgroups(self):
14891489 self .assertListEqual (groups , posix .getgroups ())
14901490
14911491
1492- @unittest .skipUnless (hasattr (os , 'posix_spawn' ), "test needs os.posix_spawn" )
1493- class TestPosixSpawn (unittest .TestCase ):
1494- # Program which does nothing and exit with status 0 (success)
1492+ class _PosixSpawnMixin :
1493+ # Program which does nothing and exits with status 0 (success)
14951494 NOOP_PROGRAM = (sys .executable , '-I' , '-S' , '-c' , 'pass' )
1495+ spawn_func = None
14961496
14971497 def python_args (self , * args ):
14981498 # Disable site module to avoid side effects. For example,
@@ -1511,17 +1511,17 @@ def test_returns_pid(self):
15111511 pidfile.write(str(os.getpid()))
15121512 """
15131513 args = self .python_args ('-c' , script )
1514- pid = posix . posix_spawn (args [0 ], args , os .environ )
1514+ pid = self . spawn_func (args [0 ], args , os .environ )
15151515 self .assertEqual (os .waitpid (pid , 0 ), (pid , 0 ))
15161516 with open (pidfile ) as f :
15171517 self .assertEqual (f .read (), str (pid ))
15181518
15191519 def test_no_such_executable (self ):
15201520 no_such_executable = 'no_such_executable'
15211521 try :
1522- pid = posix . posix_spawn (no_such_executable ,
1523- [no_such_executable ],
1524- os .environ )
1522+ pid = self . spawn_func (no_such_executable ,
1523+ [no_such_executable ],
1524+ os .environ )
15251525 except FileNotFoundError as exc :
15261526 self .assertEqual (exc .filename , no_such_executable )
15271527 else :
@@ -1538,14 +1538,14 @@ def test_specify_environment(self):
15381538 envfile.write(os.environ['foo'])
15391539 """
15401540 args = self .python_args ('-c' , script )
1541- pid = posix . posix_spawn (args [0 ], args ,
1542- {** os .environ , 'foo' : 'bar' })
1541+ pid = self . spawn_func (args [0 ], args ,
1542+ {** os .environ , 'foo' : 'bar' })
15431543 self .assertEqual (os .waitpid (pid , 0 ), (pid , 0 ))
15441544 with open (envfile ) as f :
15451545 self .assertEqual (f .read (), 'bar' )
15461546
15471547 def test_empty_file_actions (self ):
1548- pid = posix . posix_spawn (
1548+ pid = self . spawn_func (
15491549 self .NOOP_PROGRAM [0 ],
15501550 self .NOOP_PROGRAM ,
15511551 os .environ ,
@@ -1554,7 +1554,7 @@ def test_empty_file_actions(self):
15541554 self .assertEqual (os .waitpid (pid , 0 ), (pid , 0 ))
15551555
15561556 def test_resetids_explicit_default (self ):
1557- pid = posix . posix_spawn (
1557+ pid = self . spawn_func (
15581558 sys .executable ,
15591559 [sys .executable , '-c' , 'pass' ],
15601560 os .environ ,
@@ -1563,7 +1563,7 @@ def test_resetids_explicit_default(self):
15631563 self .assertEqual (os .waitpid (pid , 0 ), (pid , 0 ))
15641564
15651565 def test_resetids (self ):
1566- pid = posix . posix_spawn (
1566+ pid = self . spawn_func (
15671567 sys .executable ,
15681568 [sys .executable , '-c' , 'pass' ],
15691569 os .environ ,
@@ -1573,12 +1573,12 @@ def test_resetids(self):
15731573
15741574 def test_resetids_wrong_type (self ):
15751575 with self .assertRaises (TypeError ):
1576- posix . posix_spawn (sys .executable ,
1577- [sys .executable , "-c" , "pass" ],
1578- os .environ , resetids = None )
1576+ self . spawn_func (sys .executable ,
1577+ [sys .executable , "-c" , "pass" ],
1578+ os .environ , resetids = None )
15791579
15801580 def test_setpgroup (self ):
1581- pid = posix . posix_spawn (
1581+ pid = self . spawn_func (
15821582 sys .executable ,
15831583 [sys .executable , '-c' , 'pass' ],
15841584 os .environ ,
@@ -1588,9 +1588,9 @@ def test_setpgroup(self):
15881588
15891589 def test_setpgroup_wrong_type (self ):
15901590 with self .assertRaises (TypeError ):
1591- posix . posix_spawn (sys .executable ,
1592- [sys .executable , "-c" , "pass" ],
1593- os .environ , setpgroup = "023" )
1591+ self . spawn_func (sys .executable ,
1592+ [sys .executable , "-c" , "pass" ],
1593+ os .environ , setpgroup = "023" )
15941594
15951595 @unittest .skipUnless (hasattr (signal , 'pthread_sigmask' ),
15961596 'need signal.pthread_sigmask()' )
@@ -1599,7 +1599,7 @@ def test_setsigmask(self):
15991599 import signal
16001600 signal.raise_signal(signal.SIGUSR1)""" )
16011601
1602- pid = posix . posix_spawn (
1602+ pid = self . spawn_func (
16031603 sys .executable ,
16041604 [sys .executable , '-c' , code ],
16051605 os .environ ,
@@ -1609,18 +1609,18 @@ def test_setsigmask(self):
16091609
16101610 def test_setsigmask_wrong_type (self ):
16111611 with self .assertRaises (TypeError ):
1612- posix . posix_spawn (sys .executable ,
1613- [sys .executable , "-c" , "pass" ],
1614- os .environ , setsigmask = 34 )
1612+ self . spawn_func (sys .executable ,
1613+ [sys .executable , "-c" , "pass" ],
1614+ os .environ , setsigmask = 34 )
16151615 with self .assertRaises (TypeError ):
1616- posix . posix_spawn (sys .executable ,
1617- [sys .executable , "-c" , "pass" ],
1618- os .environ , setsigmask = ["j" ])
1616+ self . spawn_func (sys .executable ,
1617+ [sys .executable , "-c" , "pass" ],
1618+ os .environ , setsigmask = ["j" ])
16191619 with self .assertRaises (ValueError ):
1620- posix . posix_spawn (sys .executable ,
1621- [sys .executable , "-c" , "pass" ],
1622- os .environ , setsigmask = [signal .NSIG ,
1623- signal .NSIG + 1 ])
1620+ self . spawn_func (sys .executable ,
1621+ [sys .executable , "-c" , "pass" ],
1622+ os .environ , setsigmask = [signal .NSIG ,
1623+ signal .NSIG + 1 ])
16241624
16251625 @unittest .skipUnless (hasattr (signal , 'pthread_sigmask' ),
16261626 'need signal.pthread_sigmask()' )
@@ -1630,7 +1630,7 @@ def test_setsigdef(self):
16301630 import signal
16311631 signal.raise_signal(signal.SIGUSR1)""" )
16321632 try :
1633- pid = posix . posix_spawn (
1633+ pid = self . spawn_func (
16341634 sys .executable ,
16351635 [sys .executable , '-c' , code ],
16361636 os .environ ,
@@ -1646,17 +1646,17 @@ def test_setsigdef(self):
16461646
16471647 def test_setsigdef_wrong_type (self ):
16481648 with self .assertRaises (TypeError ):
1649- posix . posix_spawn (sys .executable ,
1650- [sys .executable , "-c" , "pass" ],
1651- os .environ , setsigdef = 34 )
1649+ self . spawn_func (sys .executable ,
1650+ [sys .executable , "-c" , "pass" ],
1651+ os .environ , setsigdef = 34 )
16521652 with self .assertRaises (TypeError ):
1653- posix . posix_spawn (sys .executable ,
1654- [sys .executable , "-c" , "pass" ],
1655- os .environ , setsigdef = ["j" ])
1653+ self . spawn_func (sys .executable ,
1654+ [sys .executable , "-c" , "pass" ],
1655+ os .environ , setsigdef = ["j" ])
16561656 with self .assertRaises (ValueError ):
1657- posix . posix_spawn (sys .executable ,
1658- [sys .executable , "-c" , "pass" ],
1659- os .environ , setsigdef = [signal .NSIG , signal .NSIG + 1 ])
1657+ self . spawn_func (sys .executable ,
1658+ [sys .executable , "-c" , "pass" ],
1659+ os .environ , setsigdef = [signal .NSIG , signal .NSIG + 1 ])
16601660
16611661 @requires_sched
16621662 @unittest .skipIf (sys .platform .startswith (('freebsd' , 'netbsd' )),
@@ -1670,7 +1670,7 @@ def test_setscheduler_only_param(self):
16701670 sys.exit(101)
16711671 if os.sched_getparam(0).sched_priority != { priority } :
16721672 sys.exit(102)""" )
1673- pid = posix . posix_spawn (
1673+ pid = self . spawn_func (
16741674 sys .executable ,
16751675 [sys .executable , '-c' , code ],
16761676 os .environ ,
@@ -1690,7 +1690,7 @@ def test_setscheduler_with_policy(self):
16901690 sys.exit(101)
16911691 if os.sched_getparam(0).sched_priority != { priority } :
16921692 sys.exit(102)""" )
1693- pid = posix . posix_spawn (
1693+ pid = self . spawn_func (
16941694 sys .executable ,
16951695 [sys .executable , '-c' , code ],
16961696 os .environ ,
@@ -1704,40 +1704,40 @@ def test_multiple_file_actions(self):
17041704 (os .POSIX_SPAWN_CLOSE , 0 ),
17051705 (os .POSIX_SPAWN_DUP2 , 1 , 4 ),
17061706 ]
1707- pid = posix . posix_spawn (self .NOOP_PROGRAM [0 ],
1708- self .NOOP_PROGRAM ,
1709- os .environ ,
1710- file_actions = file_actions )
1707+ pid = self . spawn_func (self .NOOP_PROGRAM [0 ],
1708+ self .NOOP_PROGRAM ,
1709+ os .environ ,
1710+ file_actions = file_actions )
17111711 self .assertEqual (os .waitpid (pid , 0 ), (pid , 0 ))
17121712
17131713 def test_bad_file_actions (self ):
17141714 args = self .NOOP_PROGRAM
17151715 with self .assertRaises (TypeError ):
1716- posix . posix_spawn (args [0 ], args , os .environ ,
1717- file_actions = [None ])
1716+ self . spawn_func (args [0 ], args , os .environ ,
1717+ file_actions = [None ])
17181718 with self .assertRaises (TypeError ):
1719- posix . posix_spawn (args [0 ], args , os .environ ,
1720- file_actions = [()])
1719+ self . spawn_func (args [0 ], args , os .environ ,
1720+ file_actions = [()])
17211721 with self .assertRaises (TypeError ):
1722- posix . posix_spawn (args [0 ], args , os .environ ,
1723- file_actions = [(None ,)])
1722+ self . spawn_func (args [0 ], args , os .environ ,
1723+ file_actions = [(None ,)])
17241724 with self .assertRaises (TypeError ):
1725- posix . posix_spawn (args [0 ], args , os .environ ,
1726- file_actions = [(12345 ,)])
1725+ self . spawn_func (args [0 ], args , os .environ ,
1726+ file_actions = [(12345 ,)])
17271727 with self .assertRaises (TypeError ):
1728- posix . posix_spawn (args [0 ], args , os .environ ,
1729- file_actions = [(os .POSIX_SPAWN_CLOSE ,)])
1728+ self . spawn_func (args [0 ], args , os .environ ,
1729+ file_actions = [(os .POSIX_SPAWN_CLOSE ,)])
17301730 with self .assertRaises (TypeError ):
1731- posix . posix_spawn (args [0 ], args , os .environ ,
1732- file_actions = [(os .POSIX_SPAWN_CLOSE , 1 , 2 )])
1731+ self . spawn_func (args [0 ], args , os .environ ,
1732+ file_actions = [(os .POSIX_SPAWN_CLOSE , 1 , 2 )])
17331733 with self .assertRaises (TypeError ):
1734- posix . posix_spawn (args [0 ], args , os .environ ,
1735- file_actions = [(os .POSIX_SPAWN_CLOSE , None )])
1734+ self . spawn_func (args [0 ], args , os .environ ,
1735+ file_actions = [(os .POSIX_SPAWN_CLOSE , None )])
17361736 with self .assertRaises (ValueError ):
1737- posix . posix_spawn (args [0 ], args , os .environ ,
1738- file_actions = [(os .POSIX_SPAWN_OPEN ,
1739- 3 , __file__ + '\0 ' ,
1740- os .O_RDONLY , 0 )])
1737+ self . spawn_func (args [0 ], args , os .environ ,
1738+ file_actions = [(os .POSIX_SPAWN_OPEN ,
1739+ 3 , __file__ + '\0 ' ,
1740+ os .O_RDONLY , 0 )])
17411741
17421742 def test_open_file (self ):
17431743 outfile = support .TESTFN
@@ -1752,8 +1752,8 @@ def test_open_file(self):
17521752 stat .S_IRUSR | stat .S_IWUSR ),
17531753 ]
17541754 args = self .python_args ('-c' , script )
1755- pid = posix . posix_spawn (args [0 ], args , os .environ ,
1756- file_actions = file_actions )
1755+ pid = self . spawn_func (args [0 ], args , os .environ ,
1756+ file_actions = file_actions )
17571757 self .assertEqual (os .waitpid (pid , 0 ), (pid , 0 ))
17581758 with open (outfile ) as f :
17591759 self .assertEqual (f .read (), 'hello' )
@@ -1770,8 +1770,8 @@ def test_close_file(self):
17701770 closefile.write('is closed %d' % e.errno)
17711771 """
17721772 args = self .python_args ('-c' , script )
1773- pid = posix . posix_spawn (args [0 ], args , os .environ ,
1774- file_actions = [(os .POSIX_SPAWN_CLOSE , 0 ), ])
1773+ pid = self . spawn_func (args [0 ], args , os .environ ,
1774+ file_actions = [(os .POSIX_SPAWN_CLOSE , 0 )])
17751775 self .assertEqual (os .waitpid (pid , 0 ), (pid , 0 ))
17761776 with open (closefile ) as f :
17771777 self .assertEqual (f .read (), 'is closed %d' % errno .EBADF )
@@ -1788,16 +1788,64 @@ def test_dup2(self):
17881788 (os .POSIX_SPAWN_DUP2 , childfile .fileno (), 1 ),
17891789 ]
17901790 args = self .python_args ('-c' , script )
1791- pid = posix . posix_spawn (args [0 ], args , os .environ ,
1792- file_actions = file_actions )
1791+ pid = self . spawn_func (args [0 ], args , os .environ ,
1792+ file_actions = file_actions )
17931793 self .assertEqual (os .waitpid (pid , 0 ), (pid , 0 ))
17941794 with open (dupfile ) as f :
17951795 self .assertEqual (f .read (), 'hello' )
17961796
17971797
1798+ @unittest .skipUnless (hasattr (os , 'posix_spawn' ), "test needs os.posix_spawn" )
1799+ class TestPosixSpawn (unittest .TestCase , _PosixSpawnMixin ):
1800+ spawn_func = getattr (posix , 'posix_spawn' , None )
1801+
1802+
1803+ @unittest .skipUnless (hasattr (os , 'posix_spawnp' ), "test needs os.posix_spawnp" )
1804+ class TestPosixSpawnP (unittest .TestCase , _PosixSpawnMixin ):
1805+ spawn_func = getattr (posix , 'posix_spawnp' , None )
1806+
1807+ @support .skip_unless_symlink
1808+ def test_posix_spawnp (self ):
1809+ # Use a symlink to create a program in its own temporary directory
1810+ temp_dir = tempfile .mkdtemp ()
1811+ self .addCleanup (support .rmtree , temp_dir )
1812+
1813+ program = 'posix_spawnp_test_program.exe'
1814+ program_fullpath = os .path .join (temp_dir , program )
1815+ os .symlink (sys .executable , program_fullpath )
1816+
1817+ try :
1818+ path = os .pathsep .join ((temp_dir , os .environ ['PATH' ]))
1819+ except KeyError :
1820+ path = temp_dir # PATH is not set
1821+
1822+ spawn_args = (program , '-I' , '-S' , '-c' , 'pass' )
1823+ code = textwrap .dedent ("""
1824+ import os
1825+ args = %a
1826+ pid = os.posix_spawnp(args[0], args, os.environ)
1827+ pid2, status = os.waitpid(pid, 0)
1828+ if pid2 != pid:
1829+ raise Exception(f"pid {pid2} != {pid}")
1830+ if status != 0:
1831+ raise Exception(f"status {status} != 0")
1832+ """ % (spawn_args ,))
1833+
1834+ # Use a subprocess to test os.posix_spawnp() with a modified PATH
1835+ # environment variable: posix_spawnp() uses the current environment
1836+ # to locate the program, not its environment argument.
1837+ args = ('-c' , code )
1838+ assert_python_ok (* args , PATH = path )
1839+
1840+
17981841def test_main ():
17991842 try :
1800- support .run_unittest (PosixTester , PosixGroupsTester , TestPosixSpawn )
1843+ support .run_unittest (
1844+ PosixTester ,
1845+ PosixGroupsTester ,
1846+ TestPosixSpawn ,
1847+ TestPosixSpawnP ,
1848+ )
18011849 finally :
18021850 support .reap_children ()
18031851
0 commit comments