mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
gh-100320: Fix path calculations on Windows when python.exe is moved outside of the normal location (GH-100947)
This commit is contained in:
parent
7b14c2ef19
commit
df10571a13
4 changed files with 54 additions and 33 deletions
|
@ -714,8 +714,10 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
if MS_WINDOWS:
|
if MS_WINDOWS:
|
||||||
value = config.get(key := 'program_name')
|
value = config.get(key := 'program_name')
|
||||||
if value and isinstance(value, str):
|
if value and isinstance(value, str):
|
||||||
ext = '_d.exe' if debug_build(sys.executable) else '.exe'
|
value = value[:len(value.lower().removesuffix('.exe'))]
|
||||||
config[key] = value[:len(value.lower().removesuffix(ext))]
|
if debug_build(sys.executable):
|
||||||
|
value = value[:len(value.lower().removesuffix('_d'))]
|
||||||
|
config[key] = value
|
||||||
for key, value in list(expected.items()):
|
for key, value in list(expected.items()):
|
||||||
if value is self.IGNORE_CONFIG:
|
if value is self.IGNORE_CONFIG:
|
||||||
config.pop(key, None)
|
config.pop(key, None)
|
||||||
|
@ -1292,7 +1294,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
stdlib = os.path.join(home, "Lib")
|
stdlib = os.path.join(home, "Lib")
|
||||||
# Because we are specifying 'home', module search paths
|
# Because we are specifying 'home', module search paths
|
||||||
# are fairly static
|
# are fairly static
|
||||||
expected_paths = [paths[0], stdlib, os.path.join(home, 'DLLs')]
|
expected_paths = [paths[0], os.path.join(home, 'DLLs'), stdlib]
|
||||||
else:
|
else:
|
||||||
version = f'{sys.version_info.major}.{sys.version_info.minor}'
|
version = f'{sys.version_info.major}.{sys.version_info.minor}'
|
||||||
stdlib = os.path.join(home, sys.platlibdir, f'python{version}')
|
stdlib = os.path.join(home, sys.platlibdir, f'python{version}')
|
||||||
|
@ -1333,7 +1335,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
stdlib = os.path.join(home, "Lib")
|
stdlib = os.path.join(home, "Lib")
|
||||||
# Because we are specifying 'home', module search paths
|
# Because we are specifying 'home', module search paths
|
||||||
# are fairly static
|
# are fairly static
|
||||||
expected_paths = [paths[0], stdlib, os.path.join(home, 'DLLs')]
|
expected_paths = [paths[0], os.path.join(home, 'DLLs'), stdlib]
|
||||||
else:
|
else:
|
||||||
version = f'{sys.version_info.major}.{sys.version_info.minor}'
|
version = f'{sys.version_info.major}.{sys.version_info.minor}'
|
||||||
stdlib = os.path.join(home, sys.platlibdir, f'python{version}')
|
stdlib = os.path.join(home, sys.platlibdir, f'python{version}')
|
||||||
|
@ -1361,7 +1363,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
config['_is_python_build'] = 1
|
config['_is_python_build'] = 1
|
||||||
exedir = os.path.dirname(sys.executable)
|
exedir = os.path.dirname(sys.executable)
|
||||||
with open(os.path.join(exedir, 'pybuilddir.txt'), encoding='utf8') as f:
|
with open(os.path.join(exedir, 'pybuilddir.txt'), encoding='utf8') as f:
|
||||||
expected_paths[2] = os.path.normpath(
|
expected_paths[1 if MS_WINDOWS else 2] = os.path.normpath(
|
||||||
os.path.join(exedir, f'{f.read()}\n$'.splitlines()[0]))
|
os.path.join(exedir, f'{f.read()}\n$'.splitlines()[0]))
|
||||||
if not MS_WINDOWS:
|
if not MS_WINDOWS:
|
||||||
# PREFIX (default) is set when running in build directory
|
# PREFIX (default) is set when running in build directory
|
||||||
|
@ -1438,8 +1440,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
|
|
||||||
module_search_paths = self.module_search_paths()
|
module_search_paths = self.module_search_paths()
|
||||||
module_search_paths[-3] = os.path.join(tmpdir, os.path.basename(module_search_paths[-3]))
|
module_search_paths[-3] = os.path.join(tmpdir, os.path.basename(module_search_paths[-3]))
|
||||||
module_search_paths[-2] = stdlibdir
|
module_search_paths[-2] = tmpdir
|
||||||
module_search_paths[-1] = tmpdir
|
module_search_paths[-1] = stdlibdir
|
||||||
|
|
||||||
executable = self.test_exe
|
executable = self.test_exe
|
||||||
config = {
|
config = {
|
||||||
|
|
|
@ -37,8 +37,9 @@ class MockGetPathTests(unittest.TestCase):
|
||||||
module_search_paths_set=1,
|
module_search_paths_set=1,
|
||||||
module_search_paths=[
|
module_search_paths=[
|
||||||
r"C:\Python\python98.zip",
|
r"C:\Python\python98.zip",
|
||||||
r"C:\Python\Lib",
|
|
||||||
r"C:\Python\DLLs",
|
r"C:\Python\DLLs",
|
||||||
|
r"C:\Python\Lib",
|
||||||
|
r"C:\Python",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
actual = getpath(ns, expected)
|
actual = getpath(ns, expected)
|
||||||
|
@ -63,8 +64,8 @@ class MockGetPathTests(unittest.TestCase):
|
||||||
module_search_paths_set=1,
|
module_search_paths_set=1,
|
||||||
module_search_paths=[
|
module_search_paths=[
|
||||||
r"C:\CPython\PCbuild\amd64\python98.zip",
|
r"C:\CPython\PCbuild\amd64\python98.zip",
|
||||||
r"C:\CPython\Lib",
|
|
||||||
r"C:\CPython\PCbuild\amd64",
|
r"C:\CPython\PCbuild\amd64",
|
||||||
|
r"C:\CPython\Lib",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
actual = getpath(ns, expected)
|
actual = getpath(ns, expected)
|
||||||
|
@ -133,8 +134,9 @@ class MockGetPathTests(unittest.TestCase):
|
||||||
r"C:\Python\python98.zip",
|
r"C:\Python\python98.zip",
|
||||||
"path1-dir",
|
"path1-dir",
|
||||||
# should not contain not-subdirs
|
# should not contain not-subdirs
|
||||||
r"C:\Python\Lib",
|
|
||||||
r"C:\Python\DLLs",
|
r"C:\Python\DLLs",
|
||||||
|
r"C:\Python\Lib",
|
||||||
|
r"C:\Python",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
actual = getpath(ns, expected)
|
actual = getpath(ns, expected)
|
||||||
|
@ -147,8 +149,9 @@ class MockGetPathTests(unittest.TestCase):
|
||||||
module_search_paths_set=1,
|
module_search_paths_set=1,
|
||||||
module_search_paths=[
|
module_search_paths=[
|
||||||
r"C:\Python\python98.zip",
|
r"C:\Python\python98.zip",
|
||||||
r"C:\Python\Lib",
|
|
||||||
r"C:\Python\DLLs",
|
r"C:\Python\DLLs",
|
||||||
|
r"C:\Python\Lib",
|
||||||
|
r"C:\Python",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
actual = getpath(ns, expected)
|
actual = getpath(ns, expected)
|
||||||
|
@ -173,8 +176,9 @@ class MockGetPathTests(unittest.TestCase):
|
||||||
module_search_paths_set=1,
|
module_search_paths_set=1,
|
||||||
module_search_paths=[
|
module_search_paths=[
|
||||||
r"C:\Python\python98.zip",
|
r"C:\Python\python98.zip",
|
||||||
r"C:\Python\Lib",
|
|
||||||
r"C:\Python\DLLs",
|
r"C:\Python\DLLs",
|
||||||
|
r"C:\Python\Lib",
|
||||||
|
r"C:\Python",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
actual = getpath(ns, expected)
|
actual = getpath(ns, expected)
|
||||||
|
@ -201,8 +205,8 @@ class MockGetPathTests(unittest.TestCase):
|
||||||
module_search_paths_set=1,
|
module_search_paths_set=1,
|
||||||
module_search_paths=[
|
module_search_paths=[
|
||||||
r"C:\CPython\PCbuild\amd64\python98.zip",
|
r"C:\CPython\PCbuild\amd64\python98.zip",
|
||||||
r"C:\CPython\Lib",
|
|
||||||
r"C:\CPython\PCbuild\amd64",
|
r"C:\CPython\PCbuild\amd64",
|
||||||
|
r"C:\CPython\Lib",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
actual = getpath(ns, expected)
|
actual = getpath(ns, expected)
|
||||||
|
@ -231,8 +235,8 @@ class MockGetPathTests(unittest.TestCase):
|
||||||
module_search_paths_set=1,
|
module_search_paths_set=1,
|
||||||
module_search_paths=[
|
module_search_paths=[
|
||||||
r"C:\Out\python98.zip",
|
r"C:\Out\python98.zip",
|
||||||
r"C:\CPython\Lib",
|
|
||||||
r"C:\Out",
|
r"C:\Out",
|
||||||
|
r"C:\CPython\Lib",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
actual = getpath(ns, expected)
|
actual = getpath(ns, expected)
|
||||||
|
@ -254,8 +258,8 @@ class MockGetPathTests(unittest.TestCase):
|
||||||
module_search_paths_set=1,
|
module_search_paths_set=1,
|
||||||
module_search_paths=[
|
module_search_paths=[
|
||||||
r"C:\Python\python98.zip",
|
r"C:\Python\python98.zip",
|
||||||
r"C:\Python\Lib",
|
|
||||||
r"C:\Python",
|
r"C:\Python",
|
||||||
|
r"C:\Python\Lib",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
actual = getpath(ns, expected)
|
actual = getpath(ns, expected)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Ensures the ``PythonPath`` registry key from an install is used when
|
||||||
|
launching from a different copy of Python that relies on an existing install
|
||||||
|
to provide a copy of its modules and standard library.
|
|
@ -597,25 +597,27 @@ 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 os_name == 'nt':
|
||||||
if os_name == 'nt':
|
# QUIRK: Windows always assumed these were the same
|
||||||
# QUIRK: For compatibility and security, do not search for DLLs
|
# gh-100320: Our PYDs are assumed to be relative to the Lib directory
|
||||||
# directory. The fallback below will cover it
|
# (that is, prefix) rather than the executable (that is, executable_dir)
|
||||||
exec_prefix = executable_dir
|
exec_prefix = prefix
|
||||||
else:
|
if not exec_prefix and executable_dir:
|
||||||
exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir)
|
exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir)
|
||||||
if not exec_prefix and EXEC_PREFIX:
|
if not exec_prefix and EXEC_PREFIX:
|
||||||
exec_prefix = EXEC_PREFIX
|
exec_prefix = EXEC_PREFIX
|
||||||
if not exec_prefix or not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)):
|
if not exec_prefix or not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)):
|
||||||
if os_name == 'nt':
|
if os_name == 'nt':
|
||||||
# QUIRK: If DLLs is missing on Windows, don't warn, just assume
|
# QUIRK: If DLLs is missing on Windows, don't warn, just assume
|
||||||
# that it's all the same as prefix.
|
# that they're in exec_prefix
|
||||||
# gh-98790: We set platstdlib_dir here to avoid adding "DLLs" into
|
if not platstdlib_dir:
|
||||||
# sys.path when it doesn't exist, which would give site-packages
|
# gh-98790: We set platstdlib_dir here to avoid adding "DLLs" into
|
||||||
# precedence over executable_dir, which is *probably* where our PYDs
|
# sys.path when it doesn't exist in the platstdlib place, which
|
||||||
# live. Ideally, whoever changes our layout will tell us what the
|
# would give Lib packages precedence over executable_dir where our
|
||||||
# layout is, but in the past this worked, so it should keep working.
|
# PYDs *probably* live. Ideally, whoever changes our layout will tell
|
||||||
platstdlib_dir = exec_prefix = prefix
|
# us what the layout is, but in the past this worked, so it should
|
||||||
|
# keep working.
|
||||||
|
platstdlib_dir = exec_prefix
|
||||||
else:
|
else:
|
||||||
warn('Could not find platform dependent libraries <exec_prefix>')
|
warn('Could not find platform dependent libraries <exec_prefix>')
|
||||||
|
|
||||||
|
@ -701,8 +703,14 @@ elif not pythonpath_was_set:
|
||||||
except OSError:
|
except OSError:
|
||||||
break
|
break
|
||||||
if isinstance(v, str):
|
if isinstance(v, str):
|
||||||
pythonpath.append(v)
|
pythonpath.extend(v.split(DELIM))
|
||||||
i += 1
|
i += 1
|
||||||
|
# Paths from the core key get appended last, but only
|
||||||
|
# when home was not set and we aren't in a build dir
|
||||||
|
if not home_was_set and not venv_prefix and not build_prefix:
|
||||||
|
v = winreg.QueryValue(key, None)
|
||||||
|
if isinstance(v, str):
|
||||||
|
pythonpath.extend(v.split(DELIM))
|
||||||
finally:
|
finally:
|
||||||
winreg.CloseKey(key)
|
winreg.CloseKey(key)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -714,13 +722,17 @@ elif not pythonpath_was_set:
|
||||||
pythonpath.append(joinpath(prefix, p))
|
pythonpath.append(joinpath(prefix, p))
|
||||||
|
|
||||||
# Then add stdlib_dir and platstdlib_dir
|
# Then add stdlib_dir and platstdlib_dir
|
||||||
if os_name == 'nt' and venv_prefix:
|
if os_name == 'nt':
|
||||||
# QUIRK: Windows generates paths differently in a venv
|
# QUIRK: Windows generates paths differently
|
||||||
if platstdlib_dir:
|
if platstdlib_dir:
|
||||||
pythonpath.append(platstdlib_dir)
|
pythonpath.append(platstdlib_dir)
|
||||||
if stdlib_dir:
|
if stdlib_dir:
|
||||||
pythonpath.append(stdlib_dir)
|
pythonpath.append(stdlib_dir)
|
||||||
if executable_dir not in pythonpath:
|
if executable_dir and executable_dir not in pythonpath:
|
||||||
|
# QUIRK: the executable directory is on sys.path
|
||||||
|
# We keep it low priority, so that properly installed modules are
|
||||||
|
# found first. It may be earlier in the order if we found some
|
||||||
|
# reason to put it there.
|
||||||
pythonpath.append(executable_dir)
|
pythonpath.append(executable_dir)
|
||||||
else:
|
else:
|
||||||
if stdlib_dir:
|
if stdlib_dir:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue