mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00

See https://github.com/astral-sh/uv/issues/2617
Note this also includes:
- #2918
- #2931 (pending)
A first step towards Python toolchain management in Rust.
First, we add a new crate to manage Python download metadata:
- Adds a new `uv-toolchain` crate
- Adds Rust structs for Python version download metadata
- Duplicates the script which downloads Python version metadata
- Adds a script to generate Rust code from the JSON metadata
- Adds a utility to download and extract the Python version
I explored some alternatives like a build script using things like
`serde` and `uneval` to automatically construct the code from our
structs but deemed it to heavy. Unlike Rye, I don't generate the Rust
directly from the web requests and have an intermediate JSON layer to
speed up iteration on the Rust types.
Next, we add add a `uv-dev` command `fetch-python` to download Python
versions per the bootstrapping script.
- Downloads a requested version or reads from `.python-versions`
- Extracts to `UV_BOOTSTRAP_DIR`
- Links executables for path extension
This command is not really intended to be user facing, but it's a good
PoC for the `uv-toolchain` API. Hash checking (via the sha256) isn't
implemented yet, we can do that in a follow-up.
Finally, we remove the `scripts/bootstrap` directory, update CI to use
the new command, and update the CONTRIBUTING docs.
<img width="1023" alt="Screenshot 2024-04-08 at 17 12 15"
src="57bd3cf1
-7477-4bb8-a8e9-802a00d772cb">
114 lines
3.3 KiB
Text
114 lines
3.3 KiB
Text
//! DO NOT EDIT
|
|
//!
|
|
//! Generated with `{{generated_with}}`
|
|
//! Scenarios from <{{generated_from}}>
|
|
//!
|
|
#![cfg(all(feature = "python", feature = "pypi", unix))]
|
|
|
|
use std::env;
|
|
use std::process::Command;
|
|
|
|
use anyhow::Result;
|
|
use assert_cmd::assert::OutputAssertExt;
|
|
use assert_fs::fixture::{FileWriteStr, PathChild};
|
|
use predicates::prelude::predicate;
|
|
|
|
use common::{python_path_with_versions, get_bin, uv_snapshot, TestContext};
|
|
|
|
mod common;
|
|
|
|
/// Provision python binaries and return a `pip compile` command with options shared across all scenarios.
|
|
fn command(context: &TestContext, python_versions: &[&str]) -> Command {
|
|
let python_path = python_path_with_versions(&context.temp_dir, python_versions)
|
|
.expect("Failed to create Python test path");
|
|
let mut command = Command::new(get_bin());
|
|
command
|
|
.arg("pip")
|
|
.arg("compile")
|
|
.arg("requirements.in")
|
|
.arg("--index-url")
|
|
.arg("{{index_url}}")
|
|
.arg("--find-links")
|
|
.arg("{{vendor_links}}")
|
|
.arg("--cache-dir")
|
|
.arg(context.cache_dir.path())
|
|
.env("VIRTUAL_ENV", context.venv.as_os_str())
|
|
.env("UV_NO_WRAP", "1")
|
|
.env("UV_TEST_PYTHON_PATH", python_path)
|
|
.current_dir(&context.temp_dir);
|
|
|
|
if cfg!(all(windows, debug_assertions)) {
|
|
// TODO(konstin): Reduce stack usage in debug mode enough that the tests pass with the
|
|
// default windows stack of 1MB
|
|
command.env("UV_STACK_SIZE", (8 * 1024 * 1024).to_string());
|
|
}
|
|
|
|
command
|
|
}
|
|
|
|
{{#scenarios}}
|
|
|
|
{{#description_lines}}
|
|
/// {{.}}
|
|
{{/description_lines}}
|
|
///
|
|
/// ```text
|
|
/// {{name}}
|
|
{{#tree}}
|
|
/// {{.}}
|
|
{{/tree}}
|
|
/// ```
|
|
{{#python_patch}}
|
|
#[cfg(feature = "python-patch")]
|
|
{{/python_patch}}
|
|
#[test]
|
|
fn {{module_name}}() -> Result<()> {
|
|
let context = TestContext::new("{{environment.python}}");
|
|
let python_versions = &[{{#environment.additional_python}}"{{.}}", {{/environment.additional_python}}];
|
|
|
|
// In addition to the standard filters, swap out package names for shorter messages
|
|
let mut filters = context.filters();
|
|
filters.push((r"{{name}}-", "package-"));
|
|
|
|
let requirements_in = context.temp_dir.child("requirements.in");
|
|
{{#root.requires}}
|
|
requirements_in.write_str("{{requirement}}")?;
|
|
{{/root.requires}}
|
|
|
|
{{#expected.explanation_lines}}
|
|
// {{.}}
|
|
{{/expected.explanation_lines}}
|
|
let output = uv_snapshot!(filters, command(&context, python_versions)
|
|
{{#resolver_options.prereleases}}
|
|
.arg("--prerelease=allow")
|
|
{{/resolver_options.prereleases}}
|
|
{{#resolver_options.no_build}}
|
|
.arg("--only-binary")
|
|
.arg("{{.}}")
|
|
{{/resolver_options.no_build}}
|
|
{{#resolver_options.no_binary}}
|
|
.arg("--no-binary")
|
|
.arg("{{.}}")
|
|
{{/resolver_options.no_binary}}
|
|
{{#resolver_options.python}}
|
|
.arg("--python-version={{.}}")
|
|
{{/resolver_options.python}}, @r###"<snapshot>
|
|
"###
|
|
);
|
|
|
|
output
|
|
.assert()
|
|
{{#expected.satisfiable}}
|
|
.success()
|
|
{{#expected.packages}}
|
|
.stdout(predicate::str::contains("{{name}}=={{version}}"))
|
|
{{/expected.packages}}
|
|
{{/expected.satisfiable}}
|
|
{{^expected.satisfiable}}
|
|
.failure()
|
|
{{/expected.satisfiable}}
|
|
;
|
|
|
|
Ok(())
|
|
}
|
|
{{/scenarios}}
|