Skip to content

Commit 3ff659a

Browse files
authored
gh-98790: When DLLs directory is missing on Windows, assume executable_dir contains PYD files instead (GH-98936)
1 parent dc4bf6b commit 3ff659a

File tree

4 files changed

+53
-20
lines changed

4 files changed

+53
-20
lines changed

Lib/test/test_embed.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,17 +1479,11 @@ def test_init_pyvenv_cfg(self):
14791479
if not MS_WINDOWS:
14801480
paths[-1] = lib_dynload
14811481
else:
1482-
# Include DLLs directory as well
1483-
paths.insert(1, '.\\DLLs')
1484-
for index, path in enumerate(paths):
1485-
if index == 0:
1486-
# Because we copy the DLLs into tmpdir as well, the zip file
1487-
# entry in sys.path will be there. For a regular venv, it will
1488-
# usually be in the home directory.
1489-
paths[index] = os.path.join(tmpdir, os.path.basename(path))
1490-
else:
1491-
paths[index] = os.path.join(pyvenv_home, os.path.basename(path))
1492-
paths[-1] = pyvenv_home
1482+
paths = [
1483+
os.path.join(tmpdir, os.path.basename(paths[0])),
1484+
pyvenv_home,
1485+
os.path.join(pyvenv_home, "Lib"),
1486+
]
14931487

14941488
executable = self.test_exe
14951489
base_executable = os.path.join(pyvenv_home, os.path.basename(executable))
@@ -1506,12 +1500,12 @@ def test_init_pyvenv_cfg(self):
15061500
config['base_prefix'] = pyvenv_home
15071501
config['prefix'] = pyvenv_home
15081502
config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib')
1509-
config['use_frozen_modules'] = not Py_DEBUG
1503+
config['use_frozen_modules'] = int(not Py_DEBUG)
15101504
else:
15111505
# cannot reliably assume stdlib_dir here because it
15121506
# depends too much on our build. But it ought to be found
15131507
config['stdlib_dir'] = self.IGNORE_CONFIG
1514-
config['use_frozen_modules'] = not Py_DEBUG
1508+
config['use_frozen_modules'] = int(not Py_DEBUG)
15151509

15161510
env = self.copy_paths_by_env(config)
15171511
self.check_all_configs("test_init_compat_config", config,

Lib/test/test_getpath.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,29 @@ def test_buildtree_pythonhome_win32(self):
239239
actual = getpath(ns, expected)
240240
self.assertEqual(expected, actual)
241241

242+
def test_no_dlls_win32(self):
243+
"Test a layout on Windows with no DLLs directory."
244+
ns = MockNTNamespace(
245+
argv0=r"C:\Python\python.exe",
246+
real_executable=r"C:\Python\python.exe",
247+
)
248+
ns.add_known_xfile(r"C:\Python\python.exe")
249+
ns.add_known_file(r"C:\Python\Lib\os.py")
250+
expected = dict(
251+
executable=r"C:\Python\python.exe",
252+
base_executable=r"C:\Python\python.exe",
253+
prefix=r"C:\Python",
254+
exec_prefix=r"C:\Python",
255+
module_search_paths_set=1,
256+
module_search_paths=[
257+
r"C:\Python\python98.zip",
258+
r"C:\Python\Lib",
259+
r"C:\Python",
260+
],
261+
)
262+
actual = getpath(ns, expected)
263+
self.assertEqual(expected, actual)
264+
242265
def test_normal_posix(self):
243266
"Test a 'standard' install layout on *nix"
244267
ns = MockPosixNamespace(
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Assumes that a missing ``DLLs`` directory means that standard extension
2+
modules are in the executable's directory.

Modules/getpath.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -579,15 +579,28 @@ def search_up(prefix, *landmarks, test=isfile):
579579
# Detect exec_prefix by searching from executable for the platstdlib_dir
580580
if PLATSTDLIB_LANDMARK and not exec_prefix:
581581
if executable_dir:
582-
exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir)
583-
if not exec_prefix:
584-
if EXEC_PREFIX:
585-
exec_prefix = EXEC_PREFIX
586-
if not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)):
587-
warn('Could not find platform dependent libraries <exec_prefix>')
582+
if os_name == 'nt':
583+
# QUIRK: For compatibility and security, do not search for DLLs
584+
# directory. The fallback below will cover it
585+
exec_prefix = executable_dir
586+
else:
587+
exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir)
588+
if not exec_prefix and EXEC_PREFIX:
589+
exec_prefix = EXEC_PREFIX
590+
if not exec_prefix or not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)):
591+
if os_name == 'nt':
592+
# QUIRK: If DLLs is missing on Windows, don't warn, just assume
593+
# that it's all the same as prefix.
594+
# gh-98790: We set platstdlib_dir here to avoid adding "DLLs" into
595+
# sys.path when it doesn't exist, which would give site-packages
596+
# precedence over executable_dir, which is *probably* where our PYDs
597+
# live. Ideally, whoever changes our layout will tell us what the
598+
# layout is, but in the past this worked, so it should keep working.
599+
platstdlib_dir = exec_prefix = prefix
588600
else:
589601
warn('Could not find platform dependent libraries <exec_prefix>')
590602

603+
591604
# Fallback: assume exec_prefix == prefix
592605
if not exec_prefix:
593606
exec_prefix = prefix
@@ -689,7 +702,8 @@ def search_up(prefix, *landmarks, test=isfile):
689702
pythonpath.append(platstdlib_dir)
690703
if stdlib_dir:
691704
pythonpath.append(stdlib_dir)
692-
pythonpath.append(executable_dir)
705+
if executable_dir not in pythonpath:
706+
pythonpath.append(executable_dir)
693707
else:
694708
if stdlib_dir:
695709
pythonpath.append(stdlib_dir)

0 commit comments

Comments
 (0)