Respect `.python-version` in `--isolated` runs (#5741)

Closes https://github.com/astral-sh/uv/issues/5740.
This commit is contained in:
Charlie Marsh 2024-08-02 19:34:25 -04:00 committed by GitHub
parent b26794bf6f
commit 030d477653
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 150 additions and 13 deletions

View File

@ -154,17 +154,23 @@ pub(crate) enum FoundInterpreter {
Environment(PythonEnvironment), Environment(PythonEnvironment),
} }
impl FoundInterpreter { /// The resolved Python request and requirement for a [`Workspace`].
/// Discover the interpreter to use in the current [`Workspace`]. #[derive(Debug, Clone)]
pub(crate) async fn discover( pub(crate) struct WorkspacePython {
workspace: &Workspace, /// The resolved Python request, computed by considering (1) any explicit request from the user
/// via `--python`, (2) any implicit request from the user via `.python-version`, and (3) any
/// `Requires-Python` specifier in the `pyproject.toml`.
python_request: Option<PythonRequest>,
/// The resolved Python requirement for the project, computed by taking the intersection of all
/// `Requires-Python` specifiers in the workspace.
requires_python: Option<RequiresPython>,
}
impl WorkspacePython {
/// Determine the [`WorkspacePython`] for the current [`Workspace`].
pub(crate) async fn from_request(
python_request: Option<PythonRequest>, python_request: Option<PythonRequest>,
python_preference: PythonPreference, workspace: &Workspace,
python_fetch: PythonFetch,
connectivity: Connectivity,
native_tls: bool,
cache: &Cache,
printer: Printer,
) -> Result<Self, ProjectError> { ) -> Result<Self, ProjectError> {
let requires_python = find_requires_python(workspace)?; let requires_python = find_requires_python(workspace)?;
@ -182,6 +188,31 @@ impl FoundInterpreter {
.map(|specifiers| PythonRequest::Version(VersionRequest::Range(specifiers.clone()))) .map(|specifiers| PythonRequest::Version(VersionRequest::Range(specifiers.clone())))
}; };
Ok(Self {
python_request,
requires_python,
})
}
}
impl FoundInterpreter {
/// Discover the interpreter to use in the current [`Workspace`].
pub(crate) async fn discover(
workspace: &Workspace,
python_request: Option<PythonRequest>,
python_preference: PythonPreference,
python_fetch: PythonFetch,
connectivity: Connectivity,
native_tls: bool,
cache: &Cache,
printer: Printer,
) -> Result<Self, ProjectError> {
// Resolve the Python request and requirement for the workspace.
let WorkspacePython {
python_request,
requires_python,
} = WorkspacePython::from_request(python_request, workspace).await?;
// Read from the virtual environment first. // Read from the virtual environment first.
match find_environment(workspace, cache) { match find_environment(workspace, cache) {
Ok(venv) => { Ok(venv) => {

View File

@ -27,7 +27,7 @@ use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace, WorkspaceError};
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
use crate::commands::project::environment::CachedEnvironment; use crate::commands::project::environment::CachedEnvironment;
use crate::commands::project::ProjectError; use crate::commands::project::{ProjectError, WorkspacePython};
use crate::commands::reporters::PythonDownloadReporter; use crate::commands::reporters::PythonDownloadReporter;
use crate::commands::{pip, project, ExitStatus, SharedState}; use crate::commands::{pip, project, ExitStatus, SharedState};
use crate::printer::Printer; use crate::printer::Printer;
@ -206,9 +206,16 @@ pub(crate) async fn run(
.connectivity(connectivity) .connectivity(connectivity)
.native_tls(native_tls); .native_tls(native_tls);
// Note we force preview on during `uv run` for now since the entire interface is in preview // Resolve the Python request and requirement for the workspace.
PythonInstallation::find_or_fetch( let WorkspacePython { python_request, .. } = WorkspacePython::from_request(
python.as_deref().map(PythonRequest::parse), python.as_deref().map(PythonRequest::parse),
project.workspace(),
)
.await?;
// Note we force preview on during `uv run` for now since the entire interface is in preview.
PythonInstallation::find_or_fetch(
python_request,
EnvironmentPreference::Any, EnvironmentPreference::Any,
python_preference, python_preference,
python_fetch, python_fetch,

View File

@ -915,3 +915,102 @@ fn run_without_output() -> Result<()> {
Ok(()) Ok(())
} }
/// Ensure that we can import from the root project when layering `--with` requirements.
#[test]
fn run_isolated_python_version() -> Result<()> {
let context = TestContext::new_with_versions(&["3.8", "3.12"]);
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "foo"
version = "1.0.0"
requires-python = ">=3.8"
dependencies = ["anyio"]
"#
})?;
let src = context.temp_dir.child("src").child("foo");
src.create_dir_all()?;
let init = src.child("__init__.py");
init.touch()?;
let main = context.temp_dir.child("main.py");
main.write_str(indoc! { r"
import sys
print((sys.version_info.major, sys.version_info.minor))
"
})?;
uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
success: true
exit_code: 0
----- stdout -----
(3, 8)
----- stderr -----
warning: `uv run` is experimental and may change without warning
Using Python 3.8.[X] interpreter at: [PYTHON-3.8]
Creating virtualenv at: .venv
Resolved 6 packages in [TIME]
Prepared 6 packages in [TIME]
Installed 6 packages in [TIME]
+ anyio==4.3.0
+ exceptiongroup==1.2.0
+ foo==1.0.0 (from file://[TEMP_DIR]/)
+ idna==3.6
+ sniffio==1.3.1
+ typing-extensions==4.10.0
"###);
uv_snapshot!(context.filters(), context.run().arg("--isolated").arg("main.py"), @r###"
success: true
exit_code: 0
----- stdout -----
(3, 8)
----- stderr -----
warning: `uv run` is experimental and may change without warning
Resolved 6 packages in [TIME]
Prepared 5 packages in [TIME]
Installed 6 packages in [TIME]
+ anyio==4.3.0
+ exceptiongroup==1.2.0
+ foo==1.0.0 (from file://[TEMP_DIR]/)
+ idna==3.6
+ sniffio==1.3.1
+ typing-extensions==4.10.0
"###);
// Set the `.python-version` to `3.12`.
context
.temp_dir
.child(PYTHON_VERSION_FILENAME)
.write_str("3.12")?;
uv_snapshot!(context.filters(), context.run().arg("--isolated").arg("main.py"), @r###"
success: true
exit_code: 0
----- stdout -----
(3, 12)
----- stderr -----
warning: `uv run` is experimental and may change without warning
Resolved 6 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 4 packages in [TIME]
+ anyio==4.3.0
+ foo==1.0.0 (from file://[TEMP_DIR]/)
+ idna==3.6
+ sniffio==1.3.1
"###);
Ok(())
}