Include the parent interpreter in Python discovery when --system is used (#7440)

Closes https://github.com/astral-sh/uv/issues/7417

Tested with this epic blurb

```
❯ uv pip install --python /Users/zb/.local/share/uv/python/cpython-3.13.0rc2-macos-aarch64-none/bin/python3 . --break-system-packages --reinstall
Resolved 1 package in 0.91ms
   Built uv @ file:///Users/zb/workspace/uv
Prepared 1 package in 2m 48s
Uninstalled 1 package in 1ms
Installed 1 package in 1ms
 - uv==0.4.10
 + uv==0.4.10 (from file:///Users/zb/workspace/uv)
❯ /Users/zb/.local/share/uv/python/cpython-3.13.0rc2-macos-aarch64-none/bin/python3 -m uv pip install --system -v httpx
DEBUG uv 0.4.10
DEBUG Searching for Python interpreter in system path
DEBUG Found `cpython-3.13.0rc2-macos-aarch64-none` at `/Users/zb/.local/share/uv/python/cpython-3.13.0rc2-macos-aarch64-none/bin/python3` (parent interpreter)
DEBUG Using Python 3.13.0rc2 environment at /Users/zb/.local/share/uv/python/cpython-3.13.0rc2-macos-aarch64-none/bin/python3
```
This commit is contained in:
Zanie Blue 2024-09-16 22:51:50 -05:00 committed by GitHub
parent e9378be919
commit f679987fe1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 16 deletions

View file

@ -211,7 +211,6 @@ pub enum Error {
/// ///
/// The following sources are supported: /// The following sources are supported:
/// ///
/// - Spawning interpreter (via `UV_INTERNAL__PARENT_INTERPRETER`)
/// - Active virtual environment (via `VIRTUAL_ENV`) /// - Active virtual environment (via `VIRTUAL_ENV`)
/// - Active conda environment (via `CONDA_PREFIX`) /// - Active conda environment (via `CONDA_PREFIX`)
/// - Discovered virtual environment (e.g. `.venv` in a parent directory) /// - Discovered virtual environment (e.g. `.venv` in a parent directory)
@ -219,13 +218,6 @@ pub enum Error {
/// Notably, "system" environments are excluded. See [`python_executables_from_installed`]. /// Notably, "system" environments are excluded. See [`python_executables_from_installed`].
fn python_executables_from_environments<'a>( fn python_executables_from_environments<'a>(
) -> impl Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a { ) -> impl Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a {
let from_parent_interpreter = std::iter::once_with(|| {
std::env::var_os("UV_INTERNAL__PARENT_INTERPRETER")
.into_iter()
.map(|path| Ok((PythonSource::ParentInterpreter, PathBuf::from(path))))
})
.flatten();
let from_virtual_environment = std::iter::once_with(|| { let from_virtual_environment = std::iter::once_with(|| {
virtualenv_from_env() virtualenv_from_env()
.into_iter() .into_iter()
@ -253,8 +245,7 @@ fn python_executables_from_environments<'a>(
}) })
.flatten_ok(); .flatten_ok();
from_parent_interpreter from_virtual_environment
.chain(from_virtual_environment)
.chain(from_conda_environment) .chain(from_conda_environment)
.chain(from_discovered_environment) .chain(from_discovered_environment)
} }
@ -383,15 +374,31 @@ fn python_executables<'a>(
environments: EnvironmentPreference, environments: EnvironmentPreference,
preference: PythonPreference, preference: PythonPreference,
) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> { ) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> {
// Always read from `UV_INTERNAL__PARENT_INTERPRETER` — it could be a system interpreter
let from_parent_interpreter = std::iter::once_with(|| {
std::env::var_os("UV_INTERNAL__PARENT_INTERPRETER")
.into_iter()
.map(|path| Ok((PythonSource::ParentInterpreter, PathBuf::from(path))))
})
.flatten();
let from_environments = python_executables_from_environments(); let from_environments = python_executables_from_environments();
let from_installed = python_executables_from_installed(version, implementation, preference); let from_installed = python_executables_from_installed(version, implementation, preference);
// Limit the search to the relevant environment preference; we later validate that they match
// the preference but queries are expensive and we query less interpreters this way.
match environments { match environments {
EnvironmentPreference::OnlyVirtual => Box::new(from_environments), EnvironmentPreference::OnlyVirtual => {
EnvironmentPreference::ExplicitSystem | EnvironmentPreference::Any => { Box::new(from_parent_interpreter.chain(from_environments))
Box::new(from_environments.chain(from_installed)) }
EnvironmentPreference::ExplicitSystem | EnvironmentPreference::Any => Box::new(
from_parent_interpreter
.chain(from_environments)
.chain(from_installed),
),
EnvironmentPreference::OnlySystem => {
Box::new(from_parent_interpreter.chain(from_installed))
} }
EnvironmentPreference::OnlySystem => Box::new(from_installed),
} }
} }

View file

@ -1247,8 +1247,8 @@ mod tests {
)??; )??;
assert_eq!( assert_eq!(
python.interpreter().python_full_version().to_string(), python.interpreter().python_full_version().to_string(),
"3.12.3", "3.12.0",
"We should prefer the system interpreter" "We should prefer the parent interpreter since it's not virtual"
); );
// Test with `EnvironmentPreference::OnlyVirtual` // Test with `EnvironmentPreference::OnlyVirtual`