@@ -1797,13 +1797,28 @@ def test_failed_child_execute_fd_leak(self):
17971797 fds_after_exception = os .listdir (fd_directory )
17981798 self .assertEqual (fds_before_popen , fds_after_exception )
17991799
1800- @unittest .skipIf (mswindows , "behavior currently not supported on Windows" )
18011800 def test_file_not_found_includes_filename (self ):
1801+ missing = (r'C:\opt\nonexistent_binary' if mswindows
1802+ else '/opt/nonexistent_binary' )
18021803 with self .assertRaises (FileNotFoundError ) as c :
1803- subprocess .call (['/opt/nonexistent_binary' , 'with' , 'some' , 'args' ])
1804- self .assertEqual (c .exception .filename , '/opt/nonexistent_binary' )
1804+ subprocess .call ([missing , 'with' , 'some' , 'args' ])
1805+ self .assertEqual (c .exception .filename , missing )
1806+
1807+ def test_args_filter_iterable (self ):
1808+ # gh-119646: Windows used to index args[0] before list2cmdline.
1809+ # test_faulthandler.test_sys_xoptions passes a filter() object.
1810+ args = filter (None , (sys .executable , "-c" , "import sys; sys.exit(17)" ))
1811+ self .assertEqual (subprocess .call (args ), 17 )
1812+
1813+ def test_file_not_found_includes_filename_from_iterable (self ):
1814+ missing = (r'C:\opt\nonexistent_binary' if mswindows
1815+ else '/opt/nonexistent_binary' )
1816+ args = filter (None , (missing , "with" , "some" , "args" ))
1817+ with self .assertRaises (FileNotFoundError ) as c :
1818+ subprocess .call (args )
1819+ self .assertEqual (c .exception .filename , missing )
18051820
1806- @unittest .skipIf (mswindows , "behavior currently not supported on Windows " )
1821+ @unittest .skipIf (mswindows , "Windows reports NotADirectoryError (WinError 267) " )
18071822 def test_file_not_found_with_bad_cwd (self ):
18081823 with self .assertRaises (FileNotFoundError ) as c :
18091824 subprocess .Popen (['exit' , '0' ], cwd = '/some/nonexistent/directory' )
@@ -3718,6 +3733,34 @@ def test_vfork_used_when_expected(self):
37183733@unittest .skipUnless (mswindows , "Windows specific tests" )
37193734class Win32ProcessTestCase (BaseTestCase ):
37203735
3736+ def test_createprocess_bad_cwd_includes_filename (self ):
3737+ # gh-119646: invalid cwd should appear on OSError.filename.
3738+ missing_cwd = r'C:\some\nonexistent\directory'
3739+ with self .assertRaises (OSError ) as c :
3740+ subprocess .Popen ([sys .executable , '-c' , 'pass' ], cwd = missing_cwd )
3741+ self .assertEqual (c .exception .filename , missing_cwd )
3742+ self .assertEqual (c .exception .winerror , 267 )
3743+
3744+ def test_command_string_filename_omits_later_args (self ):
3745+ # gh-119646: a command-line string must not put later arguments
3746+ # on OSError.filename. Those arguments can hold secrets.
3747+ missing = r'C:\opt\nonexistent_binary'
3748+ secret = 'NOT-A-REAL-SECRET'
3749+ quoted = r'C:\Program Files\nonexistent_binary'
3750+ cases = [
3751+ (missing , missing ),
3752+ (f'{ missing } --token { secret } ' , missing ),
3753+ (f'"{ missing } " --token { secret } ' , missing ),
3754+ (f'"{ quoted } " --token { secret } ' , quoted ),
3755+ ]
3756+ for command , expected in cases :
3757+ with self .subTest (command = command ):
3758+ with self .assertRaises (FileNotFoundError ) as c :
3759+ subprocess .call (command )
3760+ self .assertEqual (c .exception .filename , expected )
3761+ self .assertNotIn (secret , c .exception .filename or '' )
3762+ self .assertNotIn (secret , str (c .exception ))
3763+
37213764 def test_startupinfo (self ):
37223765 # startupinfo argument
37233766 # We uses hardcoded constants, because we do not want to
0 commit comments