Skip to content

Commit

Permalink
Respect .python-version in uv venv --preview (#4360)
Browse files Browse the repository at this point in the history
Adds support for reading Python version files (introduced in #4335) to
`uv venv`. If present, we'll use the file version as the default.
  • Loading branch information
zanieb committed Jun 18, 2024
1 parent 76c26db commit 903dfc2
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 12 deletions.
10 changes: 5 additions & 5 deletions .python-versions
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
3.8.12
3.8.18
3.9.18
3.10.13
3.11.7
3.12.1
3.11.7
3.10.13
3.9.18
3.8.18
3.8.12
2 changes: 1 addition & 1 deletion crates/uv-toolchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub use crate::prefix::Prefix;
pub use crate::python_version::PythonVersion;
pub use crate::target::Target;
pub use crate::toolchain::Toolchain;
pub use crate::version_files::{request_from_version_files, requests_from_version_files};
pub use crate::version_files::{request_from_version_file, requests_from_version_file};
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};

mod discovery;
Expand Down
4 changes: 2 additions & 2 deletions crates/uv-toolchain/src/version_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::ToolchainRequest;
///
/// Prefers `.python-versions` then `.python-version`.
/// If only one Python version is desired, use [`request_from_version_files`] which prefers the `.python-version` file.
pub async fn requests_from_version_files() -> Result<Option<Vec<ToolchainRequest>>, io::Error> {
pub async fn requests_from_version_file() -> Result<Option<Vec<ToolchainRequest>>, io::Error> {
if let Some(versions) = read_versions_file().await? {
Ok(Some(
versions
Expand All @@ -27,7 +27,7 @@ pub async fn requests_from_version_files() -> Result<Option<Vec<ToolchainRequest
///
/// Prefers `.python-version` then the first entry of `.python-versions`.
/// If multiple Python versions are desired, use [`requests_from_version_files`] instead.
pub async fn request_from_version_files() -> Result<Option<ToolchainRequest>, io::Error> {
pub async fn request_from_version_file() -> Result<Option<ToolchainRequest>, io::Error> {
if let Some(version) = read_version_file().await? {
Ok(Some(ToolchainRequest::parse(&version)))
} else if let Some(versions) = read_versions_file().await? {
Expand Down
4 changes: 2 additions & 2 deletions crates/uv/src/commands/toolchain/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use uv_configuration::PreviewMode;
use uv_fs::Simplified;
use uv_toolchain::downloads::{self, DownloadResult, PythonDownload, PythonDownloadRequest};
use uv_toolchain::managed::{InstalledToolchain, InstalledToolchains};
use uv_toolchain::{requests_from_version_files, ToolchainRequest};
use uv_toolchain::{requests_from_version_file, ToolchainRequest};
use uv_warnings::warn_user;

use crate::commands::ExitStatus;
Expand All @@ -35,7 +35,7 @@ pub(crate) async fn install(
let toolchain_dir = toolchains.root();

let requests: Vec<_> = if targets.is_empty() {
if let Some(requests) = requests_from_version_files().await? {
if let Some(requests) = requests_from_version_file().await? {
requests
} else {
vec![ToolchainRequest::Any]
Expand Down
9 changes: 7 additions & 2 deletions crates/uv/src/commands/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use uv_dispatch::BuildDispatch;
use uv_fs::Simplified;
use uv_git::GitResolver;
use uv_resolver::{ExcludeNewer, FlatIndex, InMemoryIndex, OptionsBuilder};
use uv_toolchain::{SystemPython, Toolchain, ToolchainRequest};
use uv_toolchain::{request_from_version_file, SystemPython, Toolchain, ToolchainRequest};
use uv_types::{BuildContext, BuildIsolation, HashStrategy, InFlight};

use crate::commands::{pip, ExitStatus};
Expand Down Expand Up @@ -125,9 +125,14 @@ async fn venv_impl(
.connectivity(connectivity)
.native_tls(native_tls);

let mut interpreter_request = python_request.map(ToolchainRequest::parse);
if preview.is_enabled() && interpreter_request.is_none() {
interpreter_request = request_from_version_file().await.into_diagnostic()?;
}

// Locate the Python interpreter to use in the environment
let interpreter = Toolchain::find_or_fetch(
python_request.map(ToolchainRequest::parse),
interpreter_request,
SystemPython::Required,
preview,
client_builder,
Expand Down
108 changes: 108 additions & 0 deletions crates/uv/tests/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,114 @@ fn create_venv_ignores_virtual_env_variable() {
);
}

#[test]
fn create_venv_reads_request_from_python_version_file() {
let context = TestContext::new_with_versions(&["3.11", "3.12"]);

// Without the file, we should use the first on the PATH
uv_snapshot!(context.filters(), context.venv()
.arg("--preview"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.11.[X] interpreter at: [PYTHON-3.11]
Creating virtualenv at: .venv
Activate with: source .venv/bin/activate
"###
);

// With a version file, we should prefer that version
context
.temp_dir
.child(".python-version")
.write_str("3.12")
.unwrap();

uv_snapshot!(context.filters(), context.venv()
.arg("--preview"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Creating virtualenv at: .venv
Activate with: source .venv/bin/activate
"###
);

context.venv.assert(predicates::path::is_dir());
}

#[test]
fn create_venv_reads_request_from_python_versions_file() {
let context = TestContext::new_with_versions(&["3.11", "3.12"]);

// Without the file, we should use the first on the PATH
uv_snapshot!(context.filters(), context.venv()
.arg("--preview"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.11.[X] interpreter at: [PYTHON-3.11]
Creating virtualenv at: .venv
Activate with: source .venv/bin/activate
"###
);

// With a versions file, we should prefer the first listed version
context
.temp_dir
.child(".python-versions")
.write_str("3.12\n3.11")
.unwrap();

uv_snapshot!(context.filters(), context.venv()
.arg("--preview"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Creating virtualenv at: .venv
Activate with: source .venv/bin/activate
"###
);

context.venv.assert(predicates::path::is_dir());
}

#[test]
fn create_venv_explicit_request_takes_priority_over_python_version_file() {
let context = TestContext::new_with_versions(&["3.11", "3.12"]);

context
.temp_dir
.child(".python-version")
.write_str("3.12")
.unwrap();

uv_snapshot!(context.filters(), context.venv()
.arg("--preview").arg("--python").arg("3.11"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.11.[X] interpreter at: [PYTHON-3.11]
Creating virtualenv at: .venv
Activate with: source .venv/bin/activate
"###
);

context.venv.assert(predicates::path::is_dir());
}

#[test]
fn seed() {
let context = TestContext::new_with_versions(&["3.12"]);
Expand Down

0 comments on commit 903dfc2

Please sign in to comment.