Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
01c152e
GH-75586 - Fix case where PATHEXT isn't applied to items in PATH (Win…
csm10495 Apr 2, 2023
0d4cd7b
PR updates
csm10495 Apr 2, 2023
5fac84a
Add tests
csm10495 Apr 2, 2023
fa145da
PR updates
csm10495 Apr 2, 2023
84a7976
line len fix
csm10495 Apr 2, 2023
63a06c4
📜🤖 Added by blurb_it.
blurb-it[bot] Apr 2, 2023
7686a23
Add changelog entry
csm10495 Apr 2, 2023
381e4fe
Double backticks
csm10495 Apr 2, 2023
e7c0b58
Update Lib/shutil.py
csm10495 Apr 2, 2023
26e3b15
PR updates
csm10495 Apr 2, 2023
6272b62
pep8 fix
csm10495 Apr 2, 2023
b6d29c8
Update Lib/test/test_shutil.py
csm10495 Apr 2, 2023
1096cb7
Update Lib/test/test_shutil.py
csm10495 Apr 2, 2023
92955d0
Update Lib/shutil.py
csm10495 Apr 3, 2023
616df6c
docs updates
csm10495 Apr 3, 2023
255e4ff
Add another test
csm10495 Apr 3, 2023
f52868d
Update Doc/library/shutil.rst
csm10495 Apr 3, 2023
6e9269f
rewording
csm10495 Apr 3, 2023
a6b7eab
Rewording
csm10495 Apr 3, 2023
7480daa
Reword whats new
csm10495 Apr 3, 2023
a48260c
Clarify
csm10495 Apr 3, 2023
6bb6f6c
Clarify how to not search cwd for exes
csm10495 Apr 3, 2023
b5f3eba
Doc updates
csm10495 Apr 3, 2023
3bf4b8d
Update Doc/library/shutil.rst
csm10495 Apr 3, 2023
be73608
Update 2023-04-02-22-04-26.gh-issue-75586.526iJm.rst
csm10495 Apr 3, 2023
0169ba9
Update Doc/library/shutil.rst
csm10495 Apr 4, 2023
499d2de
Update Lib/shutil.py
csm10495 Apr 4, 2023
3ead780
Add test for behavior
csm10495 Apr 4, 2023
f9267da
kick ci
csm10495 Apr 4, 2023
d1e68ff
Mention cwd first behavior
csm10495 Apr 4, 2023
9badf8c
Wording
csm10495 Apr 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
PR updates
  • Loading branch information
csm10495 committed Apr 2, 2023
commit fa145da4e2ee79055e1e03558fefc59807f91421
23 changes: 8 additions & 15 deletions Lib/shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@
import nt

if sys.platform == 'win32':
try:
import ctypes
_CTYPES_SUPPORTED = True
except ImportError:
_CTYPES_SUPPORTED = False
import _winapi

COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024
# This should never be removed, see rationale in:
Expand Down Expand Up @@ -1456,16 +1452,13 @@ def _access_check(fn, mode):
and not os.path.isdir(fn))


def _win32_need_current_directory_for_exe_path(cmd):
def _win_path_needs_curdir(cmd, mode):
"""
On Windows, we can use NeedCurrentDirectoryForExePathW to figure out
if we should add the cwd to PATH when searching for executables.

If we don't have ctypes, we'll fallback to old behavior which is to always add cwd.
On Windows, we can use NeedCurrentDirectoryForExePath to figure out
if we should add the cwd to PATH when searching for executables if
the mode is executable.
"""
if _CTYPES_SUPPORTED:
return bool(ctypes.windll.kernel32.NeedCurrentDirectoryForExePathW(cmd))
return True
return mode & os.X_OK and _winapi.NeedCurrentDirectoryForExePath(cmd if isinstance(cmd, str) else os.fsdecode(cmd))


def which(cmd, mode=os.F_OK | os.X_OK, path=None):
Expand Down Expand Up @@ -1510,7 +1503,7 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
path = os.fsdecode(path)
path = path.split(os.pathsep)

if sys.platform == "win32" and _win32_need_current_directory_for_exe_path(cmd):
if sys.platform == "win32" and _win_path_needs_curdir(cmd, mode):
curdir = os.curdir
if use_bytes:
curdir = os.fsencode(curdir)
Expand All @@ -1519,7 +1512,7 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):

if sys.platform == "win32":
# PATHEXT is necessary to check on Windows.
pathext_source = os.getenv("PATHEXT", _WIN_DEFAULT_PATHEXT)
pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT
pathext = [ext for ext in pathext_source.split(os.pathsep) if ext]

if use_bytes:
Expand Down
23 changes: 9 additions & 14 deletions Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -2049,11 +2049,11 @@ def test_cwd_non_win32(self):
def test_cwd_win32(self):
base_dir = os.path.dirname(self.dir)
with os_helper.change_cwd(path=self.dir):
with unittest.mock.patch('shutil._win32_need_current_directory_for_exe_path', return_value=True):
with unittest.mock.patch('shutil._win_path_needs_curdir', return_value=True):
rv = shutil.which(self.file, path=base_dir)
# Current directory implicitly on PATH
self.assertEqual(rv, os.path.join(self.curdir, self.file))
with unittest.mock.patch('shutil._win32_need_current_directory_for_exe_path', return_value=False):
with unittest.mock.patch('shutil._win_path_needs_curdir', return_value=False):
rv = shutil.which(self.file, path=base_dir)
# Current directory not on PATH
self.assertIsNone(rv)
Expand Down Expand Up @@ -2205,18 +2205,13 @@ def test_pathext_applied_on_files_in_path(self):

# See GH-75586
@unittest.skipUnless(sys.platform == "win32", 'test specific to Windows')
def test_win32_need_current_directory_for_exe_path_true_without_ctypes(self):
with unittest.mock.patch('shutil._CTYPES_SUPPORTED', False):
self.assertTrue(shutil._win32_need_current_directory_for_exe_path('anything'))

# See GH-75586
@unittest.skipUnless(sys.platform == "win32", 'test specific to Windows')
def test_win32_need_current_directory_for_exe_path_with_ctypes(self):
with unittest.mock.patch('shutil._CTYPES_SUPPORTED', True):
with unittest.mock.patch('shutil.ctypes') as ctypes_mock:
ctypes_mock.windll.kernel32.NeedCurrentDirectoryForExePathW.return_value = 0
self.assertFalse(shutil._win32_need_current_directory_for_exe_path('test.exe'))
ctypes_mock.windll.kernel32.NeedCurrentDirectoryForExePathW.assert_called_once_with('test.exe')
def test_win_path_needs_curdir(self):
with unittest.mock.patch('shutil._winapi.NeedCurrentDirectoryForExePath', return_value=True) as need_curdir_mock:
self.assertTrue(shutil._win_path_needs_curdir('dontcare', os.X_OK))
need_curdir_mock.assert_called_once_with('dontcare')
need_curdir_mock.reset_mock()
self.assertFalse(shutil._win_path_needs_curdir('dontcare', 0))
need_curdir_mock.assert_not_called()


class TestWhichBytes(TestWhich):
Expand Down
21 changes: 21 additions & 0 deletions Modules/_winapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -2054,6 +2054,26 @@ _winapi__mimetypes_read_windows_registry_impl(PyObject *module,
#undef CB_TYPE
}

/*[clinic input]
_winapi.NeedCurrentDirectoryForExePath -> bool

exe_name: LPCWSTR
/
[clinic start generated code]*/

static int
_winapi_NeedCurrentDirectoryForExePath_impl(PyObject *module,
LPCWSTR exe_name)
/*[clinic end generated code: output=a65ec879502b58fc input=972aac88a1ec2f00]*/
{
BOOL result;

Py_BEGIN_ALLOW_THREADS
result = NeedCurrentDirectoryForExePathW(exe_name);
Py_END_ALLOW_THREADS

return result;
}

static PyMethodDef winapi_functions[] = {
_WINAPI_CLOSEHANDLE_METHODDEF
Expand Down Expand Up @@ -2089,6 +2109,7 @@ static PyMethodDef winapi_functions[] = {
_WINAPI_GETACP_METHODDEF
_WINAPI_GETFILETYPE_METHODDEF
_WINAPI__MIMETYPES_READ_WINDOWS_REGISTRY_METHODDEF
_WINAPI_NEEDCURRENTDIRECTORYFOREXEPATH_METHODDEF
{NULL, NULL}
};

Expand Down
42 changes: 41 additions & 1 deletion Modules/clinic/_winapi.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.