mirror of https://github.com/astral-sh/uv
Respect `.python-version` in `--isolated` runs (#5741)
Closes https://github.com/astral-sh/uv/issues/5740.
This commit is contained in:
parent
b26794bf6f
commit
030d477653
|
|
@ -154,17 +154,23 @@ pub(crate) enum FoundInterpreter {
|
|||
Environment(PythonEnvironment),
|
||||
}
|
||||
|
||||
impl FoundInterpreter {
|
||||
/// Discover the interpreter to use in the current [`Workspace`].
|
||||
pub(crate) async fn discover(
|
||||
workspace: &Workspace,
|
||||
/// The resolved Python request and requirement for a [`Workspace`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct WorkspacePython {
|
||||
/// 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_preference: PythonPreference,
|
||||
python_fetch: PythonFetch,
|
||||
connectivity: Connectivity,
|
||||
native_tls: bool,
|
||||
cache: &Cache,
|
||||
printer: Printer,
|
||||
workspace: &Workspace,
|
||||
) -> Result<Self, ProjectError> {
|
||||
let requires_python = find_requires_python(workspace)?;
|
||||
|
||||
|
|
@ -182,6 +188,31 @@ impl FoundInterpreter {
|
|||
.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.
|
||||
match find_environment(workspace, cache) {
|
||||
Ok(venv) => {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace, WorkspaceError};
|
|||
|
||||
use crate::commands::pip::operations::Modifications;
|
||||
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::{pip, project, ExitStatus, SharedState};
|
||||
use crate::printer::Printer;
|
||||
|
|
@ -206,9 +206,16 @@ pub(crate) async fn run(
|
|||
.connectivity(connectivity)
|
||||
.native_tls(native_tls);
|
||||
|
||||
// Note we force preview on during `uv run` for now since the entire interface is in preview
|
||||
PythonInstallation::find_or_fetch(
|
||||
// Resolve the Python request and requirement for the workspace.
|
||||
let WorkspacePython { python_request, .. } = WorkspacePython::from_request(
|
||||
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,
|
||||
python_preference,
|
||||
python_fetch,
|
||||
|
|
|
|||
|
|
@ -915,3 +915,102 @@ fn run_without_output() -> Result<()> {
|
|||
|
||||
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(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue