Skip to content

Commit 3d889dc

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

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
@@ -1492,17 +1492,11 @@ def test_init_pyvenv_cfg(self):
14921492
if not MS_WINDOWS:
14931493
paths[-1] = lib_dynload
14941494
else:
1495-
# Include DLLs directory as well
1496-
paths.insert(1, '.\\DLLs')
1497-
for index, path in enumerate(paths):
1498-
if index == 0:
1499-
# Because we copy the DLLs into tmpdir as well, the zip file
1500-
# entry in sys.path will be there. For a regular venv, it will
1501-
# usually be in the home directory.
1502-
paths[index] = os.path.join(tmpdir, os.path.basename(path))
1503-
else:
1504-
paths[index] = os.path.join(pyvenv_home, os.path.basename(path))
1505-
paths[-1] = pyvenv_home
1495+
paths = [
1496+
os.path.join(tmpdir, os.path.basename(paths[0])),
1497+
pyvenv_home,
1498+
os.path.join(pyvenv_home, "Lib"),
1499+
]
15061500

15071501
executable = self.test_exe
15081502
base_executable = os.path.join(pyvenv_home, os.path.basename(executable))
@@ -1519,12 +1513,12 @@ def test_init_pyvenv_cfg(self):
15191513
config['base_prefix'] = pyvenv_home
15201514
config['prefix'] = pyvenv_home
15211515
config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib')
1522-
config['use_frozen_modules'] = not support.Py_DEBUG
1516+
config['use_frozen_modules'] = int(not support.Py_DEBUG)
15231517
else:
15241518
# cannot reliably assume stdlib_dir here because it
15251519
# depends too much on our build. But it ought to be found
15261520
config['stdlib_dir'] = self.IGNORE_CONFIG
1527-
config['use_frozen_modules'] = not support.Py_DEBUG
1521+
config['use_frozen_modules'] = int(not support.Py_DEBUG)
15281522

15291523
env = self.copy_paths_by_env(config)
15301524
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
@@ -238,6 +238,29 @@ def test_buildtree_pythonhome_win32(self):
238238
actual = getpath(ns, expected)
239239
self.assertEqual(expected, actual)
240240

241+
def test_no_dlls_win32(self):
242+
"Test a layout on Windows with no DLLs directory."
243+
ns = MockNTNamespace(
244+
argv0=r"C:\Python\python.exe",
245+
real_executable=r"C:\Python\python.exe",
246+
)
247+
ns.add_known_xfile(r"C:\Python\python.exe")
248+
ns.add_known_file(r"C:\Python\Lib\os.py")
249+
expected = dict(
250+
executable=r"C:\Python\python.exe",
251+
base_executable=r"C:\Python\python.exe",
252+
prefix=r"C:\Python",
253+
exec_prefix=r"C:\Python",
254+
module_search_paths_set=1,
255+
module_search_paths=[
256+
r"C:\Python\python98.zip",
257+
r"C:\Python\Lib",
258+
r"C:\Python",
259+
],
260+
)
261+
actual = getpath(ns, expected)
262+
self.assertEqual(expected, actual)
263+
241264
def test_normal_posix(self):
242265
"Test a 'standard' install layout on *nix"
243266
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)