45
45
# Ignore errors that indicate the command was not found
46
46
NONEXISTING_ERRORS = (FileNotFoundError , NotADirectoryError , PermissionError )
47
47
48
+ ZERO_RETURN_CMD = (sys .executable , '-c' , 'pass' )
49
+
50
+
51
+ def setUpModule ():
52
+ shell_true = shutil .which ('true' )
53
+ if shell_true is None :
54
+ return
55
+ if (os .access (shell_true , os .X_OK ) and
56
+ subprocess .run ([shell_true ]).returncode == 0 ):
57
+ global ZERO_RETURN_CMD
58
+ ZERO_RETURN_CMD = (shell_true ,) # Faster than Python startup.
59
+
48
60
49
61
class BaseTestCase (unittest .TestCase ):
50
62
def setUp (self ):
@@ -89,7 +101,7 @@ def _execute_child(self, *args, **kwargs):
89
101
class ProcessTestCase (BaseTestCase ):
90
102
91
103
def test_io_buffered_by_default (self ):
92
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
104
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
93
105
stdin = subprocess .PIPE , stdout = subprocess .PIPE ,
94
106
stderr = subprocess .PIPE )
95
107
try :
@@ -103,7 +115,7 @@ def test_io_buffered_by_default(self):
103
115
p .wait ()
104
116
105
117
def test_io_unbuffered_works (self ):
106
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
118
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
107
119
stdin = subprocess .PIPE , stdout = subprocess .PIPE ,
108
120
stderr = subprocess .PIPE , bufsize = 0 )
109
121
try :
@@ -133,8 +145,7 @@ def test_call_timeout(self):
133
145
134
146
def test_check_call_zero (self ):
135
147
# check_call() function with zero return code
136
- rc = subprocess .check_call ([sys .executable , "-c" ,
137
- "import sys; sys.exit(0)" ])
148
+ rc = subprocess .check_call (ZERO_RETURN_CMD )
138
149
self .assertEqual (rc , 0 )
139
150
140
151
def test_check_call_nonzero (self ):
@@ -700,19 +711,19 @@ def test_invalid_env(self):
700
711
newenv = os .environ .copy ()
701
712
newenv ["FRUIT\0 VEGETABLE" ] = "cabbage"
702
713
with self .assertRaises (ValueError ):
703
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , env = newenv )
714
+ subprocess .Popen (ZERO_RETURN_CMD , env = newenv )
704
715
705
716
# null character in the environment variable value
706
717
newenv = os .environ .copy ()
707
718
newenv ["FRUIT" ] = "orange\0 VEGETABLE=cabbage"
708
719
with self .assertRaises (ValueError ):
709
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , env = newenv )
720
+ subprocess .Popen (ZERO_RETURN_CMD , env = newenv )
710
721
711
722
# equal character in the environment variable name
712
723
newenv = os .environ .copy ()
713
724
newenv ["FRUIT=ORANGE" ] = "lemon"
714
725
with self .assertRaises (ValueError ):
715
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , env = newenv )
726
+ subprocess .Popen (ZERO_RETURN_CMD , env = newenv )
716
727
717
728
# equal character in the environment variable value
718
729
newenv = os .environ .copy ()
@@ -813,7 +824,7 @@ def test_communicate_pipe_fd_leak(self):
813
824
options ['stderr' ] = subprocess .PIPE
814
825
if not options :
815
826
continue
816
- p = subprocess .Popen (( sys . executable , "-c" , "pass" ) , ** options )
827
+ p = subprocess .Popen (ZERO_RETURN_CMD , ** options )
817
828
p .communicate ()
818
829
if p .stdin is not None :
819
830
self .assertTrue (p .stdin .closed )
@@ -952,7 +963,7 @@ def test_universal_newlines_communicate_input_none(self):
952
963
#
953
964
# We set stdout to PIPE because, as of this writing, a different
954
965
# code path is tested when the number of pipes is zero or one.
955
- p = subprocess .Popen ([ sys . executable , "-c" , "pass" ] ,
966
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
956
967
stdin = subprocess .PIPE ,
957
968
stdout = subprocess .PIPE ,
958
969
universal_newlines = True )
@@ -1100,7 +1111,7 @@ def test_poll(self):
1100
1111
self .assertEqual (p .poll (), 0 )
1101
1112
1102
1113
def test_wait (self ):
1103
- p = subprocess .Popen ([ sys . executable , "-c" , "pass" ] )
1114
+ p = subprocess .Popen (ZERO_RETURN_CMD )
1104
1115
self .assertEqual (p .wait (), 0 )
1105
1116
# Subsequent invocations should just return the returncode
1106
1117
self .assertEqual (p .wait (), 0 )
@@ -1119,14 +1130,14 @@ def test_invalid_bufsize(self):
1119
1130
# an invalid type of the bufsize argument should raise
1120
1131
# TypeError.
1121
1132
with self .assertRaises (TypeError ):
1122
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , "orange" )
1133
+ subprocess .Popen (ZERO_RETURN_CMD , "orange" )
1123
1134
1124
1135
def test_bufsize_is_none (self ):
1125
1136
# bufsize=None should be the same as bufsize=0.
1126
- p = subprocess .Popen ([ sys . executable , "-c" , "pass" ] , None )
1137
+ p = subprocess .Popen (ZERO_RETURN_CMD , None )
1127
1138
self .assertEqual (p .wait (), 0 )
1128
1139
# Again with keyword arg
1129
- p = subprocess .Popen ([ sys . executable , "-c" , "pass" ] , bufsize = None )
1140
+ p = subprocess .Popen (ZERO_RETURN_CMD , bufsize = None )
1130
1141
self .assertEqual (p .wait (), 0 )
1131
1142
1132
1143
def _test_bufsize_equal_one (self , line , expected , universal_newlines ):
@@ -1331,7 +1342,7 @@ def test_handles_closed_on_exception(self):
1331
1342
1332
1343
def test_communicate_epipe (self ):
1333
1344
# Issue 10963: communicate() should hide EPIPE
1334
- p = subprocess .Popen ([ sys . executable , "-c" , 'pass' ] ,
1345
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
1335
1346
stdin = subprocess .PIPE ,
1336
1347
stdout = subprocess .PIPE ,
1337
1348
stderr = subprocess .PIPE )
@@ -1342,7 +1353,7 @@ def test_communicate_epipe(self):
1342
1353
1343
1354
def test_communicate_epipe_only_stdin (self ):
1344
1355
# Issue 10963: communicate() should hide EPIPE
1345
- p = subprocess .Popen ([ sys . executable , "-c" , 'pass' ] ,
1356
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
1346
1357
stdin = subprocess .PIPE )
1347
1358
self .addCleanup (p .stdin .close )
1348
1359
p .wait ()
@@ -1381,7 +1392,7 @@ def test_failed_child_execute_fd_leak(self):
1381
1392
fds_before_popen = os .listdir (fd_directory )
1382
1393
with self .assertRaises (PopenTestException ):
1383
1394
PopenExecuteChildRaises (
1384
- [ sys . executable , '-c' , 'pass' ] , stdin = subprocess .PIPE ,
1395
+ ZERO_RETURN_CMD , stdin = subprocess .PIPE ,
1385
1396
stdout = subprocess .PIPE , stderr = subprocess .PIPE )
1386
1397
1387
1398
# NOTE: This test doesn't verify that the real _execute_child
@@ -1424,7 +1435,7 @@ def test_check(self):
1424
1435
1425
1436
def test_check_zero (self ):
1426
1437
# check_returncode shouldn't raise when returncode is zero
1427
- cp = self . run_python ( "import sys; sys.exit(0)" , check = True )
1438
+ cp = subprocess . run ( ZERO_RETURN_CMD , check = True )
1428
1439
self .assertEqual (cp .returncode , 0 )
1429
1440
1430
1441
def test_timeout (self ):
@@ -1824,7 +1835,7 @@ def raise_it():
1824
1835
1825
1836
with self .assertRaises (subprocess .SubprocessError ):
1826
1837
self ._TestExecuteChildPopen (
1827
- self , [ sys . executable , "-c" , "pass" ] ,
1838
+ self , ZERO_RETURN_CMD ,
1828
1839
stdin = subprocess .PIPE , stdout = subprocess .PIPE ,
1829
1840
stderr = subprocess .PIPE , preexec_fn = raise_it )
1830
1841
@@ -2281,7 +2292,7 @@ def prepare():
2281
2292
2282
2293
try :
2283
2294
subprocess .call (
2284
- [ sys . executable , "-c" , "pass" ] ,
2295
+ ZERO_RETURN_CMD ,
2285
2296
preexec_fn = prepare )
2286
2297
except ValueError as err :
2287
2298
# Pure Python implementations keeps the message
@@ -2324,29 +2335,30 @@ def test_undecodable_env(self):
2324
2335
self .assertEqual (stdout .decode ('ascii' ), ascii (encoded_value ))
2325
2336
2326
2337
def test_bytes_program (self ):
2327
- abs_program = os .fsencode (sys .executable )
2328
- path , program = os .path .split (sys .executable )
2338
+ abs_program = os .fsencode (ZERO_RETURN_CMD [0 ])
2339
+ args = list (ZERO_RETURN_CMD [1 :])
2340
+ path , program = os .path .split (ZERO_RETURN_CMD [0 ])
2329
2341
program = os .fsencode (program )
2330
2342
2331
2343
# absolute bytes path
2332
- exitcode = subprocess .call ([abs_program , "-c" , "pass" ] )
2344
+ exitcode = subprocess .call ([abs_program ] + args )
2333
2345
self .assertEqual (exitcode , 0 )
2334
2346
2335
2347
# absolute bytes path as a string
2336
- cmd = b"'" + abs_program + b"' -c pass"
2348
+ cmd = b"'%s' %s" % ( abs_program , " " . join ( args ). encode ( "utf-8" ))
2337
2349
exitcode = subprocess .call (cmd , shell = True )
2338
2350
self .assertEqual (exitcode , 0 )
2339
2351
2340
2352
# bytes program, unicode PATH
2341
2353
env = os .environ .copy ()
2342
2354
env ["PATH" ] = path
2343
- exitcode = subprocess .call ([program , "-c" , "pass" ] , env = env )
2355
+ exitcode = subprocess .call ([program ] + args , env = env )
2344
2356
self .assertEqual (exitcode , 0 )
2345
2357
2346
2358
# bytes program, bytes PATH
2347
2359
envb = os .environb .copy ()
2348
2360
envb [b"PATH" ] = os .fsencode (path )
2349
- exitcode = subprocess .call ([program , "-c" , "pass" ] , env = envb )
2361
+ exitcode = subprocess .call ([program ] + args , env = envb )
2350
2362
self .assertEqual (exitcode , 0 )
2351
2363
2352
2364
def test_pipe_cloexec (self ):
@@ -2574,7 +2586,7 @@ def test_pass_fds(self):
2574
2586
# pass_fds overrides close_fds with a warning.
2575
2587
with self .assertWarns (RuntimeWarning ) as context :
2576
2588
self .assertFalse (subprocess .call (
2577
- [ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2589
+ ZERO_RETURN_CMD ,
2578
2590
close_fds = False , pass_fds = (fd , )))
2579
2591
self .assertIn ('overriding close_fds' , str (context .warning ))
2580
2592
@@ -2636,19 +2648,19 @@ def test_pass_fds_redirected(self):
2636
2648
2637
2649
def test_stdout_stdin_are_single_inout_fd (self ):
2638
2650
with io .open (os .devnull , "r+" ) as inout :
2639
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2651
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
2640
2652
stdout = inout , stdin = inout )
2641
2653
p .wait ()
2642
2654
2643
2655
def test_stdout_stderr_are_single_inout_fd (self ):
2644
2656
with io .open (os .devnull , "r+" ) as inout :
2645
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2657
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
2646
2658
stdout = inout , stderr = inout )
2647
2659
p .wait ()
2648
2660
2649
2661
def test_stderr_stdin_are_single_inout_fd (self ):
2650
2662
with io .open (os .devnull , "r+" ) as inout :
2651
- p = subprocess .Popen ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2663
+ p = subprocess .Popen (ZERO_RETURN_CMD ,
2652
2664
stderr = inout , stdin = inout )
2653
2665
p .wait ()
2654
2666
@@ -2836,7 +2848,7 @@ def __int__(self):
2836
2848
def test_communicate_BrokenPipeError_stdin_close (self ):
2837
2849
# By not setting stdout or stderr or a timeout we force the fast path
2838
2850
# that just calls _stdin_write() internally due to our mock.
2839
- proc = subprocess .Popen ([ sys . executable , '-c' , 'pass' ] )
2851
+ proc = subprocess .Popen (ZERO_RETURN_CMD )
2840
2852
with proc , mock .patch .object (proc , 'stdin' ) as mock_proc_stdin :
2841
2853
mock_proc_stdin .close .side_effect = BrokenPipeError
2842
2854
proc .communicate () # Should swallow BrokenPipeError from close.
@@ -2845,7 +2857,7 @@ def test_communicate_BrokenPipeError_stdin_close(self):
2845
2857
def test_communicate_BrokenPipeError_stdin_write (self ):
2846
2858
# By not setting stdout or stderr or a timeout we force the fast path
2847
2859
# that just calls _stdin_write() internally due to our mock.
2848
- proc = subprocess .Popen ([ sys . executable , '-c' , 'pass' ] )
2860
+ proc = subprocess .Popen (ZERO_RETURN_CMD )
2849
2861
with proc , mock .patch .object (proc , 'stdin' ) as mock_proc_stdin :
2850
2862
mock_proc_stdin .write .side_effect = BrokenPipeError
2851
2863
proc .communicate (b'stuff' ) # Should swallow the BrokenPipeError.
@@ -2884,7 +2896,7 @@ def test_communicate_BrokenPipeError_stdin_close_with_timeout(self):
2884
2896
'need _testcapi.W_STOPCODE' )
2885
2897
def test_stopped (self ):
2886
2898
"""Test wait() behavior when waitpid returns WIFSTOPPED; issue29335."""
2887
- args = [ sys . executable , '-c' , 'pass' ]
2899
+ args = ZERO_RETURN_CMD
2888
2900
proc = subprocess .Popen (args )
2889
2901
2890
2902
# Wait until the real process completes to avoid zombie process
@@ -2914,7 +2926,7 @@ def test_startupinfo(self):
2914
2926
# Since Python is a console process, it won't be affected
2915
2927
# by wShowWindow, but the argument should be silently
2916
2928
# ignored
2917
- subprocess .call ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2929
+ subprocess .call (ZERO_RETURN_CMD ,
2918
2930
startupinfo = startupinfo )
2919
2931
2920
2932
def test_startupinfo_keywords (self ):
@@ -2930,7 +2942,7 @@ def test_startupinfo_keywords(self):
2930
2942
# Since Python is a console process, it won't be affected
2931
2943
# by wShowWindow, but the argument should be silently
2932
2944
# ignored
2933
- subprocess .call ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
2945
+ subprocess .call (ZERO_RETURN_CMD ,
2934
2946
startupinfo = startupinfo )
2935
2947
2936
2948
def test_startupinfo_copy (self ):
@@ -2942,7 +2954,7 @@ def test_startupinfo_copy(self):
2942
2954
# Call Popen() twice with the same startupinfo object to make sure
2943
2955
# that it's not modified
2944
2956
for _ in range (2 ):
2945
- cmd = [ sys . executable , "-c" , "pass" ]
2957
+ cmd = ZERO_RETURN_CMD
2946
2958
with open (os .devnull , 'w' ) as null :
2947
2959
proc = subprocess .Popen (cmd ,
2948
2960
stdout = null ,
@@ -2982,7 +2994,7 @@ def test_issue31471(self):
2982
2994
class BadEnv (dict ):
2983
2995
keys = None
2984
2996
with self .assertRaises (TypeError ):
2985
- subprocess .Popen ([ sys . executable , "-c" , "pass" ] , env = BadEnv ())
2997
+ subprocess .Popen (ZERO_RETURN_CMD , env = BadEnv ())
2986
2998
2987
2999
def test_close_fds (self ):
2988
3000
# close file descriptors
@@ -3043,13 +3055,13 @@ def test_close_fds_with_stdio(self):
3043
3055
def test_empty_attribute_list (self ):
3044
3056
startupinfo = subprocess .STARTUPINFO ()
3045
3057
startupinfo .lpAttributeList = {}
3046
- subprocess .call ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
3058
+ subprocess .call (ZERO_RETURN_CMD ,
3047
3059
startupinfo = startupinfo )
3048
3060
3049
3061
def test_empty_handle_list (self ):
3050
3062
startupinfo = subprocess .STARTUPINFO ()
3051
3063
startupinfo .lpAttributeList = {"handle_list" : []}
3052
- subprocess .call ([ sys . executable , "-c" , "import sys; sys.exit(0)" ] ,
3064
+ subprocess .call (ZERO_RETURN_CMD ,
3053
3065
startupinfo = startupinfo )
3054
3066
3055
3067
def test_shell_sequence (self ):
@@ -3348,7 +3360,7 @@ def test_invalid_args(self):
3348
3360
3349
3361
def test_broken_pipe_cleanup (self ):
3350
3362
"""Broken pipe error should not prevent wait() (Issue 21619)"""
3351
- proc = subprocess .Popen ([ sys . executable , '-c' , 'pass' ] ,
3363
+ proc = subprocess .Popen (ZERO_RETURN_CMD ,
3352
3364
stdin = subprocess .PIPE ,
3353
3365
bufsize = support .PIPE_MAX_SIZE * 2 )
3354
3366
proc = proc .__enter__ ()
0 commit comments