Make Python and PyPy install mirrors configurable in uv.toml (#8695)

<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
-->

## Summary

Adds python-install-mirror and pypy-install-mirror as keys for uv.toml,
and cli args for `uv python install`.

Could leave the cli args out if we think the env vars and configs are
sufficient.

Fixes #8186 

<!-- What's the purpose of the change? What does it do, and why? -->

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
Owen Brooks 2024-11-14 03:08:55 +11:00 committed by GitHub
parent 2966471db2
commit 2ea81b3b55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 573 additions and 27 deletions

View file

@ -3938,6 +3938,22 @@ pub struct PythonInstallArgs {
/// See `uv help python` to view supported request formats.
pub targets: Vec<String>,
/// Set the URL to use as the source for downloading Python installations.
///
/// The provided URL will replace `https://github.com/indygreg/python-build-standalone/releases/download` in, e.g., `https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz`.
///
/// Distributions can be read from a local directory by using the `file://` URL scheme.
#[arg(long, env = EnvVars::UV_PYTHON_INSTALL_MIRROR)]
pub mirror: Option<String>,
/// Set the URL to use as the source for downloading PyPy installations.
///
/// The provided URL will replace `https://downloads.python.org/pypy` in, e.g., `https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2`.
///
/// Distributions can be read from a local directory by using the `file://` URL scheme.
#[arg(long, env = EnvVars::UV_PYPY_INSTALL_MIRROR)]
pub pypy_mirror: Option<String>,
/// Reinstall the requested Python version, if it's already installed.
///
/// By default, uv will exit successfully if the version is already

View file

@ -466,9 +466,11 @@ impl ManagedPythonDownload {
installation_dir: &Path,
cache_dir: &Path,
reinstall: bool,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
reporter: Option<&dyn Reporter>,
) -> Result<DownloadResult, Error> {
let url = self.download_url()?;
let url = self.download_url(python_install_mirror, pypy_install_mirror)?;
let path = installation_dir.join(self.key().to_string());
// If it is not a reinstall and the dir already exists, return it.
@ -585,10 +587,14 @@ impl ManagedPythonDownload {
/// Return the [`Url`] to use when downloading the distribution. If a mirror is set via the
/// appropriate environment variable, use it instead.
fn download_url(&self) -> Result<Url, Error> {
fn download_url(
&self,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
) -> Result<Url, Error> {
match self.key.implementation {
LenientImplementationName::Known(ImplementationName::CPython) => {
if let Ok(mirror) = std::env::var(EnvVars::UV_PYTHON_INSTALL_MIRROR) {
if let Some(mirror) = python_install_mirror {
let Some(suffix) = self.url.strip_prefix(
"https://github.com/indygreg/python-build-standalone/releases/download/",
) else {
@ -601,7 +607,7 @@ impl ManagedPythonDownload {
}
LenientImplementationName::Known(ImplementationName::PyPy) => {
if let Ok(mirror) = std::env::var(EnvVars::UV_PYPY_INSTALL_MIRROR) {
if let Some(mirror) = pypy_install_mirror {
let Some(suffix) = self.url.strip_prefix("https://downloads.python.org/pypy/")
else {
return Err(Error::Mirror(EnvVars::UV_PYPY_INSTALL_MIRROR, self.url));

View file

@ -86,6 +86,8 @@ impl PythonInstallation {
client_builder: &BaseClientBuilder<'a>,
cache: &Cache,
reporter: Option<&dyn Reporter>,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
) -> Result<Self, Error> {
let request = request.unwrap_or_else(|| &PythonRequest::Default);
@ -100,7 +102,16 @@ impl PythonInstallation {
{
if let Some(request) = PythonDownloadRequest::from_request(request) {
debug!("Requested Python not found, checking for available download...");
match Self::fetch(request.fill()?, client_builder, cache, reporter).await {
match Self::fetch(
request.fill()?,
client_builder,
cache,
reporter,
python_install_mirror,
pypy_install_mirror,
)
.await
{
Ok(installation) => Ok(installation),
Err(Error::Download(downloads::Error::NoDownloadFound(_))) => {
Err(Error::MissingPython(err))
@ -121,6 +132,8 @@ impl PythonInstallation {
client_builder: &BaseClientBuilder<'a>,
cache: &Cache,
reporter: Option<&dyn Reporter>,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
) -> Result<Self, Error> {
let installations = ManagedPythonInstallations::from_settings()?.init()?;
let installations_dir = installations.root();
@ -132,7 +145,15 @@ impl PythonInstallation {
info!("Fetching requested Python...");
let result = download
.fetch(&client, installations_dir, &cache_dir, false, reporter)
.fetch(
&client,
installations_dir,
&cache_dir,
false,
python_install_mirror,
pypy_install_mirror,
reporter,
)
.await?;
let path = match result {

View file

@ -14,6 +14,7 @@ use uv_pep508::Requirement;
use uv_pypi_types::{SupportedEnvironments, VerbatimParsedUrl};
use uv_python::{PythonDownloads, PythonPreference, PythonVersion};
use uv_resolver::{AnnotationStyle, ExcludeNewer, PrereleaseMode, ResolutionMode};
use uv_static::EnvVars;
/// A `pyproject.toml` with an (optional) `[tool.uv]` section.
#[allow(dead_code)]
@ -41,6 +42,9 @@ pub struct Options {
#[serde(flatten)]
pub top_level: ResolverInstallerOptions,
#[serde(flatten)]
pub install_mirrors: PythonInstallMirrors,
#[serde(flatten)]
pub publish: PublishOptions,
@ -676,6 +680,61 @@ pub struct ResolverInstallerOptions {
pub no_binary_package: Option<Vec<PackageName>>,
}
/// Shared settings, relevant to all operations that might create managed python installations.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, CombineOptions, OptionsMetadata)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PythonInstallMirrors {
/// Mirror URL for downloading managed Python installations.
///
/// By default, managed Python installations are downloaded from [`python-build-standalone`](https://github.com/indygreg/python-build-standalone).
/// This variable can be set to a mirror URL to use a different source for Python installations.
/// The provided URL will replace `https://github.com/indygreg/python-build-standalone/releases/download` in, e.g., `https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz`.
///
/// Distributions can be read from a local directory by using the `file://` URL scheme.
#[option(
default = "None",
value_type = "str",
example = r#"
python-install-mirror = "https://github.com/indygreg/python-build-standalone/releases/download"
"#
)]
pub python_install_mirror: Option<String>,
/// Mirror URL to use for downloading managed PyPy installations.
///
/// By default, managed PyPy installations are downloaded from [downloads.python.org](https://downloads.python.org/).
/// This variable can be set to a mirror URL to use a different source for PyPy installations.
/// The provided URL will replace `https://downloads.python.org/pypy` in, e.g., `https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2`.
///
/// Distributions can be read from a
/// local directory by using the `file://` URL scheme.
#[option(
default = "None",
value_type = "str",
example = r#"
pypy-install-mirror = "https://downloads.python.org/pypy"
"#
)]
pub pypy_install_mirror: Option<String>,
}
impl Default for PythonInstallMirrors {
fn default() -> Self {
PythonInstallMirrors::resolve(None, None)
}
}
impl PythonInstallMirrors {
pub fn resolve(python_mirror: Option<String>, pypy_mirror: Option<String>) -> Self {
let python_mirror_env = std::env::var(EnvVars::UV_PYTHON_INSTALL_MIRROR).ok();
let pypy_mirror_env = std::env::var(EnvVars::UV_PYPY_INSTALL_MIRROR).ok();
PythonInstallMirrors {
python_install_mirror: python_mirror_env.or(python_mirror),
pypy_install_mirror: pypy_mirror_env.or(pypy_mirror),
}
}
}
/// Settings that are specific to the `uv pip` command-line interface.
///
/// These values will be ignored when running commands outside the `uv pip` namespace (e.g.,
@ -1544,6 +1603,11 @@ pub struct OptionsWire {
no_binary: Option<bool>,
no_binary_package: Option<Vec<PackageName>>,
// #[serde(flatten)]
// install_mirror: PythonInstallMirrors,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
// #[serde(flatten)]
// publish: PublishOptions
publish_url: Option<Url>,
@ -1581,6 +1645,8 @@ impl From<OptionsWire> for Options {
preview,
python_preference,
python_downloads,
python_install_mirror,
pypy_install_mirror,
concurrent_downloads,
concurrent_builds,
concurrent_installs,
@ -1673,6 +1739,10 @@ impl From<OptionsWire> for Options {
override_dependencies,
constraint_dependencies,
environments,
install_mirrors: PythonInstallMirrors::resolve(
python_install_mirror,
pypy_install_mirror,
),
conflicting_groups,
publish: PublishOptions {
publish_url,

View file

@ -28,6 +28,7 @@ use uv_python::{
};
use uv_requirements::RequirementsSource;
use uv_resolver::{ExcludeNewer, FlatIndex, RequiresPython};
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildContext, BuildIsolation, HashStrategy};
use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceError};
@ -52,6 +53,7 @@ pub(crate) async fn build_frontend(
build_constraints: Vec<RequirementsSource>,
hash_checking: Option<HashCheckingMode>,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
no_config: bool,
python_preference: PythonPreference,
@ -75,6 +77,7 @@ pub(crate) async fn build_frontend(
&build_constraints,
hash_checking,
python.as_deref(),
install_mirrors,
settings.as_ref(),
no_config,
python_preference,
@ -116,6 +119,7 @@ async fn build_impl(
build_constraints: &[RequirementsSource],
hash_checking: Option<HashCheckingMode>,
python_request: Option<&str>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettingsRef<'_>,
no_config: bool,
python_preference: PythonPreference,
@ -251,6 +255,7 @@ async fn build_impl(
source.clone(),
output_dir,
python_request,
install_mirrors.clone(),
no_config,
workspace.as_ref(),
python_preference,
@ -346,6 +351,7 @@ async fn build_package(
source: AnnotatedSource<'_>,
output_dir: Option<&Path>,
python_request: Option<&str>,
install_mirrors: PythonInstallMirrors,
no_config: bool,
workspace: Result<&Workspace, &WorkspaceError>,
python_preference: PythonPreference,
@ -424,6 +430,8 @@ async fn build_package(
client_builder,
cache,
Some(&PythonDownloadReporter::single(printer)),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();

View file

@ -33,6 +33,7 @@ use uv_python::{
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
use uv_resolver::{FlatIndex, InstallTarget};
use uv_scripts::{Pep723Item, Pep723Script};
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user_once;
use uv_workspace::pyproject::{DependencyType, Source, SourceError};
@ -71,6 +72,7 @@ pub(crate) async fn add(
extras: Vec<ExtraName>,
package: Option<PackageName>,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverInstallerSettings,
script: Option<PathBuf>,
python_preference: PythonPreference,
@ -140,6 +142,7 @@ pub(crate) async fn add(
} else {
let requires_python = init_script_python_requirement(
python.as_deref(),
install_mirrors.clone(),
project_dir,
false,
python_preference,
@ -173,6 +176,8 @@ pub(crate) async fn add(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();
@ -227,6 +232,7 @@ pub(crate) async fn add(
connectivity,
native_tls,
allow_insecure_host,
install_mirrors.clone(),
no_config,
cache,
printer,
@ -240,6 +246,7 @@ pub(crate) async fn add(
let venv = project::get_or_init_environment(
project.workspace(),
python.as_deref().map(PythonRequest::parse),
install_mirrors.clone(),
python_preference,
python_downloads,
connectivity,

View file

@ -4,6 +4,7 @@ use anyhow::{Context, Result};
use itertools::Itertools;
use owo_colors::OwoColorize;
use std::path::{Path, PathBuf};
use uv_settings::PythonInstallMirrors;
use uv_cache::Cache;
use uv_client::Connectivity;
@ -42,6 +43,7 @@ pub(crate) async fn export(
frozen: bool,
include_header: bool,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
@ -107,6 +109,7 @@ pub(crate) async fn export(
connectivity,
native_tls,
allow_insecure_host,
install_mirrors,
no_config,
cache,
printer,

View file

@ -23,6 +23,7 @@ use uv_python::{
};
use uv_resolver::RequiresPython;
use uv_scripts::{Pep723Script, ScriptTag};
use uv_settings::PythonInstallMirrors;
use uv_warnings::warn_user_once;
use uv_workspace::pyproject_mut::{DependencyTarget, PyProjectTomlMut};
use uv_workspace::{DiscoveryOptions, MemberDiscovery, Workspace, WorkspaceError};
@ -46,6 +47,7 @@ pub(crate) async fn init(
author_from: Option<AuthorFrom>,
no_pin_python: bool,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
no_workspace: bool,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
@ -65,6 +67,7 @@ pub(crate) async fn init(
init_script(
path,
python,
install_mirrors,
connectivity,
python_preference,
python_downloads,
@ -128,6 +131,7 @@ pub(crate) async fn init(
author_from,
no_pin_python,
python,
install_mirrors,
no_workspace,
python_preference,
python_downloads,
@ -175,6 +179,7 @@ pub(crate) async fn init(
async fn init_script(
script_path: &Path,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
connectivity: Connectivity,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
@ -233,6 +238,7 @@ async fn init_script(
let requires_python = init_script_python_requirement(
python.as_deref(),
install_mirrors,
&CWD,
no_pin_python,
python_preference,
@ -266,6 +272,7 @@ async fn init_project(
author_from: Option<AuthorFrom>,
no_pin_python: bool,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
no_workspace: bool,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
@ -416,6 +423,8 @@ async fn init_project(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();
@ -438,6 +447,8 @@ async fn init_project(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();
@ -498,6 +509,8 @@ async fn init_project(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();
@ -520,6 +533,8 @@ async fn init_project(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();

View file

@ -30,6 +30,7 @@ use uv_resolver::{
FlatIndex, InMemoryIndex, Lock, LockVersion, Options, OptionsBuilder, PythonRequirement,
RequiresPython, ResolverEnvironment, ResolverManifest, SatisfiesResult, VERSION,
};
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy};
use uv_warnings::{warn_user, warn_user_once};
use uv_workspace::{DiscoveryOptions, Workspace};
@ -76,6 +77,7 @@ pub(crate) async fn lock(
frozen: bool,
dry_run: bool,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
@ -105,6 +107,7 @@ pub(crate) async fn lock(
connectivity,
native_tls,
allow_insecure_host,
install_mirrors,
no_config,
cache,
printer,

View file

@ -35,6 +35,7 @@ use uv_resolver::{
ResolverEnvironment,
};
use uv_scripts::Pep723Item;
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
use uv_warnings::{warn_user, warn_user_once};
use uv_workspace::dependency_groups::DependencyGroupError;
@ -544,6 +545,7 @@ impl ProjectInterpreter {
connectivity: Connectivity,
native_tls: bool,
allow_insecure_host: &[TrustedHost],
install_mirrors: PythonInstallMirrors,
no_config: bool,
cache: &Cache,
printer: Printer,
@ -638,6 +640,8 @@ impl ProjectInterpreter {
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?;
@ -682,6 +686,7 @@ impl ProjectInterpreter {
pub(crate) async fn get_or_init_environment(
workspace: &Workspace,
python: Option<PythonRequest>,
install_mirrors: PythonInstallMirrors,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
connectivity: Connectivity,
@ -700,6 +705,7 @@ pub(crate) async fn get_or_init_environment(
connectivity,
native_tls,
allow_insecure_host,
install_mirrors,
no_config,
cache,
printer,
@ -1498,6 +1504,7 @@ pub(crate) async fn update_environment(
/// Determine the [`RequiresPython`] requirement for a new PEP 723 script.
pub(crate) async fn init_script_python_requirement(
python: Option<&str>,
install_mirrors: PythonInstallMirrors,
directory: &Path,
no_pin_python: bool,
python_preference: PythonPreference,
@ -1534,6 +1541,8 @@ pub(crate) async fn init_script_python_requirement(
client_builder,
cache,
Some(reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();

View file

@ -1,6 +1,7 @@
use anyhow::{Context, Result};
use std::fmt::Write;
use std::path::Path;
use uv_settings::PythonInstallMirrors;
use owo_colors::OwoColorize;
use uv_cache::Cache;
@ -39,6 +40,7 @@ pub(crate) async fn remove(
dependency_type: DependencyType,
package: Option<PackageName>,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverInstallerSettings,
script: Option<Pep723Script>,
python_preference: PythonPreference,
@ -189,6 +191,7 @@ pub(crate) async fn remove(
let venv = project::get_or_init_environment(
project.workspace(),
python.as_deref().map(PythonRequest::parse),
install_mirrors,
python_preference,
python_downloads,
connectivity,

View file

@ -32,6 +32,7 @@ use uv_python::{
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_resolver::{InstallTarget, Lock};
use uv_scripts::Pep723Item;
use uv_settings::PythonInstallMirrors;
use uv_static::EnvVars;
use uv_warnings::warn_user;
use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace, WorkspaceError};
@ -71,6 +72,7 @@ pub(crate) async fn run(
dev: DevGroupsSpecification,
editable: EditableMode,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverInstallerSettings,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
@ -201,6 +203,8 @@ pub(crate) async fn run(
&client_builder,
cache,
Some(&download_reporter),
install_mirrors.python_install_mirror.clone(),
install_mirrors.pypy_install_mirror.clone(),
)
.await?
.into_interpreter();
@ -509,6 +513,8 @@ pub(crate) async fn run(
&client_builder,
cache,
Some(&download_reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();
@ -539,6 +545,7 @@ pub(crate) async fn run(
project::get_or_init_environment(
project.workspace(),
python.as_deref().map(PythonRequest::parse),
install_mirrors,
python_preference,
python_downloads,
connectivity,
@ -712,6 +719,8 @@ pub(crate) async fn run(
&client_builder,
cache,
Some(&download_reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?;

View file

@ -22,6 +22,7 @@ use uv_pypi_types::{
};
use uv_python::{PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest};
use uv_resolver::{FlatIndex, InstallTarget};
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user;
use uv_workspace::pyproject::{DependencyGroupSpecifier, Source, Sources, ToolUvSources};
@ -52,6 +53,7 @@ pub(crate) async fn sync(
install_options: InstallOptions,
modifications: Modifications,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
settings: ResolverInstallerSettings,
@ -114,6 +116,7 @@ pub(crate) async fn sync(
let venv = project::get_or_init_environment(
project.workspace(),
python.as_deref().map(PythonRequest::parse),
install_mirrors,
python_preference,
python_downloads,
connectivity,

View file

@ -15,6 +15,7 @@ use uv_pep440::Version;
use uv_pep508::PackageName;
use uv_python::{PythonDownloads, PythonPreference, PythonRequest, PythonVersion};
use uv_resolver::{PackageMap, TreeDisplay};
use uv_settings::PythonInstallMirrors;
use uv_workspace::{DiscoveryOptions, Workspace};
use crate::commands::pip::latest::LatestClient;
@ -45,6 +46,7 @@ pub(crate) async fn tree(
python_version: Option<PythonVersion>,
python_platform: Option<TargetTriple>,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
@ -82,6 +84,7 @@ pub(crate) async fn tree(
connectivity,
native_tls,
allow_insecure_host,
install_mirrors,
no_config,
cache,
printer,

View file

@ -113,6 +113,8 @@ pub(crate) async fn install(
targets: Vec<String>,
reinstall: bool,
force: bool,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
python_downloads: PythonDownloads,
native_tls: bool,
connectivity: Connectivity,
@ -234,6 +236,8 @@ pub(crate) async fn install(
installations_dir,
&cache_dir,
reinstall,
python_install_mirror.clone(),
pypy_install_mirror.clone(),
Some(&reporter),
)
.await,

View file

@ -17,7 +17,7 @@ use uv_python::{
EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest,
};
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_settings::{ResolverInstallerOptions, ToolOptions};
use uv_settings::{PythonInstallMirrors, ResolverInstallerOptions, ToolOptions};
use uv_tool::InstalledTools;
use uv_warnings::warn_user;
@ -43,6 +43,7 @@ pub(crate) async fn install(
from: Option<String>,
with: &[RequirementsSource],
python: Option<String>,
install_mirrors: PythonInstallMirrors,
force: bool,
options: ResolverInstallerOptions,
settings: ResolverInstallerSettings,
@ -74,6 +75,8 @@ pub(crate) async fn install(
&client_builder,
&cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();

View file

@ -26,6 +26,7 @@ use uv_python::{
PythonPreference, PythonRequest,
};
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_settings::PythonInstallMirrors;
use uv_static::EnvVars;
use uv_tool::{entrypoint_paths, InstalledTools};
use uv_warnings::warn_user;
@ -68,6 +69,7 @@ pub(crate) async fn run(
with: &[RequirementsSource],
show_resolution: bool,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverInstallerSettings,
invocation_source: ToolRunCommand,
isolated: bool,
@ -111,6 +113,7 @@ pub(crate) async fn run(
with,
show_resolution,
python.as_deref(),
install_mirrors,
&settings,
isolated,
python_preference,
@ -426,6 +429,7 @@ async fn get_or_create_environment(
with: &[RequirementsSource],
show_resolution: bool,
python: Option<&str>,
install_mirrors: PythonInstallMirrors,
settings: &ResolverInstallerSettings,
isolated: bool,
python_preference: PythonPreference,
@ -455,6 +459,8 @@ async fn get_or_create_environment(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();

View file

@ -14,7 +14,7 @@ use uv_python::{
PythonRequest,
};
use uv_requirements::RequirementsSpecification;
use uv_settings::{Combine, ResolverInstallerOptions, ToolOptions};
use uv_settings::{Combine, PythonInstallMirrors, ResolverInstallerOptions, ToolOptions};
use uv_tool::InstalledTools;
use crate::commands::pip::loggers::{
@ -33,6 +33,7 @@ use crate::settings::ResolverInstallerSettings;
pub(crate) async fn upgrade(
name: Vec<PackageName>,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
connectivity: Connectivity,
args: ResolverInstallerOptions,
filesystem: ResolverInstallerOptions,
@ -84,6 +85,8 @@ pub(crate) async fn upgrade(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter(),

View file

@ -24,6 +24,7 @@ use uv_python::{
EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest,
};
use uv_resolver::{ExcludeNewer, FlatIndex};
use uv_settings::PythonInstallMirrors;
use uv_shell::Shell;
use uv_types::{BuildContext, BuildIsolation, HashStrategy};
use uv_warnings::{warn_user, warn_user_once};
@ -42,6 +43,7 @@ pub(crate) async fn venv(
project_dir: &Path,
path: Option<PathBuf>,
python_request: Option<&str>,
install_mirrors: PythonInstallMirrors,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
link_mode: LinkMode,
@ -68,6 +70,7 @@ pub(crate) async fn venv(
project_dir,
path,
python_request,
install_mirrors,
link_mode,
index_locations,
index_strategy,
@ -125,6 +128,7 @@ async fn venv_impl(
project_dir: &Path,
path: Option<PathBuf>,
python_request: Option<&str>,
install_mirrors: PythonInstallMirrors,
link_mode: LinkMode,
index_locations: &IndexLocations,
index_strategy: IndexStrategy,
@ -205,6 +209,8 @@ async fn venv_impl(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await
.into_diagnostic()?;

View file

@ -734,6 +734,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
build_constraints,
args.hash_checking,
args.python,
args.install_mirrors,
args.settings,
cli.top_level.no_config,
globals.python_preference,
@ -778,6 +779,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&project_dir,
args.path,
args.settings.python.as_deref(),
args.settings.install_mirrors,
globals.python_preference,
globals.python_downloads,
args.settings.link_mode,
@ -906,6 +908,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&requirements,
args.show_resolution || globals.verbose > 0,
args.python,
args.install_mirrors,
args.settings,
invocation_source,
args.isolated,
@ -956,6 +959,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.from,
&requirements,
args.python,
args.install_mirrors,
args.force,
args.options,
args.settings,
@ -1001,6 +1005,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
Box::pin(commands::tool_upgrade(
args.name,
args.python,
args.install_mirrors,
globals.connectivity,
args.args,
args.filesystem,
@ -1072,6 +1077,8 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.targets,
args.reinstall,
args.force,
args.python_install_mirror,
args.pypy_install_mirror,
globals.python_downloads,
globals.native_tls,
globals.connectivity,
@ -1274,6 +1281,7 @@ async fn run_project(
args.author_from,
args.no_pin_python,
args.python,
args.install_mirrors,
args.no_workspace,
globals.python_preference,
globals.python_downloads,
@ -1332,6 +1340,7 @@ async fn run_project(
args.dev,
args.editable,
args.python,
args.install_mirrors,
args.settings,
globals.python_preference,
globals.python_downloads,
@ -1370,6 +1379,7 @@ async fn run_project(
args.install_options,
args.modifications,
args.python,
args.install_mirrors,
globals.python_preference,
globals.python_downloads,
args.settings,
@ -1400,6 +1410,7 @@ async fn run_project(
args.frozen,
args.dry_run,
args.python,
args.install_mirrors,
args.settings,
globals.python_preference,
globals.python_downloads,
@ -1452,6 +1463,7 @@ async fn run_project(
args.extras,
args.package,
args.python,
args.install_mirrors,
args.settings,
args.script,
globals.python_preference,
@ -1494,6 +1506,7 @@ async fn run_project(
args.dependency_type,
args.package,
args.python,
args.install_mirrors,
args.settings,
script,
globals.python_preference,
@ -1531,6 +1544,7 @@ async fn run_project(
args.python_version,
args.python_platform,
args.python,
args.install_mirrors,
args.resolver,
globals.python_preference,
globals.python_downloads,
@ -1567,6 +1581,7 @@ async fn run_project(
args.frozen,
args.include_header,
args.python,
args.install_mirrors,
args.settings,
globals.python_preference,
globals.python_downloads,

View file

@ -33,8 +33,8 @@ use uv_pypi_types::{Requirement, SupportedEnvironments};
use uv_python::{Prefix, PythonDownloads, PythonPreference, PythonVersion, Target};
use uv_resolver::{AnnotationStyle, DependencyMode, ExcludeNewer, PrereleaseMode, ResolutionMode};
use uv_settings::{
Combine, FilesystemOptions, Options, PipOptions, PublishOptions, ResolverInstallerOptions,
ResolverOptions,
Combine, FilesystemOptions, Options, PipOptions, PublishOptions, PythonInstallMirrors,
ResolverInstallerOptions, ResolverOptions,
};
use uv_static::EnvVars;
use uv_warnings::warn_user_once;
@ -191,12 +191,13 @@ pub(crate) struct InitSettings {
pub(crate) no_pin_python: bool,
pub(crate) no_workspace: bool,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
}
impl InitSettings {
/// Resolve the [`InitSettings`] from the CLI and filesystem configuration.
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn resolve(args: InitArgs, _filesystem: Option<FilesystemOptions>) -> Self {
pub(crate) fn resolve(args: InitArgs, filesystem: Option<FilesystemOptions>) -> Self {
let InitArgs {
path,
name,
@ -226,6 +227,10 @@ impl InitSettings {
let package = flag(package || build_backend.is_some(), no_package || r#virtual)
.unwrap_or(kind.packaged_by_default());
let install_mirrors = filesystem
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
Self {
path,
name,
@ -238,6 +243,7 @@ impl InitSettings {
no_pin_python,
no_workspace,
python: python.and_then(Maybe::into_option),
install_mirrors,
}
}
}
@ -261,6 +267,7 @@ pub(crate) struct RunSettings {
pub(crate) no_project: bool,
pub(crate) no_sync: bool,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings,
pub(crate) env_file: Vec<PathBuf>,
@ -304,6 +311,11 @@ impl RunSettings {
no_env_file,
} = args;
let install_mirrors = filesystem
.clone()
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
Self {
locked,
frozen,
@ -341,6 +353,7 @@ impl RunSettings {
),
env_file,
no_env_file,
install_mirrors,
}
}
}
@ -357,6 +370,7 @@ pub(crate) struct ToolRunSettings {
pub(crate) isolated: bool,
pub(crate) show_resolution: bool,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings,
}
@ -402,6 +416,11 @@ impl ToolRunSettings {
}
}
let install_mirrors = filesystem
.clone()
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
Self {
command,
from,
@ -425,6 +444,7 @@ impl ToolRunSettings {
resolver_installer_options(installer, build),
filesystem,
),
install_mirrors,
}
}
}
@ -444,6 +464,7 @@ pub(crate) struct ToolInstallSettings {
pub(crate) settings: ResolverInstallerSettings,
pub(crate) force: bool,
pub(crate) editable: bool,
pub(crate) install_mirrors: PythonInstallMirrors,
}
impl ToolInstallSettings {
@ -466,11 +487,17 @@ impl ToolInstallSettings {
let options = resolver_installer_options(installer, build).combine(
filesystem
.clone()
.map(FilesystemOptions::into_options)
.map(|options| options.top_level)
.unwrap_or_default(),
);
let install_mirrors = filesystem
.map(FilesystemOptions::into_options)
.map(|options| options.install_mirrors)
.unwrap_or_default();
let settings = ResolverInstallerSettings::from(options.clone());
Self {
@ -494,6 +521,7 @@ impl ToolInstallSettings {
refresh: Refresh::from(refresh),
options,
settings,
install_mirrors,
}
}
}
@ -504,10 +532,10 @@ impl ToolInstallSettings {
pub(crate) struct ToolUpgradeSettings {
pub(crate) name: Vec<PackageName>,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) args: ResolverInstallerOptions,
pub(crate) filesystem: ResolverInstallerOptions,
}
impl ToolUpgradeSettings {
/// Resolve the [`ToolUpgradeSettings`] from the CLI and filesystem configuration.
#[allow(clippy::needless_pass_by_value)]
@ -529,8 +557,12 @@ impl ToolUpgradeSettings {
}
let args = resolver_installer_options(installer, build);
let filesystem = filesystem
.map(FilesystemOptions::into_options)
let filesystem = filesystem.map(FilesystemOptions::into_options);
let install_mirrors = filesystem
.clone()
.map(|options| options.install_mirrors)
.unwrap_or_default();
let top_level = filesystem
.map(|options| options.top_level)
.unwrap_or_default();
@ -538,7 +570,8 @@ impl ToolUpgradeSettings {
name: if all { vec![] } else { name },
python: python.and_then(Maybe::into_option),
args,
filesystem,
filesystem: top_level,
install_mirrors,
}
}
}
@ -669,22 +702,39 @@ pub(crate) struct PythonInstallSettings {
pub(crate) targets: Vec<String>,
pub(crate) reinstall: bool,
pub(crate) force: bool,
pub(crate) python_install_mirror: Option<String>,
pub(crate) pypy_install_mirror: Option<String>,
}
impl PythonInstallSettings {
/// Resolve the [`PythonInstallSettings`] from the CLI and filesystem configuration.
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn resolve(args: PythonInstallArgs, _filesystem: Option<FilesystemOptions>) -> Self {
pub(crate) fn resolve(args: PythonInstallArgs, filesystem: Option<FilesystemOptions>) -> Self {
let options = filesystem.map(uv_settings::FilesystemOptions::into_options);
let (python_mirror, pypy_mirror) = match options {
Some(options) => (
options.install_mirrors.python_install_mirror,
options.install_mirrors.pypy_install_mirror,
),
None => (None, None),
};
let python_mirror = args.mirror.or(python_mirror);
let pypy_mirror = args.pypy_mirror.or(pypy_mirror);
let PythonInstallArgs {
targets,
reinstall,
force,
mirror: _,
pypy_mirror: _,
} = args;
Self {
targets,
reinstall,
force,
python_install_mirror: python_mirror,
pypy_install_mirror: pypy_mirror,
}
}
}
@ -780,6 +830,7 @@ pub(crate) struct SyncSettings {
pub(crate) all_packages: bool,
pub(crate) package: Option<PackageName>,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings,
}
@ -813,6 +864,10 @@ impl SyncSettings {
package,
python,
} = args;
let install_mirrors = filesystem
.clone()
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
let settings = ResolverInstallerSettings::combine(
resolver_installer_options(installer, build),
@ -845,6 +900,7 @@ impl SyncSettings {
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings,
install_mirrors,
}
}
}
@ -857,6 +913,7 @@ pub(crate) struct LockSettings {
pub(crate) frozen: bool,
pub(crate) dry_run: bool,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverSettings,
}
@ -875,6 +932,11 @@ impl LockSettings {
python,
} = args;
let install_mirrors = filesystem
.clone()
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
Self {
locked,
frozen,
@ -882,6 +944,7 @@ impl LockSettings {
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
install_mirrors,
}
}
}
@ -905,6 +968,7 @@ pub(crate) struct AddSettings {
pub(crate) package: Option<PackageName>,
pub(crate) script: Option<PathBuf>,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) indexes: Vec<Index>,
pub(crate) settings: ResolverInstallerSettings,
@ -993,6 +1057,11 @@ impl AddSettings {
}
}
let install_mirrors = filesystem
.clone()
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
Self {
locked,
frozen,
@ -1015,6 +1084,7 @@ impl AddSettings {
resolver_installer_options(installer, build),
filesystem,
),
install_mirrors,
}
}
}
@ -1031,6 +1101,7 @@ pub(crate) struct RemoveSettings {
pub(crate) package: Option<PackageName>,
pub(crate) script: Option<PathBuf>,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings,
}
@ -1065,6 +1136,11 @@ impl RemoveSettings {
DependencyType::Production
};
let install_mirrors = filesystem
.clone()
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
Self {
locked,
frozen,
@ -1079,6 +1155,7 @@ impl RemoveSettings {
resolver_installer_options(installer, build),
filesystem,
),
install_mirrors,
}
}
}
@ -1100,6 +1177,7 @@ pub(crate) struct TreeSettings {
pub(crate) python_version: Option<PythonVersion>,
pub(crate) python_platform: Option<TargetTriple>,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) resolver: ResolverSettings,
}
@ -1123,6 +1201,10 @@ impl TreeSettings {
python_platform,
python,
} = args;
let install_mirrors = filesystem
.clone()
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
Self {
dev: DevGroupsSpecification::from_args(
@ -1141,6 +1223,7 @@ impl TreeSettings {
python_platform,
python: python.and_then(Maybe::into_option),
resolver: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
install_mirrors,
}
}
}
@ -1162,6 +1245,7 @@ pub(crate) struct ExportSettings {
pub(crate) frozen: bool,
pub(crate) include_header: bool,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverSettings,
}
@ -1199,6 +1283,10 @@ impl ExportSettings {
refresh,
python,
} = args;
let install_mirrors = filesystem
.clone()
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
Self {
format,
@ -1225,6 +1313,7 @@ impl ExportSettings {
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
install_mirrors,
}
}
}
@ -1868,6 +1957,7 @@ pub(crate) struct BuildSettings {
pub(crate) build_constraint: Vec<PathBuf>,
pub(crate) hash_checking: Option<HashCheckingMode>,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverSettings,
}
@ -1895,6 +1985,11 @@ impl BuildSettings {
resolver,
} = args;
let install_mirrors = match &filesystem {
Some(fs) => fs.install_mirrors.clone(),
None => PythonInstallMirrors::default(),
};
Self {
src,
package,
@ -1914,6 +2009,7 @@ impl BuildSettings {
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
install_mirrors,
}
}
}
@ -2269,6 +2365,7 @@ impl From<ResolverInstallerOptions> for ResolverInstallerSettings {
pub(crate) struct PipSettings {
pub(crate) index_locations: IndexLocations,
pub(crate) python: Option<String>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) system: bool,
pub(crate) extras: ExtrasSpecification,
pub(crate) break_system_packages: bool,
@ -2315,7 +2412,12 @@ pub(crate) struct PipSettings {
impl PipSettings {
/// Resolve the [`PipSettings`] from the CLI and filesystem configuration.
pub(crate) fn combine(args: PipOptions, filesystem: Option<FilesystemOptions>) -> Self {
let Options { top_level, pip, .. } = filesystem
let Options {
top_level,
pip,
install_mirrors,
..
} = filesystem
.map(FilesystemOptions::into_options)
.unwrap_or_default();
@ -2592,6 +2694,7 @@ impl PipSettings {
top_level_no_build_package.unwrap_or_default(),
)),
),
install_mirrors,
}
}
}

View file

@ -450,7 +450,7 @@ fn help_subcommand() {
fn help_subsubcommand() {
let context = TestContext::new_with_versions(&[]);
uv_snapshot!(context.filters(), context.help().arg("python").arg("install"), @r###"
uv_snapshot!(context.filters(), context.help().arg("python").arg("install"), @r##"
success: true
exit_code: 0
----- stdout -----
@ -483,6 +483,27 @@ fn help_subsubcommand() {
See `uv help python` to view supported request formats.
Options:
--mirror <MIRROR>
Set the URL to use as the source for downloading Python installations.
The provided URL will replace
`https://github.com/indygreg/python-build-standalone/releases/download` in, e.g.,
`https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz`.
Distributions can be read from a local directory by using the `file://` URL scheme.
[env: UV_PYTHON_INSTALL_MIRROR=]
--pypy-mirror <PYPY_MIRROR>
Set the URL to use as the source for downloading PyPy installations.
The provided URL will replace `https://downloads.python.org/pypy` in, e.g.,
`https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2`.
Distributions can be read from a local directory by using the `file://` URL scheme.
[env: UV_PYPY_INSTALL_MIRROR=]
-r, --reinstall
Reinstall the requested Python version, if it's already installed.
@ -638,7 +659,7 @@ fn help_subsubcommand() {
----- stderr -----
"###);
"##);
}
#[test]
@ -724,6 +745,10 @@ fn help_flag_subsubcommand() {
[TARGETS]... The Python version(s) to install
Options:
--mirror <MIRROR> Set the URL to use as the source for downloading Python
installations [env: UV_PYTHON_INSTALL_MIRROR=]
--pypy-mirror <PYPY_MIRROR> Set the URL to use as the source for downloading PyPy
installations [env: UV_PYPY_INSTALL_MIRROR=]
-r, --reinstall Reinstall the requested Python version, if it's already installed
-f, --force Replace existing Python executables during installation

View file

@ -4286,7 +4286,7 @@ fn lock_requires_python() -> Result<()> {
"#,
)?;
uv_snapshot!(context.filters(), context.lock(), @r###"
uv_snapshot!(context.filters(), context.lock(), @r"
success: false
exit_code: 1
----- stdout -----
@ -4309,7 +4309,7 @@ fn lock_requires_python() -> Result<()> {
hint: Pre-releases are available for pygls in the requested range (e.g., 2.0.0a2), but pre-releases weren't enabled (try: `--prerelease=allow`)
hint: The `requires-python` value (>=3.7) includes Python versions that are not supported by your dependencies (e.g., pygls>=1.1.0,<=1.2.1 only supports >=3.7.9, <4). Consider using a more restrictive `requires-python` value (like >=3.7.9, <4).
"###);
");
// Require >=3.7, and allow locking to a version of `pygls` that is compatible (==1.0.1).
pyproject_toml.write_str(

View file

@ -191,7 +191,7 @@ fn invalid_pyproject_toml_option_unknown_field() -> Result<()> {
|
2 | unknown = "field"
| ^^^^^^^
unknown field `unknown`, expected one of `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `dependency-metadata`, `config-settings`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `publish-url`, `trusted-publishing`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `environments`, `conflicting-groups`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dev-dependencies`
unknown field `unknown`, expected one of `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `dependency-metadata`, `config-settings`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `python-install-mirror`, `pypy-install-mirror`, `publish-url`, `trusted-publishing`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `environments`, `conflicting-groups`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dev-dependencies`
Resolved in [TIME]
Audited in [TIME]

View file

@ -123,6 +123,10 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -270,6 +274,10 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -418,6 +426,10 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -598,6 +610,10 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -718,6 +734,10 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -876,6 +896,10 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -1076,6 +1100,10 @@ fn resolve_index_url() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -1283,6 +1311,10 @@ fn resolve_index_url() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -1455,6 +1487,10 @@ fn resolve_find_links() -> anyhow::Result<()> {
no_index: true,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -1597,6 +1633,10 @@ fn resolve_top_level() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -1789,6 +1829,10 @@ fn resolve_top_level() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -1964,6 +2008,10 @@ fn resolve_top_level() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -2106,6 +2154,10 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -2231,6 +2283,10 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -2356,6 +2412,10 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -2483,6 +2543,10 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -2687,6 +2751,10 @@ fn resolve_tool() -> anyhow::Result<()> {
},
force: false,
editable: false,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
}
----- stderr -----
@ -2786,6 +2854,10 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -2968,6 +3040,10 @@ fn resolve_both() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -3238,6 +3314,10 @@ fn resolve_config_file() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -3325,7 +3405,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
|
1 | [project]
| ^^^^^^^
unknown field `project`, expected one of `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `dependency-metadata`, `config-settings`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `publish-url`, `trusted-publishing`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `environments`, `conflicting-groups`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dev-dependencies`
unknown field `project`, expected one of `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `dependency-metadata`, `config-settings`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `python-install-mirror`, `pypy-install-mirror`, `publish-url`, `trusted-publishing`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `environments`, `conflicting-groups`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dev-dependencies`
"###
);
@ -3457,6 +3537,10 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -3585,6 +3669,10 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -3732,6 +3820,10 @@ fn allow_insecure_host() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -3930,6 +4022,10 @@ fn index_priority() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -4107,6 +4203,10 @@ fn index_priority() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -4290,6 +4390,10 @@ fn index_priority() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -4468,6 +4572,10 @@ fn index_priority() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -4653,6 +4761,10 @@ fn index_priority() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,
@ -4831,6 +4943,10 @@ fn index_priority() -> anyhow::Result<()> {
no_index: false,
},
python: None,
install_mirrors: PythonInstallMirrors {
python_install_mirror: None,
pypy_install_mirror: None,
},
system: false,
extras: None,
break_system_packages: false,

View file

@ -30,7 +30,7 @@ fn sync() -> Result<()> {
)?;
// Running `uv sync` should generate a lockfile.
uv_snapshot!(context.filters(), context.sync(), @r###"
uv_snapshot!(context.filters(), context.sync(), @r"
success: true
exit_code: 0
----- stdout -----
@ -41,7 +41,7 @@ fn sync() -> Result<()> {
Installed 2 packages in [TIME]
+ iniconfig==2.0.0
+ project==0.1.0 (from file://[TEMP_DIR]/)
"###);
");
assert!(context.temp_dir.child("uv.lock").exists());

View file

@ -4457,6 +4457,13 @@ uv python install [OPTIONS] [TARGETS]...
</dd><dt><code>--help</code>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>
</dd><dt><code>--mirror</code> <i>mirror</i></dt><dd><p>Set the URL to use as the source for downloading Python installations.</p>
<p>The provided URL will replace <code>https://github.com/indygreg/python-build-standalone/releases/download</code> in, e.g., <code>https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz</code>.</p>
<p>Distributions can be read from a local directory by using the <code>file://</code> URL scheme.</p>
<p>May also be set with the <code>UV_PYTHON_INSTALL_MIRROR</code> environment variable.</p>
</dd><dt><code>--native-tls</code></dt><dd><p>Whether to load TLS certificates from the platform&#8217;s native certificate store.</p>
<p>By default, uv loads certificates from the bundled <code>webpki-roots</code> crate. The <code>webpki-roots</code> are a reliable set of trust roots from Mozilla, and including them in uv improves portability and performance (especially on macOS).</p>
@ -4493,6 +4500,13 @@ uv python install [OPTIONS] [TARGETS]...
<p>This setting has no effect when used in the <code>uv pip</code> interface.</p>
</dd><dt><code>--pypy-mirror</code> <i>pypy-mirror</i></dt><dd><p>Set the URL to use as the source for downloading PyPy installations.</p>
<p>The provided URL will replace <code>https://downloads.python.org/pypy</code> in, e.g., <code>https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2</code>.</p>
<p>Distributions can be read from a local directory by using the <code>file://</code> URL scheme.</p>
<p>May also be set with the <code>UV_PYPY_INSTALL_MIRROR</code> environment variable.</p>
</dd><dt><code>--python-preference</code> <i>python-preference</i></dt><dd><p>Whether to prefer uv-managed or system Python installations.</p>
<p>By default, uv prefers using Python versions it manages. However, it will use system Python installations if a uv-managed Python is not installed. This option allows prioritizing or ignoring system Python installations.</p>

View file

@ -1226,6 +1226,37 @@ The URL for publishing packages to the Python package index (by default:
---
### [`pypy-install-mirror`](#pypy-install-mirror) {: #pypy-install-mirror }
Mirror URL to use for downloading managed PyPy installations.
By default, managed PyPy installations are downloaded from [downloads.python.org](https://downloads.python.org/).
This variable can be set to a mirror URL to use a different source for PyPy installations.
The provided URL will replace `https://downloads.python.org/pypy` in, e.g., `https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2`.
Distributions can be read from a
local directory by using the `file://` URL scheme.
**Default value**: `None`
**Type**: `str`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv]
pypy-install-mirror = "https://downloads.python.org/pypy"
```
=== "uv.toml"
```toml
pypy-install-mirror = "https://downloads.python.org/pypy"
```
---
### [`python-downloads`](#python-downloads) {: #python-downloads }
Whether to allow Python downloads.
@ -1254,6 +1285,36 @@ Whether to allow Python downloads.
---
### [`python-install-mirror`](#python-install-mirror) {: #python-install-mirror }
Mirror URL for downloading managed Python installations.
By default, managed Python installations are downloaded from [`python-build-standalone`](https://github.com/indygreg/python-build-standalone).
This variable can be set to a mirror URL to use a different source for Python installations.
The provided URL will replace `https://github.com/indygreg/python-build-standalone/releases/download` in, e.g., `https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz`.
Distributions can be read from a local directory by using the `file://` URL scheme.
**Default value**: `None`
**Type**: `str`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv]
python-install-mirror = "https://github.com/indygreg/python-build-standalone/releases/download"
```
=== "uv.toml"
```toml
python-install-mirror = "https://github.com/indygreg/python-build-standalone/releases/download"
```
---
### [`python-preference`](#python-preference) {: #python-preference }
Whether to prefer using Python installations that are already present on the system, or

14
uv.schema.json generated
View file

@ -357,6 +357,13 @@
],
"format": "uri"
},
"pypy-install-mirror": {
"description": "Mirror URL to use for downloading managed PyPy installations.\n\nBy default, managed PyPy installations are downloaded from [downloads.python.org](https://downloads.python.org/). This variable can be set to a mirror URL to use a different source for PyPy installations. The provided URL will replace `https://downloads.python.org/pypy` in, e.g., `https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2`.\n\nDistributions can be read from a local directory by using the `file://` URL scheme.",
"type": [
"string",
"null"
]
},
"python-downloads": {
"description": "Whether to allow Python downloads.",
"anyOf": [
@ -368,6 +375,13 @@
}
]
},
"python-install-mirror": {
"description": "Mirror URL for downloading managed Python installations.\n\nBy default, managed Python installations are downloaded from [`python-build-standalone`](https://github.com/indygreg/python-build-standalone). This variable can be set to a mirror URL to use a different source for Python installations. The provided URL will replace `https://github.com/indygreg/python-build-standalone/releases/download` in, e.g., `https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz`.\n\nDistributions can be read from a local directory by using the `file://` URL scheme.",
"type": [
"string",
"null"
]
},
"python-preference": {
"description": "Whether to prefer using Python installations that are already present on the system, or those that are downloaded and installed by uv.",
"anyOf": [