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

This commit is contained in:
Steve Dower 2022-11-02 18:38:40 +00:00 committed by GitHub
parent f520d720f6
commit 3d889dc0a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 20 deletions

View File

@ -1492,17 +1492,11 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
if not MS_WINDOWS: if not MS_WINDOWS:
paths[-1] = lib_dynload paths[-1] = lib_dynload
else: else:
# Include DLLs directory as well paths = [
paths.insert(1, '.\\DLLs') os.path.join(tmpdir, os.path.basename(paths[0])),
for index, path in enumerate(paths): pyvenv_home,
if index == 0: os.path.join(pyvenv_home, "Lib"),
# 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
executable = self.test_exe executable = self.test_exe
base_executable = os.path.join(pyvenv_home, os.path.basename(executable)) base_executable = os.path.join(pyvenv_home, os.path.basename(executable))
@ -1519,12 +1513,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
config['base_prefix'] = pyvenv_home config['base_prefix'] = pyvenv_home
config['prefix'] = pyvenv_home config['prefix'] = pyvenv_home
config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib') 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: else:
# cannot reliably assume stdlib_dir here because it # cannot reliably assume stdlib_dir here because it
# depends too much on our build. But it ought to be found # depends too much on our build. But it ought to be found
config['stdlib_dir'] = self.IGNORE_CONFIG 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) env = self.copy_paths_by_env(config)
self.check_all_configs("test_init_compat_config", config, self.check_all_configs("test_init_compat_config", config,

View File

@ -238,6 +238,29 @@ class MockGetPathTests(unittest.TestCase):
actual = getpath(ns, expected) actual = getpath(ns, expected)
self.assertEqual(expected, actual) 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): def test_normal_posix(self):
"Test a 'standard' install layout on *nix" "Test a 'standard' install layout on *nix"
ns = MockPosixNamespace( ns = MockPosixNamespace(

View File

@ -0,0 +1,2 @@
Assumes that a missing ``DLLs`` directory means that standard extension
modules are in the executable's directory.

View File

@ -579,15 +579,28 @@ else:
# Detect exec_prefix by searching from executable for the platstdlib_dir # Detect exec_prefix by searching from executable for the platstdlib_dir
if PLATSTDLIB_LANDMARK and not exec_prefix: if PLATSTDLIB_LANDMARK and not exec_prefix:
if executable_dir: if executable_dir:
exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir) if os_name == 'nt':
if not exec_prefix: # QUIRK: For compatibility and security, do not search for DLLs
if EXEC_PREFIX: # directory. The fallback below will cover it
exec_prefix = EXEC_PREFIX exec_prefix = executable_dir
if not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)): else:
warn('Could not find platform dependent libraries <exec_prefix>') 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: else:
warn('Could not find platform dependent libraries <exec_prefix>') warn('Could not find platform dependent libraries <exec_prefix>')
# Fallback: assume exec_prefix == prefix # Fallback: assume exec_prefix == prefix
if not exec_prefix: if not exec_prefix:
exec_prefix = prefix exec_prefix = prefix
@ -689,7 +702,8 @@ elif not pythonpath_was_set:
pythonpath.append(platstdlib_dir) pythonpath.append(platstdlib_dir)
if stdlib_dir: if stdlib_dir:
pythonpath.append(stdlib_dir) pythonpath.append(stdlib_dir)
pythonpath.append(executable_dir) if executable_dir not in pythonpath:
pythonpath.append(executable_dir)
else: else:
if stdlib_dir: if stdlib_dir:
pythonpath.append(stdlib_dir) pythonpath.append(stdlib_dir)