mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +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 @@ def check_config(self, configs, expected): | ||||||
|         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 @@ def test_init_setpythonhome(self): | ||||||
|             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 @@ def test_init_is_python_build_with_home(self): | ||||||
|             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 @@ def test_init_is_python_build_with_home(self): | ||||||
|         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 @@ def test_init_pybuilddir_win32(self): | ||||||
| 
 | 
 | ||||||
|             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 @@ def test_normal_win32(self): | ||||||
|             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 @@ def test_buildtree_win32(self): | ||||||
|             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 @@ def test_registry_win32(self): | ||||||
|                 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 @@ def test_registry_win32(self): | ||||||
|             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 @@ def test_symlink_normal_win32(self): | ||||||
|             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 @@ def test_symlink_buildtree_win32(self): | ||||||
|             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 @@ def test_buildtree_pythonhome_win32(self): | ||||||
|             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 @@ def test_no_dlls_win32(self): | ||||||
|             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 @@ def search_up(prefix, *landmarks, test=isfile): | ||||||
| 
 | 
 | ||||||
|     # 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 @@ def search_up(prefix, *landmarks, test=isfile): | ||||||
|                         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 @@ def search_up(prefix, *landmarks, test=isfile): | ||||||
|             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
	
	 Steve Dower
						Steve Dower