Skip to content

gh-98790: When DLLs directory is missing on Windows, assume executable_dir contains PYD files instead #98936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 7 additions & 13 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -1484,17 +1484,11 @@ def test_init_pyvenv_cfg(self):
if not MS_WINDOWS:
paths[-1] = lib_dynload
else:
# Include DLLs directory as well
paths.insert(1, '.\\DLLs')
for index, path in enumerate(paths):
if index == 0:
# Because we copy the DLLs into tmpdir as well, the zip file
# entry in sys.path will be there. For a regular venv, it will
# usually be in the home directory.
paths[index] = os.path.join(tmpdir, os.path.basename(path))
else:
paths[index] = os.path.join(pyvenv_home, os.path.basename(path))
paths[-1] = pyvenv_home
paths = [
os.path.join(tmpdir, os.path.basename(paths[0])),
pyvenv_home,
os.path.join(pyvenv_home, "Lib"),
]

executable = self.test_exe
base_executable = os.path.join(pyvenv_home, os.path.basename(executable))
Expand All @@ -1511,12 +1505,12 @@ def test_init_pyvenv_cfg(self):
config['base_prefix'] = pyvenv_home
config['prefix'] = pyvenv_home
config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib')
config['use_frozen_modules'] = not support.Py_DEBUG
config['use_frozen_modules'] = int(not support.Py_DEBUG)
else:
# cannot reliably assume stdlib_dir here because it
# depends too much on our build. But it ought to be found
config['stdlib_dir'] = self.IGNORE_CONFIG
config['use_frozen_modules'] = not support.Py_DEBUG
config['use_frozen_modules'] = int(not support.Py_DEBUG)

env = self.copy_paths_by_env(config)
self.check_all_configs("test_init_compat_config", config,
Expand Down
23 changes: 23 additions & 0 deletions Lib/test/test_getpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,29 @@ def test_buildtree_pythonhome_win32(self):
actual = getpath(ns, expected)
self.assertEqual(expected, actual)

def test_no_dlls_win32(self):
"Test a layout on Windows with no DLLs directory."
ns = MockNTNamespace(
argv0=r"C:\Python\python.exe",
real_executable=r"C:\Python\python.exe",
)
ns.add_known_xfile(r"C:\Python\python.exe")
ns.add_known_file(r"C:\Python\Lib\os.py")
expected = dict(
executable=r"C:\Python\python.exe",
base_executable=r"C:\Python\python.exe",
prefix=r"C:\Python",
exec_prefix=r"C:\Python",
module_search_paths_set=1,
module_search_paths=[
r"C:\Python\python98.zip",
r"C:\Python\Lib",
r"C:\Python",
],
)
actual = getpath(ns, expected)
self.assertEqual(expected, actual)

def test_normal_posix(self):
"Test a 'standard' install layout on *nix"
ns = MockPosixNamespace(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Assumes that a missing ``DLLs`` directory means that standard extension
modules are in the executable's directory.
28 changes: 21 additions & 7 deletions Modules/getpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,15 +579,28 @@ def search_up(prefix, *landmarks, test=isfile):
# Detect exec_prefix by searching from executable for the platstdlib_dir
if PLATSTDLIB_LANDMARK and not exec_prefix:
if executable_dir:
exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir)
if not exec_prefix:
if EXEC_PREFIX:
exec_prefix = EXEC_PREFIX
if not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)):
warn('Could not find platform dependent libraries <exec_prefix>')
if os_name == 'nt':
# QUIRK: For compatibility and security, do not search for DLLs
# directory. The fallback below will cover it
exec_prefix = executable_dir
else:
exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir)
if not exec_prefix and EXEC_PREFIX:
exec_prefix = EXEC_PREFIX
if not exec_prefix or not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)):
if os_name == 'nt':
# QUIRK: If DLLs is missing on Windows, don't warn, just assume
# that it's all the same as prefix.
# gh-98790: We set platstdlib_dir here to avoid adding "DLLs" into
# sys.path when it doesn't exist, which would give site-packages
# precedence over executable_dir, which is *probably* where our PYDs
# live. Ideally, whoever changes our layout will tell us what the
# layout is, but in the past this worked, so it should keep working.
platstdlib_dir = exec_prefix = prefix
else:
warn('Could not find platform dependent libraries <exec_prefix>')


# Fallback: assume exec_prefix == prefix
if not exec_prefix:
exec_prefix = prefix
Expand Down Expand Up @@ -689,7 +702,8 @@ def search_up(prefix, *landmarks, test=isfile):
pythonpath.append(platstdlib_dir)
if stdlib_dir:
pythonpath.append(stdlib_dir)
pythonpath.append(executable_dir)
if executable_dir not in pythonpath:
pythonpath.append(executable_dir)
else:
if stdlib_dir:
pythonpath.append(stdlib_dir)
Expand Down