mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-19 03:28:42 +00:00
1435 lines
39 KiB
Rust
1435 lines
39 KiB
Rust
use assert_cmd::assert::OutputAssertExt;
|
|
use assert_fs::prelude::{FileTouch, PathChild};
|
|
use assert_fs::{fixture::FileWriteStr, prelude::PathCreateDir};
|
|
use indoc::indoc;
|
|
|
|
use uv_platform::{Arch, Os};
|
|
use uv_static::EnvVars;
|
|
|
|
use crate::common::{TestContext, uv_snapshot, venv_bin_path};
|
|
|
|
#[test]
|
|
fn python_find() {
|
|
let mut context: TestContext =
|
|
TestContext::new_with_versions(&["3.11", "3.12"]).with_filtered_python_sources();
|
|
|
|
// No interpreters on the path
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::UV_TEST_PYTHON_PATH, ""), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found in [PYTHON SOURCES]
|
|
");
|
|
|
|
// We find the first interpreter on the path
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request Python 3.12
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.12"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Request Python 3.12
|
|
uv_snapshot!(context.filters(), context.python_find().arg("==3.12.*"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Request Python 3.11
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Request CPython
|
|
uv_snapshot!(context.filters(), context.python_find().arg("cpython"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request CPython 3.12
|
|
uv_snapshot!(context.filters(), context.python_find().arg("cpython@3.12"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Request CPython 3.12 via partial key syntax
|
|
uv_snapshot!(context.filters(), context.python_find().arg("cpython-3.12"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Request Python 3.12 via partial key syntax with placeholders
|
|
uv_snapshot!(context.filters(), context.python_find().arg("any-3.12-any"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Request CPython 3.12 for the current platform
|
|
let os = Os::from_env();
|
|
let arch = Arch::from_env();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find()
|
|
.arg(format!("cpython-3.12-{os}-{arch}")), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request PyPy (which should be missing)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("pypy"), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found for PyPy in [PYTHON SOURCES]
|
|
");
|
|
|
|
// Swap the order of the Python versions
|
|
context.python_versions.reverse();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request Python 3.11
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_pin() {
|
|
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"]);
|
|
|
|
// Pin to a version
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Pinned `.python-version` to `3.12`
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// We should find the pinned version, not the first on the path
|
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Unless explicitly requested
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Or `--no-config` is used
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--no-config"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
let child_dir = context.temp_dir.child("child");
|
|
child_dir.create_dir_all().unwrap();
|
|
|
|
// We should also find pinned versions in the parent directory
|
|
uv_snapshot!(context.filters(), context.python_find().current_dir(&child_dir), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.11").current_dir(&child_dir), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Pinned `.python-version` to `3.11`
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Unless the child directory also has a pin
|
|
uv_snapshot!(context.filters(), context.python_find().current_dir(&child_dir), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_pin_arbitrary_name() {
|
|
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"]);
|
|
|
|
// Try to pin to an arbitrary name
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("foo"), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: Requests for arbitrary names (e.g., `foo`) are not supported in version files
|
|
");
|
|
|
|
// Pin to an arbitrary name, bypassing uv
|
|
context
|
|
.temp_dir
|
|
.child(".python-version")
|
|
.write_str("foo")
|
|
.unwrap();
|
|
|
|
// The arbitrary name should be ignored
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
warning: Ignoring unsupported Python request `foo` in version file: [TEMP_DIR]/.python-version
|
|
");
|
|
|
|
// The pin should be updatable
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.11"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Pinned `.python-version` to `3.11`
|
|
|
|
----- stderr -----
|
|
warning: Ignoring unsupported Python request `foo` in version file: [TEMP_DIR]/.python-version
|
|
");
|
|
|
|
// Warnings shouldn't appear afterwards...
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Updated `.python-version` from `3.11` -> `3.12`
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Pin in a sub-directory
|
|
context.temp_dir.child("foo").create_dir_all().unwrap();
|
|
context
|
|
.temp_dir
|
|
.child("foo")
|
|
.child(".python-version")
|
|
.write_str("foo")
|
|
.unwrap();
|
|
|
|
// The arbitrary name should be ignored, but we won't walk up to the parent `.python-version`
|
|
// file (which contains 3.12); this behavior is a little questionable but we probably want to
|
|
// ignore all empty version files if we want to change this?
|
|
uv_snapshot!(context.filters(), context.python_find().current_dir(context.temp_dir.child("foo").path()), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
warning: Ignoring unsupported Python request `foo` in version file: [TEMP_DIR]/foo/.python-version
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_project() {
|
|
let context: TestContext = TestContext::new_with_versions(&["3.10", "3.11", "3.12"]);
|
|
|
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
|
pyproject_toml
|
|
.write_str(indoc! {r#"
|
|
[project]
|
|
name = "project"
|
|
version = "0.1.0"
|
|
requires-python = ">=3.11"
|
|
dependencies = ["anyio==3.7.0"]
|
|
"#})
|
|
.unwrap();
|
|
|
|
// We should respect the project's required version, not the first on the path
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Unless explicitly requested
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.10"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.10]
|
|
|
|
----- stderr -----
|
|
warning: The requested interpreter resolved to Python 3.10.[X], which is incompatible with the project's Python requirement: `>=3.11` (from `project.requires-python`)
|
|
");
|
|
|
|
// Or `--no-project` is used
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--no-project"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.10]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// But a pin should take precedence
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Pinned `.python-version` to `3.12`
|
|
|
|
----- stderr -----
|
|
"###);
|
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Create a pin that's incompatible with the project
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.10").arg("--no-workspace"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Updated `.python-version` from `3.12` -> `3.10`
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// We should warn on subsequent uses, but respect the pinned version?
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.10]
|
|
|
|
----- stderr -----
|
|
warning: The Python request from `.python-version` resolved to Python 3.10.[X], which is incompatible with the project's Python requirement: `>=3.11` (from `project.requires-python`)
|
|
Use `uv python pin` to update the `.python-version` file to a compatible version
|
|
");
|
|
|
|
// Unless the pin file is outside the project, in which case we should just ignore it
|
|
let child_dir = context.temp_dir.child("child");
|
|
child_dir.create_dir_all().unwrap();
|
|
|
|
let pyproject_toml = child_dir.child("pyproject.toml");
|
|
pyproject_toml
|
|
.write_str(indoc! {r#"
|
|
[project]
|
|
name = "project"
|
|
version = "0.1.0"
|
|
requires-python = ">=3.11"
|
|
dependencies = ["anyio==3.7.0"]
|
|
"#})
|
|
.unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().current_dir(&child_dir), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn virtual_empty() {
|
|
// testing how `uv python find` reacts to a pyproject with no `[project]` and nothing useful to it
|
|
let context = TestContext::new_with_versions(&["3.10", "3.11", "3.12"]);
|
|
|
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
|
pyproject_toml
|
|
.write_str(indoc! {r#"
|
|
[tool.mycooltool]
|
|
wow = "someconfig"
|
|
"#})
|
|
.unwrap();
|
|
|
|
// Ask for the python
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.10]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Ask for the python (--no-project)
|
|
uv_snapshot!(context.filters(), context.python_find()
|
|
.arg("--no-project"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.10]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Ask for specific python (3.11)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Create a pin
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Pinned `.python-version` to `3.12`
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Ask for the python
|
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Ask for specific python (3.11)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Ask for the python (--no-project)
|
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn virtual_dependency_group() {
|
|
// testing basic `uv python find` functionality
|
|
// when the pyproject.toml is fully virtual (no `[project]`, but `[dependency-groups]` defined,
|
|
// which really shouldn't matter)
|
|
let context = TestContext::new_with_versions(&["3.10", "3.11", "3.12"]);
|
|
|
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
|
pyproject_toml
|
|
.write_str(indoc! {r#"
|
|
[dependency-groups]
|
|
foo = ["sortedcontainers"]
|
|
bar = ["iniconfig"]
|
|
dev = ["sniffio"]
|
|
"#})
|
|
.unwrap();
|
|
|
|
// Ask for the python
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.10]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Ask for the python (--no-project)
|
|
uv_snapshot!(context.filters(), context.python_find()
|
|
.arg("--no-project"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.10]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Ask for specific python (3.11)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Create a pin
|
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Pinned `.python-version` to `3.12`
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Ask for the python
|
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
|
|
// Ask for specific python (3.11)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Ask for the python (--no-project)
|
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_venv() {
|
|
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"])
|
|
// Enable additional filters for Windows compatibility
|
|
.with_filtered_exe_suffix()
|
|
.with_filtered_python_names()
|
|
.with_filtered_virtualenv_bin();
|
|
|
|
// Create a virtual environment
|
|
uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.12").arg("-q"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// We should find it first
|
|
// TODO(zanieb): On Windows, this has in a different display path for virtual environments which
|
|
// is super annoying and requires some changes to how we represent working directories in the
|
|
// test context to resolve.
|
|
#[cfg(not(windows))]
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[VENV]/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Even if the `VIRTUAL_ENV` is not set (the test context includes this by default)
|
|
#[cfg(not(windows))]
|
|
uv_snapshot!(context.filters(), context.python_find().env_remove(EnvVars::VIRTUAL_ENV), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[VENV]/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
let child_dir = context.temp_dir.child("child");
|
|
child_dir.create_dir_all().unwrap();
|
|
|
|
// Unless the system flag is passed
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--system"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Or, `UV_SYSTEM_PYTHON` is set
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::UV_SYSTEM_PYTHON, "1"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Unless, `--no-system` is included
|
|
// TODO(zanieb): Report this as a bug upstream — this should be allowed.
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--no-system").env(EnvVars::UV_SYSTEM_PYTHON, "1"), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: the argument '--no-system' cannot be used with '--system'
|
|
|
|
Usage: uv python find --cache-dir [CACHE_DIR] [REQUEST]
|
|
|
|
For more information, try '--help'.
|
|
");
|
|
|
|
// We should find virtual environments from a child directory
|
|
#[cfg(not(windows))]
|
|
uv_snapshot!(context.filters(), context.python_find().current_dir(&child_dir).env_remove(EnvVars::VIRTUAL_ENV), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[VENV]/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// A virtual environment in the child directory takes precedence over the parent
|
|
uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.11").arg("-q").current_dir(&child_dir), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
#[cfg(not(windows))]
|
|
uv_snapshot!(context.filters(), context.python_find().current_dir(&child_dir).env_remove(EnvVars::VIRTUAL_ENV), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/child/.venv/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// But if we delete the parent virtual environment
|
|
fs_err::remove_dir_all(context.temp_dir.child(".venv")).unwrap();
|
|
|
|
// And query from there... we should not find the child virtual environment
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Unless, it is requested by path
|
|
#[cfg(not(windows))]
|
|
uv_snapshot!(context.filters(), context.python_find().arg("child/.venv"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/child/.venv/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Or activated via `VIRTUAL_ENV`
|
|
#[cfg(not(windows))]
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::VIRTUAL_ENV, child_dir.join(".venv").as_os_str()), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/child/.venv/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Or at the front of the PATH
|
|
#[cfg(not(windows))]
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::UV_TEST_PYTHON_PATH, child_dir.join(".venv").join("bin").as_os_str()), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/child/.venv/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// This holds even if there are other directories before it in the path, as long as they do
|
|
// not contain a Python executable
|
|
#[cfg(not(windows))]
|
|
{
|
|
let path = std::env::join_paths(&[
|
|
context.temp_dir.to_path_buf(),
|
|
child_dir.join(".venv").join("bin"),
|
|
])
|
|
.unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::UV_TEST_PYTHON_PATH, path.as_os_str()), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/child/.venv/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
// But, if there's an executable _before_ the virtual environment — we prefer that
|
|
#[cfg(not(windows))]
|
|
{
|
|
let path = std::env::join_paths(
|
|
std::env::split_paths(&context.python_path())
|
|
.chain(std::iter::once(child_dir.join(".venv").join("bin"))),
|
|
)
|
|
.unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::UV_TEST_PYTHON_PATH, path.as_os_str()), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn python_find_unsupported_version() {
|
|
let context: TestContext = TestContext::new_with_versions(&["3.12"]);
|
|
|
|
// Request a low version
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.6"), @r###"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: Invalid version request: Python <3.7 is not supported but 3.6 was requested.
|
|
"###);
|
|
|
|
// Request a low version with a patch
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.6.9"), @r###"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: Invalid version request: Python <3.7 is not supported but 3.6.9 was requested.
|
|
"###);
|
|
|
|
// Request a really low version
|
|
uv_snapshot!(context.filters(), context.python_find().arg("2.6"), @r###"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: Invalid version request: Python <3.7 is not supported but 2.6 was requested.
|
|
"###);
|
|
|
|
// Request a really low version with a patch
|
|
uv_snapshot!(context.filters(), context.python_find().arg("2.6.8"), @r###"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: Invalid version request: Python <3.7 is not supported but 2.6.8 was requested.
|
|
"###);
|
|
|
|
// Request a future version
|
|
uv_snapshot!(context.filters(), context.python_find().arg("4.2"), @r###"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found for Python 4.2 in virtual environments, managed installations, or search path
|
|
"###);
|
|
|
|
// Request a low version with a range
|
|
uv_snapshot!(context.filters(), context.python_find().arg("<3.0"), @r###"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found for Python <3.0 in virtual environments, managed installations, or search path
|
|
"###);
|
|
|
|
// Request free-threaded Python on unsupported version
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.12t"), @r###"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: Invalid version request: Python <3.13 does not support free-threading but 3.12t was requested.
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_venv_invalid() {
|
|
let context: TestContext = TestContext::new("3.12")
|
|
.with_filtered_python_names()
|
|
.with_filtered_virtualenv_bin()
|
|
.with_filtered_exe_suffix();
|
|
|
|
// We find the virtual environment
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::VIRTUAL_ENV, context.venv.as_os_str()), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[VENV]/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// If the binaries are missing from a virtual environment, we fail
|
|
fs_err::remove_dir_all(venv_bin_path(&context.venv)).unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::VIRTUAL_ENV, context.venv.as_os_str()), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: Failed to inspect Python interpreter from active virtual environment at `.venv/[BIN]/[PYTHON]`
|
|
Caused by: Python interpreter not found at `[VENV]/[BIN]/[PYTHON]`
|
|
");
|
|
|
|
// Unless the virtual environment is not active
|
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// If there's not a `pyvenv.cfg` file, it's also non-fatal, we ignore the environment
|
|
fs_err::remove_file(context.venv.join("pyvenv.cfg")).unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::VIRTUAL_ENV, context.venv.as_os_str()), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_managed() {
|
|
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"])
|
|
.with_filtered_python_sources()
|
|
.with_versions_as_managed(&["3.12"]);
|
|
|
|
// We find the managed interpreter
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--managed-python"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request an interpreter that cannot be satisfied
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--managed-python").arg("3.11"), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found for Python 3.11 in virtual environments or managed installations
|
|
");
|
|
|
|
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"])
|
|
.with_filtered_python_sources()
|
|
.with_versions_as_managed(&["3.11"]);
|
|
|
|
// We find the unmanaged interpreter with managed Python disabled
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--no-managed-python"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request an interpreter that cannot be satisfied
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--no-managed-python").arg("3.11"), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found for Python 3.11 in [PYTHON SOURCES]
|
|
");
|
|
|
|
// We find the unmanaged interpreter with system Python preferred
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--python-preference").arg("system"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.12]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// But, if no system Python meets the request, we'll use the managed interpreter
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--python-preference").arg("system").arg("3.11"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[PYTHON-3.11]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
/// See: <https://github.com/astral-sh/uv/issues/11825>
|
|
///
|
|
/// This test will not succeed on macOS if using a Homebrew provided interpreter. The interpreter
|
|
/// reports `sys.executable` as the canonicalized path instead of `[TEMP_DIR]/...`. For this reason,
|
|
/// it's marked as requiring our `python-managed` feature — but it does not enforce that these are
|
|
/// used in the test context.
|
|
#[test]
|
|
#[cfg(unix)]
|
|
#[cfg(feature = "python-managed")]
|
|
fn python_required_python_major_minor() {
|
|
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"]);
|
|
|
|
// Find the Python 3.11 executable.
|
|
let path = &context.python_versions.first().unwrap().1;
|
|
|
|
// Symlink it to `python3.11`.
|
|
fs_err::create_dir_all(context.temp_dir.child("child")).unwrap();
|
|
fs_err::os::unix::fs::symlink(path, context.temp_dir.child("child").join("python3.11"))
|
|
.unwrap();
|
|
|
|
// 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"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/child/python3.11
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// 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"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/child/python3.11
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// 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###"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found for Python >3.11.[X], <3.12 in virtual environments, managed installations, or search path
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_script() {
|
|
let context = TestContext::new("3.13")
|
|
.with_filtered_virtualenv_bin()
|
|
.with_filtered_python_names()
|
|
.with_filtered_exe_suffix();
|
|
|
|
uv_snapshot!(context.filters(), context.init().arg("--script").arg("foo.py"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
Initialized script at `foo.py`
|
|
");
|
|
|
|
uv_snapshot!(context.filters(), context.sync().arg("--script").arg("foo.py"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
Creating script environment at: [CACHE_DIR]/environments-v2/foo-[HASH]
|
|
Resolved in [TIME]
|
|
Audited in [TIME]
|
|
");
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--script").arg("foo.py"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[CACHE_DIR]/environments-v2/foo-[HASH]/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_script_no_environment() {
|
|
let context = TestContext::new("3.13")
|
|
.with_filtered_virtualenv_bin()
|
|
.with_filtered_python_names()
|
|
.with_filtered_exe_suffix();
|
|
|
|
let script = context.temp_dir.child("foo.py");
|
|
|
|
script
|
|
.write_str(indoc! {r"
|
|
# /// script
|
|
# dependencies = []
|
|
# ///
|
|
"})
|
|
.unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--script").arg("foo.py"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[VENV]/[BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_script_python_not_found() {
|
|
let context = TestContext::new_with_versions(&[]).with_filtered_python_sources();
|
|
|
|
let script = context.temp_dir.child("foo.py");
|
|
|
|
script
|
|
.write_str(indoc! {r"
|
|
# /// script
|
|
# dependencies = []
|
|
# ///
|
|
"})
|
|
.unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--script").arg("foo.py"), @r"
|
|
success: false
|
|
exit_code: 1
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
No interpreter found in [PYTHON SOURCES]
|
|
|
|
hint: A managed Python download is available, but Python downloads are set to 'never'
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_script_no_such_version() {
|
|
let context = TestContext::new("3.13")
|
|
.with_filtered_virtualenv_bin()
|
|
.with_filtered_python_names()
|
|
.with_filtered_exe_suffix()
|
|
.with_filtered_python_sources();
|
|
let script = context.temp_dir.child("foo.py");
|
|
script
|
|
.write_str(indoc! {r#"
|
|
# /// script
|
|
# requires-python = ">=3.13"
|
|
# dependencies = []
|
|
# ///
|
|
"#})
|
|
.unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.sync().arg("--script").arg("foo.py"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
Creating script environment at: [CACHE_DIR]/environments-v2/foo-[HASH]
|
|
Resolved in [TIME]
|
|
Audited in [TIME]
|
|
");
|
|
|
|
script
|
|
.write_str(indoc! {r#"
|
|
# /// script
|
|
# requires-python = ">=3.15"
|
|
# dependencies = []
|
|
# ///
|
|
"#})
|
|
.unwrap();
|
|
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--script").arg("foo.py"), @r"
|
|
success: false
|
|
exit_code: 1
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
No interpreter found for Python >=3.15 in [PYTHON SOURCES]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_show_version() {
|
|
let context: TestContext =
|
|
TestContext::new_with_versions(&["3.11", "3.12"]).with_filtered_python_sources();
|
|
|
|
// No interpreters found
|
|
uv_snapshot!(context.filters(), context.python_find().env(EnvVars::UV_TEST_PYTHON_PATH, "").arg("--show-version"), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found in [PYTHON SOURCES]
|
|
");
|
|
|
|
// Show the first version found
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--show-version"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
3.11.[X]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request Python 3.12
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--show-version").arg("3.12"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
3.12.[X]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request Python 3.11
|
|
uv_snapshot!(context.filters(), context.python_find().arg("--show-version").arg("3.11"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
3.11.[X]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_path() {
|
|
let context: TestContext = TestContext::new_with_versions(&[]).with_filtered_not_executable();
|
|
|
|
context.temp_dir.child("foo").create_dir_all().unwrap();
|
|
context.temp_dir.child("bar").touch().unwrap();
|
|
|
|
// No interpreter in a directory
|
|
uv_snapshot!(context.filters(), context.python_find().arg(context.temp_dir.child("foo").as_os_str()), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found in directory `foo`
|
|
");
|
|
|
|
// No interpreter at a file
|
|
uv_snapshot!(context.filters(), context.python_find().arg(context.temp_dir.child("bar").as_os_str()), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: Failed to inspect Python interpreter from provided path at `bar`
|
|
Caused by: Failed to query Python interpreter at `[TEMP_DIR]/bar`
|
|
Caused by: [PERMISSION DENIED]
|
|
");
|
|
|
|
// No interpreter at a file that does not exist
|
|
uv_snapshot!(context.filters(), context.python_find().arg(context.temp_dir.child("foobar").as_os_str()), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found at path `foobar`
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_freethreaded_313() {
|
|
let context: TestContext = TestContext::new_with_versions(&[])
|
|
.with_filtered_python_keys()
|
|
.with_filtered_python_sources()
|
|
.with_managed_python_dirs()
|
|
.with_python_download_cache()
|
|
.with_filtered_python_install_bin()
|
|
.with_filtered_python_names()
|
|
.with_filtered_exe_suffix();
|
|
|
|
context
|
|
.python_install()
|
|
.arg("--preview")
|
|
.arg("3.13t")
|
|
.assert()
|
|
.success();
|
|
|
|
// Request Python 3.13 (without opt-in)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.13"), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found for Python 3.13 in [PYTHON SOURCES]
|
|
");
|
|
|
|
// Request Python 3.13t (with explicit opt-in)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.13t"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.13+freethreaded-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_freethreaded_314() {
|
|
let context: TestContext = TestContext::new_with_versions(&[])
|
|
.with_filtered_python_keys()
|
|
.with_filtered_python_sources()
|
|
.with_managed_python_dirs()
|
|
.with_python_download_cache()
|
|
.with_filtered_python_install_bin()
|
|
.with_filtered_python_names()
|
|
.with_filtered_exe_suffix();
|
|
|
|
context
|
|
.python_install()
|
|
.arg("--preview")
|
|
.arg("3.14t")
|
|
.assert()
|
|
.success();
|
|
|
|
// Request Python 3.14 (without opt-in)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.14"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14+freethreaded-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Request Python 3.14t (with explicit opt-in)
|
|
uv_snapshot!(context.filters(), context.python_find().arg("3.14t"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14+freethreaded-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn python_find_prerelease_version_specifiers() {
|
|
let context: TestContext = TestContext::new_with_versions(&[])
|
|
.with_filtered_python_keys()
|
|
.with_filtered_python_sources()
|
|
.with_managed_python_dirs()
|
|
.with_python_download_cache()
|
|
.with_filtered_python_install_bin()
|
|
.with_filtered_python_names()
|
|
.with_filtered_exe_suffix();
|
|
|
|
context.python_install().arg("3.14.0rc2").assert().success();
|
|
context.python_install().arg("3.14.0rc3").assert().success();
|
|
|
|
// `>=3.14` should allow pre-release versions
|
|
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14.0rc3-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// `>3.14rc2` should not match rc2
|
|
uv_snapshot!(context.filters(), context.python_find().arg(">3.14.0rc2"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14.0rc3-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// `>3.14rc3` should not match rc3
|
|
uv_snapshot!(context.filters(), context.python_find().arg(">3.14.0rc3"), @r"
|
|
success: false
|
|
exit_code: 2
|
|
----- stdout -----
|
|
|
|
----- stderr -----
|
|
error: No interpreter found for Python >3.14.0rc3 in [PYTHON SOURCES]
|
|
");
|
|
|
|
// `>=3.14.0rc3` should match rc3
|
|
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14.0rc3"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14.0rc3-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// `<3.14.0rc3` should match rc2
|
|
uv_snapshot!(context.filters(), context.python_find().arg("<3.14.0rc3"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14.0rc2-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// `<=3.14.0rc3` should match rc3
|
|
uv_snapshot!(context.filters(), context.python_find().arg("<=3.14.0rc3"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14.0rc3-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// Install the stable version
|
|
context.python_install().arg("3.14.0").assert().success();
|
|
|
|
// `>=3.14` should prefer stable
|
|
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14.0-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
|
|
// `>3.14rc2` should prefer stable
|
|
uv_snapshot!(context.filters(), context.python_find().arg(">3.14.0rc2"), @r"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
[TEMP_DIR]/managed/cpython-3.14.0-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
|
|
|
----- stderr -----
|
|
");
|
|
}
|