Avoid using 3.8 in tests when feasible (#13680)

See https://github.com/astral-sh/uv/issues/13676

Python 3.8 is EOL, and we should avoid using it for tests unless we're
covering 3.8-specific behaviors.

In some cases, the Python version we require for the test is arbitrary,
for which I've added a new constant (set to 3.12). In other cases, we
require an older Python version for compatibility with various packages,
like `setuptools`. For those, we can _probably_ switch to a newer
version but it'd be more invasive as we'd need to change our constraints
or `exclude-newer`.

Adds a feature flag for cases where we still need to use the EOL
version.

There's a lot of usage in packse scenarios too, I'll update those
separately because the diff will be large.
This commit is contained in:
Zanie Blue 2025-05-27 13:34:49 -05:00 committed by GitHub
parent df00189ec5
commit 32b949c3a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 150 additions and 138 deletions

View File

@ -156,6 +156,7 @@ default-tests = [
"pypi", "pypi",
"python", "python",
"python-managed", "python-managed",
"python-eol",
"slow-tests", "slow-tests",
"test-ecosystem" "test-ecosystem"
] ]
@ -169,6 +170,8 @@ pypi = []
python = [] python = []
# Introduces a testing dependency on a local Python installation with specific patch versions. # Introduces a testing dependency on a local Python installation with specific patch versions.
python-patch = [] python-patch = []
# Introduces a testing dependency on a local Python installation with an EOL version.
python-eol = []
# Introduces a testing dependency on managed Python installations. # Introduces a testing dependency on managed Python installations.
python-managed = [] python-managed = []
# Include "slow" test cases. # Include "slow" test cases.

View File

@ -1,4 +1,4 @@
use crate::common::{TestContext, uv_snapshot}; use crate::common::{DEFAULT_PYTHON_VERSION, TestContext, uv_snapshot};
use anyhow::Result; use anyhow::Result;
use assert_cmd::assert::OutputAssertExt; use assert_cmd::assert::OutputAssertExt;
use assert_fs::prelude::*; use assert_fs::prelude::*;
@ -898,7 +898,7 @@ fn build_constraints() -> Result<()> {
#[test] #[test]
fn build_sha() -> Result<()> { fn build_sha() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()

View File

@ -33,6 +33,7 @@ use uv_static::EnvVars;
static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z"; static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z";
pub const PACKSE_VERSION: &str = "0.3.46"; pub const PACKSE_VERSION: &str = "0.3.46";
pub const DEFAULT_PYTHON_VERSION: &str = "3.12";
/// Using a find links url allows using `--index-url` instead of `--extra-index-url` in tests /// Using a find links url allows using `--index-url` instead of `--extra-index-url` in tests
/// to prevent dependency confusion attacks against our test suite. /// to prevent dependency confusion attacks against our test suite.

View File

@ -10982,7 +10982,7 @@ fn repeated_index_cli_reversed() -> Result<()> {
#[test] #[test]
fn add_with_build_constraints() -> Result<()> { fn add_with_build_constraints() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! {r#" pyproject_toml.write_str(indoc! {r#"

View File

@ -2365,7 +2365,7 @@ fn init_requires_python_version() -> Result<()> {
})?; })?;
let child = context.temp_dir.join("foo"); let child = context.temp_dir.join("foo");
uv_snapshot!(context.filters(), context.init().current_dir(&context.temp_dir).arg(&child).arg("--python").arg("3.8"), @r###" uv_snapshot!(context.filters(), context.init().current_dir(&context.temp_dir).arg(&child).arg("--python").arg("3.9"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -2380,15 +2380,15 @@ fn init_requires_python_version() -> Result<()> {
filters => context.filters(), filters => context.filters(),
}, { }, {
assert_snapshot!( assert_snapshot!(
pyproject_toml, @r###" pyproject_toml, @r#"
[project] [project]
name = "foo" name = "foo"
version = "0.1.0" version = "0.1.0"
description = "Add your description here" description = "Add your description here"
readme = "README.md" readme = "README.md"
requires-python = ">=3.8" requires-python = ">=3.9"
dependencies = [] dependencies = []
"### "#
); );
}); });
@ -2397,7 +2397,7 @@ fn init_requires_python_version() -> Result<()> {
filters => context.filters(), filters => context.filters(),
}, { }, {
assert_snapshot!( assert_snapshot!(
python_version, @"3.8" python_version, @"3.9"
); );
}); });
@ -2466,9 +2466,9 @@ fn init_requires_python_specifiers() -> Result<()> {
/// Run `uv init`, inferring the `requires-python` from the `.python-version` file. /// Run `uv init`, inferring the `requires-python` from the `.python-version` file.
#[test] #[test]
fn init_requires_python_version_file() -> Result<()> { fn init_requires_python_version_file() -> Result<()> {
let context = TestContext::new_with_versions(&["3.8", "3.12"]); let context = TestContext::new_with_versions(&["3.9", "3.12"]);
context.temp_dir.child(".python-version").write_str("3.8")?; context.temp_dir.child(".python-version").write_str("3.9")?;
let child = context.temp_dir.join("foo"); let child = context.temp_dir.join("foo");
uv_snapshot!(context.filters(), context.init().current_dir(&context.temp_dir).arg(&child), @r###" uv_snapshot!(context.filters(), context.init().current_dir(&context.temp_dir).arg(&child), @r###"
@ -2485,15 +2485,15 @@ fn init_requires_python_version_file() -> Result<()> {
filters => context.filters(), filters => context.filters(),
}, { }, {
assert_snapshot!( assert_snapshot!(
pyproject_toml, @r###" pyproject_toml, @r#"
[project] [project]
name = "foo" name = "foo"
version = "0.1.0" version = "0.1.0"
description = "Add your description here" description = "Add your description here"
readme = "README.md" readme = "README.md"
requires-python = ">=3.8" requires-python = ">=3.9"
dependencies = [] dependencies = []
"### "#
); );
}); });

View File

@ -2220,6 +2220,7 @@ fn lock_dependency_extra() -> Result<()> {
} }
/// Lock a project with a dependency that has a conditional extra. /// Lock a project with a dependency that has a conditional extra.
#[cfg(feature = "python-eol")]
#[test] #[test]
fn lock_conditional_dependency_extra() -> Result<()> { fn lock_conditional_dependency_extra() -> Result<()> {
let context = TestContext::new("3.12"); let context = TestContext::new("3.12");

View File

@ -17,7 +17,9 @@ use wiremock::{Mock, MockServer, ResponseTemplate};
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_static::EnvVars; use uv_static::EnvVars;
use crate::common::{TestContext, download_to_disk, packse_index_url, uv_snapshot}; use crate::common::{
DEFAULT_PYTHON_VERSION, TestContext, download_to_disk, packse_index_url, uv_snapshot,
};
#[test] #[test]
fn compile_requirements_in() -> Result<()> { fn compile_requirements_in() -> Result<()> {
@ -2105,6 +2107,7 @@ fn omit_non_matching_annotation() -> Result<()> {
/// Test that we select the last 3.8 compatible numpy version instead of trying to compile an /// Test that we select the last 3.8 compatible numpy version instead of trying to compile an
/// incompatible sdist <https://github.com/astral-sh/uv/issues/388> /// incompatible sdist <https://github.com/astral-sh/uv/issues/388>
#[cfg(feature = "python-eol")]
#[test] #[test]
fn compile_numpy_py38() -> Result<()> { fn compile_numpy_py38() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.8");
@ -5563,6 +5566,7 @@ coverage = ["example[test]", "extras>=0.0.1,<=0.0.2"]
/// ///
/// `voluptuous==0.15.1` requires Python 3.9 or later, so we should resolve to an earlier version /// `voluptuous==0.15.1` requires Python 3.9 or later, so we should resolve to an earlier version
/// and avoiding building 0.15.1 at all. /// and avoiding building 0.15.1 at all.
#[cfg(feature = "python-eol")]
#[test] #[test]
fn requires_python_prefetch() -> Result<()> { fn requires_python_prefetch() -> Result<()> {
let context = TestContext::new("3.8").with_exclude_newer("2025-01-01T00:00:00Z"); let context = TestContext::new("3.8").with_exclude_newer("2025-01-01T00:00:00Z");
@ -13551,7 +13555,7 @@ fn ignore_invalid_constraint() -> Result<()> {
/// Include a `build_constraints.txt` file with an incompatible constraint. /// Include a `build_constraints.txt` file with an incompatible constraint.
#[test] #[test]
fn incompatible_build_constraint() -> Result<()> { fn incompatible_build_constraint() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("requests==1.2")?; requirements_txt.write_str("requests==1.2")?;
@ -13580,7 +13584,7 @@ fn incompatible_build_constraint() -> Result<()> {
/// Include a `build_constraints.txt` file with a compatible constraint. /// Include a `build_constraints.txt` file with a compatible constraint.
#[test] #[test]
fn compatible_build_constraint() -> Result<()> { fn compatible_build_constraint() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("requests==1.2")?; requirements_txt.write_str("requests==1.2")?;
@ -13590,7 +13594,7 @@ fn compatible_build_constraint() -> Result<()> {
uv_snapshot!(context.pip_compile() uv_snapshot!(context.pip_compile()
.arg("requirements.txt") .arg("requirements.txt")
.arg("--build-constraint") .arg("--build-constraint")
.arg("build_constraints.txt"), @r###" .arg("build_constraints.txt"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -13601,7 +13605,7 @@ fn compatible_build_constraint() -> Result<()> {
----- stderr ----- ----- stderr -----
Resolved 1 package in [TIME] Resolved 1 package in [TIME]
"### "
); );
Ok(()) Ok(())
@ -13610,7 +13614,7 @@ fn compatible_build_constraint() -> Result<()> {
/// Include `build-constraint-dependencies` in pyproject.toml with an incompatible constraint. /// Include `build-constraint-dependencies` in pyproject.toml with an incompatible constraint.
#[test] #[test]
fn incompatible_build_constraint_in_pyproject_toml() -> Result<()> { fn incompatible_build_constraint_in_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str( pyproject_toml.write_str(
r#"[build-system] r#"[build-system]
@ -13648,6 +13652,7 @@ build-constraint-dependencies = [
} }
/// Include `build-constraint-dependencies` in pyproject.toml with a compatible constraint. /// Include `build-constraint-dependencies` in pyproject.toml with a compatible constraint.
#[cfg(feature = "python-eol")]
#[test] #[test]
fn compatible_build_constraint_in_pyproject_toml() -> Result<()> { fn compatible_build_constraint_in_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.8");
@ -13691,7 +13696,7 @@ build-constraint-dependencies = [
/// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with an incompatible constraint. /// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with an incompatible constraint.
#[test] #[test]
fn incompatible_build_constraint_merged_with_pyproject_toml() -> Result<()> { fn incompatible_build_constraint_merged_with_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
// incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt // incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt
let constraints_txt = context.temp_dir.child("build_constraints.txt"); let constraints_txt = context.temp_dir.child("build_constraints.txt");
@ -13776,7 +13781,7 @@ build-constraint-dependencies = [
/// Merge CLI args `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with a compatible constraint. /// Merge CLI args `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with a compatible constraint.
#[test] #[test]
fn compatible_build_constraint_merged_with_pyproject_toml() -> Result<()> { fn compatible_build_constraint_merged_with_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
// incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt // incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt
let constraints_txt = context.temp_dir.child("build_constraints.txt"); let constraints_txt = context.temp_dir.child("build_constraints.txt");
@ -13942,7 +13947,7 @@ fn invalid_extra() -> Result<()> {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn symlink() -> Result<()> { fn symlink() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let requirements_in = context.temp_dir.child("requirements.in"); let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("anyio")?; requirements_in.write_str("anyio")?;
@ -13958,7 +13963,7 @@ fn symlink() -> Result<()> {
uv_snapshot!(context.pip_compile() uv_snapshot!(context.pip_compile()
.arg("requirements.in") .arg("requirements.in")
.arg("--output-file") .arg("--output-file")
.arg("requirements-symlink.txt"), @r###" .arg("requirements-symlink.txt"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -13966,18 +13971,14 @@ fn symlink() -> Result<()> {
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --output-file requirements-symlink.txt # uv pip compile --cache-dir [CACHE_DIR] requirements.in --output-file requirements-symlink.txt
anyio==4.3.0 anyio==4.3.0
# via -r requirements.in # via -r requirements.in
exceptiongroup==1.2.0
# via anyio
idna==3.6 idna==3.6
# via anyio # via anyio
sniffio==1.3.1 sniffio==1.3.1
# via anyio # via anyio
typing-extensions==4.10.0
# via anyio
----- stderr ----- ----- stderr -----
Resolved 5 packages in [TIME] Resolved 3 packages in [TIME]
"### "
); );
// The symlink should still be a symlink. // The symlink should still be a symlink.
@ -14486,6 +14487,7 @@ fn unsupported_requires_python_static_metadata() -> Result<()> {
/// dynamic metadata. /// dynamic metadata.
/// ///
/// See: <https://github.com/astral-sh/uv/issues/8767> /// See: <https://github.com/astral-sh/uv/issues/8767>
#[cfg(feature = "python-eol")]
#[test] #[test]
fn unsupported_requires_python_dynamic_metadata() -> Result<()> { fn unsupported_requires_python_dynamic_metadata() -> Result<()> {
let context = TestContext::new("3.8").with_exclude_newer("2024-11-04T00:00:00Z"); let context = TestContext::new("3.8").with_exclude_newer("2024-11-04T00:00:00Z");
@ -14986,6 +14988,7 @@ fn compile_lowest_extra_unpinned_warning() -> Result<()> {
Ok(()) Ok(())
} }
#[cfg(feature = "python-eol")]
#[test] #[test]
fn disjoint_requires_python() -> Result<()> { fn disjoint_requires_python() -> Result<()> {
let context = TestContext::new("3.8").with_exclude_newer("2025-01-29T00:00:00Z"); let context = TestContext::new("3.8").with_exclude_newer("2025-01-29T00:00:00Z");
@ -15086,6 +15089,7 @@ fn dynamic_version_source_dist() -> Result<()> {
Ok(()) Ok(())
} }
#[cfg(feature = "python-eol")]
#[test] #[test]
fn max_python_requirement() -> Result<()> { fn max_python_requirement() -> Result<()> {
let context = TestContext::new("3.8").with_exclude_newer("2024-12-18T00:00:00Z"); let context = TestContext::new("3.8").with_exclude_newer("2024-12-18T00:00:00Z");
@ -15994,6 +15998,7 @@ fn directory_and_group() -> Result<()> {
} }
/// See: <https://github.com/astral-sh/uv/issues/10957> /// See: <https://github.com/astral-sh/uv/issues/10957>
#[cfg(feature = "python-eol")]
#[test] #[test]
fn compile_preserve_requires_python_split() -> Result<()> { fn compile_preserve_requires_python_split() -> Result<()> {
let context = TestContext::new("3.8").with_exclude_newer("2025-01-01T00:00:00Z"); let context = TestContext::new("3.8").with_exclude_newer("2025-01-01T00:00:00Z");

View File

@ -18,8 +18,8 @@ use wiremock::{
#[cfg(feature = "git")] #[cfg(feature = "git")]
use crate::common::{self, decode_token}; use crate::common::{self, decode_token};
use crate::common::{ use crate::common::{
TestContext, build_vendor_links_url, download_to_disk, get_bin, uv_snapshot, venv_bin_path, DEFAULT_PYTHON_VERSION, TestContext, build_vendor_links_url, download_to_disk, get_bin,
venv_to_interpreter, uv_snapshot, venv_bin_path, venv_to_interpreter,
}; };
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_static::EnvVars; use uv_static::EnvVars;
@ -1949,7 +1949,7 @@ async fn install_deduplicated_indices() {
#[test] #[test]
#[cfg(feature = "git")] #[cfg(feature = "git")]
fn install_git_public_https() { fn install_git_public_https() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
uv_snapshot!( uv_snapshot!(
context context
@ -1974,7 +1974,7 @@ fn install_git_public_https() {
#[test] #[test]
#[cfg(feature = "git")] #[cfg(feature = "git")]
fn install_implicit_git_public_https() { fn install_implicit_git_public_https() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
uv_snapshot!( uv_snapshot!(
context context
@ -1999,7 +1999,7 @@ fn install_implicit_git_public_https() {
#[test] #[test]
#[cfg(feature = "git")] #[cfg(feature = "git")]
fn update_ref_git_public_https() { fn update_ref_git_public_https() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
uv_snapshot!( uv_snapshot!(
context context
@ -2046,7 +2046,7 @@ fn update_ref_git_public_https() {
#[test] #[test]
#[cfg(feature = "git")] #[cfg(feature = "git")]
fn install_git_public_https_missing_branch_or_tag() { fn install_git_public_https_missing_branch_or_tag() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let mut filters = context.filters(); let mut filters = context.filters();
// Windows does not style the command the same as Unix, so we must omit it from the snapshot // Windows does not style the command the same as Unix, so we must omit it from the snapshot
@ -2075,7 +2075,7 @@ fn install_git_public_https_missing_branch_or_tag() {
#[test] #[test]
#[cfg(feature = "git")] #[cfg(feature = "git")]
fn install_git_public_https_missing_commit() { fn install_git_public_https_missing_commit() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let mut filters = context.filters(); let mut filters = context.filters();
// Windows does not style the command the same as Unix, so we must omit it from the snapshot // Windows does not style the command the same as Unix, so we must omit it from the snapshot
@ -2117,7 +2117,7 @@ fn install_git_public_https_missing_commit() {
fn install_git_private_https_pat() { fn install_git_private_https_pat() {
use crate::common::decode_token; use crate::common::decode_token;
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let token = decode_token(common::READ_ONLY_GITHUB_TOKEN); let token = decode_token(common::READ_ONLY_GITHUB_TOKEN);
let package = format!( let package = format!(
"uv-private-pypackage@ git+https://{token}@github.com/astral-test/uv-private-pypackage" "uv-private-pypackage@ git+https://{token}@github.com/astral-test/uv-private-pypackage"
@ -2144,7 +2144,7 @@ fn install_git_private_https_pat() {
#[test] #[test]
#[cfg(all(not(windows), feature = "git"))] #[cfg(all(not(windows), feature = "git"))]
fn install_git_private_https_pat_mixed_with_public() { fn install_git_private_https_pat_mixed_with_public() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let token = decode_token(common::READ_ONLY_GITHUB_TOKEN); let token = decode_token(common::READ_ONLY_GITHUB_TOKEN);
let package = format!( let package = format!(
@ -2172,7 +2172,7 @@ fn install_git_private_https_pat_mixed_with_public() {
#[test] #[test]
#[cfg(all(not(windows), feature = "git"))] #[cfg(all(not(windows), feature = "git"))]
fn install_git_private_https_multiple_pat() { fn install_git_private_https_multiple_pat() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let token_1 = decode_token(common::READ_ONLY_GITHUB_TOKEN); let token_1 = decode_token(common::READ_ONLY_GITHUB_TOKEN);
let token_2 = decode_token(common::READ_ONLY_GITHUB_TOKEN_2); let token_2 = decode_token(common::READ_ONLY_GITHUB_TOKEN_2);
@ -2204,7 +2204,7 @@ fn install_git_private_https_multiple_pat() {
#[test] #[test]
#[cfg(feature = "git")] #[cfg(feature = "git")]
fn install_git_private_https_pat_at_ref() { fn install_git_private_https_pat_at_ref() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let token = decode_token(common::READ_ONLY_GITHUB_TOKEN); let token = decode_token(common::READ_ONLY_GITHUB_TOKEN);
let mut filters = context.filters(); let mut filters = context.filters();
@ -2246,7 +2246,7 @@ fn install_git_private_https_pat_at_ref() {
#[cfg(feature = "git")] #[cfg(feature = "git")]
#[ignore] #[ignore]
fn install_git_private_https_pat_and_username() { fn install_git_private_https_pat_and_username() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let token = decode_token(common::READ_ONLY_GITHUB_TOKEN); let token = decode_token(common::READ_ONLY_GITHUB_TOKEN);
let user = "astral-test-bot"; let user = "astral-test-bot";
@ -2270,7 +2270,7 @@ fn install_git_private_https_pat_and_username() {
#[test] #[test]
#[cfg(all(not(windows), feature = "git"))] #[cfg(all(not(windows), feature = "git"))]
fn install_git_private_https_pat_not_authorized() { fn install_git_private_https_pat_not_authorized() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
// A revoked token // A revoked token
let token = "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"; let token = "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";
@ -2309,7 +2309,7 @@ fn install_git_private_https_pat_not_authorized() {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn install_github_artifact_private_https_pat_mixed_with_public() { fn install_github_artifact_private_https_pat_mixed_with_public() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let token = decode_token(common::READ_ONLY_GITHUB_TOKEN); let token = decode_token(common::READ_ONLY_GITHUB_TOKEN);
let private_package = format!( let private_package = format!(
@ -2339,7 +2339,7 @@ fn install_github_artifact_private_https_pat_mixed_with_public() {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn install_github_artifact_private_https_multiple_pat() { fn install_github_artifact_private_https_multiple_pat() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let token_1 = decode_token(common::READ_ONLY_GITHUB_TOKEN); let token_1 = decode_token(common::READ_ONLY_GITHUB_TOKEN);
let token_2 = decode_token(common::READ_ONLY_GITHUB_TOKEN_2); let token_2 = decode_token(common::READ_ONLY_GITHUB_TOKEN_2);
@ -2373,7 +2373,7 @@ fn install_github_artifact_private_https_multiple_pat() {
#[test] #[test]
#[cfg(feature = "git")] #[cfg(feature = "git")]
fn install_git_private_https_interactive() { fn install_git_private_https_interactive() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let package = "uv-private-pypackage@ git+https://github.com/astral-test/uv-private-pypackage"; let package = "uv-private-pypackage@ git+https://github.com/astral-test/uv-private-pypackage";
@ -6343,7 +6343,7 @@ fn already_installed_multiple_versions() -> Result<()> {
#[test] #[test]
#[cfg(feature = "git")] #[cfg(feature = "git")]
fn already_installed_remote_url() { fn already_installed_remote_url() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
// First, install from the remote URL // First, install from the remote URL
uv_snapshot!(context.filters(), context.pip_install().arg("uv-public-pypackage @ git+https://github.com/astral-test/uv-public-pypackage"), @r###" uv_snapshot!(context.filters(), context.pip_install().arg("uv-public-pypackage @ git+https://github.com/astral-test/uv-public-pypackage"), @r###"
@ -8262,7 +8262,7 @@ fn install_unsupported_environment_yml() -> Result<()> {
/// Include a `build_constraints.txt` file with an incompatible constraint. /// Include a `build_constraints.txt` file with an incompatible constraint.
#[test] #[test]
fn incompatible_build_constraint() -> Result<()> { fn incompatible_build_constraint() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let constraints_txt = context.temp_dir.child("build_constraints.txt"); let constraints_txt = context.temp_dir.child("build_constraints.txt");
constraints_txt.write_str("setuptools==1")?; constraints_txt.write_str("setuptools==1")?;
@ -8289,7 +8289,7 @@ fn incompatible_build_constraint() -> Result<()> {
/// Include a `build_constraints.txt` file with a compatible constraint. /// Include a `build_constraints.txt` file with a compatible constraint.
#[test] #[test]
fn compatible_build_constraint() -> Result<()> { fn compatible_build_constraint() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let constraints_txt = context.temp_dir.child("build_constraints.txt"); let constraints_txt = context.temp_dir.child("build_constraints.txt");
constraints_txt.write_str("setuptools>=40")?; constraints_txt.write_str("setuptools>=40")?;
@ -8297,7 +8297,7 @@ fn compatible_build_constraint() -> Result<()> {
uv_snapshot!(context.pip_install() uv_snapshot!(context.pip_install()
.arg("requests==1.2") .arg("requests==1.2")
.arg("--build-constraint") .arg("--build-constraint")
.arg("build_constraints.txt"), @r###" .arg("build_constraints.txt"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -8307,7 +8307,7 @@ fn compatible_build_constraint() -> Result<()> {
Prepared 1 package in [TIME] Prepared 1 package in [TIME]
Installed 1 package in [TIME] Installed 1 package in [TIME]
+ requests==1.2.0 + requests==1.2.0
"### "
); );
Ok(()) Ok(())
@ -8316,7 +8316,7 @@ fn compatible_build_constraint() -> Result<()> {
/// Include `build-constraint-dependencies` in pyproject.toml with an incompatible constraint. /// Include `build-constraint-dependencies` in pyproject.toml with an incompatible constraint.
#[test] #[test]
fn incompatible_build_constraint_in_pyproject_toml() -> Result<()> { fn incompatible_build_constraint_in_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str( pyproject_toml.write_str(
@ -8345,6 +8345,7 @@ build-constraint-dependencies = [
} }
/// Include a `build_constraints.txt` file with a compatible constraint. /// Include a `build_constraints.txt` file with a compatible constraint.
#[cfg(feature = "python-eol")]
#[test] #[test]
fn compatible_build_constraint_in_pyproject_toml() -> Result<()> { fn compatible_build_constraint_in_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.8");
@ -8359,7 +8360,7 @@ build-constraint-dependencies = [
)?; )?;
uv_snapshot!(context.pip_install() uv_snapshot!(context.pip_install()
.arg("requests==1.2"), @r###" .arg("requests==1.2"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -8369,7 +8370,7 @@ build-constraint-dependencies = [
Prepared 1 package in [TIME] Prepared 1 package in [TIME]
Installed 1 package in [TIME] Installed 1 package in [TIME]
+ requests==1.2.0 + requests==1.2.0
"### "
); );
Ok(()) Ok(())
@ -8378,7 +8379,7 @@ build-constraint-dependencies = [
/// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with an incompatible constraint. /// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with an incompatible constraint.
#[test] #[test]
fn incompatible_build_constraint_merged_with_pyproject_toml() -> Result<()> { fn incompatible_build_constraint_merged_with_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
// Incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt. // Incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt.
let constraints_txt = context.temp_dir.child("build_constraints.txt"); let constraints_txt = context.temp_dir.child("build_constraints.txt");
@ -8442,7 +8443,7 @@ build-constraint-dependencies = [
/// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with a compatible constraint. /// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with a compatible constraint.
#[test] #[test]
fn compatible_build_constraint_merged_with_pyproject_toml() -> Result<()> { fn compatible_build_constraint_merged_with_pyproject_toml() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let constraints_txt = context.temp_dir.child("build_constraints.txt"); let constraints_txt = context.temp_dir.child("build_constraints.txt");
constraints_txt.write_str("setuptools>=40")?; constraints_txt.write_str("setuptools>=40")?;
@ -8458,7 +8459,7 @@ build-constraint-dependencies = [
uv_snapshot!(context.pip_install() uv_snapshot!(context.pip_install()
.arg("requests==1.2") .arg("requests==1.2")
.arg("--build-constraint") .arg("--build-constraint")
.arg("build_constraints.txt"), @r###" .arg("build_constraints.txt"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -8468,7 +8469,7 @@ build-constraint-dependencies = [
Prepared 1 package in [TIME] Prepared 1 package in [TIME]
Installed 1 package in [TIME] Installed 1 package in [TIME]
+ requests==1.2.0 + requests==1.2.0
"### "
); );
Ok(()) Ok(())
} }
@ -8569,7 +8570,7 @@ fn install_build_isolation_package() -> Result<()> {
/// Install a package with an unsupported extension. /// Install a package with an unsupported extension.
#[test] #[test]
fn invalid_extension() { fn invalid_extension() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
uv_snapshot!(context.filters(), context.pip_install() uv_snapshot!(context.filters(), context.pip_install()
.arg("ruff @ https://files.pythonhosted.org/packages/f7/69/96766da2cdb5605e6a31ef2734aff0be17901cefb385b885c2ab88896d76/ruff-0.5.6.tar.baz") .arg("ruff @ https://files.pythonhosted.org/packages/f7/69/96766da2cdb5605e6a31ef2734aff0be17901cefb385b885c2ab88896d76/ruff-0.5.6.tar.baz")
@ -8589,7 +8590,7 @@ fn invalid_extension() {
/// Install a package without unsupported extension. /// Install a package without unsupported extension.
#[test] #[test]
fn no_extension() { fn no_extension() {
let context = TestContext::new("3.8"); let context = TestContext::new(DEFAULT_PYTHON_VERSION);
uv_snapshot!(context.filters(), context.pip_install() uv_snapshot!(context.filters(), context.pip_install()
.arg("ruff @ https://files.pythonhosted.org/packages/f7/69/96766da2cdb5605e6a31ef2734aff0be17901cefb385b885c2ab88896d76/ruff-0.5.6") .arg("ruff @ https://files.pythonhosted.org/packages/f7/69/96766da2cdb5605e6a31ef2734aff0be17901cefb385b885c2ab88896d76/ruff-0.5.6")

View File

@ -779,7 +779,7 @@ fn install_sdist_url() -> Result<()> {
/// Install a package with source archive format `.tar.bz2`. /// Install a package with source archive format `.tar.bz2`.
#[test] #[test]
fn install_sdist_archive_type_bz2() -> Result<()> { fn install_sdist_archive_type_bz2() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str(&format!( requirements_txt.write_str(&format!(
@ -922,6 +922,7 @@ fn install_version_then_install_url() -> Result<()> {
/// Test that we select the last 3.8 compatible numpy version instead of trying to compile an /// Test that we select the last 3.8 compatible numpy version instead of trying to compile an
/// incompatible sdist <https://github.com/astral-sh/uv/issues/388> /// incompatible sdist <https://github.com/astral-sh/uv/issues/388>
#[cfg(feature = "python-eol")]
#[test] #[test]
fn install_numpy_py38() -> Result<()> { fn install_numpy_py38() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.8");
@ -5538,7 +5539,7 @@ fn preserve_markers() -> Result<()> {
/// Include a `build_constraints.txt` file with an incompatible constraint. /// Include a `build_constraints.txt` file with an incompatible constraint.
#[test] #[test]
fn incompatible_build_constraint() -> Result<()> { fn incompatible_build_constraint() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("requests==1.2")?; requirements_txt.write_str("requests==1.2")?;
@ -5568,7 +5569,7 @@ fn incompatible_build_constraint() -> Result<()> {
/// Include a `build_constraints.txt` file with a compatible constraint. /// Include a `build_constraints.txt` file with a compatible constraint.
#[test] #[test]
fn compatible_build_constraint() -> Result<()> { fn compatible_build_constraint() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("requests==1.2")?; requirements_txt.write_str("requests==1.2")?;
@ -5596,7 +5597,7 @@ fn compatible_build_constraint() -> Result<()> {
#[test] #[test]
fn sync_seed() -> Result<()> { fn sync_seed() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("requests==1.2")?; requirements_txt.write_str("requests==1.2")?;
@ -5618,7 +5619,7 @@ fn sync_seed() -> Result<()> {
// Syncing should remove the seed packages. // Syncing should remove the seed packages.
uv_snapshot!(context.filters(), context.pip_sync() uv_snapshot!(context.filters(), context.pip_sync()
.arg("requirements.txt"), @r###" .arg("requirements.txt"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -5630,29 +5631,29 @@ fn sync_seed() -> Result<()> {
Installed 1 package in [TIME] Installed 1 package in [TIME]
- pip==24.0 - pip==24.0
+ requests==1.2.0 + requests==1.2.0
"### "
); );
// Re-create the environment with seed packages. // Re-create the environment with seed packages.
uv_snapshot!(context.filters(), context.venv() uv_snapshot!(context.filters(), context.venv()
.arg("--seed"), @r###" .arg("--seed"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
Using CPython 3.8.[X] interpreter at: [PYTHON-3.8] Using CPython 3.9.[X] interpreter at: [PYTHON-3.9]
Creating virtual environment with seed packages at: .venv Creating virtual environment with seed packages at: .venv
+ pip==24.0 + pip==24.0
+ setuptools==69.2.0 + setuptools==69.2.0
+ wheel==0.43.0 + wheel==0.43.0
Activate with: source .venv/[BIN]/activate Activate with: source .venv/[BIN]/activate
"### "
); );
// Syncing should retain the seed packages. // Syncing should retain the seed packages.
uv_snapshot!(context.filters(), context.pip_sync() uv_snapshot!(context.filters(), context.pip_sync()
.arg("requirements.txt"), @r###" .arg("requirements.txt"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -5661,7 +5662,7 @@ fn sync_seed() -> Result<()> {
Resolved 1 package in [TIME] Resolved 1 package in [TIME]
Installed 1 package in [TIME] Installed 1 package in [TIME]
+ requests==1.2.0 + requests==1.2.0
"### "
); );
Ok(()) Ok(())

View File

@ -750,24 +750,24 @@ fn python_required_python_major_minor() {
.unwrap(); .unwrap();
// Find `python3.11`, which is `>=3.11.4`. // Find `python3.11`, which is `>=3.11.4`.
uv_snapshot!(context.filters(), context.python_find().arg(">=3.11.4, <3.12").env(EnvVars::UV_TEST_PYTHON_PATH, context.temp_dir.child("child").path()), @r###" uv_snapshot!(context.filters(), context.python_find().arg(">=3.11.4, <3.12").env(EnvVars::UV_TEST_PYTHON_PATH, context.temp_dir.child("child").path()), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
[TEMP_DIR]/child/python3.11 [TEMP_DIR]/child/python3.11
----- stderr ----- ----- stderr -----
"###); ");
// Find `python3.11`, which is `>3.11.4`. // Find `python3.11`, which is `>3.11.4`.
uv_snapshot!(context.filters(), context.python_find().arg(">3.11.4, <3.12").env(EnvVars::UV_TEST_PYTHON_PATH, context.temp_dir.child("child").path()), @r###" uv_snapshot!(context.filters(), context.python_find().arg(">3.11.4, <3.12").env(EnvVars::UV_TEST_PYTHON_PATH, context.temp_dir.child("child").path()), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
[TEMP_DIR]/child/python3.11 [TEMP_DIR]/child/python3.11
----- stderr ----- ----- stderr -----
"###); ");
// Fail to find any matching Python interpreter. // Fail to find any matching Python interpreter.
uv_snapshot!(context.filters(), context.python_find().arg(">3.11.255, <3.12").env(EnvVars::UV_TEST_PYTHON_PATH, context.temp_dir.child("child").path()), @r###" uv_snapshot!(context.filters(), context.python_find().arg(">3.11.255, <3.12").env(EnvVars::UV_TEST_PYTHON_PATH, context.temp_dir.child("child").path()), @r###"

View File

@ -15,7 +15,7 @@ use crate::common::{TestContext, uv_snapshot};
#[test] #[test]
fn run_with_python_version() -> Result<()> { fn run_with_python_version() -> Result<()> {
let context = TestContext::new_with_versions(&["3.12", "3.11", "3.8"]); let context = TestContext::new_with_versions(&["3.12", "3.11", "3.9"]);
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#" pyproject_toml.write_str(indoc! { r#"
@ -116,25 +116,25 @@ fn run_with_python_version() -> Result<()> {
+ sniffio==1.3.1 + sniffio==1.3.1
"###); "###);
// This time, we target Python 3.8 instead. // This time, we target Python 3.9 instead.
let mut command = context.run(); let mut command = context.run();
let command_with_args = command let command_with_args = command
.arg("-p") .arg("-p")
.arg("3.8") .arg("3.9")
.arg("python") .arg("python")
.arg("-B") .arg("-B")
.arg("main.py") .arg("main.py")
.env_remove(EnvVars::VIRTUAL_ENV); .env_remove(EnvVars::VIRTUAL_ENV);
uv_snapshot!(context.filters(), command_with_args, @r###" uv_snapshot!(context.filters(), command_with_args, @r"
success: false success: false
exit_code: 2 exit_code: 2
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
Using CPython 3.8.[X] interpreter at: [PYTHON-3.8] Using CPython 3.9.[X] interpreter at: [PYTHON-3.9]
error: The requested interpreter resolved to Python 3.8.[X], which is incompatible with the project's Python requirement: `>=3.11, <4` error: The requested interpreter resolved to Python 3.9.[X], which is incompatible with the project's Python requirement: `>=3.11, <4`
"###); ");
Ok(()) Ok(())
} }
@ -145,7 +145,7 @@ fn run_args() -> Result<()> {
let mut filters = context.filters(); let mut filters = context.filters();
filters.push((r"Usage: (uv|\.exe) run \[OPTIONS\] (?s).*", "[UV RUN HELP]")); filters.push((r"Usage: (uv|\.exe) run \[OPTIONS\] (?s).*", "[UV RUN HELP]"));
filters.push((r"usage: (\[VENV\]|\[PYTHON-3.12\])(?s).*", "[PYTHON HELP]")); filters.push((r"usage: .*(\n|.*)*", "usage: [PYTHON HELP]"));
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#" pyproject_toml.write_str(indoc! { r#"
@ -176,7 +176,7 @@ fn run_args() -> Result<()> {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
[PYTHON HELP] usage: [PYTHON HELP]
"); ");
// Can use `--` to separate uv arguments from the command arguments. // Can use `--` to separate uv arguments from the command arguments.
@ -475,11 +475,11 @@ fn run_pep723_script() -> Result<()> {
#[test] #[test]
fn run_pep723_script_requires_python() -> Result<()> { fn run_pep723_script_requires_python() -> Result<()> {
let context = TestContext::new_with_versions(&["3.8", "3.11"]); let context = TestContext::new_with_versions(&["3.9", "3.11"]);
// If we have a `.python-version` that's incompatible with the script, we should error. // If we have a `.python-version` that's incompatible with the script, we should error.
let python_version = context.temp_dir.child(PYTHON_VERSION_FILENAME); let python_version = context.temp_dir.child(PYTHON_VERSION_FILENAME);
python_version.write_str("3.8")?; python_version.write_str("3.9")?;
// If the script contains a PEP 723 tag, we should install its requirements. // If the script contains a PEP 723 tag, we should install its requirements.
let test_script = context.temp_dir.child("main.py"); let test_script = context.temp_dir.child("main.py");
@ -498,22 +498,22 @@ fn run_pep723_script_requires_python() -> Result<()> {
"# "#
})?; })?;
uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###" uv_snapshot!(context.filters(), context.run().arg("main.py"), @r#"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
warning: The Python request from `.python-version` resolved to Python 3.8.[X], which is incompatible with the script's Python requirement: `>=3.11` warning: The Python request from `.python-version` resolved to Python 3.9.[X], which is incompatible with the script's Python requirement: `>=3.11`
Resolved 1 package in [TIME] Resolved 1 package in [TIME]
Prepared 1 package in [TIME] Prepared 1 package in [TIME]
Installed 1 package in [TIME] Installed 1 package in [TIME]
+ iniconfig==2.0.0 + iniconfig==2.0.0
Traceback (most recent call last): Traceback (most recent call last):
File "main.py", line 10, in <module> File "[TEMP_DIR]/main.py", line 10, in <module>
x: str | int = "hello" x: str | int = "hello"
TypeError: unsupported operand type(s) for |: 'type' and 'type' TypeError: unsupported operand type(s) for |: 'type' and 'type'
"###); "#);
// Delete the `.python-version` file to allow the script to run. // Delete the `.python-version` file to allow the script to run.
fs_err::remove_file(&python_version)?; fs_err::remove_file(&python_version)?;
@ -765,14 +765,14 @@ fn run_pep723_script_overrides() -> Result<()> {
/// Run a PEP 723-compatible script with `tool.uv` build constraints. /// Run a PEP 723-compatible script with `tool.uv` build constraints.
#[test] #[test]
fn run_pep723_script_build_constraints() -> Result<()> { fn run_pep723_script_build_constraints() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let test_script = context.temp_dir.child("main.py"); let test_script = context.temp_dir.child("main.py");
// Incompatible build constraints. // Incompatible build constraints.
test_script.write_str(indoc! { r#" test_script.write_str(indoc! { r#"
# /// script # /// script
# requires-python = ">=3.8" # requires-python = ">=3.9"
# dependencies = [ # dependencies = [
# "anyio>=3", # "anyio>=3",
# "requests==1.2" # "requests==1.2"
@ -801,7 +801,7 @@ fn run_pep723_script_build_constraints() -> Result<()> {
// Compatible build constraints. // Compatible build constraints.
test_script.write_str(indoc! { r#" test_script.write_str(indoc! { r#"
# /// script # /// script
# requires-python = ">=3.8" # requires-python = ">=3.9"
# dependencies = [ # dependencies = [
# "anyio>=3", # "anyio>=3",
# "requests==1.2" # "requests==1.2"
@ -1322,14 +1322,14 @@ fn run_with_pyvenv_cfg_file() -> Result<()> {
#[test] #[test]
fn run_with_build_constraints() -> Result<()> { fn run_with_build_constraints() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#" pyproject_toml.write_str(indoc! { r#"
[project] [project]
name = "foo" name = "foo"
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.9"
dependencies = ["anyio"] dependencies = ["anyio"]
[tool.uv] [tool.uv]
@ -1370,7 +1370,7 @@ fn run_with_build_constraints() -> Result<()> {
[project] [project]
name = "foo" name = "foo"
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.9"
dependencies = ["anyio"] dependencies = ["anyio"]
[tool.uv] [tool.uv]
@ -3107,7 +3107,7 @@ fn run_project_toml_error() -> Result<()> {
#[test] #[test]
fn run_isolated_incompatible_python() -> Result<()> { fn run_isolated_incompatible_python() -> Result<()> {
let context = TestContext::new_with_versions(&["3.8", "3.11"]); let context = TestContext::new_with_versions(&["3.9", "3.11"]);
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#" pyproject_toml.write_str(indoc! { r#"
@ -3124,7 +3124,7 @@ fn run_isolated_incompatible_python() -> Result<()> {
})?; })?;
let python_version = context.temp_dir.child(PYTHON_VERSION_FILENAME); let python_version = context.temp_dir.child(PYTHON_VERSION_FILENAME);
python_version.write_str("3.8")?; python_version.write_str("3.9")?;
let test_script = context.temp_dir.child("main.py"); let test_script = context.temp_dir.child("main.py");
test_script.write_str(indoc! { r#" test_script.write_str(indoc! { r#"
@ -3135,15 +3135,15 @@ fn run_isolated_incompatible_python() -> Result<()> {
"# "#
})?; })?;
// We should reject Python 3.8... // We should reject Python 3.9...
uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###" uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
success: false success: false
exit_code: 2 exit_code: 2
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
Using CPython 3.8.[X] interpreter at: [PYTHON-3.8] Using CPython 3.9.[X] interpreter at: [PYTHON-3.9]
error: The Python request from `.python-version` resolved to Python 3.8.[X], which is incompatible with the project's Python requirement: `>=3.12`. Use `uv python pin` to update the `.python-version` file to a compatible version. error: The Python request from `.python-version` resolved to Python 3.9.[X], which is incompatible with the project's Python requirement: `>=3.12`. Use `uv python pin` to update the `.python-version` file to a compatible version.
"###); "###);
// ...even if `--isolated` is provided. // ...even if `--isolated` is provided.
@ -3153,7 +3153,7 @@ fn run_isolated_incompatible_python() -> Result<()> {
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
error: The Python request from `.python-version` resolved to Python 3.8.[X], which is incompatible with the project's Python requirement: `>=3.12`. Use `uv python pin` to update the `.python-version` file to a compatible version. error: The Python request from `.python-version` resolved to Python 3.9.[X], which is incompatible with the project's Python requirement: `>=3.12`. Use `uv python pin` to update the `.python-version` file to a compatible version.
"###); "###);
Ok(()) Ok(())
@ -3685,7 +3685,7 @@ fn run_linked_environment_path() -> Result<()> {
uv_snapshot!(context.filters(), context.run() uv_snapshot!(context.filters(), context.run()
.env_remove("VIRTUAL_ENV") // Ignore the test context's active virtual environment .env_remove("VIRTUAL_ENV") // Ignore the test context's active virtual environment
.env(EnvVars::UV_PROJECT_ENVIRONMENT, "target") .env(EnvVars::UV_PROJECT_ENVIRONMENT, "target")
.arg("python").arg("-c").arg("import sys; print(sys.prefix); print(sys.executable)"), @r###" .arg("python").arg("-c").arg("import sys; print(sys.prefix); print(sys.executable)"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -3695,7 +3695,7 @@ fn run_linked_environment_path() -> Result<()> {
----- stderr ----- ----- stderr -----
Resolved 8 packages in [TIME] Resolved 8 packages in [TIME]
Audited 6 packages in [TIME] Audited 6 packages in [TIME]
"###); ");
// And, similarly, the entrypoint should use `target` // And, similarly, the entrypoint should use `target`
let black_entrypoint = context.read("target/bin/black"); let black_entrypoint = context.read("target/bin/black");
@ -3703,7 +3703,7 @@ fn run_linked_environment_path() -> Result<()> {
filters => context.filters(), filters => context.filters(),
}, { }, {
assert_snapshot!( assert_snapshot!(
black_entrypoint, @r###" black_entrypoint, @r##"
#![TEMP_DIR]/target/[BIN]/python #![TEMP_DIR]/target/[BIN]/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys
@ -3714,7 +3714,7 @@ fn run_linked_environment_path() -> Result<()> {
elif sys.argv[0].endswith(".exe"): elif sys.argv[0].endswith(".exe"):
sys.argv[0] = sys.argv[0][:-4] sys.argv[0] = sys.argv[0][:-4]
sys.exit(patched_main()) sys.exit(patched_main())
"### "##
); );
}); });
@ -5193,7 +5193,7 @@ fn run_pep723_script_with_constraints() -> Result<()> {
#[test] #[test]
fn run_no_sync_incompatible_python() -> Result<()> { fn run_no_sync_incompatible_python() -> Result<()> {
let context = TestContext::new_with_versions(&["3.12", "3.11", "3.8"]); let context = TestContext::new_with_versions(&["3.12", "3.11", "3.9"]);
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#" pyproject_toml.write_str(indoc! { r#"
@ -5229,14 +5229,14 @@ fn run_no_sync_incompatible_python() -> Result<()> {
+ iniconfig==2.0.0 + iniconfig==2.0.0
"); ");
uv_snapshot!(context.filters(), context.run().arg("--no-sync").arg("--python").arg("3.8").arg("main.py"), @r" uv_snapshot!(context.filters(), context.run().arg("--no-sync").arg("--python").arg("3.9").arg("main.py"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Hello, world! Hello, world!
----- stderr ----- ----- stderr -----
warning: Using incompatible environment (`.venv`) due to `--no-sync` (The project environment's Python version does not satisfy the request: `Python 3.8`) warning: Using incompatible environment (`.venv`) due to `--no-sync` (The project environment's Python version does not satisfy the request: `Python 3.9`)
"); ");
Ok(()) Ok(())

View File

@ -270,7 +270,7 @@ fn package() -> Result<()> {
/// Ensure that we use the maximum Python version when a workspace contains mixed requirements. /// Ensure that we use the maximum Python version when a workspace contains mixed requirements.
#[test] #[test]
fn mixed_requires_python() -> Result<()> { fn mixed_requires_python() -> Result<()> {
let context = TestContext::new_with_versions(&["3.8", "3.12"]); let context = TestContext::new_with_versions(&["3.9", "3.12"]);
// Create a workspace root with a minimum Python requirement of Python 3.12. // Create a workspace root with a minimum Python requirement of Python 3.12.
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
@ -296,7 +296,7 @@ fn mixed_requires_python() -> Result<()> {
let init = src.child("__init__.py"); let init = src.child("__init__.py");
init.touch()?; init.touch()?;
// Create a child with a minimum Python requirement of Python 3.8. // Create a child with a minimum Python requirement of Python 3.9.
let child = context.temp_dir.child("packages").child("bird-feeder"); let child = context.temp_dir.child("packages").child("bird-feeder");
child.create_dir_all()?; child.create_dir_all()?;
@ -312,7 +312,7 @@ fn mixed_requires_python() -> Result<()> {
[project] [project]
name = "bird-feeder" name = "bird-feeder"
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.8" requires-python = ">=3.9"
[build-system] [build-system]
requires = ["setuptools>=42"] requires = ["setuptools>=42"]
@ -339,15 +339,15 @@ fn mixed_requires_python() -> Result<()> {
"###); "###);
// Running `uv sync` again should fail. // Running `uv sync` again should fail.
uv_snapshot!(context.filters(), context.sync().arg("-p").arg("3.8"), @r###" uv_snapshot!(context.filters(), context.sync().arg("-p").arg("3.9"), @r"
success: false success: false
exit_code: 2 exit_code: 2
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
Using CPython 3.8.[X] interpreter at: [PYTHON-3.8] Using CPython 3.9.[X] interpreter at: [PYTHON-3.9]
error: The requested interpreter resolved to Python 3.8.[X], which is incompatible with the project's Python requirement: `>=3.12`. However, a workspace member (`bird-feeder`) supports Python >=3.8. To install the workspace member on its own, navigate to `packages/bird-feeder`, then run `uv venv --python 3.8.[X]` followed by `uv pip install -e .`. error: The requested interpreter resolved to Python 3.9.[X], which is incompatible with the project's Python requirement: `>=3.12`. However, a workspace member (`bird-feeder`) supports Python >=3.9. To install the workspace member on its own, navigate to `packages/bird-feeder`, then run `uv venv --python 3.9.[X]` followed by `uv pip install -e .`.
"###); ");
Ok(()) Ok(())
} }
@ -8401,14 +8401,14 @@ fn sync_locked_script() -> Result<()> {
#[test] #[test]
fn sync_script_with_compatible_build_constraints() -> Result<()> { fn sync_script_with_compatible_build_constraints() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let test_script = context.temp_dir.child("script.py"); let test_script = context.temp_dir.child("script.py");
// Compatible build constraints. // Compatible build constraints.
test_script.write_str(indoc! { r#" test_script.write_str(indoc! { r#"
# /// script # /// script
# requires-python = ">=3.8" # requires-python = ">=3.9"
# dependencies = [ # dependencies = [
# "anyio>=3", # "anyio>=3",
# "requests==1.2" # "requests==1.2"
@ -8454,7 +8454,7 @@ fn sync_script_with_compatible_build_constraints() -> Result<()> {
#[test] #[test]
fn sync_script_with_incompatible_build_constraints() -> Result<()> { fn sync_script_with_incompatible_build_constraints() -> Result<()> {
let context = TestContext::new("3.8"); let context = TestContext::new("3.9");
let test_script = context.temp_dir.child("script.py"); let test_script = context.temp_dir.child("script.py");
let filters = context let filters = context
@ -8469,7 +8469,7 @@ fn sync_script_with_incompatible_build_constraints() -> Result<()> {
// Incompatible build constraints. // Incompatible build constraints.
test_script.write_str(indoc! { r#" test_script.write_str(indoc! { r#"
# /// script # /// script
# requires-python = ">=3.8" # requires-python = ">=3.9"
# dependencies = [ # dependencies = [
# "anyio>=3", # "anyio>=3",
# "requests==1.2" # "requests==1.2"

View File

@ -326,7 +326,7 @@ fn tool_install_with_editable() -> Result<()> {
#[test] #[test]
fn tool_install_with_compatible_build_constraints() -> Result<()> { fn tool_install_with_compatible_build_constraints() -> Result<()> {
let context = TestContext::new("3.8") let context = TestContext::new("3.9")
.with_exclude_newer("2024-05-04T00:00:00Z") .with_exclude_newer("2024-05-04T00:00:00Z")
.with_filtered_counts() .with_filtered_counts()
.with_filtered_exe_suffix(); .with_filtered_exe_suffix();
@ -344,7 +344,7 @@ fn tool_install_with_compatible_build_constraints() -> Result<()> {
.arg("build_constraints.txt") .arg("build_constraints.txt")
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str()) .env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()) .env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
.env(EnvVars::PATH, bin_dir.as_os_str()), @r###" .env(EnvVars::PATH, bin_dir.as_os_str()), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -363,7 +363,7 @@ fn tool_install_with_compatible_build_constraints() -> Result<()> {
+ tomli==2.0.1 + tomli==2.0.1
+ typing-extensions==4.11.0 + typing-extensions==4.11.0
Installed 2 executables: black, blackd Installed 2 executables: black, blackd
"###); ");
tool_dir tool_dir
.child("black") .child("black")
@ -396,7 +396,7 @@ fn tool_install_with_compatible_build_constraints() -> Result<()> {
#[test] #[test]
fn tool_install_with_incompatible_build_constraints() -> Result<()> { fn tool_install_with_incompatible_build_constraints() -> Result<()> {
let context = TestContext::new("3.8") let context = TestContext::new("3.9")
.with_exclude_newer("2024-05-04T00:00:00Z") .with_exclude_newer("2024-05-04T00:00:00Z")
.with_filtered_counts() .with_filtered_counts()
.with_filtered_exe_suffix(); .with_filtered_exe_suffix();
@ -414,7 +414,7 @@ fn tool_install_with_incompatible_build_constraints() -> Result<()> {
.arg("build_constraints.txt") .arg("build_constraints.txt")
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str()) .env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()) .env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
.env(EnvVars::PATH, bin_dir.as_os_str()), @r###" .env(EnvVars::PATH, bin_dir.as_os_str()), @r"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
@ -425,7 +425,7 @@ fn tool_install_with_incompatible_build_constraints() -> Result<()> {
Failed to resolve requirements from `setup.py` build Failed to resolve requirements from `setup.py` build
No solution found when resolving: `setuptools>=40.8.0` No solution found when resolving: `setuptools>=40.8.0`
Because you require setuptools>=40.8.0 and setuptools==2, we can conclude that your requirements are unsatisfiable. Because you require setuptools>=40.8.0 and setuptools==2, we can conclude that your requirements are unsatisfiable.
"###); ");
tool_dir tool_dir
.child("black") .child("black")

View File

@ -2348,7 +2348,7 @@ fn tool_run_with_script_and_from_script() {
#[test] #[test]
fn tool_run_with_compatible_build_constraints() -> Result<()> { fn tool_run_with_compatible_build_constraints() -> Result<()> {
let context = TestContext::new("3.8") let context = TestContext::new("3.9")
.with_exclude_newer("2024-05-04T00:00:00Z") .with_exclude_newer("2024-05-04T00:00:00Z")
.with_filtered_counts() .with_filtered_counts()
.with_filtered_exe_suffix(); .with_filtered_exe_suffix();
@ -2361,7 +2361,7 @@ fn tool_run_with_compatible_build_constraints() -> Result<()> {
.arg("--build-constraints") .arg("--build-constraints")
.arg("build_constraints.txt") .arg("build_constraints.txt")
.arg("pytest") .arg("pytest")
.arg("--version"), @r###" .arg("--version"), @r"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
@ -2378,14 +2378,14 @@ fn tool_run_with_compatible_build_constraints() -> Result<()> {
+ pytest==8.2.0 + pytest==8.2.0
+ requests==1.2.0 + requests==1.2.0
+ tomli==2.0.1 + tomli==2.0.1
"###); ");
Ok(()) Ok(())
} }
#[test] #[test]
fn tool_run_with_incompatible_build_constraints() -> Result<()> { fn tool_run_with_incompatible_build_constraints() -> Result<()> {
let context = TestContext::new("3.8") let context = TestContext::new("3.9")
.with_exclude_newer("2024-05-04T00:00:00Z") .with_exclude_newer("2024-05-04T00:00:00Z")
.with_filtered_counts() .with_filtered_counts()
.with_filtered_exe_suffix(); .with_filtered_exe_suffix();
@ -2404,7 +2404,7 @@ fn tool_run_with_incompatible_build_constraints() -> Result<()> {
.arg("--version") .arg("--version")
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str()) .env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()) .env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
.env(EnvVars::PATH, bin_dir.as_os_str()), @r###" .env(EnvVars::PATH, bin_dir.as_os_str()), @r"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
@ -2415,7 +2415,7 @@ fn tool_run_with_incompatible_build_constraints() -> Result<()> {
Failed to resolve requirements from `setup.py` build Failed to resolve requirements from `setup.py` build
No solution found when resolving: `setuptools>=40.8.0` No solution found when resolving: `setuptools>=40.8.0`
Because you require setuptools>=40.8.0 and setuptools==2, we can conclude that your requirements are unsatisfiable. Because you require setuptools>=40.8.0 and setuptools==2, we can conclude that your requirements are unsatisfiable.
"###); ");
Ok(()) Ok(())
} }

View File

@ -875,27 +875,27 @@ fn non_empty_dir_exists_allow_existing() -> Result<()> {
#[test] #[test]
#[cfg(windows)] #[cfg(windows)]
fn windows_shims() -> Result<()> { fn windows_shims() -> Result<()> {
let context = TestContext::new_with_versions(&["3.9", "3.8"]); let context = TestContext::new_with_versions(&["3.10", "3.9"]);
let shim_path = context.temp_dir.child("shim"); let shim_path = context.temp_dir.child("shim");
let py38 = context let py39 = context
.python_versions .python_versions
.last() .last()
.expect("python_path_with_versions to set up the python versions"); .expect("python_path_with_versions to set up the python versions");
// We want 3.8 and the first version should be 3.9. // We want 3.9 and the first version should be 3.10.
// Picking the last is necessary to prove that shims work because the python version selects // Picking the last is necessary to prove that shims work because the python version selects
// the python version from the first path segment by default, so we take the last to prove it's not // the python version from the first path segment by default, so we take the last to prove it's not
// returning that version. // returning that version.
assert!(py38.0.to_string().contains("3.8")); assert!(py39.0.to_string().contains("3.9"));
// Write the shim script that forwards the arguments to the python3.8 installation. // Write the shim script that forwards the arguments to the python3.9 installation.
fs_err::create_dir(&shim_path)?; fs_err::create_dir(&shim_path)?;
fs_err::write( fs_err::write(
shim_path.child("python.bat"), shim_path.child("python.bat"),
format!( format!(
"@echo off\r\n{}/python.exe %*", "@echo off\r\n{}/python.exe %*",
py38.1.parent().unwrap().display() py39.1.parent().unwrap().display()
), ),
)?; )?;
@ -908,7 +908,7 @@ fn windows_shims() -> Result<()> {
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
Using CPython 3.8.[X] interpreter at: [PYTHON-3.8] Using CPython 3.9.[X] interpreter at: [PYTHON-3.9]
Creating virtual environment at: .venv Creating virtual environment at: .venv
Activate with: source .venv/[BIN]/activate Activate with: source .venv/[BIN]/activate
"### "###