54
54
# Ignore errors that indicate the command was not found
55
55
NONEXISTING_ERRORS = (FileNotFoundError , NotADirectoryError , PermissionError )
56
56
57
+ ZERO_RETURN_CMD = (sys .executable , '-c' , 'pass' )
58
+
59
+
60
+ def setUpModule ():
61
+ shell_true = shutil .which ('true' )
62
+ if (os .access (shell_true , os .X_OK ) and
63
+ subprocess .run ([shell_true ]).returncode == 0 ):
64
+ global ZERO_RETURN_CMD
65
+ ZERO_RETURN_CMD = (shell_true ,) # Faster than Python startup.
66
+
57
67
58
68
class BaseTestCase (unittest .TestCase ):
59
69
def setUp (self ):
@@ -98,7 +108,7 @@ def _execute_child(self, *args, **kwargs):
98
108
class ProcessTestCase (BaseTestCase ):
99
109
100
110
def test_io_buffered_by_default (self ):
101
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
111
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
102
112
stdin = subprocess .PIPE , stdout = subprocess .PIPE ,
103
113
stderr = subprocess .PIPE )
104
114
try :
@@ -112,7 +122,7 @@ def test_io_buffered_by_default(self):
112
122
p .wait ()
113
123
114
124
def test_io_unbuffered_works (self ):
115
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
125
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
116
126
stdin = subprocess .PIPE , stdout = subprocess .PIPE ,
117
127
stderr = subprocess .PIPE , bufsize = 0 )
118
128
try :
@@ -142,8 +152,7 @@ def test_call_timeout(self):
142
152
143
153
def test_check_call_zero (self ):
144
154
# check_call() function with zero return code
145
- rc = subprocess .check_call ([sys .executable , "-c" ,
146
- "import sys; sys.exit(0)" ])
155
+ rc = subprocess .check_call (ZERO_RETURN_CMD )
147
156
self .assertEqual (rc , 0 )
148
157
149
158
def test_check_call_nonzero (self ):
@@ -709,19 +718,19 @@ def test_invalid_env(self):
709
718
newenv = os .environ .copy ()
710
719
newenv ["FRUIT\0 VEGETABLE" ] = "cabbage"
711
720
with self .assertRaises (ValueError ):
712
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , env = newenv )
721
+ subprocess .Popen (ZERO_RETURN_CMD , env = newenv )
713
722
714
723
# null character in the environment variable value
715
724
newenv = os .environ .copy ()
716
725
newenv ["FRUIT" ] = "orange\0 VEGETABLE=cabbage"
717
726
with self .assertRaises (ValueError ):
718
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , env = newenv )
727
+ subprocess .Popen (ZERO_RETURN_CMD , env = newenv )
719
728
720
729
# equal character in the environment variable name
721
730
newenv = os .environ .copy ()
722
731
newenv ["FRUIT=ORANGE" ] = "lemon"
723
732
with self .assertRaises (ValueError ):
724
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , env = newenv )
733
+ subprocess .Popen (ZERO_RETURN_CMD , env = newenv )
725
734
726
735
# equal character in the environment variable value
727
736
newenv = os .environ .copy ()
@@ -822,7 +831,7 @@ def test_communicate_pipe_fd_leak(self):
822
831
options ['stderr' ] = subprocess .PIPE
823
832
if not options :
824
833
continue
825
- p = subprocess .Popen (( sys . executable , "-c" , "pass" ) , ** options )
834
+ p = subprocess .Popen (ZERO_RETURN_CMD , ** options )
826
835
p .communicate ()
827
836
if p .stdin is not None :
828
837
self .assertTrue (p .stdin .closed )
@@ -961,7 +970,7 @@ def test_universal_newlines_communicate_input_none(self):
961
970
#
962
971
# We set stdout to PIPE because, as of this writing, a different
963
972
# code path is tested when the number of pipes is zero or one.
964
- p = subprocess .Popen ([ sys . executable , "-c" , "pass" ] ,
973
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
965
974
stdin = subprocess .PIPE ,
966
975
stdout = subprocess .PIPE ,
967
976
universal_newlines = True )
@@ -1109,7 +1118,7 @@ def test_poll(self):
1109
1118
self .assertEqual (p .poll (), 0 )
1110
1119
1111
1120
def test_wait (self ):
1112
- p = subprocess .Popen ([ sys . executable , "-c" , "pass" ] )
1121
+ p = subprocess .Popen (ZERO_RETURN_CMD )
1113
1122
self .assertEqual (p .wait (), 0 )
1114
1123
# Subsequent invocations should just return the returncode
1115
1124
self .assertEqual (p .wait (), 0 )
@@ -1128,14 +1137,14 @@ def test_invalid_bufsize(self):
1128
1137
# an invalid type of the bufsize argument should raise
1129
1138
# TypeError.
1130
1139
with self .assertRaises (TypeError ):
1131
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , "orange" )
1140
+ subprocess .Popen (ZERO_RETURN_CMD , "orange" )
1132
1141
1133
1142
def test_bufsize_is_none (self ):
1134
1143
# bufsize=None should be the same as bufsize=0.
1135
- p = subprocess .Popen ([ sys . executable , "-c" , "pass" ] , None )
1144
+ p = subprocess .Popen (ZERO_RETURN_CMD , None )
1136
1145
self .assertEqual (p .wait (), 0 )
1137
1146
# Again with keyword arg
1138
- p = subprocess .Popen ([ sys . executable , "-c" , "pass" ] , bufsize = None )
1147
+ p = subprocess .Popen (ZERO_RETURN_CMD , bufsize = None )
1139
1148
self .assertEqual (p .wait (), 0 )
1140
1149
1141
1150
def _test_bufsize_equal_one (self , line , expected , universal_newlines ):
@@ -1340,7 +1349,7 @@ def test_handles_closed_on_exception(self):
1340
1349
1341
1350
def test_communicate_epipe (self ):
1342
1351
# Issue 10963: communicate() should hide EPIPE
1343
- p = subprocess .Popen ([ sys . executable , "-c" , 'pass' ] ,
1352
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
1344
1353
stdin = subprocess .PIPE ,
1345
1354
stdout = subprocess .PIPE ,
1346
1355
stderr = subprocess .PIPE )
@@ -1351,7 +1360,7 @@ def test_communicate_epipe(self):
1351
1360
1352
1361
def test_communicate_epipe_only_stdin (self ):
1353
1362
# Issue 10963: communicate() should hide EPIPE
1354
- p = subprocess .Popen ([ sys . executable , "-c" , 'pass' ] ,
1363
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
1355
1364
stdin = subprocess .PIPE )
1356
1365
self .addCleanup (p .stdin .close )
1357
1366
p .wait ()
@@ -1390,7 +1399,7 @@ def test_failed_child_execute_fd_leak(self):
1390
1399
fds_before_popen = os .listdir (fd_directory )
1391
1400
with self .assertRaises (PopenTestException ):
1392
1401
PopenExecuteChildRaises (
1393
- [ sys . executable , '-c' , 'pass' ] , stdin = subprocess .PIPE ,
1402
+ ZERO_RETURN_CMD , stdin = subprocess .PIPE ,
1394
1403
stdout = subprocess .PIPE , stderr = subprocess .PIPE )
1395
1404
1396
1405
# NOTE: This test doesn't verify that the real _execute_child
@@ -1433,7 +1442,7 @@ def test_check(self):
1433
1442
1434
1443
def test_check_zero (self ):
1435
1444
# check_returncode shouldn't raise when returncode is zero
1436
- cp = self . run_python ( "import sys; sys.exit(0)" , check = True )
1445
+ cp = subprocess . run ( ZERO_RETURN_CMD , check = True )
1437
1446
self .assertEqual (cp .returncode , 0 )
1438
1447
1439
1448
def test_timeout (self ):
@@ -1791,16 +1800,16 @@ def test_user(self):
1791
1800
self .assertEqual (child_user , user_uid )
1792
1801
1793
1802
with self .assertRaises (ValueError ):
1794
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] , user = - 1 )
1803
+ subprocess .check_call (ZERO_RETURN_CMD , user = - 1 )
1795
1804
1796
1805
if pwd is None :
1797
1806
with self .assertRaises (ValueError ):
1798
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] , user = name_uid )
1807
+ subprocess .check_call (ZERO_RETURN_CMD , user = name_uid )
1799
1808
1800
1809
@unittest .skipIf (hasattr (os , 'setreuid' ), 'setreuid() available on platform' )
1801
1810
def test_user_error (self ):
1802
1811
with self .assertRaises (ValueError ):
1803
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] , user = 65535 )
1812
+ subprocess .check_call (ZERO_RETURN_CMD , user = 65535 )
1804
1813
1805
1814
@unittest .skipUnless (hasattr (os , 'setregid' ), 'no setregid() on platform' )
1806
1815
def test_group (self ):
@@ -1834,16 +1843,16 @@ def test_group(self):
1834
1843
1835
1844
# make sure we bomb on negative values
1836
1845
with self .assertRaises (ValueError ):
1837
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] , group = - 1 )
1846
+ subprocess .check_call (ZERO_RETURN_CMD , group = - 1 )
1838
1847
1839
1848
if grp is None :
1840
1849
with self .assertRaises (ValueError ):
1841
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] , group = name_group )
1850
+ subprocess .check_call (ZERO_RETURN_CMD , group = name_group )
1842
1851
1843
1852
@unittest .skipIf (hasattr (os , 'setregid' ), 'setregid() available on platform' )
1844
1853
def test_group_error (self ):
1845
1854
with self .assertRaises (ValueError ):
1846
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] , group = 65535 )
1855
+ subprocess .check_call (ZERO_RETURN_CMD , group = 65535 )
1847
1856
1848
1857
@unittest .skipUnless (hasattr (os , 'setgroups' ), 'no setgroups() on platform' )
1849
1858
def test_extra_groups (self ):
@@ -1882,17 +1891,17 @@ def test_extra_groups(self):
1882
1891
1883
1892
# make sure we bomb on negative values
1884
1893
with self .assertRaises (ValueError ):
1885
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] , extra_groups = [- 1 ])
1894
+ subprocess .check_call (ZERO_RETURN_CMD , extra_groups = [- 1 ])
1886
1895
1887
1896
if grp is None :
1888
1897
with self .assertRaises (ValueError ):
1889
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] ,
1898
+ subprocess .check_call (ZERO_RETURN_CMD ,
1890
1899
extra_groups = [name_group ])
1891
1900
1892
1901
@unittest .skipIf (hasattr (os , 'setgroups' ), 'setgroups() available on platform' )
1893
1902
def test_extra_groups_error (self ):
1894
1903
with self .assertRaises (ValueError ):
1895
- subprocess .check_call ([ sys . executable , "-c" , "pass" ] , extra_groups = [])
1904
+ subprocess .check_call (ZERO_RETURN_CMD , extra_groups = [])
1896
1905
1897
1906
@unittest .skipIf (mswindows or not hasattr (os , 'umask' ),
1898
1907
'POSIX umask() is not available.' )
@@ -1904,7 +1913,7 @@ def test_umask(self):
1904
1913
# We set an unusual umask in the child so as a unique mode
1905
1914
# for us to test the child's touched file for.
1906
1915
subprocess .check_call (
1907
- [sys .executable , "-c" , f"open({ name !r} , 'w')" ], # touch
1916
+ [sys .executable , "-c" , f"open({ name !r} , 'w').close() " ],
1908
1917
umask = 0o053 )
1909
1918
# Ignore execute permissions entirely in our test,
1910
1919
# filesystems could be mounted to ignore or force that.
@@ -2007,7 +2016,7 @@ def raise_it():
2007
2016
2008
2017
with self .assertRaises (subprocess .SubprocessError ):
2009
2018
self ._TestExecuteChildPopen (
2010
- self , [ sys . executable , "-c" , "pass" ] ,
2019
+ self , ZERO_RETURN_CMD ,
2011
2020
stdin = subprocess .PIPE , stdout = subprocess .PIPE ,
2012
2021
stderr = subprocess .PIPE , preexec_fn = raise_it )
2013
2022
@@ -2464,7 +2473,7 @@ def prepare():
2464
2473
2465
2474
try :
2466
2475
subprocess .call (
2467
- [ sys . executable , "-c" , "pass" ] ,
2476
+ ZERO_RETURN_CMD ,
2468
2477
preexec_fn = prepare )
2469
2478
except ValueError as err :
2470
2479
# Pure Python implementations keeps the message
@@ -2507,29 +2516,30 @@ def test_undecodable_env(self):
2507
2516
self .assertEqual (stdout .decode ('ascii' ), ascii (encoded_value ))
2508
2517
2509
2518
def test_bytes_program (self ):
2510
- abs_program = os .fsencode (sys .executable )
2511
- path , program = os .path .split (sys .executable )
2519
+ abs_program = os .fsencode (ZERO_RETURN_CMD [0 ])
2520
+ args = list (ZERO_RETURN_CMD [1 :])
2521
+ path , program = os .path .split (ZERO_RETURN_CMD [0 ])
2512
2522
program = os .fsencode (program )
2513
2523
2514
2524
# absolute bytes path
2515
- exitcode = subprocess .call ([abs_program , "-c" , "pass" ] )
2525
+ exitcode = subprocess .call ([abs_program ] + args )
2516
2526
self .assertEqual (exitcode , 0 )
2517
2527
2518
2528
# absolute bytes path as a string
2519
- cmd = b"'" + abs_program + b"' -c pass"
2529
+ cmd = b"'%s' %s" % ( abs_program , " " . join ( args ). encode ( "utf-8" ))
2520
2530
exitcode = subprocess .call (cmd , shell = True )
2521
2531
self .assertEqual (exitcode , 0 )
2522
2532
2523
2533
# bytes program, unicode PATH
2524
2534
env = os .environ .copy ()
2525
2535
env ["PATH" ] = path
2526
- exitcode = subprocess .call ([program , "-c" , "pass" ] , env = env )
2536
+ exitcode = subprocess .call ([program ] + args , env = env )
2527
2537
self .assertEqual (exitcode , 0 )
2528
2538
2529
2539
# bytes program, bytes PATH
2530
2540
envb = os .environb .copy ()
2531
2541
envb [b"PATH" ] = os .fsencode (path )
2532
- exitcode = subprocess .call ([program , "-c" , "pass" ] , env = envb )
2542
+ exitcode = subprocess .call ([program ] + args , env = envb )
2533
2543
self .assertEqual (exitcode , 0 )
2534
2544
2535
2545
def test_pipe_cloexec (self ):
@@ -2757,7 +2767,7 @@ def test_pass_fds(self):
2757
2767
# pass_fds overrides close_fds with a warning.
2758
2768
with self .assertWarns (RuntimeWarning ) as context :
2759
2769
self .assertFalse (subprocess .call (
2760
- [ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2770
+ ZERO_RETURN_CMD ,
2761
2771
close_fds = False , pass_fds = (fd , )))
2762
2772
self .assertIn ('overriding close_fds' , str (context .warning ))
2763
2773
@@ -2819,19 +2829,19 @@ def test_pass_fds_redirected(self):
2819
2829
2820
2830
def test_stdout_stdin_are_single_inout_fd (self ):
2821
2831
with io .open (os .devnull , "r+" ) as inout :
2822
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2832
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
2823
2833
stdout = inout , stdin = inout )
2824
2834
p .wait ()
2825
2835
2826
2836
def test_stdout_stderr_are_single_inout_fd (self ):
2827
2837
with io .open (os .devnull , "r+" ) as inout :
2828
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2838
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
2829
2839
stdout = inout , stderr = inout )
2830
2840
p .wait ()
2831
2841
2832
2842
def test_stderr_stdin_are_single_inout_fd (self ):
2833
2843
with io .open (os .devnull , "r+" ) as inout :
2834
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2844
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
2835
2845
stderr = inout , stdin = inout )
2836
2846
p .wait ()
2837
2847
@@ -3031,7 +3041,7 @@ def __int__(self):
3031
3041
def test_communicate_BrokenPipeError_stdin_close (self ):
3032
3042
# By not setting stdout or stderr or a timeout we force the fast path
3033
3043
# that just calls _stdin_write() internally due to our mock.
3034
- proc = subprocess .Popen ([ sys . executable , '-c' , 'pass' ] )
3044
+ proc = subprocess .Popen (ZERO_RETURN_CMD )
3035
3045
with proc , mock .patch .object (proc , 'stdin' ) as mock_proc_stdin :
3036
3046
mock_proc_stdin .close .side_effect = BrokenPipeError
3037
3047
proc .communicate () # Should swallow BrokenPipeError from close.
@@ -3040,7 +3050,7 @@ def test_communicate_BrokenPipeError_stdin_close(self):
3040
3050
def test_communicate_BrokenPipeError_stdin_write (self ):
3041
3051
# By not setting stdout or stderr or a timeout we force the fast path
3042
3052
# that just calls _stdin_write() internally due to our mock.
3043
- proc = subprocess .Popen ([ sys . executable , '-c' , 'pass' ] )
3053
+ proc = subprocess .Popen (ZERO_RETURN_CMD )
3044
3054
with proc , mock .patch .object (proc , 'stdin' ) as mock_proc_stdin :
3045
3055
mock_proc_stdin .write .side_effect = BrokenPipeError
3046
3056
proc .communicate (b'stuff' ) # Should swallow the BrokenPipeError.
@@ -3079,7 +3089,7 @@ def test_communicate_BrokenPipeError_stdin_close_with_timeout(self):
3079
3089
'need _testcapi.W_STOPCODE' )
3080
3090
def test_stopped (self ):
3081
3091
"""Test wait() behavior when waitpid returns WIFSTOPPED; issue29335."""
3082
- args = [ sys . executable , '-c' , 'pass' ]
3092
+ args = ZERO_RETURN_CMD
3083
3093
proc = subprocess .Popen (args )
3084
3094
3085
3095
# Wait until the real process completes to avoid zombie process
@@ -3109,7 +3119,7 @@ def test_startupinfo(self):
3109
3119
# Since Python is a console process, it won't be affected
3110
3120
# by wShowWindow, but the argument should be silently
3111
3121
# ignored
3112
- subprocess .call ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
3122
+ subprocess .call (ZERO_RETURN_CMD ,
3113
3123
startupinfo = startupinfo )
3114
3124
3115
3125
def test_startupinfo_keywords (self ):
@@ -3125,7 +3135,7 @@ def test_startupinfo_keywords(self):
3125
3135
# Since Python is a console process, it won't be affected
3126
3136
# by wShowWindow, but the argument should be silently
3127
3137
# ignored
3128
- subprocess .call ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
3138
+ subprocess .call (ZERO_RETURN_CMD ,
3129
3139
startupinfo = startupinfo )
3130
3140
3131
3141
def test_startupinfo_copy (self ):
@@ -3137,7 +3147,7 @@ def test_startupinfo_copy(self):
3137
3147
# Call Popen() twice with the same startupinfo object to make sure
3138
3148
# that it's not modified
3139
3149
for _ in range (2 ):
3140
- cmd = [ sys . executable , "-c" , "pass" ]
3150
+ cmd = ZERO_RETURN_CMD
3141
3151
with open (os .devnull , 'w' ) as null :
3142
3152
proc = subprocess .Popen (cmd ,
3143
3153
stdout = null ,
@@ -3177,7 +3187,7 @@ def test_issue31471(self):
3177
3187
class BadEnv (dict ):
3178
3188
keys = None
3179
3189
with self .assertRaises (TypeError ):
3180
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , env = BadEnv ())
3190
+ subprocess .Popen (ZERO_RETURN_CMD , env = BadEnv ())
3181
3191
3182
3192
def test_close_fds (self ):
3183
3193
# close file descriptors
@@ -3238,13 +3248,13 @@ def test_close_fds_with_stdio(self):
3238
3248
def test_empty_attribute_list (self ):
3239
3249
startupinfo = subprocess .STARTUPINFO ()
3240
3250
startupinfo .lpAttributeList = {}
3241
- subprocess .call ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
3251
+ subprocess .call (ZERO_RETURN_CMD ,
3242
3252
startupinfo = startupinfo )
3243
3253
3244
3254
def test_empty_handle_list (self ):
3245
3255
startupinfo = subprocess .STARTUPINFO ()
3246
3256
startupinfo .lpAttributeList = {"handle_list" : []}
3247
- subprocess .call ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
3257
+ subprocess .call (ZERO_RETURN_CMD ,
3248
3258
startupinfo = startupinfo )
3249
3259
3250
3260
def test_shell_sequence (self ):
@@ -3543,7 +3553,7 @@ def test_invalid_args(self):
3543
3553
3544
3554
def test_broken_pipe_cleanup (self ):
3545
3555
"""Broken pipe error should not prevent wait() (Issue 21619)"""
3546
- proc = subprocess .Popen ([ sys . executable , '-c' , 'pass' ] ,
3556
+ proc = subprocess .Popen (ZERO_RETURN_CMD ,
3547
3557
stdin = subprocess .PIPE ,
3548
3558
bufsize = support .PIPE_MAX_SIZE * 2 )
3549
3559
proc = proc .__enter__ ()
0 commit comments