mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Discover and prefer the parent interpreter when invoked with python -m uv
(#3736)
Closes #2222 Closes https://github.com/astral-sh/uv/issues/2058 Replaces https://github.com/astral-sh/uv/pull/2338 See also https://github.com/astral-sh/uv/issues/2649 We use an environment variable (`UV_INTERNAL__PARENT_INTERPRETER`) to track the invoking interpreter when `python -m uv` is used. The parent interpreter is preferred over all other sources (though it will be skipped if it does not meet a `--python` request or if `--system` is used and it belongs to a virtual environment). We warn if `--system` is not provided and this interpreter would mutate system packages, but allow it.
This commit is contained in:
parent
b92321bd2d
commit
5fe891082d
7 changed files with 296 additions and 40 deletions
|
@ -62,8 +62,10 @@ pub enum VersionRequest {
|
||||||
/// The policy for discovery of "system" Python interpreters.
|
/// The policy for discovery of "system" Python interpreters.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
pub enum SystemPython {
|
pub enum SystemPython {
|
||||||
/// Do not allow a system Python
|
/// Only allow a system Python if passed directly i.e. via [`InterpreterSource::ProvidedPath`] or [`InterpreterSource::ParentInterpreter`]
|
||||||
#[default]
|
#[default]
|
||||||
|
Explicit,
|
||||||
|
/// Do not allow a system Python
|
||||||
Disallowed,
|
Disallowed,
|
||||||
/// Allow a system Python to be used if no virtual environment is active.
|
/// Allow a system Python to be used if no virtual environment is active.
|
||||||
Allowed,
|
Allowed,
|
||||||
|
@ -125,8 +127,9 @@ pub enum InterpreterSource {
|
||||||
PyLauncher,
|
PyLauncher,
|
||||||
/// The interpreter was found in the uv toolchain directory
|
/// The interpreter was found in the uv toolchain directory
|
||||||
ManagedToolchain,
|
ManagedToolchain,
|
||||||
|
/// The interpreter invoked uv i.e. via `python -m uv ...`
|
||||||
|
ParentInterpreter,
|
||||||
// TODO(zanieb): Add support for fetching the interpreter from a remote source
|
// TODO(zanieb): Add support for fetching the interpreter from a remote source
|
||||||
// TODO(zanieb): Add variant for: The interpreter path was inherited from the parent process
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -158,6 +161,7 @@ pub enum Error {
|
||||||
///
|
///
|
||||||
/// In order, we look in:
|
/// In order, we look in:
|
||||||
///
|
///
|
||||||
|
/// - The spawning interpreter
|
||||||
/// - The active environment
|
/// - The active environment
|
||||||
/// - A discovered environment (e.g. `.venv`)
|
/// - A discovered environment (e.g. `.venv`)
|
||||||
/// - Installed managed toolchains
|
/// - Installed managed toolchains
|
||||||
|
@ -179,14 +183,22 @@ fn python_executables<'a>(
|
||||||
) -> impl Iterator<Item = Result<(InterpreterSource, PathBuf), Error>> + 'a {
|
) -> impl Iterator<Item = Result<(InterpreterSource, PathBuf), Error>> + 'a {
|
||||||
// Note we are careful to ensure the iterator chain is lazy to avoid unnecessary work
|
// Note we are careful to ensure the iterator chain is lazy to avoid unnecessary work
|
||||||
|
|
||||||
// (1) The active environment
|
// (1) The parent interpreter
|
||||||
sources.contains(InterpreterSource::ActiveEnvironment).then(||
|
sources.contains(InterpreterSource::ParentInterpreter).then(||
|
||||||
virtualenv_from_env()
|
std::env::var_os("UV_INTERNAL__PARENT_INTERPRETER")
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(virtualenv_python_executable)
|
.map(|path| Ok((InterpreterSource::ParentInterpreter, PathBuf::from(path))))
|
||||||
.map(|path| Ok((InterpreterSource::ActiveEnvironment, path)))
|
|
||||||
).into_iter().flatten()
|
).into_iter().flatten()
|
||||||
// (2) A discovered environment
|
// (2) The active environment
|
||||||
|
.chain(
|
||||||
|
sources.contains(InterpreterSource::ActiveEnvironment).then(||
|
||||||
|
virtualenv_from_env()
|
||||||
|
.into_iter()
|
||||||
|
.map(virtualenv_python_executable)
|
||||||
|
.map(|path| Ok((InterpreterSource::ActiveEnvironment, path)))
|
||||||
|
).into_iter().flatten()
|
||||||
|
)
|
||||||
|
// (3) A discovered environment
|
||||||
.chain(
|
.chain(
|
||||||
sources.contains(InterpreterSource::DiscoveredEnvironment).then(||
|
sources.contains(InterpreterSource::DiscoveredEnvironment).then(||
|
||||||
std::iter::once(
|
std::iter::once(
|
||||||
|
@ -201,7 +213,7 @@ fn python_executables<'a>(
|
||||||
).flatten_ok()
|
).flatten_ok()
|
||||||
).into_iter().flatten()
|
).into_iter().flatten()
|
||||||
)
|
)
|
||||||
// (3) Managed toolchains
|
// (4) Managed toolchains
|
||||||
.chain(
|
.chain(
|
||||||
sources.contains(InterpreterSource::ManagedToolchain).then(move ||
|
sources.contains(InterpreterSource::ManagedToolchain).then(move ||
|
||||||
std::iter::once(
|
std::iter::once(
|
||||||
|
@ -219,14 +231,14 @@ fn python_executables<'a>(
|
||||||
).flatten_ok()
|
).flatten_ok()
|
||||||
).into_iter().flatten()
|
).into_iter().flatten()
|
||||||
)
|
)
|
||||||
// (4) The search path
|
// (5) The search path
|
||||||
.chain(
|
.chain(
|
||||||
sources.contains(InterpreterSource::SearchPath).then(move ||
|
sources.contains(InterpreterSource::SearchPath).then(move ||
|
||||||
python_executables_from_search_path(version, implementation)
|
python_executables_from_search_path(version, implementation)
|
||||||
.map(|path| Ok((InterpreterSource::SearchPath, path))),
|
.map(|path| Ok((InterpreterSource::SearchPath, path))),
|
||||||
).into_iter().flatten()
|
).into_iter().flatten()
|
||||||
)
|
)
|
||||||
// (5) The `py` launcher (windows only)
|
// (6) The `py` launcher (windows only)
|
||||||
// TODO(konstin): Implement <https://peps.python.org/pep-0514/> to read python installations from the registry instead.
|
// TODO(konstin): Implement <https://peps.python.org/pep-0514/> to read python installations from the registry instead.
|
||||||
.chain(
|
.chain(
|
||||||
(sources.contains(InterpreterSource::PyLauncher) && cfg!(windows)).then(||
|
(sources.contains(InterpreterSource::PyLauncher) && cfg!(windows)).then(||
|
||||||
|
@ -344,8 +356,27 @@ fn python_interpreters<'a>(
|
||||||
})
|
})
|
||||||
.filter(move |result| match result {
|
.filter(move |result| match result {
|
||||||
// Filter the returned interpreters to conform to the system request
|
// Filter the returned interpreters to conform to the system request
|
||||||
Ok((_, interpreter)) => match (system, interpreter.is_virtualenv()) {
|
Ok((source, interpreter)) => match (system, interpreter.is_virtualenv()) {
|
||||||
(SystemPython::Allowed, _) => true,
|
(SystemPython::Allowed, _) => true,
|
||||||
|
(SystemPython::Explicit, false) => {
|
||||||
|
if matches!(
|
||||||
|
source,
|
||||||
|
InterpreterSource::ProvidedPath | InterpreterSource::ParentInterpreter
|
||||||
|
) {
|
||||||
|
debug!(
|
||||||
|
"Allowing system Python interpreter at `{}`",
|
||||||
|
interpreter.sys_executable().display()
|
||||||
|
);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Ignoring Python interpreter at `{}`: system intepreter not explicit",
|
||||||
|
interpreter.sys_executable().display()
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(SystemPython::Explicit, true) => true,
|
||||||
(SystemPython::Disallowed, false) => {
|
(SystemPython::Disallowed, false) => {
|
||||||
debug!(
|
debug!(
|
||||||
"Ignoring Python interpreter at `{}`: system intepreter not allowed",
|
"Ignoring Python interpreter at `{}`: system intepreter not allowed",
|
||||||
|
@ -1073,31 +1104,22 @@ impl SourceSelector {
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
match system {
|
match system {
|
||||||
SystemPython::Allowed => Self::All,
|
SystemPython::Allowed | SystemPython::Explicit => Self::All,
|
||||||
SystemPython::Required => {
|
SystemPython::Required => Self::from_sources([
|
||||||
debug!("Excluding virtual environment Python due to system flag");
|
InterpreterSource::ProvidedPath,
|
||||||
Self::from_sources([
|
InterpreterSource::SearchPath,
|
||||||
InterpreterSource::ProvidedPath,
|
#[cfg(windows)]
|
||||||
InterpreterSource::SearchPath,
|
InterpreterSource::PyLauncher,
|
||||||
#[cfg(windows)]
|
InterpreterSource::ManagedToolchain,
|
||||||
InterpreterSource::PyLauncher,
|
InterpreterSource::ParentInterpreter,
|
||||||
InterpreterSource::ManagedToolchain,
|
]),
|
||||||
])
|
SystemPython::Disallowed => Self::from_sources([
|
||||||
}
|
InterpreterSource::DiscoveredEnvironment,
|
||||||
SystemPython::Disallowed => {
|
InterpreterSource::ActiveEnvironment,
|
||||||
debug!("Only considering virtual environment Python interpreters");
|
]),
|
||||||
Self::virtualenvs()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn virtualenvs() -> Self {
|
|
||||||
Self::from_sources([
|
|
||||||
InterpreterSource::DiscoveredEnvironment,
|
|
||||||
InterpreterSource::ActiveEnvironment,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemPython {
|
impl SystemPython {
|
||||||
|
@ -1138,6 +1160,7 @@ impl fmt::Display for InterpreterSource {
|
||||||
Self::SearchPath => f.write_str("search path"),
|
Self::SearchPath => f.write_str("search path"),
|
||||||
Self::PyLauncher => f.write_str("`py` launcher output"),
|
Self::PyLauncher => f.write_str("`py` launcher output"),
|
||||||
Self::ManagedToolchain => f.write_str("managed toolchains"),
|
Self::ManagedToolchain => f.write_str("managed toolchains"),
|
||||||
|
Self::ParentInterpreter => f.write_str("parent interpreter"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,9 @@ use uv_fs::{LockedFile, Simplified};
|
||||||
|
|
||||||
use crate::discovery::{InterpreterRequest, SourceSelector, SystemPython, VersionRequest};
|
use crate::discovery::{InterpreterRequest, SourceSelector, SystemPython, VersionRequest};
|
||||||
use crate::virtualenv::{virtualenv_python_executable, PyVenvConfiguration};
|
use crate::virtualenv::{virtualenv_python_executable, PyVenvConfiguration};
|
||||||
use crate::{find_default_interpreter, find_interpreter, Error, Interpreter, Target};
|
use crate::{
|
||||||
|
find_default_interpreter, find_interpreter, Error, Interpreter, InterpreterSource, Target,
|
||||||
|
};
|
||||||
|
|
||||||
/// A Python environment, consisting of a Python [`Interpreter`] and its associated paths.
|
/// A Python environment, consisting of a Python [`Interpreter`] and its associated paths.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -31,6 +33,14 @@ impl PythonEnvironment {
|
||||||
} else if system.is_preferred() {
|
} else if system.is_preferred() {
|
||||||
Self::from_default_python(cache)
|
Self::from_default_python(cache)
|
||||||
} else {
|
} else {
|
||||||
|
// First check for a parent intepreter
|
||||||
|
match Self::from_parent_interpreter(system, cache) {
|
||||||
|
Ok(env) => return Ok(env),
|
||||||
|
Err(Error::NotFound(_)) => {}
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then a virtual environment
|
||||||
match Self::from_virtualenv(cache) {
|
match Self::from_virtualenv(cache) {
|
||||||
Ok(venv) => Ok(venv),
|
Ok(venv) => Ok(venv),
|
||||||
Err(Error::NotFound(_)) if system.is_allowed() => Self::from_default_python(cache),
|
Err(Error::NotFound(_)) if system.is_allowed() => Self::from_default_python(cache),
|
||||||
|
@ -41,12 +51,15 @@ impl PythonEnvironment {
|
||||||
|
|
||||||
/// Create a [`PythonEnvironment`] for an existing virtual environment.
|
/// Create a [`PythonEnvironment`] for an existing virtual environment.
|
||||||
pub fn from_virtualenv(cache: &Cache) -> Result<Self, Error> {
|
pub fn from_virtualenv(cache: &Cache) -> Result<Self, Error> {
|
||||||
let sources = SourceSelector::virtualenvs();
|
let sources = SourceSelector::from_sources([
|
||||||
|
InterpreterSource::DiscoveredEnvironment,
|
||||||
|
InterpreterSource::ActiveEnvironment,
|
||||||
|
]);
|
||||||
let request = InterpreterRequest::Version(VersionRequest::Default);
|
let request = InterpreterRequest::Version(VersionRequest::Default);
|
||||||
let found = find_interpreter(&request, SystemPython::Disallowed, &sources, cache)??;
|
let found = find_interpreter(&request, SystemPython::Disallowed, &sources, cache)??;
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
found.interpreter().base_prefix() == found.interpreter().base_exec_prefix(),
|
found.interpreter().is_virtualenv(),
|
||||||
"Not a virtualenv (source: {}, prefix: {})",
|
"Not a virtualenv (source: {}, prefix: {})",
|
||||||
found.source(),
|
found.source(),
|
||||||
found.interpreter().base_prefix().display()
|
found.interpreter().base_prefix().display()
|
||||||
|
@ -58,6 +71,18 @@ impl PythonEnvironment {
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a [`PythonEnvironment`] for the parent interpreter i.e. the executable in `python -m uv ...`
|
||||||
|
pub fn from_parent_interpreter(system: SystemPython, cache: &Cache) -> Result<Self, Error> {
|
||||||
|
let sources = SourceSelector::from_sources([InterpreterSource::ParentInterpreter]);
|
||||||
|
let request = InterpreterRequest::Version(VersionRequest::Default);
|
||||||
|
let found = find_interpreter(&request, system, &sources, cache)??;
|
||||||
|
|
||||||
|
Ok(Self(Arc::new(PythonEnvironmentShared {
|
||||||
|
root: found.interpreter().prefix().to_path_buf(),
|
||||||
|
interpreter: found.into_interpreter(),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a [`PythonEnvironment`] from the virtual environment at the given root.
|
/// Create a [`PythonEnvironment`] from the virtual environment at the given root.
|
||||||
pub fn from_root(root: &Path, cache: &Cache) -> Result<Self, Error> {
|
pub fn from_root(root: &Path, cache: &Cache) -> Result<Self, Error> {
|
||||||
let venv = match fs_err::canonicalize(root) {
|
let venv = match fs_err::canonicalize(root) {
|
||||||
|
|
|
@ -1360,6 +1360,174 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_environment_from_parent_interpreter() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let cache = Cache::temp()?;
|
||||||
|
let pwd = tempdir.child("pwd");
|
||||||
|
pwd.create_dir_all()?;
|
||||||
|
let venv = mock_venv(&tempdir, "3.12.0")?;
|
||||||
|
let python = tempdir.child("python").to_path_buf();
|
||||||
|
create_mock_interpreter(
|
||||||
|
&python,
|
||||||
|
&PythonVersion::from_str("3.12.1").unwrap(),
|
||||||
|
ImplementationName::CPython,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
with_vars(
|
||||||
|
[
|
||||||
|
("UV_TEST_PYTHON_PATH", None),
|
||||||
|
("UV_BOOTSTRAP_DIR", None),
|
||||||
|
(
|
||||||
|
"PATH",
|
||||||
|
Some(simple_mock_interpreters(&tempdir, &["3.12.2", "3.12.3"])?),
|
||||||
|
),
|
||||||
|
("UV_INTERNAL__PARENT_INTERPRETER", Some(python.into())),
|
||||||
|
("VIRTUAL_ENV", Some(venv.into())),
|
||||||
|
("PWD", Some(pwd.path().into())),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
let environment =
|
||||||
|
PythonEnvironment::find(None, crate::SystemPython::Allowed, &cache)
|
||||||
|
.expect("An environment is found");
|
||||||
|
assert_eq!(
|
||||||
|
environment.interpreter().python_full_version().to_string(),
|
||||||
|
"3.12.1",
|
||||||
|
"We should prefer parent interpreter"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_environment_from_parent_interpreter_system_explicit() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let cache = Cache::temp()?;
|
||||||
|
let pwd = tempdir.child("pwd");
|
||||||
|
pwd.create_dir_all()?;
|
||||||
|
let venv = mock_venv(&tempdir, "3.12.0")?;
|
||||||
|
let python = tempdir.child("python").to_path_buf();
|
||||||
|
create_mock_interpreter(
|
||||||
|
&python,
|
||||||
|
&PythonVersion::from_str("3.12.1").unwrap(),
|
||||||
|
ImplementationName::CPython,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
with_vars(
|
||||||
|
[
|
||||||
|
("UV_TEST_PYTHON_PATH", None),
|
||||||
|
("UV_BOOTSTRAP_DIR", None),
|
||||||
|
(
|
||||||
|
"PATH",
|
||||||
|
Some(simple_mock_interpreters(&tempdir, &["3.12.2", "3.12.3"])?),
|
||||||
|
),
|
||||||
|
("UV_INTERNAL__PARENT_INTERPRETER", Some(python.into())),
|
||||||
|
("VIRTUAL_ENV", Some(venv.into())),
|
||||||
|
("PWD", Some(pwd.path().into())),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
let environment =
|
||||||
|
PythonEnvironment::find(None, crate::SystemPython::Explicit, &cache)
|
||||||
|
.expect("An environment is found");
|
||||||
|
assert_eq!(
|
||||||
|
environment.interpreter().python_full_version().to_string(),
|
||||||
|
"3.12.1",
|
||||||
|
"We prefer the parent interpreter even though it is system"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_environment_from_parent_interpreter_system_disallowed() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let cache = Cache::temp()?;
|
||||||
|
let pwd = tempdir.child("pwd");
|
||||||
|
pwd.create_dir_all()?;
|
||||||
|
let venv = mock_venv(&tempdir, "3.12.0")?;
|
||||||
|
let python = tempdir.child("python").to_path_buf();
|
||||||
|
create_mock_interpreter(
|
||||||
|
&python,
|
||||||
|
&PythonVersion::from_str("3.12.1").unwrap(),
|
||||||
|
ImplementationName::CPython,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
with_vars(
|
||||||
|
[
|
||||||
|
("UV_TEST_PYTHON_PATH", None),
|
||||||
|
("UV_BOOTSTRAP_DIR", None),
|
||||||
|
(
|
||||||
|
"PATH",
|
||||||
|
Some(simple_mock_interpreters(&tempdir, &["3.12.2", "3.12.3"])?),
|
||||||
|
),
|
||||||
|
("UV_INTERNAL__PARENT_INTERPRETER", Some(python.into())),
|
||||||
|
("VIRTUAL_ENV", Some(venv.into())),
|
||||||
|
("PWD", Some(pwd.path().into())),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
let environment =
|
||||||
|
PythonEnvironment::find(None, crate::SystemPython::Disallowed, &cache)
|
||||||
|
.expect("An environment is found");
|
||||||
|
assert_eq!(
|
||||||
|
environment.interpreter().python_full_version().to_string(),
|
||||||
|
"3.12.0",
|
||||||
|
"We find the virtual environment Python because the system is explicitly not allowed"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_environment_from_parent_interpreter_system_required() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let cache = Cache::temp()?;
|
||||||
|
let pwd = tempdir.child("pwd");
|
||||||
|
pwd.create_dir_all()?;
|
||||||
|
let venv = mock_venv(&tempdir, "3.12.0")?;
|
||||||
|
let python = tempdir.child("python").to_path_buf();
|
||||||
|
create_mock_interpreter(
|
||||||
|
&python,
|
||||||
|
&PythonVersion::from_str("3.12.1").unwrap(),
|
||||||
|
ImplementationName::CPython,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
with_vars(
|
||||||
|
[
|
||||||
|
("UV_TEST_PYTHON_PATH", None),
|
||||||
|
("UV_BOOTSTRAP_DIR", None),
|
||||||
|
(
|
||||||
|
"PATH",
|
||||||
|
Some(simple_mock_interpreters(&tempdir, &["3.12.2", "3.12.3"])?),
|
||||||
|
),
|
||||||
|
("UV_INTERNAL__PARENT_INTERPRETER", Some(python.into())),
|
||||||
|
("VIRTUAL_ENV", Some(venv.into())),
|
||||||
|
("PWD", Some(pwd.path().into())),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
let environment =
|
||||||
|
PythonEnvironment::find(None, crate::SystemPython::Required, &cache)
|
||||||
|
.expect("An environment is found");
|
||||||
|
assert_eq!(
|
||||||
|
environment.interpreter().python_full_version().to_string(),
|
||||||
|
"3.12.2",
|
||||||
|
"We should skip the parent interpreter since its in a virtual environment"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn find_environment_active_environment_skipped_if_system_required() -> Result<()> {
|
fn find_environment_active_environment_skipped_if_system_required() -> Result<()> {
|
||||||
let tempdir = TempDir::new()?;
|
let tempdir = TempDir::new()?;
|
||||||
|
@ -1604,6 +1772,43 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_environment_allows_file_path_with_system_explicit() -> Result<()> {
|
||||||
|
let tempdir = TempDir::new()?;
|
||||||
|
let cache = Cache::temp()?;
|
||||||
|
tempdir.child("foo").create_dir_all()?;
|
||||||
|
let python = tempdir.child("foo").join("bar");
|
||||||
|
create_mock_interpreter(
|
||||||
|
&python,
|
||||||
|
&PythonVersion::from_str("3.10.0").unwrap(),
|
||||||
|
ImplementationName::default(),
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
with_vars(
|
||||||
|
[
|
||||||
|
("UV_TEST_PYTHON_PATH", None::<OsString>),
|
||||||
|
("PATH", None),
|
||||||
|
("PWD", Some(tempdir.path().into())),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
let environment = PythonEnvironment::find(
|
||||||
|
Some(python.to_str().expect("Test path is valid unicode")),
|
||||||
|
crate::SystemPython::Explicit,
|
||||||
|
&cache,
|
||||||
|
)
|
||||||
|
.expect("Environment should be found");
|
||||||
|
assert_eq!(
|
||||||
|
environment.interpreter().python_full_version().to_string(),
|
||||||
|
"3.10.0",
|
||||||
|
"We should find the interpreter"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn find_environment_does_not_allow_file_path_with_system_disallowed() -> Result<()> {
|
fn find_environment_does_not_allow_file_path_with_system_disallowed() -> Result<()> {
|
||||||
let tempdir = TempDir::new()?;
|
let tempdir = TempDir::new()?;
|
||||||
|
|
|
@ -117,7 +117,7 @@ pub(crate) async fn pip_install(
|
||||||
let system = if system {
|
let system = if system {
|
||||||
SystemPython::Required
|
SystemPython::Required
|
||||||
} else {
|
} else {
|
||||||
SystemPython::Disallowed
|
SystemPython::Explicit
|
||||||
};
|
};
|
||||||
let venv = PythonEnvironment::find(python.as_deref(), system, &cache)?;
|
let venv = PythonEnvironment::find(python.as_deref(), system, &cache)?;
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,7 @@ pub(crate) async fn pip_sync(
|
||||||
let system = if system {
|
let system = if system {
|
||||||
SystemPython::Required
|
SystemPython::Required
|
||||||
} else {
|
} else {
|
||||||
SystemPython::Disallowed
|
SystemPython::Explicit
|
||||||
};
|
};
|
||||||
let venv = PythonEnvironment::find(python.as_deref(), system, &cache)?;
|
let venv = PythonEnvironment::find(python.as_deref(), system, &cache)?;
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub(crate) async fn pip_uninstall(
|
||||||
let system = if system {
|
let system = if system {
|
||||||
SystemPython::Required
|
SystemPython::Required
|
||||||
} else {
|
} else {
|
||||||
SystemPython::Disallowed
|
SystemPython::Explicit
|
||||||
};
|
};
|
||||||
let venv = PythonEnvironment::find(python.as_deref(), system, &cache)?;
|
let venv = PythonEnvironment::find(python.as_deref(), system, &cache)?;
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,9 @@ def _run() -> None:
|
||||||
if venv:
|
if venv:
|
||||||
env.setdefault("VIRTUAL_ENV", venv)
|
env.setdefault("VIRTUAL_ENV", venv)
|
||||||
|
|
||||||
|
# Let `uv` know that it was spawned by this Python interpreter
|
||||||
|
env["UV_INTERNAL__PARENT_INTERPRETER"] = sys.executable
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue