From dec6f5aa022dae01b14d46ab3da4cb820061c591 Mon Sep 17 00:00:00 2001
From: Luca Leonardo Scorcia
Date: Mon, 30 Dec 2024 17:49:57 +0100
Subject: [PATCH 001/135] Initial support for ARMv5TE platform via cross
compilation (#10234)
## Summary
Allows uv to recognize the ARMv5TE platform. This platform is currently
supported on Debian distributions. It is an older 32 bit platform mostly
used in embedded devices, currently in rust tier 2.5 so it requires
cross compilation.
Fixes #10157 .
## Test Plan
Tested directly on device by applying a slightly different patch to tag
0.5.4 which is used by the current Home Assistant version (2024.12.5).
After the patch Home Assistant is able to recognize the Python venv and
setup its dependencies.
Patched uv was built with
```
$ CARGO_TARGET_ARMV5TE_UNKNOWN_LINUX_GNUEABI_LINKER="/usr/bin/arm-linux-gnueabi-gcc" maturin build --release --target armv5te-unknown-linux-gnueabi --manylinux off
```
The target wheel was then moved on the device and installed via pip
install.
---
Cargo.toml | 2 +-
crates/uv-platform-tags/src/platform.rs | 4 +++-
crates/uv-python/src/platform.rs | 6 +++++-
crates/uv-python/template-download-metadata.py | 2 ++
4 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 706789b1157e..f1b73bd65c10 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -80,7 +80,7 @@ axoupdater = { version = "0.9.0", default-features = false }
backoff = { version = "0.4.0" }
base64 = { version = "0.22.1" }
bitflags = { version = "2.6.0" }
-boxcar = { version = "0.2.5" }
+boxcar = { version = "0.2.8" }
bytecheck = { version = "0.8.0" }
cachedir = { version = "0.3.1" }
cargo-util = { version = "0.2.14" }
diff --git a/crates/uv-platform-tags/src/platform.rs b/crates/uv-platform-tags/src/platform.rs
index 12876bb0831a..e3418a17dc45 100644
--- a/crates/uv-platform-tags/src/platform.rs
+++ b/crates/uv-platform-tags/src/platform.rs
@@ -77,6 +77,7 @@ impl fmt::Display for Os {
pub enum Arch {
#[serde(alias = "arm64")]
Aarch64,
+ Armv5TEL,
Armv6L,
#[serde(alias = "armv8l")]
Armv7L,
@@ -96,6 +97,7 @@ impl fmt::Display for Arch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Aarch64 => write!(f, "aarch64"),
+ Self::Armv5TEL => write!(f, "armv5tel"),
Self::Armv6L => write!(f, "armv6l"),
Self::Armv7L => write!(f, "armv7l"),
Self::Powerpc64Le => write!(f, "ppc64le"),
@@ -122,7 +124,7 @@ impl Arch {
// manylinux_2_31
Self::Riscv64 => Some(31),
// unsupported
- Self::Armv6L => None,
+ Self::Armv5TEL | Self::Armv6L => None,
}
}
}
diff --git a/crates/uv-python/src/platform.rs b/crates/uv-python/src/platform.rs
index 9f933633d0c5..67342ae1a988 100644
--- a/crates/uv-python/src/platform.rs
+++ b/crates/uv-python/src/platform.rs
@@ -54,7 +54,7 @@ impl Libc {
// Checks if the CPU supports hardware floating-point operations.
// Depending on the result, it selects either the `gnueabihf` (hard-float) or `gnueabi` (soft-float) environment.
// download-metadata.json only includes armv7.
- "arm" | "armv7" => match detect_hardware_floating_point_support() {
+ "arm" | "armv5te" | "armv7" => match detect_hardware_floating_point_support() {
Ok(true) => target_lexicon::Environment::Gnueabihf,
Ok(false) => target_lexicon::Environment::Gnueabi,
Err(_) => target_lexicon::Environment::Gnu,
@@ -240,6 +240,10 @@ impl From<&uv_platform_tags::Arch> for Arch {
),
variant: None,
},
+ uv_platform_tags::Arch::Armv5TEL => Self {
+ family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv5te),
+ variant: None,
+ },
uv_platform_tags::Arch::Armv6L => Self {
family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv6),
variant: None,
diff --git a/crates/uv-python/template-download-metadata.py b/crates/uv-python/template-download-metadata.py
index 95313a699777..4be2d0ef98bb 100755
--- a/crates/uv-python/template-download-metadata.py
+++ b/crates/uv-python/template-download-metadata.py
@@ -69,6 +69,8 @@ def prepare_arch(arch: dict) -> tuple[str, str]:
family = "X86_32(target_lexicon::X86_32Architecture::I686)"
case "aarch64":
family = "Aarch64(target_lexicon::Aarch64Architecture::Aarch64)"
+ case "armv5tel":
+ family = "Arm(target_lexicon::ArmArchitecture::Armv5te)"
case "armv7":
family = "Arm(target_lexicon::ArmArchitecture::Armv7)"
case value:
From dcd96a83aa5254d6b5068d2c68b074a0376d4100 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Mon, 30 Dec 2024 12:47:06 -0500
Subject: [PATCH 002/135] Respect static metadata for already-installed
distributions (#10242)
## Summary
Closes
https://github.com/astral-sh/uv/issues/10239#issuecomment-2565663046
---
.../uv-distribution-types/src/dist_error.rs | 22 ++-
crates/uv-distribution-types/src/lib.rs | 2 +
crates/uv-distribution-types/src/requested.rs | 71 ++++++++++
.../src/distribution_database.rs | 29 +++-
crates/uv-distribution/src/error.rs | 4 +-
crates/uv-installer/src/preparer.rs | 2 +-
crates/uv-requirements/src/lib.rs | 6 +-
crates/uv-resolver/src/error.rs | 8 +-
crates/uv-resolver/src/resolver/mod.rs | 37 ++---
crates/uv-resolver/src/resolver/provider.rs | 28 +++-
crates/uv/src/commands/diagnostics.rs | 51 ++++++-
crates/uv/tests/it/pip_install.rs | 134 ++++++++++++++++++
12 files changed, 347 insertions(+), 47 deletions(-)
create mode 100644 crates/uv-distribution-types/src/requested.rs
diff --git a/crates/uv-distribution-types/src/dist_error.rs b/crates/uv-distribution-types/src/dist_error.rs
index 9bfcf8e974bb..fcbb13dde83b 100644
--- a/crates/uv-distribution-types/src/dist_error.rs
+++ b/crates/uv-distribution-types/src/dist_error.rs
@@ -1,12 +1,17 @@
-use crate::{BuiltDist, Dist, DistRef, Edge, Name, Node, Resolution, ResolvedDist, SourceDist};
+use std::collections::VecDeque;
+use std::fmt::{Debug, Display, Formatter};
+
use petgraph::prelude::EdgeRef;
use petgraph::Direction;
use rustc_hash::FxHashSet;
-use std::collections::VecDeque;
-use std::fmt::{Debug, Display, Formatter};
+use version_ranges::Ranges;
+
use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_pep440::Version;
-use version_ranges::Ranges;
+
+use crate::{
+ BuiltDist, Dist, DistRef, Edge, Name, Node, RequestedDist, Resolution, ResolvedDist, SourceDist,
+};
/// Inspect whether an error type is a build error.
pub trait IsBuildBackendError: std::error::Error + Send + Sync + 'static {
@@ -25,7 +30,14 @@ pub enum DistErrorKind {
}
impl DistErrorKind {
- pub fn from_dist_and_err(dist: &Dist, err: &impl IsBuildBackendError) -> Self {
+ pub fn from_requested_dist(dist: &RequestedDist, err: &impl IsBuildBackendError) -> Self {
+ match dist {
+ RequestedDist::Installed(_) => DistErrorKind::Read,
+ RequestedDist::Installable(dist) => Self::from_dist(dist, err),
+ }
+ }
+
+ pub fn from_dist(dist: &Dist, err: &impl IsBuildBackendError) -> Self {
if err.is_build_backend_error() {
DistErrorKind::BuildBackend
} else {
diff --git a/crates/uv-distribution-types/src/lib.rs b/crates/uv-distribution-types/src/lib.rs
index 9aba9e5c5f14..c9a0c48b50cd 100644
--- a/crates/uv-distribution-types/src/lib.rs
+++ b/crates/uv-distribution-types/src/lib.rs
@@ -67,6 +67,7 @@ pub use crate::installed::*;
pub use crate::origin::*;
pub use crate::pip_index::*;
pub use crate::prioritized_distribution::*;
+pub use crate::requested::*;
pub use crate::resolution::*;
pub use crate::resolved::*;
pub use crate::specified_requirement::*;
@@ -90,6 +91,7 @@ mod installed;
mod origin;
mod pip_index;
mod prioritized_distribution;
+mod requested;
mod resolution;
mod resolved;
mod specified_requirement;
diff --git a/crates/uv-distribution-types/src/requested.rs b/crates/uv-distribution-types/src/requested.rs
new file mode 100644
index 000000000000..b804a16adb88
--- /dev/null
+++ b/crates/uv-distribution-types/src/requested.rs
@@ -0,0 +1,71 @@
+use std::fmt::{Display, Formatter};
+
+use crate::{
+ Dist, DistributionId, DistributionMetadata, Identifier, InstalledDist, Name, ResourceId,
+ VersionOrUrlRef,
+};
+use uv_normalize::PackageName;
+use uv_pep440::Version;
+
+/// A distribution that can be requested during resolution.
+///
+/// Either an already-installed distribution or a distribution that can be installed.
+#[derive(Debug, Clone)]
+#[allow(clippy::large_enum_variant)]
+pub enum RequestedDist {
+ Installed(InstalledDist),
+ Installable(Dist),
+}
+
+impl RequestedDist {
+ /// Returns the version of the distribution, if it is known.
+ pub fn version(&self) -> Option<&Version> {
+ match self {
+ Self::Installed(dist) => Some(dist.version()),
+ Self::Installable(dist) => dist.version(),
+ }
+ }
+}
+
+impl Name for RequestedDist {
+ fn name(&self) -> &PackageName {
+ match self {
+ Self::Installable(dist) => dist.name(),
+ Self::Installed(dist) => dist.name(),
+ }
+ }
+}
+
+impl DistributionMetadata for RequestedDist {
+ fn version_or_url(&self) -> VersionOrUrlRef {
+ match self {
+ Self::Installed(dist) => dist.version_or_url(),
+ Self::Installable(dist) => dist.version_or_url(),
+ }
+ }
+}
+
+impl Identifier for RequestedDist {
+ fn distribution_id(&self) -> DistributionId {
+ match self {
+ Self::Installed(dist) => dist.distribution_id(),
+ Self::Installable(dist) => dist.distribution_id(),
+ }
+ }
+
+ fn resource_id(&self) -> ResourceId {
+ match self {
+ Self::Installed(dist) => dist.resource_id(),
+ Self::Installable(dist) => dist.resource_id(),
+ }
+ }
+}
+
+impl Display for RequestedDist {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Installed(dist) => dist.fmt(f),
+ Self::Installable(dist) => dist.fmt(f),
+ }
+ }
+}
diff --git a/crates/uv-distribution/src/distribution_database.rs b/crates/uv-distribution/src/distribution_database.rs
index 95b52b516051..d1ab5a5d0519 100644
--- a/crates/uv-distribution/src/distribution_database.rs
+++ b/crates/uv-distribution/src/distribution_database.rs
@@ -21,7 +21,8 @@ use uv_client::{
};
use uv_distribution_filename::WheelFilename;
use uv_distribution_types::{
- BuildableSource, BuiltDist, Dist, FileLocation, HashPolicy, Hashed, Name, SourceDist,
+ BuildableSource, BuiltDist, Dist, FileLocation, HashPolicy, Hashed, InstalledDist, Name,
+ SourceDist,
};
use uv_extract::hash::Hasher;
use uv_fs::write_atomic;
@@ -115,6 +116,32 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
}
}
+ /// Either fetch the only wheel metadata (directly from the index or with range requests) or
+ /// fetch and build the source distribution.
+ ///
+ /// While hashes will be generated in some cases, hash-checking is only enforced for source
+ /// distributions, and should be enforced by the caller for wheels.
+ #[instrument(skip_all, fields(%dist))]
+ pub async fn get_installed_metadata(
+ &self,
+ dist: &InstalledDist,
+ ) -> Result {
+ // If the metadata was provided by the user directly, prefer it.
+ if let Some(metadata) = self
+ .build_context
+ .dependency_metadata()
+ .get(dist.name(), Some(dist.version()))
+ {
+ return Ok(ArchiveMetadata::from_metadata23(metadata.clone()));
+ }
+
+ let metadata = dist
+ .metadata()
+ .map_err(|err| Error::ReadInstalled(Box::new(dist.clone()), err))?;
+
+ Ok(ArchiveMetadata::from_metadata23(metadata))
+ }
+
/// Either fetch the only wheel metadata (directly from the index or with range requests) or
/// fetch and build the source distribution.
///
diff --git a/crates/uv-distribution/src/error.rs b/crates/uv-distribution/src/error.rs
index 217682501bd8..84315658e42e 100644
--- a/crates/uv-distribution/src/error.rs
+++ b/crates/uv-distribution/src/error.rs
@@ -8,7 +8,7 @@ use zip::result::ZipError;
use crate::metadata::MetadataError;
use uv_client::WrappedReqwestError;
use uv_distribution_filename::WheelFilenameError;
-use uv_distribution_types::IsBuildBackendError;
+use uv_distribution_types::{InstalledDist, InstalledDistError, IsBuildBackendError};
use uv_fs::Simplified;
use uv_normalize::PackageName;
use uv_pep440::{Version, VersionSpecifiers};
@@ -82,6 +82,8 @@ pub enum Error {
Metadata(#[from] uv_pypi_types::MetadataError),
#[error("Failed to read metadata: `{}`", _0.user_display())]
WheelMetadata(PathBuf, #[source] Box),
+ #[error("Failed to read metadata from installed package `{0}`")]
+ ReadInstalled(Box, #[source] InstalledDistError),
#[error("Failed to read zip archive from built wheel")]
Zip(#[from] ZipError),
#[error("Source distribution directory contains neither readable `pyproject.toml` nor `setup.py`: `{}`", _0.user_display())]
diff --git a/crates/uv-installer/src/preparer.rs b/crates/uv-installer/src/preparer.rs
index fac49057025c..541383de10da 100644
--- a/crates/uv-installer/src/preparer.rs
+++ b/crates/uv-installer/src/preparer.rs
@@ -230,7 +230,7 @@ impl Error {
let chain =
DerivationChain::from_resolution(resolution, (&dist).into()).unwrap_or_default();
Self::Dist(
- DistErrorKind::from_dist_and_err(&dist, &err),
+ DistErrorKind::from_dist(&dist, &err),
Box::new(dist),
chain,
err,
diff --git a/crates/uv-requirements/src/lib.rs b/crates/uv-requirements/src/lib.rs
index adb342e133c6..a6bff1998a61 100644
--- a/crates/uv-requirements/src/lib.rs
+++ b/crates/uv-requirements/src/lib.rs
@@ -35,11 +35,7 @@ pub enum Error {
impl Error {
/// Create an [`Error`] from a distribution error.
pub(crate) fn from_dist(dist: Dist, err: uv_distribution::Error) -> Self {
- Self::Dist(
- DistErrorKind::from_dist_and_err(&dist, &err),
- Box::new(dist),
- err,
- )
+ Self::Dist(DistErrorKind::from_dist(&dist, &err), Box::new(dist), err)
}
}
diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs
index 70ef20f60e0d..2cd333c40a82 100644
--- a/crates/uv-resolver/src/error.rs
+++ b/crates/uv-resolver/src/error.rs
@@ -10,8 +10,7 @@ use rustc_hash::FxHashMap;
use tracing::trace;
use uv_distribution_types::{
- DerivationChain, Dist, DistErrorKind, IndexCapabilities, IndexLocations, IndexUrl,
- InstalledDist, InstalledDistError,
+ DerivationChain, DistErrorKind, IndexCapabilities, IndexLocations, IndexUrl, RequestedDist,
};
use uv_normalize::{ExtraName, PackageName};
use uv_pep440::{LocalVersionSlice, Version};
@@ -98,14 +97,11 @@ pub enum ResolveError {
#[error("{0} `{1}`")]
Dist(
DistErrorKind,
- Box,
+ Box,
DerivationChain,
#[source] Arc,
),
- #[error("Failed to read metadata from installed package `{0}`")]
- ReadInstalled(Box, #[source] InstalledDistError),
-
#[error(transparent)]
NoSolution(#[from] NoSolutionError),
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index d27f0df1b76b..7873a44e0a89 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -21,7 +21,7 @@ use tokio_stream::wrappers::ReceiverStream;
use tracing::{debug, info, instrument, trace, warn, Level};
use uv_configuration::{Constraints, Overrides};
-use uv_distribution::{ArchiveMetadata, DistributionDatabase};
+use uv_distribution::DistributionDatabase;
use uv_distribution_types::{
BuiltDist, CompatibleDist, DerivationChain, Dist, DistErrorKind, DistributionMetadata,
IncompatibleDist, IncompatibleSource, IncompatibleWheel, IndexCapabilities, IndexLocations,
@@ -33,9 +33,7 @@ use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_pep440::{release_specifiers_to_ranges, Version, VersionSpecifiers, MIN_VERSION};
use uv_pep508::MarkerTree;
use uv_platform_tags::Tags;
-use uv_pypi_types::{
- ConflictItem, ConflictItemRef, Conflicts, Requirement, ResolutionMetadata, VerbatimParsedUrl,
-};
+use uv_pypi_types::{ConflictItem, ConflictItemRef, Conflicts, Requirement, VerbatimParsedUrl};
use uv_types::{BuildContext, HashStrategy, InstalledPackagesProvider};
use uv_warnings::warn_user_once;
@@ -1097,11 +1095,11 @@ impl ResolverState {
- // TODO(charlie): Add derivation chain for URL dependencies. In practice, this isn't
- // critical since we fetch URL dependencies _prior_ to invoking the resolver.
return Err(ResolveError::Dist(
- DistErrorKind::from_dist_and_err(dist, &**err),
+ DistErrorKind::from_requested_dist(dist, &**err),
dist.clone(),
DerivationChain::default(),
err.clone(),
@@ -1642,7 +1640,7 @@ impl ResolverState ResolverState {
trace!("Received installed distribution metadata for: {dist}");
- self.index.distributions().done(
- dist.version_id(),
- Arc::new(MetadataResponse::Found(ArchiveMetadata::from_metadata23(
- metadata,
- ))),
- );
+ self.index
+ .distributions()
+ .done(dist.version_id(), Arc::new(metadata));
}
Some(Response::Dist { dist, metadata }) => {
let dist_kind = match dist {
@@ -2134,10 +2129,8 @@ impl ResolverState {
- // TODO(charlie): This should be return a `MetadataResponse`.
- let metadata = dist
- .metadata()
- .map_err(|err| ResolveError::ReadInstalled(Box::new(dist.clone()), err))?;
+ let metadata = provider.get_installed_metadata(&dist).boxed_local().await?;
+
Ok(Some(Response::Installed { dist, metadata }))
}
@@ -2251,9 +2244,9 @@ impl ResolverState {
- let metadata = dist.metadata().map_err(|err| {
- ResolveError::ReadInstalled(Box::new(dist.clone()), err)
- })?;
+ let metadata =
+ provider.get_installed_metadata(&dist).boxed_local().await?;
+
Response::Installed { dist, metadata }
}
};
@@ -3079,7 +3072,7 @@ enum Response {
/// The returned metadata for an already-installed distribution.
Installed {
dist: InstalledDist,
- metadata: ResolutionMetadata,
+ metadata: MetadataResponse,
},
}
diff --git a/crates/uv-resolver/src/resolver/provider.rs b/crates/uv-resolver/src/resolver/provider.rs
index 29e02384699e..03cda439d294 100644
--- a/crates/uv-resolver/src/resolver/provider.rs
+++ b/crates/uv-resolver/src/resolver/provider.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
use uv_configuration::BuildOptions;
use uv_distribution::{ArchiveMetadata, DistributionDatabase};
-use uv_distribution_types::{Dist, IndexCapabilities, IndexUrl};
+use uv_distribution_types::{Dist, IndexCapabilities, IndexUrl, InstalledDist, RequestedDist};
use uv_normalize::PackageName;
use uv_pep440::{Version, VersionSpecifiers};
use uv_platform_tags::Tags;
@@ -37,7 +37,7 @@ pub enum MetadataResponse {
/// A non-fatal error.
Unavailable(MetadataUnavailable),
/// The distribution could not be built or downloaded, a fatal error.
- Error(Box, Arc),
+ Error(Box, Arc),
}
/// Non-fatal metadata fetching error.
@@ -83,7 +83,7 @@ pub trait ResolverProvider {
/// Get the metadata for a distribution.
///
- /// For a wheel, this is done by querying it's (remote) metadata, for a source dist we
+ /// For a wheel, this is done by querying it (remote) metadata. For a source distribution, we
/// (fetch and) build the source distribution and return the metadata from the built
/// distribution.
fn get_or_build_wheel_metadata<'io>(
@@ -91,6 +91,12 @@ pub trait ResolverProvider {
dist: &'io Dist,
) -> impl Future
May also be set with the UV_CACHE_DIR
environment variable.
---color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -6801,9 +6825,10 @@ uv pip uninstall [OPTIONS] >
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -6953,9 +6978,10 @@ uv pip freeze [OPTIONS]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -7087,9 +7113,10 @@ uv pip list [OPTIONS]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -7317,9 +7344,10 @@ uv pip show [OPTIONS] [PACKAGE]...
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -7451,9 +7479,10 @@ uv pip tree [OPTIONS]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -7668,9 +7697,10 @@ uv pip check [OPTIONS]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -7822,9 +7852,10 @@ uv venv [OPTIONS] [PATH]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -8086,9 +8117,10 @@ uv build [OPTIONS] [SRC]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -8433,9 +8465,10 @@ uv publish [OPTIONS] [FILES]...
The index must provide one of the supported hashes (SHA-256, SHA-384, or SHA-512).
May also be set with the UV_PUBLISH_CHECK_URL
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -8629,9 +8662,10 @@ uv cache clean [OPTIONS] [PACKAGE]...
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -8751,9 +8785,10 @@ uv cache prune [OPTIONS]
In --ci
mode, uv will prune any pre-built wheels from the cache, but retain any wheels that were built from source.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -8875,9 +8910,10 @@ uv cache dir [OPTIONS]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -9013,9 +9049,10 @@ uv self update [OPTIONS] [TARGET_VERSION]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -9132,9 +9169,10 @@ uv version [OPTIONS]
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
@@ -9297,9 +9335,10 @@ uv help [OPTIONS] [COMMAND]...
To view the location of the cache directory, run uv cache dir
.
May also be set with the UV_CACHE_DIR
environment variable.
-
--color
color-choiceControl colors in output
+--color
color-choiceControl the use of color in output.
+
+By default, uv will automatically detect support for colors when writing to a terminal.
-[default: auto]
Possible values:
From 5aefe69ecfecd369d70df45fa6c3c33ed5b0c0fd Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 5 Jan 2025 21:27:14 -0500
Subject: [PATCH 031/135] Update Rust crate itertools to 0.14.0 (#10316)
---
Cargo.lock | 43 ++++++++++++++++-----------
Cargo.toml | 2 +-
crates/uv-python/Cargo.toml | 2 +-
crates/uv-requirements-txt/Cargo.toml | 2 +-
4 files changed, 29 insertions(+), 20 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 8af23a32f969..113dd38681d7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -672,7 +672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -1861,6 +1861,15 @@ dependencies = [
"either",
]
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "itoa"
version = "1.0.14"
@@ -4467,7 +4476,7 @@ dependencies = [
"indicatif",
"indoc",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"jiff",
"miette",
"nix",
@@ -4604,7 +4613,7 @@ dependencies = [
"globset",
"indoc",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"serde",
"sha2",
"spdx",
@@ -4635,7 +4644,7 @@ dependencies = [
"fs-err 3.0.0",
"indoc",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"owo-colors",
"regex",
"rustc-hash",
@@ -4749,7 +4758,7 @@ dependencies = [
"hyper",
"hyper-util",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"jiff",
"reqwest",
"reqwest-middleware",
@@ -4830,7 +4839,7 @@ dependencies = [
"anyhow",
"clap",
"fs-err 3.0.0",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"markdown",
"owo-colors",
"poloto",
@@ -4879,7 +4888,7 @@ version = "0.0.1"
dependencies = [
"anyhow",
"futures",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"rustc-hash",
"thiserror 2.0.9",
"tokio",
@@ -4970,7 +4979,7 @@ dependencies = [
"anyhow",
"bitflags 2.6.0",
"fs-err 3.0.0",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"jiff",
"petgraph",
"rkyv",
@@ -5229,7 +5238,7 @@ dependencies = [
"boxcar",
"indexmap",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"regex",
"rustc-hash",
"schemars",
@@ -5282,7 +5291,7 @@ dependencies = [
"futures",
"glob",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"krata-tokio-tar",
"reqwest",
"reqwest-middleware",
@@ -5315,7 +5324,7 @@ dependencies = [
"anyhow",
"hashbrown 0.15.2",
"indexmap",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"jiff",
"mailparse",
"regex",
@@ -5349,7 +5358,7 @@ dependencies = [
"goblin",
"indoc",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"owo-colors",
"procfs",
"regex",
@@ -5436,7 +5445,7 @@ dependencies = [
"fs-err 3.0.0",
"indoc",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"regex",
"reqwest",
"reqwest-middleware",
@@ -5468,7 +5477,7 @@ dependencies = [
"hashbrown 0.15.2",
"indexmap",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"jiff",
"owo-colors",
"petgraph",
@@ -5658,7 +5667,7 @@ name = "uv-virtualenv"
version = "0.0.4"
dependencies = [
"fs-err 3.0.0",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"pathdiff",
"self-replace",
"thiserror 2.0.9",
@@ -5689,7 +5698,7 @@ dependencies = [
"fs-err 3.0.0",
"glob",
"insta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"owo-colors",
"regex",
"rustc-hash",
@@ -5942,7 +5951,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 706789b1157e..a4df23b68568 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -113,7 +113,7 @@ http = { version = "1.1.0" }
indexmap = { version = "2.5.0" }
indicatif = { version = "0.17.8" }
indoc = { version = "2.0.5" }
-itertools = { version = "0.13.0" }
+itertools = { version = "0.14.0" }
jiff = { version = "0.1.14", features = ["serde"] }
junction = { version = "1.2.0" }
krata-tokio-tar = { version = "0.4.2" }
diff --git a/crates/uv-python/Cargo.toml b/crates/uv-python/Cargo.toml
index 92930aa298ac..fcea7d5ecb60 100644
--- a/crates/uv-python/Cargo.toml
+++ b/crates/uv-python/Cargo.toml
@@ -73,7 +73,7 @@ anyhow = { version = "1.0.89" }
assert_fs = { version = "1.1.2" }
indoc = { workspace = true }
insta = { version = "1.40.0" }
-itertools = { version = "0.13.0" }
+itertools = { version = "0.14.0" }
temp-env = { version = "0.3.6" }
tempfile = { workspace = true }
test-log = { version = "0.2.16", features = ["trace"], default-features = false }
diff --git a/crates/uv-requirements-txt/Cargo.toml b/crates/uv-requirements-txt/Cargo.toml
index dae6b3870482..11e7d1666866 100644
--- a/crates/uv-requirements-txt/Cargo.toml
+++ b/crates/uv-requirements-txt/Cargo.toml
@@ -41,7 +41,7 @@ anyhow = { version = "1.0.89" }
assert_fs = { version = "1.1.2" }
indoc = { workspace = true }
insta = { version = "1.40.0", features = ["filters"] }
-itertools = { version = "0.13.0" }
+itertools = { version = "0.14.0" }
tempfile = { workspace = true }
test-case = { version = "3.3.1" }
tokio = { version = "1.40.0" }
From 98d06f40f586a8870dd7ea4620f4e7e3a16d3b4f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 5 Jan 2025 21:27:30 -0500
Subject: [PATCH 032/135] Update Rust crate insta to v1.42.0 (#10314)
---
Cargo.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 113dd38681d7..d21adf1c4508 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1788,13 +1788,13 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "insta"
-version = "1.41.1"
+version = "1.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8"
+checksum = "6513e4067e16e69ed1db5ab56048ed65db32d10ba5fc1217f5393f8f17d8b5a5"
dependencies = [
"console",
- "lazy_static",
"linked-hash-map",
+ "once_cell",
"pest",
"pest_derive",
"regex",
From 16097b51c451585c157f93ecab3ae3409f22ec0e Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 5 Jan 2025 21:29:38 -0500
Subject: [PATCH 033/135] Update Rust crate tempfile to v3.15.0 (#10318)
---
Cargo.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index d21adf1c4508..5c026b5775aa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3762,12 +3762,13 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.14.0"
+version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
+checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
dependencies = [
"cfg-if",
"fastrand",
+ "getrandom",
"once_cell",
"rustix",
"windows-sys 0.52.0",
From 27bc8dd3dcc46e756b9b002ffec949e935744ac7 Mon Sep 17 00:00:00 2001
From: vladislav doster
Date: Mon, 6 Jan 2025 01:41:48 -0600
Subject: [PATCH 034/135] docs(python-versions.md): fix spelling (#10322)
## Summary
Correct misspelling of `interopability` to `interoperability`.
## Test Plan
N/A
---
docs/concepts/python-versions.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/concepts/python-versions.md b/docs/concepts/python-versions.md
index 85dbb25c6b43..70da18fa89b5 100644
--- a/docs/concepts/python-versions.md
+++ b/docs/concepts/python-versions.md
@@ -53,7 +53,7 @@ This behavior can be
The `.python-version` file can be used to create a default Python version request. uv searches for a
`.python-version` file in the working directory and each of its parents. Any of the request formats
-described above can be used, though use of a version number is recommended for interopability with
+described above can be used, though use of a version number is recommended for interoperability with
other tools.
A `.python-version` file can be created in the current directory with the
From 523b88edf0da6908de7be8b4f47efd9848b5def4 Mon Sep 17 00:00:00 2001
From: vladislav doster
Date: Mon, 6 Jan 2025 08:33:46 -0600
Subject: [PATCH 035/135] docs(tools.md): remove hyphenation for consistency
(#10321)
---
docs/concepts/tools.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/concepts/tools.md b/docs/concepts/tools.md
index c80f8d1589b2..4d3603bc1711 100644
--- a/docs/concepts/tools.md
+++ b/docs/concepts/tools.md
@@ -160,7 +160,7 @@ Tool upgrades will respect the version constraints provided when installing the
`uv tool install black >=23,<24` followed by `uv tool upgrade black` will upgrade Black to the
latest version in the range `>=23,<24`.
-To instead replace the version constraints, re-install the tool with `uv tool install`:
+To instead replace the version constraints, reinstall the tool with `uv tool install`:
```console
$ uv tool install black>=24
From 54b9e8ff82587153e204325dde5a36f7ba96b02c Mon Sep 17 00:00:00 2001
From: Trevor Manz
Date: Mon, 6 Jan 2025 10:03:21 -0500
Subject: [PATCH 036/135] Clarify exclude-newer only allows full timestamps in
settings docs (#9135)
## Summary
Follow up to #8553
Clarifies that the `exclude-newer` setting must be a full timestamp and
not a date.
## Test Plan
N/A
---
crates/uv-settings/src/settings.rs | 10 +++++-----
docs/reference/settings.md | 12 ++++++------
uv.schema.json | 2 +-
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs
index bf9815e7f070..6f645ebc6436 100644
--- a/crates/uv-settings/src/settings.rs
+++ b/crates/uv-settings/src/settings.rs
@@ -1276,16 +1276,16 @@ pub struct PipOptions {
"#
)]
pub universal: Option,
- /// Limit candidate packages to those that were uploaded prior to the given date.
+ /// Limit candidate packages to those that were uploaded prior to a given point in time.
///
- /// Accepts both [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) timestamps (e.g.,
- /// `2006-12-02T02:07:43Z`) and local dates in the same format (e.g., `2006-12-02`) in your
- /// system's configured time zone.
+ /// Accepts a superset of [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) (e.g.,
+ /// `2006-12-02T02:07:43Z`). A full timestamp is required to ensure that the resolver will
+ /// behave consistently across timezones.
#[option(
default = "None",
value_type = "str",
example = r#"
- exclude-newer = "2006-12-02"
+ exclude-newer = "2006-12-02T02:07:43Z"
"#
)]
pub exclude_newer: Option,
diff --git a/docs/reference/settings.md b/docs/reference/settings.md
index e8a142900a40..f401eef33adc 100644
--- a/docs/reference/settings.md
+++ b/docs/reference/settings.md
@@ -2055,11 +2055,11 @@ be correct.
#### [`exclude-newer`](#pip_exclude-newer) {: #pip_exclude-newer }
-Limit candidate packages to those that were uploaded prior to the given date.
+Limit candidate packages to those that were uploaded prior to a given point in time.
-Accepts both [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) timestamps (e.g.,
-`2006-12-02T02:07:43Z`) and local dates in the same format (e.g., `2006-12-02`) in your
-system's configured time zone.
+Accepts a superset of [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) (e.g.,
+`2006-12-02T02:07:43Z`). A full timestamp is required to ensure that the resolver will
+behave consistently across timezones.
**Default value**: `None`
@@ -2071,13 +2071,13 @@ system's configured time zone.
```toml
[tool.uv.pip]
- exclude-newer = "2006-12-02"
+ exclude-newer = "2006-12-02T02:07:43Z"
```
=== "uv.toml"
```toml
[pip]
- exclude-newer = "2006-12-02"
+ exclude-newer = "2006-12-02T02:07:43Z"
```
---
diff --git a/uv.schema.json b/uv.schema.json
index fcba78967b0d..5304a2a6fe52 100644
--- a/uv.schema.json
+++ b/uv.schema.json
@@ -960,7 +960,7 @@
]
},
"exclude-newer": {
- "description": "Limit candidate packages to those that were uploaded prior to the given date.\n\nAccepts both [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same format (e.g., `2006-12-02`) in your system's configured time zone.",
+ "description": "Limit candidate packages to those that were uploaded prior to a given point in time.\n\nAccepts a superset of [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) (e.g., `2006-12-02T02:07:43Z`). A full timestamp is required to ensure that the resolver will behave consistently across timezones.",
"anyOf": [
{
"$ref": "#/definitions/ExcludeNewer"
From 66a603b6c49508b77183bf5933b2262892c1a351 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Mon, 6 Jan 2025 12:04:00 -0500
Subject: [PATCH 037/135] Use dynamic dispatch to simplify reporters (#10086)
## Summary
Sort of undecided on this. These are already stored as `dyn Reporter` in
each struct, so we're already using dynamic dispatch in that sense. But
all the methods take `impl Reporter`. This is sometimes nice (the
callsites are simpler?), but it also means that in practice, you often
_can't_ pass `None` to these methods that accept `Option`, because Rust can't infer the generic type.
Anyway, this adds more consistency and simplifies the setup by using
`Arc` everywhere.
---
.../src/distribution_database.rs | 7 ++---
crates/uv-distribution/src/reporter.rs | 17 ++++++-----
crates/uv-distribution/src/source/mod.rs | 25 +++++++++++++----
crates/uv-git/src/resolver.rs | 6 ++--
crates/uv-git/src/source.rs | 11 ++++----
crates/uv-installer/src/installer.rs | 17 +++++------
crates/uv-installer/src/preparer.rs | 28 +++++++++++--------
crates/uv-requirements/src/extras.rs | 2 +-
crates/uv-requirements/src/lookahead.rs | 2 +-
crates/uv-requirements/src/source_tree.rs | 2 +-
crates/uv-requirements/src/unnamed.rs | 2 +-
crates/uv-resolver/src/resolver/mod.rs | 9 +++---
crates/uv-resolver/src/resolver/provider.rs | 10 +++----
crates/uv-resolver/src/resolver/reporter.rs | 15 ++++++++--
crates/uv/src/commands/pip/operations.rs | 19 ++++++++-----
crates/uv/src/commands/project/add.rs | 12 ++++----
crates/uv/src/commands/project/lock.rs | 3 +-
crates/uv/src/commands/project/mod.rs | 3 +-
crates/uv/src/commands/reporters.rs | 1 +
19 files changed, 117 insertions(+), 74 deletions(-)
diff --git a/crates/uv-distribution/src/distribution_database.rs b/crates/uv-distribution/src/distribution_database.rs
index 06c21d007974..79dc85187090 100644
--- a/crates/uv-distribution/src/distribution_database.rs
+++ b/crates/uv-distribution/src/distribution_database.rs
@@ -82,11 +82,10 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
/// Set the [`Reporter`] to use for the [`DistributionDatabase`].
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
- let reporter = Arc::new(reporter);
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
- reporter: Some(reporter.clone()),
- builder: self.builder.with_reporter(reporter),
+ builder: self.builder.with_reporter(reporter.clone()),
+ reporter: Some(reporter),
..self
}
}
diff --git a/crates/uv-distribution/src/reporter.rs b/crates/uv-distribution/src/reporter.rs
index e8e8b40d5237..9be3a5fa4355 100644
--- a/crates/uv-distribution/src/reporter.rs
+++ b/crates/uv-distribution/src/reporter.rs
@@ -29,15 +29,18 @@ pub trait Reporter: Send + Sync {
fn on_download_complete(&self, name: &PackageName, id: usize);
}
-/// A facade for converting from [`Reporter`] to [`uv_git::Reporter`].
-pub(crate) struct Facade {
- reporter: Arc,
+impl dyn Reporter {
+ /// Converts this reporter to a [`uv_git::Reporter`].
+ pub(crate) fn into_git_reporter(self: Arc) -> Arc {
+ Arc::new(Facade {
+ reporter: self.clone(),
+ })
+ }
}
-impl From> for Facade {
- fn from(reporter: Arc) -> Self {
- Self { reporter }
- }
+/// A facade for converting from [`Reporter`] to [`uv_git::Reporter`].
+struct Facade {
+ reporter: Arc,
}
impl uv_git::Reporter for Facade {
diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs
index 772c3d384442..95d3f89d14bb 100644
--- a/crates/uv-distribution/src/source/mod.rs
+++ b/crates/uv-distribution/src/source/mod.rs
@@ -1,5 +1,13 @@
//! Fetch and build source distributions from remote sources.
+// This is to squash warnings about `|r| r.into_git_reporter()`. Clippy wants
+// me to eta-reduce that and write it as
+// `<(dyn reporter::Reporter + 'static)>::into_git_reporter`
+// instead. But that's a monster. On the other hand, applying this suppression
+// instruction more granularly is annoying. So we just slap it on the module
+// for now. ---AG
+#![allow(clippy::redundant_closure_for_method_calls)]
+
use std::borrow::Cow;
use std::ops::Bound;
use std::path::{Path, PathBuf};
@@ -9,7 +17,6 @@ use std::sync::Arc;
use crate::distribution_database::ManagedClient;
use crate::error::Error;
use crate::metadata::{ArchiveMetadata, GitWorkspaceMember, Metadata};
-use crate::reporter::Facade;
use crate::source::built_wheel_metadata::BuiltWheelMetadata;
use crate::source::revision::Revision;
use crate::{Reporter, RequiresDist};
@@ -1330,7 +1337,9 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
resource.git,
client.unmanaged.uncached_client(resource.url).clone(),
self.build_context.cache().bucket(CacheBucket::Git),
- self.reporter.clone().map(Facade::from),
+ self.reporter
+ .clone()
+ .map(|reporter| reporter.into_git_reporter()),
)
.await?;
@@ -1427,7 +1436,9 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
resource.git,
client.unmanaged.uncached_client(resource.url).clone(),
self.build_context.cache().bucket(CacheBucket::Git),
- self.reporter.clone().map(Facade::from),
+ self.reporter
+ .clone()
+ .map(|reporter| reporter.into_git_reporter()),
)
.await?;
@@ -1603,7 +1614,9 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
&source.git,
client.unmanaged.uncached_client(&source.url).clone(),
self.build_context.cache().bucket(CacheBucket::Git),
- self.reporter.clone().map(Facade::from),
+ self.reporter
+ .clone()
+ .map(|reporter| reporter.into_git_reporter()),
)
.await?;
}
@@ -1614,7 +1627,9 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
source.git,
client.unmanaged.uncached_client(source.url).clone(),
self.build_context.cache().bucket(CacheBucket::Git),
- self.reporter.clone().map(Facade::from),
+ self.reporter
+ .clone()
+ .map(|reporter| reporter.into_git_reporter()),
)
.await?;
}
diff --git a/crates/uv-git/src/resolver.rs b/crates/uv-git/src/resolver.rs
index e0636ec243d3..741ee7acb9d2 100644
--- a/crates/uv-git/src/resolver.rs
+++ b/crates/uv-git/src/resolver.rs
@@ -44,7 +44,7 @@ impl GitResolver {
url: &GitUrl,
client: ClientWithMiddleware,
cache: PathBuf,
- reporter: Option,
+ reporter: Option>,
) -> Result {
debug!("Resolving source distribution from Git: {url}");
@@ -88,7 +88,7 @@ impl GitResolver {
url: &GitUrl,
client: ClientWithMiddleware,
cache: PathBuf,
- reporter: Option,
+ reporter: Option>,
) -> Result {
debug!("Fetching source distribution from Git: {url}");
@@ -138,7 +138,7 @@ impl GitResolver {
/// For example, given a Git dependency with a reference to a branch or tag, return a URL
/// with a precise reference to the current commit of that branch or tag.
///
- /// This method takes into account various normalizations that are independent from the Git
+ /// This method takes into account various normalizations that are independent of the Git
/// layer. For example: removing `#subdirectory=pkg_dir`-like fragments, and removing `git+`
/// prefix kinds.
///
diff --git a/crates/uv-git/src/source.rs b/crates/uv-git/src/source.rs
index 5b475af90fd6..eff21786e53c 100644
--- a/crates/uv-git/src/source.rs
+++ b/crates/uv-git/src/source.rs
@@ -4,6 +4,7 @@
use std::borrow::Cow;
use std::path::{Path, PathBuf};
+use std::sync::Arc;
use anyhow::Result;
use reqwest_middleware::ClientWithMiddleware;
@@ -24,11 +25,11 @@ pub struct GitSource {
/// The path to the Git source database.
cache: PathBuf,
/// The reporter to use for this source.
- reporter: Option>,
+ reporter: Option>,
}
impl GitSource {
- /// Initialize a new Git source.
+ /// Initialize a [`GitSource`] with the given Git URL, HTTP client, and cache path.
pub fn new(
git: GitUrl,
client: impl Into,
@@ -42,11 +43,11 @@ impl GitSource {
}
}
- /// Set the [`Reporter`] to use for this `GIt` source.
+ /// Set the [`Reporter`] to use for the [`GitSource`].
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
- reporter: Some(Box::new(reporter)),
+ reporter: Some(reporter),
..self
}
}
diff --git a/crates/uv-installer/src/installer.rs b/crates/uv-installer/src/installer.rs
index f8fa0cbc0980..e2db41b4ce98 100644
--- a/crates/uv-installer/src/installer.rs
+++ b/crates/uv-installer/src/installer.rs
@@ -1,21 +1,22 @@
+use std::convert;
+use std::sync::{Arc, LazyLock};
+
use anyhow::{Context, Error, Result};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
-use std::convert;
-use std::sync::LazyLock;
use tokio::sync::oneshot;
use tracing::instrument;
-use uv_install_wheel::{linker::LinkMode, Layout};
use uv_cache::Cache;
use uv_configuration::RAYON_INITIALIZE;
use uv_distribution_types::CachedDist;
+use uv_install_wheel::{linker::LinkMode, Layout};
use uv_python::PythonEnvironment;
pub struct Installer<'a> {
venv: &'a PythonEnvironment,
link_mode: LinkMode,
cache: Option<&'a Cache>,
- reporter: Option>,
+ reporter: Option>,
installer_name: Option,
installer_metadata: bool,
}
@@ -50,9 +51,9 @@ impl<'a> Installer<'a> {
/// Set the [`Reporter`] to use for this installer.
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
- reporter: Some(Box::new(reporter)),
+ reporter: Some(reporter),
..self
}
}
@@ -66,7 +67,7 @@ impl<'a> Installer<'a> {
}
}
- /// Set the whether to link Uv specific files in dist-info
+ /// Set whether to install uv-specifier files in the dist-info directory.
#[must_use]
pub fn with_installer_metadata(self, installer_metadata: bool) -> Self {
Self {
@@ -151,7 +152,7 @@ fn install(
layout: Layout,
installer_name: Option,
link_mode: LinkMode,
- reporter: Option>,
+ reporter: Option>,
relocatable: bool,
installer_metadata: bool,
) -> Result> {
diff --git a/crates/uv-installer/src/preparer.rs b/crates/uv-installer/src/preparer.rs
index 4f3e3e46e1ad..4b53654d0fb2 100644
--- a/crates/uv-installer/src/preparer.rs
+++ b/crates/uv-installer/src/preparer.rs
@@ -48,15 +48,16 @@ impl<'a, Context: BuildContext> Preparer<'a, Context> {
/// Set the [`Reporter`] to use for operations.
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
- let reporter: Arc = Arc::new(reporter);
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
tags: self.tags,
cache: self.cache,
hashes: self.hashes,
build_options: self.build_options,
- database: self.database.with_reporter(Facade::from(reporter.clone())),
- reporter: Some(reporter.clone()),
+ database: self
+ .database
+ .with_reporter(reporter.clone().into_distribution_reporter()),
+ reporter: Some(reporter),
}
}
@@ -271,15 +272,20 @@ pub trait Reporter: Send + Sync {
fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize);
}
-/// A facade for converting from [`Reporter`] to [`uv_git::Reporter`].
-struct Facade {
- reporter: Arc,
+impl dyn Reporter {
+ /// Converts this reporter to a [`uv_distribution::Reporter`].
+ pub(crate) fn into_distribution_reporter(
+ self: Arc,
+ ) -> Arc {
+ Arc::new(Facade {
+ reporter: self.clone(),
+ })
+ }
}
-impl From> for Facade {
- fn from(reporter: Arc) -> Self {
- Self { reporter }
- }
+/// A facade for converting from [`Reporter`] to [`uv_distribution::Reporter`].
+struct Facade {
+ reporter: Arc,
}
impl uv_distribution::Reporter for Facade {
diff --git a/crates/uv-requirements/src/extras.rs b/crates/uv-requirements/src/extras.rs
index 2d72f6b1f214..d8c2e6959271 100644
--- a/crates/uv-requirements/src/extras.rs
+++ b/crates/uv-requirements/src/extras.rs
@@ -37,7 +37,7 @@ impl<'a, Context: BuildContext> ExtrasResolver<'a, Context> {
/// Set the [`Reporter`] to use for this resolver.
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
database: self.database.with_reporter(reporter),
..self
diff --git a/crates/uv-requirements/src/lookahead.rs b/crates/uv-requirements/src/lookahead.rs
index 77c18ea81ab1..3f191ed172e7 100644
--- a/crates/uv-requirements/src/lookahead.rs
+++ b/crates/uv-requirements/src/lookahead.rs
@@ -67,7 +67,7 @@ impl<'a, Context: BuildContext> LookaheadResolver<'a, Context> {
/// Set the [`Reporter`] to use for this resolver.
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
database: self.database.with_reporter(reporter),
..self
diff --git a/crates/uv-requirements/src/source_tree.rs b/crates/uv-requirements/src/source_tree.rs
index 5c55c9dff392..399120fd6dcb 100644
--- a/crates/uv-requirements/src/source_tree.rs
+++ b/crates/uv-requirements/src/source_tree.rs
@@ -65,7 +65,7 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> {
/// Set the [`Reporter`] to use for this resolver.
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
database: self.database.with_reporter(reporter),
..self
diff --git a/crates/uv-requirements/src/unnamed.rs b/crates/uv-requirements/src/unnamed.rs
index 252cc368e1d5..9356e42dea7c 100644
--- a/crates/uv-requirements/src/unnamed.rs
+++ b/crates/uv-requirements/src/unnamed.rs
@@ -49,7 +49,7 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
/// Set the [`Reporter`] to use for this resolver.
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
database: self.database.with_reporter(reporter),
..self
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index e6d36cbdb0bc..270940ee16e6 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -73,7 +73,6 @@ pub use crate::resolver::provider::{
DefaultResolverProvider, MetadataResponse, PackageVersionsResult, ResolverProvider,
VersionsResponse, WheelMetadataResult,
};
-use crate::resolver::reporter::Facade;
pub use crate::resolver::reporter::{BuildId, Reporter};
use crate::yanks::AllowedYanks;
use crate::{marker, DependencyMode, Exclusions, FlatIndex, Options, ResolutionMode, VersionMap};
@@ -243,15 +242,15 @@ impl
/// Set the [`Reporter`] to use for this installer.
#[must_use]
- pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
- let reporter = Arc::new(reporter);
-
+ pub fn with_reporter(self, reporter: Arc) -> Self {
Self {
state: ResolverState {
reporter: Some(reporter.clone()),
..self.state
},
- provider: self.provider.with_reporter(Facade { reporter }),
+ provider: self
+ .provider
+ .with_reporter(reporter.into_distribution_reporter()),
}
}
diff --git a/crates/uv-resolver/src/resolver/provider.rs b/crates/uv-resolver/src/resolver/provider.rs
index 03cda439d294..2d6173a8c6ba 100644
--- a/crates/uv-resolver/src/resolver/provider.rs
+++ b/crates/uv-resolver/src/resolver/provider.rs
@@ -2,7 +2,7 @@ use std::future::Future;
use std::sync::Arc;
use uv_configuration::BuildOptions;
-use uv_distribution::{ArchiveMetadata, DistributionDatabase};
+use uv_distribution::{ArchiveMetadata, DistributionDatabase, Reporter};
use uv_distribution_types::{Dist, IndexCapabilities, IndexUrl, InstalledDist, RequestedDist};
use uv_normalize::PackageName;
use uv_pep440::{Version, VersionSpecifiers};
@@ -97,9 +97,9 @@ pub trait ResolverProvider {
dist: &'io InstalledDist,
) -> impl Future + 'io;
- /// Set the [`uv_distribution::Reporter`] to use for this installer.
+ /// Set the [`Reporter`] to use for this installer.
#[must_use]
- fn with_reporter(self, reporter: impl uv_distribution::Reporter + 'static) -> Self;
+ fn with_reporter(self, reporter: Arc) -> Self;
}
/// The main IO backend for the resolver, which does cached requests network requests using the
@@ -273,9 +273,9 @@ impl<'a, Context: BuildContext> ResolverProvider for DefaultResolverProvider<'a,
}
}
- /// Set the [`uv_distribution::Reporter`] to use for this installer.
+ /// Set the [`Reporter`] to use for this installer.
#[must_use]
- fn with_reporter(self, reporter: impl uv_distribution::Reporter + 'static) -> Self {
+ fn with_reporter(self, reporter: Arc) -> Self {
Self {
fetcher: self.fetcher.with_reporter(reporter),
..self
diff --git a/crates/uv-resolver/src/resolver/reporter.rs b/crates/uv-resolver/src/resolver/reporter.rs
index 3010ec6388c8..f2bf0f006a2a 100644
--- a/crates/uv-resolver/src/resolver/reporter.rs
+++ b/crates/uv-resolver/src/resolver/reporter.rs
@@ -37,9 +37,20 @@ pub trait Reporter: Send + Sync {
fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize);
}
+impl dyn Reporter {
+ /// Converts this reporter to a [`uv_distribution::Reporter`].
+ pub(crate) fn into_distribution_reporter(
+ self: Arc,
+ ) -> Arc {
+ Arc::new(Facade {
+ reporter: self.clone(),
+ })
+ }
+}
+
/// A facade for converting from [`Reporter`] to [`uv_distribution::Reporter`].
-pub(crate) struct Facade {
- pub(crate) reporter: Arc,
+struct Facade {
+ reporter: Arc,
}
impl uv_distribution::Reporter for Facade {
diff --git a/crates/uv/src/commands/pip/operations.rs b/crates/uv/src/commands/pip/operations.rs
index 0e854eb54eb7..34604c3506e3 100644
--- a/crates/uv/src/commands/pip/operations.rs
+++ b/crates/uv/src/commands/pip/operations.rs
@@ -6,6 +6,7 @@ use owo_colors::OwoColorize;
use std::collections::{BTreeSet, HashSet};
use std::fmt::Write;
use std::path::PathBuf;
+use std::sync::Arc;
use tracing::debug;
use uv_tool::InstalledTools;
@@ -138,7 +139,7 @@ pub(crate) async fn resolve(
index,
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
)
- .with_reporter(ResolverReporter::from(printer))
+ .with_reporter(Arc::new(ResolverReporter::from(printer)))
.resolve(unnamed.into_iter())
.await?,
);
@@ -152,7 +153,7 @@ pub(crate) async fn resolve(
index,
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
)
- .with_reporter(ResolverReporter::from(printer))
+ .with_reporter(Arc::new(ResolverReporter::from(printer)))
.resolve(source_trees.iter().map(PathBuf::as_path))
.await?;
@@ -221,7 +222,7 @@ pub(crate) async fn resolve(
index,
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
)
- .with_reporter(ResolverReporter::from(printer))
+ .with_reporter(Arc::new(ResolverReporter::from(printer)))
.resolve(unnamed.into_iter())
.await?,
);
@@ -251,7 +252,7 @@ pub(crate) async fn resolve(
index,
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
)
- .with_reporter(ResolverReporter::from(printer))
+ .with_reporter(Arc::new(ResolverReporter::from(printer)))
.resolve(&resolver_env)
.await?
}
@@ -297,7 +298,7 @@ pub(crate) async fn resolve(
installed_packages,
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
)?
- .with_reporter(reporter);
+ .with_reporter(Arc::new(reporter));
resolver.resolve().await?
};
@@ -460,7 +461,9 @@ pub(crate) async fn install(
build_options,
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
)
- .with_reporter(PrepareReporter::from(printer).with_length(remote.len() as u64));
+ .with_reporter(Arc::new(
+ PrepareReporter::from(printer).with_length(remote.len() as u64),
+ ));
let wheels = preparer
.prepare(remote.clone(), in_flight, resolution)
@@ -519,7 +522,9 @@ pub(crate) async fn install(
.with_link_mode(link_mode)
.with_cache(cache)
.with_installer_metadata(installer_metadata)
- .with_reporter(InstallReporter::from(printer).with_length(installs.len() as u64))
+ .with_reporter(Arc::new(
+ InstallReporter::from(printer).with_length(installs.len() as u64),
+ ))
// This technically can block the runtime, but we are on the main thread and
// have no other running tasks at this point, so this lets us avoid spawning a blocking
// task.
diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs
index 1220d532dcda..c721f1527d06 100644
--- a/crates/uv/src/commands/project/add.rs
+++ b/crates/uv/src/commands/project/add.rs
@@ -1,12 +1,12 @@
-use std::collections::hash_map::Entry;
-use std::fmt::Write;
-use std::io;
-use std::path::{Path, PathBuf};
-
use anyhow::{bail, Context, Result};
use itertools::Itertools;
use owo_colors::OwoColorize;
use rustc_hash::{FxBuildHasher, FxHashMap};
+use std::collections::hash_map::Entry;
+use std::fmt::Write;
+use std::io;
+use std::path::{Path, PathBuf};
+use std::sync::Arc;
use tracing::debug;
use url::Url;
@@ -350,7 +350,7 @@ pub(crate) async fn add(
state.index(),
DistributionDatabase::new(&client, &build_dispatch, concurrency.downloads),
)
- .with_reporter(ResolverReporter::from(printer))
+ .with_reporter(Arc::new(ResolverReporter::from(printer)))
.resolve(unnamed.into_iter())
.await?,
);
diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs
index ec1b4c866a70..083aa940fc5e 100644
--- a/crates/uv/src/commands/project/lock.rs
+++ b/crates/uv/src/commands/project/lock.rs
@@ -3,6 +3,7 @@
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Write;
use std::path::Path;
+use std::sync::Arc;
use owo_colors::OwoColorize;
use rustc_hash::{FxBuildHasher, FxHashMap};
@@ -625,7 +626,7 @@ async fn do_lock(
// Resolve the requirements.
let resolution = pip::operations::resolve(
ExtrasResolver::new(&hasher, state.index(), database)
- .with_reporter(ResolverReporter::from(printer))
+ .with_reporter(Arc::new(ResolverReporter::from(printer)))
.resolve(target.members_requirements())
.await
.map_err(|err| ProjectError::Operation(err.into()))?
diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs
index d7807c29a614..0c96a5b52ac2 100644
--- a/crates/uv/src/commands/project/mod.rs
+++ b/crates/uv/src/commands/project/mod.rs
@@ -1,6 +1,7 @@
use std::collections::BTreeSet;
use std::fmt::Write;
use std::path::{Path, PathBuf};
+use std::sync::Arc;
use itertools::Itertools;
use owo_colors::OwoColorize;
@@ -1077,7 +1078,7 @@ pub(crate) async fn resolve_names(
state.index(),
DistributionDatabase::new(&client, &build_dispatch, concurrency.downloads),
)
- .with_reporter(ResolverReporter::from(printer))
+ .with_reporter(Arc::new(ResolverReporter::from(printer)))
.resolve(unnamed.into_iter())
.await?,
);
diff --git a/crates/uv/src/commands/reporters.rs b/crates/uv/src/commands/reporters.rs
index 0ceef7f8dac3..1e3fdb1e50cd 100644
--- a/crates/uv/src/commands/reporters.rs
+++ b/crates/uv/src/commands/reporters.rs
@@ -6,6 +6,7 @@ use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use owo_colors::OwoColorize;
use rustc_hash::FxHashMap;
use url::Url;
+
use uv_cache::Removal;
use uv_distribution_types::{
BuildableSource, CachedDist, DistributionMetadata, Name, SourceDist, VersionOrUrlRef,
From 243d2f8d5debd3f15c537294136824f559a3bb8c Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Mon, 6 Jan 2025 12:24:05 -0500
Subject: [PATCH 038/135] Redact new index credentials in `uv add` (#10329)
## Summary
Closes https://github.com/astral-sh/uv/issues/10328.
---
crates/uv-workspace/src/pyproject_mut.rs | 2 +-
crates/uv/tests/it/edit.rs | 87 ++++++++++++++++++++++++
2 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/crates/uv-workspace/src/pyproject_mut.rs b/crates/uv-workspace/src/pyproject_mut.rs
index cae147295100..248bcc1035b5 100644
--- a/crates/uv-workspace/src/pyproject_mut.rs
+++ b/crates/uv-workspace/src/pyproject_mut.rs
@@ -303,7 +303,7 @@ impl PyProjectTomlMut {
.and_then(|url| Url::parse(url).ok())
.is_none_or(|url| CanonicalUrl::new(&url) != CanonicalUrl::new(index.url.url()))
{
- let mut formatted = Formatted::new(index.url.to_string());
+ let mut formatted = Formatted::new(index.url.redacted().to_string());
if let Some(value) = table.get("url").and_then(Item::as_value) {
if let Some(prefix) = value.decor().prefix() {
formatted.decor_mut().set_prefix(prefix.clone());
diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs
index 7d57985dec5a..9deb6039022e 100644
--- a/crates/uv/tests/it/edit.rs
+++ b/crates/uv/tests/it/edit.rs
@@ -7039,6 +7039,93 @@ fn add_default_index_url() -> Result<()> {
fn add_index_credentials() -> Result<()> {
let context = TestContext::new("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.12"
+ dependencies = []
+ "#})?;
+
+ // Provide credentials for the index via the environment variable.
+ uv_snapshot!(context.filters(), context.add().arg("iniconfig==2.0.0").env(EnvVars::UV_DEFAULT_INDEX, "https://public:heron@pypi-proxy.fly.dev/basic-auth/simple"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 2 packages in [TIME]
+ Prepared 1 package in [TIME]
+ Installed 1 package in [TIME]
+ + iniconfig==2.0.0
+ "###);
+
+ let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ pyproject_toml, @r###"
+ [project]
+ name = "project"
+ version = "0.1.0"
+ requires-python = ">=3.12"
+ dependencies = [
+ "iniconfig==2.0.0",
+ ]
+
+ [[tool.uv.index]]
+ url = "https://pypi-proxy.fly.dev/basic-auth/simple"
+ default = true
+ "###
+ );
+ });
+
+ let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock"))?;
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.12"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [[package]]
+ name = "iniconfig"
+ version = "2.0.0"
+ source = { registry = "https://pypi-proxy.fly.dev/basic-auth/simple" }
+ sdist = { url = "https://pypi-proxy.fly.dev/basic-auth/files/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+ wheels = [
+ { url = "https://pypi-proxy.fly.dev/basic-auth/files/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+ ]
+
+ [[package]]
+ name = "project"
+ version = "0.1.0"
+ source = { virtual = "." }
+ dependencies = [
+ { name = "iniconfig" },
+ ]
+
+ [package.metadata]
+ requires-dist = [{ name = "iniconfig", specifier = "==2.0.0" }]
+ "###
+ );
+ });
+
+ Ok(())
+}
+
+#[test]
+fn existing_index_credentials() -> Result<()> {
+ let context = TestContext::new("3.12");
+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! {r#"
[project]
From 09de67039bf00c01dc58cf8766e0c54e6c294a39 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Mon, 6 Jan 2025 13:41:44 -0500
Subject: [PATCH 039/135] Tweak script `--no-project` comment (#10331)
---
docs/guides/scripts.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/guides/scripts.md b/docs/guides/scripts.md
index 1b337986049f..0696af57ac4e 100644
--- a/docs/guides/scripts.md
+++ b/docs/guides/scripts.md
@@ -74,7 +74,7 @@ install the current project before running the script. If your script does not d
project, use the `--no-project` flag to skip this:
```console
-$ # Note, it is important that the flag comes _before_ the script
+$ # Note: the `--no-project` flag must be provided _before_ the script name.
$ uv run --no-project example.py
```
From 1b34859d7ef88b9bcdb00ee5a418306debd628bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Olav=20R=C3=B8nnestad=20Birkeland?=
<6450056+o-l-a-v@users.noreply.github.com>
Date: Mon, 6 Jan 2025 20:18:01 +0100
Subject: [PATCH 040/135] docs: installation.md add info about installing with
Scoop (#10332)
## Summary
Add info about uv being available in Scoop.
## Test Plan
`uvx --with-requirements .\docs\requirements.txt mkdocs serve
--config-file mkdocs.public.yml` worked.
---
docs/getting-started/installation.md | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
index 47a7e0fd3170..705afb7e66ee 100644
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -105,14 +105,22 @@ uv is available in the core Homebrew packages.
$ brew install uv
```
-### Winget
+### WinGet
-uv is available via [winget](https://winstall.app/apps/astral-sh.uv).
+uv is available via [WinGet](https://winstall.app/apps/astral-sh.uv).
```console
$ winget install --id=astral-sh.uv -e
```
+### Scoop
+
+uv is available via [Scoop](https://scoop.sh/#/apps?q=uv).
+
+```console
+$ scoop install main/uv
+```
+
### Docker
uv provides a Docker image at
From eaaf8896eda7dd532849ff5f672b32a6592e77d8 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 6 Jan 2025 13:19:35 -0600
Subject: [PATCH 041/135] Sync latest Python releases (#10333)
Automated update for Python releases.
---------
Co-authored-by: zanieb <2586601+zanieb@users.noreply.github.com>
Co-authored-by: Zanie Blue
---
crates/uv-python/download-metadata.json | 1148 ++++++++++++-----
crates/uv-python/src/downloads.inc | 788 ++++++++---
.../uv-python/template-download-metadata.py | 3 +-
3 files changed, 1416 insertions(+), 523 deletions(-)
diff --git a/crates/uv-python/download-metadata.json b/crates/uv-python/download-metadata.json
index 6ed5bb690a53..ffc83675c803 100644
--- a/crates/uv-python/download-metadata.json
+++ b/crates/uv-python/download-metadata.json
@@ -1,4 +1,500 @@
{
+ "cpython-3.14.0a3-darwin-aarch64-none": {
+ "name": "cpython",
+ "arch": {
+ "family": "aarch64",
+ "variant": null
+ },
+ "os": "darwin",
+ "libc": "none",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "4d474a12d52c316f0b68790fc1f8d747cc665f3d9a4d44c8f6757b8e4aeed497",
+ "variant": null
+ },
+ "cpython-3.14.0a3-darwin-x86_64-none": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": null
+ },
+ "os": "darwin",
+ "libc": "none",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "f5da54ddec8866113cb2809e1dec0ec0f4617a149b21945da7b8c8f5ccab8e5e",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-aarch64-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "aarch64",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "2796c6573a5359c88cb8c8b2daa561c7377ad7876f2fe45983fb977b1d10e208",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-armv7-gnueabi": {
+ "name": "cpython",
+ "arch": {
+ "family": "armv7",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnueabi",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ "sha256": "de6d76889576fdfb63a6f0df3df651bed4c9586a6c4c352a3d5dc2d202837d83",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-armv7-gnueabihf": {
+ "name": "cpython",
+ "arch": {
+ "family": "armv7",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnueabihf",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ "sha256": "820f889a25e0ceea42742b3496ea7ffddf323a61db4a97c3712554b254650de9",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-powerpc64le-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "powerpc64le",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "e4595b35c5d0040527b9fe32ae53a3ad223ec931ccd579d0d5dd6235eaf81378",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-s390x-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "s390x",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "0a632b7493756ea057f0037a0794bb3bcaa32816fd57dde9dd6103dddc0099f8",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-x86_64-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "f143d48894e9c865a520982f28a575f138623b5ca14ca082f126171ccba46696",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-x86_64_v2-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v2"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "7aed85dc32191b6601abff3e55f1933795df97fada0982130d186f612b745407",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-x86_64_v3-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v3"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "d10663344c4bd115744afda54616053a005b51565ac9f383ff8c2bc097b20c2a",
+ "variant": null
+ },
+ "cpython-3.14.0a3-linux-x86_64_v4-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v4"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "091ba9cf1fe4810fc5fbf5513d5bec4bff255bbebba4605ea3825f8f4bb90d1d",
+ "variant": null
+ },
+ "cpython-3.14.0a3+freethreaded-darwin-aarch64-none": {
+ "name": "cpython",
+ "arch": {
+ "family": "aarch64",
+ "variant": null
+ },
+ "os": "darwin",
+ "libc": "none",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "81a500eb67385a03e7da7add8dac34a52dcb7ca391b2a4faa493f7ebc8b4ee78",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-darwin-x86_64-none": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": null
+ },
+ "os": "darwin",
+ "libc": "none",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "36a393f8d51d565e60ab3471c67c1dd029dc0f9bfb69bc0a890dc60a5764c0df",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-aarch64-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "aarch64",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ "sha256": "2af2e24d0bc0760c0e5ba2a3250c5a94624a3059d234d2bc922abb5904ca1a35",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-armv7-gnueabi": {
+ "name": "cpython",
+ "arch": {
+ "family": "armv7",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnueabi",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst",
+ "sha256": "5370785843b4cebd4d3a22d74d8c9d26179cb31ad438dcb17332922f5fc99889",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-armv7-gnueabihf": {
+ "name": "cpython",
+ "arch": {
+ "family": "armv7",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnueabihf",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst",
+ "sha256": "7572e0ef1cf8bde126c88a372f015af5d106cf507e4f2461379af5aa74b4806c",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-powerpc64le-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "powerpc64le",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ "sha256": "b727b1f3cd424e9fed726871223c64cca306e9a3074da3fe588aec2dd18f78a6",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-s390x-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "s390x",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ "sha256": "9de3a8dde0b43b1d69369b8d64f9b8174bee1a979e975fb69ed10d11875bac54",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-x86_64-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "f4f151237b4eb6c6b2a5ed34776f6621bf84f88e9195fe5fe4a44a4446bb2ba8",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-x86_64_v2-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v2"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "3b9bb813870f259c73a3400f8de211c3df03d3544d82b47a9785bb14174107ee",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-x86_64_v3-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v3"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "2f94f0c5df33e27d5e3f8e53cb96a78cf5f2453961ea496b5b72bb41ae855cc3",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+freethreaded-linux-x86_64_v4-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v4"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v4-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "ba9e511b020df7e04b5f78bb13d7cbc0776e4bddb41101fe26f4bfdaf7b2cd6a",
+ "variant": "freethreaded"
+ },
+ "cpython-3.14.0a3+debug-linux-aarch64-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "aarch64",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "0e2be777200b3c81ddf080804263959ac3922851143c5b63d0105e272134e5dd",
+ "variant": "debug"
+ },
+ "cpython-3.14.0a3+debug-linux-armv7-gnueabi": {
+ "name": "cpython",
+ "arch": {
+ "family": "armv7",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnueabi",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
+ "sha256": "056e61632cafb618b0479ea6f7ebf541f831fc8b4846a4e1b89e5a3b61edc270",
+ "variant": "debug"
+ },
+ "cpython-3.14.0a3+debug-linux-armv7-gnueabihf": {
+ "name": "cpython",
+ "arch": {
+ "family": "armv7",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnueabihf",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
+ "sha256": "4f319e38997e1ded281ecbb007a8735039aba67a5f778766e74e20023a6dcc57",
+ "variant": "debug"
+ },
+ "cpython-3.14.0a3+debug-linux-powerpc64le-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "powerpc64le",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "803e56d55390b03f559120b1e86eee5e80ca88649ba03b1a2a6493f19c586a56",
+ "variant": "debug"
+ },
+ "cpython-3.14.0a3+debug-linux-s390x-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "s390x",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-s390x-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "e1435d10067e927f5c5b64b43539b9be8b66d6205a33ad447f6c82edc8fd8c7c",
+ "variant": "debug"
+ },
+ "cpython-3.14.0a3+debug-linux-x86_64-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": null
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "7d52224d7f43a2d9d59a0bc1f8439eb5f12c182bc397a2370d331ac06ead81fc",
+ "variant": "debug"
+ },
+ "cpython-3.14.0a3+debug-linux-x86_64_v2-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v2"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "00ef15e2dbb1cb03b6321caeaac835907d82a4f4a1ac14c968d6812c5e2994f3",
+ "variant": "debug"
+ },
+ "cpython-3.14.0a3+debug-linux-x86_64_v3-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v3"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "58be4d73cc372b337bc4c90d5a6b7157ce7418545928c063f5822c94572db2ac",
+ "variant": "debug"
+ },
+ "cpython-3.14.0a3+debug-linux-x86_64_v4-gnu": {
+ "name": "cpython",
+ "arch": {
+ "family": "x86_64",
+ "variant": "v4"
+ },
+ "os": "linux",
+ "libc": "gnu",
+ "major": 3,
+ "minor": 14,
+ "patch": 0,
+ "prerelease": "a3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "a72cb0116a6511c20de6350a1fc664b96f8f5af4bdc10abdaa7208526a729fad",
+ "variant": "debug"
+ },
"cpython-3.13.1-darwin-aarch64-none": {
"name": "cpython",
"arch": {
@@ -11,8 +507,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "25ca42cfac68cfa6bd6aa6c950cb76c950dca56e01ff00458120d3f62967a498",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "26b2ceded19b2eac3f0ae59d895f5032b541270bc65288c0ea36c6d8ae25e234",
"variant": null
},
"cpython-3.13.1-darwin-x86_64-none": {
@@ -27,8 +523,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "cf3cfc41dc3017732d448f7273ba9a77a5629e3605459feff756922721c410fd",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "d1207ac02ae14a8714a5c15277396719421cd8c60184f4961905bcc6d7978c83",
"variant": null
},
"cpython-3.13.1-linux-aarch64-gnu": {
@@ -43,8 +539,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "c801a80c40ad36d7e3d38d2d550bb64b39af0efef7081a4b1de3e4a43ece5c0d",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "3c720649b2af9873262429074826ef0e35f98140e38df365b36a668573a86cf0",
"variant": null
},
"cpython-3.13.1-linux-armv7-gnueabi": {
@@ -59,8 +555,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- "sha256": "8c32b8a7a210a108ba3bf5391ecba48d30b5794c2d27783fdcec50b95797157e",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ "sha256": "660a036eced9c1052a80e152f0cfe08dde43cbc38ca73c6b969920e9382dfbe8",
"variant": null
},
"cpython-3.13.1-linux-armv7-gnueabihf": {
@@ -75,8 +571,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- "sha256": "e854912d185dfd6f1e8606cb4a74a95a7535a7c95c58b5be78f4014296b488be",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ "sha256": "48e0ee03b779be8485f2f74728b2047708626af14e2c6711a384040afcbb8ad5",
"variant": null
},
"cpython-3.13.1-linux-powerpc64le-gnu": {
@@ -91,8 +587,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "dc519bd25ee659db1b5f1f05e342fdc9c74c348b49f793ca3536f33e189002b0",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "8408e87648118b626d79ccf1b1aa0cbf427442c46b13983f1fa07bc4b7d2cb7c",
"variant": null
},
"cpython-3.13.1-linux-s390x-gnu": {
@@ -107,8 +603,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "f15df22d793733a457115292289091fbf51d64af042800b1218daa2a0498105f",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "2c6649a03c11582b3b3de95ccc25ecec39e7407ad60ac0f274ef48260070d97f",
"variant": null
},
"cpython-3.13.1-linux-x86_64-gnu": {
@@ -123,8 +619,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "05b3bbc97d113b64f4a0dab680e0dbe73e9f59ce8304312ef64d4b6c2822c520",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "726a608e2867c2867caa2cf3f668cb15018456070eee32375675f190946650bc",
"variant": null
},
"cpython-3.13.1-linux-x86_64-musl": {
@@ -139,8 +635,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "5bfb969703d2f26c26e3a31b07145d40c7a3910052fdc5f95a199868908c78e3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "a7c27836ff102e92d0c604ab5d06a0f9c474c3d87a01689f477c99e3ce4b8a67",
"variant": null
},
"cpython-3.13.1-linux-x86_64_v2-gnu": {
@@ -155,8 +651,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "07af19e001b8bce915296e2f0aaa32314515864d735175d52d604f8f8fef55ad",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "6778cc7eab5e9ee64b880025300dbf36f93bded39a23681e5977004fcecd9cfa",
"variant": null
},
"cpython-3.13.1-linux-x86_64_v2-musl": {
@@ -171,8 +667,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "27be1c4a64b3b1ace70f3e72662405c393b5e50690516b755f56c65582235e24",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "212486199b7fd545be66e363051b93297e044353ea035e7dbe94657cafa23e83",
"variant": null
},
"cpython-3.13.1-linux-x86_64_v3-gnu": {
@@ -187,8 +683,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "d56109dd712199b25477154a93eb68b867ad46d5049c07869f8161c5c9271fab",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "e0a7e81a06987129aebc5cd28b1acbc2f5dfb615cb675da37ac8462dbd421ef5",
"variant": null
},
"cpython-3.13.1-linux-x86_64_v3-musl": {
@@ -203,8 +699,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "4949fd6a337418c9eb7e55871619502b96e8be712711f61414f62a161456b83c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "a827eb8b5f736fb060a154a85091f800de320a412204c11e0e34208ebc32f707",
"variant": null
},
"cpython-3.13.1-linux-x86_64_v4-gnu": {
@@ -219,8 +715,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "410460370f55da3b4778f89acafc3ec02d2aa390fb9b48f6b5dbdccfd60d39ac",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "5d7631269cef71e59c5f0bb90c45dca550252c6a54f1b5ec5297fb44351ef94d",
"variant": null
},
"cpython-3.13.1-linux-x86_64_v4-musl": {
@@ -235,8 +731,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "aa0316647307516ee1028055f0df98f05656033f1968d6a935dd1a34dde955b7",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "f3f43199919c40d974f6c616b5e8ce820af085c0218fb2415fecbba85c15a9c1",
"variant": null
},
"cpython-3.13.1-windows-i686-none": {
@@ -251,8 +747,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "082907fea39786100190137cdcf2d2e3672a33ef90e88ad0b669ba0c5205ec7d",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "972858c2d658daa2bd7b8c6bbfe81c5252990323fb64acee2eb0b033d9ad9042",
"variant": null
},
"cpython-3.13.1-windows-x86_64-none": {
@@ -267,8 +763,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "af37b475224eb9d37aa90e68094bbc5f9186dd421eb0fdff59f24c34a1eed4f0",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "45c77db3fefd2ea1ef1c90c8595edfd6d12a53bc5c6cbaa5a2dd5b4a29a045ec",
"variant": null
},
"cpython-3.13.1+freethreaded-darwin-aarch64-none": {
@@ -283,8 +779,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
- "sha256": "b7044c2d584eb2261edb18db58a51b8b164e3cf2101a650f7be6d850a2859474",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "49f7bd481b69a99fbbd444bf03e078c7bcdc30f656042cb2cb72d6dd258425e1",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-darwin-x86_64-none": {
@@ -299,8 +795,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
- "sha256": "ccff425786a59f387d449eeff7d8afeab2d11cf2aa3631b5777e7d697f60bacd",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "a11eb407678ac127269e3d7aff658a94b14ab42cb11e2eaa057d6d7398c486ba",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-aarch64-gnu": {
@@ -315,8 +811,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
- "sha256": "5d0b6ab67f02f343348560e32664d5cf4343aa016f34334fc178fe976e5ea74b",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ "sha256": "f8910a48a7ce3587f21bd080c963a49441ac8c621d38424e3e69ca8dcc65b242",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-armv7-gnueabi": {
@@ -331,8 +827,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst",
- "sha256": "4373948ca535d2c011ddc42e9d7d6b415ac1c2495d2c8ec414cc22042ae0811c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst",
+ "sha256": "ba624a019bf9ae15d4ea271537e0e0a913463f2a1c7975c39fda87c1208a7977",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-armv7-gnueabihf": {
@@ -347,8 +843,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst",
- "sha256": "bbfb13cdf815bceffdf3b22d2992ddd24f09deb3cd3721285438151733c027db",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst",
+ "sha256": "87cd5e03afba6164009d3001cd97dcba613a23619801bcf4358dae28e36011c7",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-powerpc64le-gnu": {
@@ -363,8 +859,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
- "sha256": "9f82f0e05d37362be651f51a5028979b0409c0b4b3ceecd207b8b4539b8c72ef",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ "sha256": "b9f94f840b390a68e6c34cb0ae56be3a14c067cbdc34144f6edddeefda59391c",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-s390x-gnu": {
@@ -379,8 +875,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
- "sha256": "5b6bacbeb90bac09799f08f9937f811ab87c18e2654f9827f4ef2cb7832d8920",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ "sha256": "8ce257291bd99bdbfe93688febf5a5fe8b986370df72801b0414386bea4bc99d",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-x86_64-gnu": {
@@ -395,8 +891,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
- "sha256": "065368d84d0d271cf081cf8a3599a0bfdcab00d45ddf3cde487dc5f901992ab5",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "080133624e16068a8d74698fa673ce6f71914ca4d836a760758f383895f4e6b3",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-x86_64_v2-gnu": {
@@ -411,8 +907,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
- "sha256": "196aa926332003687eb8167e55d5761439890a763dc3f0a0758355af10979f9f",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "4d8121f8e42f18da69c8106eca6612b5edfb39ab4684af588c6626f413c78819",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-x86_64_v3-gnu": {
@@ -427,8 +923,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
- "sha256": "65176446a0aa53f8cdbcb967d3e70d09e6bb21b058657461041611dc0f6ac111",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "c756e498571375568928507fbb17bca22e8c02f22cb39fb10efa3bd3c745dc1f",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-linux-x86_64_v4-gnu": {
@@ -443,8 +939,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v4-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
- "sha256": "a3f43b7b6073ef7a932638c3fde128c9db306c26655ec0cdaf13af9e15f1f0cf",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v4-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ "sha256": "96866696116e5b886eca6052db15da38bb7ab07dce0148fdbc171f53b7da79ae",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-windows-i686-none": {
@@ -459,8 +955,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst",
- "sha256": "957da31c99b168d15116af5e1f3c285a5e0da9ed2c6440aa7080f49118c0931a",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst",
+ "sha256": "0455a9866b1b02055022e1dd9237875533c57e7bbcd8e7993da9ab16f719f1a8",
"variant": "freethreaded"
},
"cpython-3.13.1+freethreaded-windows-x86_64-none": {
@@ -475,8 +971,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst",
- "sha256": "e34828f6ae08e252b07a277f0021658963cddca8a858ae858397c1e5898d7e2a",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst",
+ "sha256": "e2c63cd840a808705f5dd72251d2e07d70ed497c2a36d151b7701ea2c68f8bc6",
"variant": "freethreaded"
},
"cpython-3.13.1+debug-linux-aarch64-gnu": {
@@ -491,8 +987,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "5d4dee75762392669cdf24e1ecef1015613fcd6e715af3bb7ff3859e47a76179",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "f60b09a91b610c48b3b7408e4616dbdb0e679c146b00a0171fe1d37d2c903390",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-armv7-gnueabi": {
@@ -507,8 +1003,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
- "sha256": "e4eafca449a02bd30cde2a2370d5719095f704cca7f1182af028704836ed9bb7",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
+ "sha256": "c0be744c74c148290183425e97c660a410af459fcfbb55fb108d3829a6450805",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-armv7-gnueabihf": {
@@ -523,8 +1019,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
- "sha256": "5fc2c50cf39fa339dec661f921396c9cf219e7093150a5606e8f1ec91119d8c5",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
+ "sha256": "516aa1e9922752034024b4dfffb06b69f469641fc9ef877c451345a680085b00",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-powerpc64le-gnu": {
@@ -539,8 +1035,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "5fbd41b63cd6c2cdd6f307b86490bafc35ad78db32379718df13aa3552ae3648",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "e7286cd32e05ada13ab77ff272e28ae4797053ff9ebca8e1c12c0317935899d9",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-s390x-gnu": {
@@ -555,8 +1051,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-s390x-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "ee1b01c237c297f9eccffe8a74e098324ee2a85ab450ac2a3872fb29eb7c3215",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-s390x-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "0896c6682de647c27c9e5f82e0dfc6fdb5ff5454852a8aa891c60dedeb1e2a1a",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-x86_64-gnu": {
@@ -571,8 +1067,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "852bd9da759f9aa4d0f3cde2f1cedf2189c1a88cb0ddfa5733dc2c8d9af308b1",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "e52e8e9d0e6b3187826d71c34a097037c8155f6d213a12e9ad5b060fe7725b0b",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-x86_64-musl": {
@@ -587,8 +1083,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "3ee21a32aad4740d8e2c48398c16d3d7caa28c22e6e8d0366d19946603c130ac",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "4bb7fdf90257208009a2b0ad26ed29c18d5bf865e39cdee03b85f0ed78f95b11",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-x86_64_v2-gnu": {
@@ -603,8 +1099,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "3a386d92162e9af301a0e783f8d9f4d223ac13bd6ce2b7417757dae1cd0d4423",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "cb58d762b220e328f083e548f47ada021a351d0a4d8cd121b1e3350be5e6b100",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-x86_64_v2-musl": {
@@ -619,8 +1115,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "f1ce84c832bccff8b625233402300df32c7d5d40621dd380512afed41dbbe687",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "1fdd3cfe6cc58f96298840ee550d6f7098bf57394461ff11edf5ce5d16c0ba2f",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-x86_64_v3-gnu": {
@@ -635,8 +1131,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "598ea7197de2fa5ff93d3417af5539d3b10d85c50b93fa944de469414c216822",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "220ea76af9ffb35008be23bdc5790ec38bcc786a1ddf7b1811ab5c7fda86b66c",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-x86_64_v3-musl": {
@@ -651,8 +1147,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "54d2c809f2098f9974d62d7c76665e79fe2c57c29b8ae96212e6c4e283e0e2d8",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "1c9d74f3dad974d4e255d6732947ac06c8e368a946c0751d38d8b15a4906a923",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-x86_64_v4-gnu": {
@@ -667,8 +1163,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "3c13e8010573aaf9012119626f6a842c19619feeb6d2670ab3d543ef61105f82",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "3fa9db6c3889e2f4f9d1d8b4bb06438828a3b28a4851061dea4e21ef5b6b372b",
"variant": "debug"
},
"cpython-3.13.1+debug-linux-x86_64_v4-musl": {
@@ -683,8 +1179,8 @@
"minor": 13,
"patch": 1,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "a84292aa6cdf1720aa5da3929cde2bf30e6838d7a36b52fdf48d09ac64adfb4a",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "ecbc6a5086bcd19207149aa8bbd7ab578d2603d6c3cdf154445cece74d2ae93d",
"variant": "debug"
},
"cpython-3.13.0-darwin-aarch64-none": {
@@ -2347,8 +2843,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "abe1de2494bb8b243fd507944f4d50292848fa00685d5288c858a72623a16635",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "3a31b4f82d589a0ff6efac3cdc1e55284cb67645470b78c5aa50f156c10a93d2",
"variant": null
},
"cpython-3.12.8-darwin-x86_64-none": {
@@ -2363,8 +2859,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "867c1af10f204224b571f8f2593fc9eb580fe0c2376224d1096ebe855ad8c722",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "00e2d29ce8a688f5d2f192718e6f87e4aedae88ac2a0958011b8af9a49e049a7",
"variant": null
},
"cpython-3.12.8-linux-aarch64-gnu": {
@@ -2379,8 +2875,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "fb983ec85952513f5f013674fcbf4306b1a142c50fcfd914c2c3f00c61a874b0",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "29df00dc1435f536c50396d1753fb507f81a81c93a04a7c24b145c9ad68798f8",
"variant": null
},
"cpython-3.12.8-linux-armv7-gnueabi": {
@@ -2395,8 +2891,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- "sha256": "0567907c0147753b54683ffdd3559d53aa4f10b203f067628586bdc545dddffb",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ "sha256": "d648de42934e775c78fc73638d35d021c9bf7306d03c505e10a270e70439bc9e",
"variant": null
},
"cpython-3.12.8-linux-armv7-gnueabihf": {
@@ -2411,8 +2907,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- "sha256": "d970da6a727a069ae765ae067ca93e369cf9881e5140e28f5030f6314595dd9b",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ "sha256": "fc1bf99d6c64386438d83e1cee031b2764c801e5480d7c221c5625f48bd7381a",
"variant": null
},
"cpython-3.12.8-linux-powerpc64le-gnu": {
@@ -2427,8 +2923,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "462b028ce314eb57ced802ce7f9459520365ce5d635d7ae24d2d532b78c9ee12",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "58b09124abbdd4408cafc6e93d7df29e21e346f6dde4df38b75dee3ef5f3ef60",
"variant": null
},
"cpython-3.12.8-linux-s390x-gnu": {
@@ -2443,8 +2939,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "6791e6572b6f412fab38e6032dac87c37d6870b12c598f17c76c230678a7a86d",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "f0b61f319a1e9d2dc03a086df2b7c4721bcdbcfd2dbf4fe24d7cb3fea75fb2bb",
"variant": null
},
"cpython-3.12.8-linux-x86_64-gnu": {
@@ -2459,8 +2955,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "698e53b264a9bcd35cfa15cd680c4d78b0878fa529838844b5ffd0cd661d6bc2",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "5591da823631467e204baa520ee379ba09b5699b2e2c322c456c44cb2c2e60eb",
"variant": null
},
"cpython-3.12.8-linux-x86_64-musl": {
@@ -2475,8 +2971,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "451c02f24d1883c2f8ed80914ca4d8567dbe429888332e7cc6cf597e8a7c2555",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "6f931dbe2058e3dfa1d5f381a501d3726ed2191ad464167904dd7b198cb852d2",
"variant": null
},
"cpython-3.12.8-linux-x86_64_v2-gnu": {
@@ -2491,8 +2987,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "9cf1273ae2fdbbbf308bd81ad2d0b384bec963097a83f8a6455bb187a7714d82",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "71fa45cf960cafd0097f4825d4539833077031e3152a439ab681c27a61665cdf",
"variant": null
},
"cpython-3.12.8-linux-x86_64_v2-musl": {
@@ -2507,8 +3003,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "630e0131c947a82b342cc8115e85a98a70bb1ebb1580c88b01e5c1dc61d25814",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "8c6430da267171cff9ac741e98d6c64785860af93f530df818ed7dcf57444d7c",
"variant": null
},
"cpython-3.12.8-linux-x86_64_v3-gnu": {
@@ -2523,8 +3019,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "07319b611e2563ef5281ab4233c78bab1eca02be498fec636a2233c46dd81334",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "3337c050775285e757406bcac24f221383958f75edb1e2ed923b0abdfd5ee350",
"variant": null
},
"cpython-3.12.8-linux-x86_64_v3-musl": {
@@ -2539,8 +3035,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "81769694bda1d525a9d9b7f493e71ae816ce939e208561c5b8b1362d6a484ed8",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "fad5ae4f20f39d0aadf42d7d79030eb5a3b56543808a5adba7020753718cc431",
"variant": null
},
"cpython-3.12.8-linux-x86_64_v4-gnu": {
@@ -2555,8 +3051,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "4e83c898973be409cf242c6dfd33a2576433f36f58f4000d6f120cc436edb5cb",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "f6f08b06acfded7fecdc62885e8ccc999cd2984dc64d792c8f8cf0a481a1623b",
"variant": null
},
"cpython-3.12.8-linux-x86_64_v4-musl": {
@@ -2571,8 +3067,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "d0c84e94534042361f715a8e1cf9139745ab6804ab0b78289b4dd13c533c9930",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "f5275e0573b082b3e75d424354eefe95ddcc85b7a550f6818d1fd03c5b1b9569",
"variant": null
},
"cpython-3.12.8-windows-i686-none": {
@@ -2587,8 +3083,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "cbdf8fca5d3a43e8fe06d2d56f969dbc46051439828211f73047b4800c9c5ed6",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "6af6bf6a7981cbe086b394c0d56c95e66d660f9b2dbfe452831990fe2da751c3",
"variant": null
},
"cpython-3.12.8-windows-x86_64-none": {
@@ -2603,8 +3099,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "1a702b3463cf87ec0d2e33902a47e95456053b0178fe96bd673c1dbb554f5d15",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "2c6af885033be26a8a2ac2e069aac4889c53ab9d212fdffe0d7c8157b66a3d14",
"variant": null
},
"cpython-3.12.8+debug-linux-aarch64-gnu": {
@@ -2619,8 +3115,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-aarch64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "d860dfa85e95daa534c810b734c1cd24457abee9f34fb044b280f1a04a371c2c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-aarch64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "7c71910f4d65ea7c3f466868d0ed218a8f3eb5dfeb14a25e53b9229237c4d0d5",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-armv7-gnueabi": {
@@ -2635,8 +3131,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
- "sha256": "2267a7c356858bbd99661f308c23af6e36a0ad383d39c44589df7f91154102b7",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
+ "sha256": "328c533bf6af310b7df899d92211ffb305b098c3399e5d5cf082c9c85b85e770",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-armv7-gnueabihf": {
@@ -2651,8 +3147,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
- "sha256": "214e513d5dc8ba88245566d40686442416c2b030f046a1052195a46464442af6",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
+ "sha256": "0bb92451ea907458b0a88d06451fc1b8c03c06e9d0eef320fd042ba5de6d1a8d",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-powerpc64le-gnu": {
@@ -2667,8 +3163,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "0da419b4733b298c9295a9beb169bd4ceb4fd644f17dc92738099c885e91ba53",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "b14918340ef09312ac34c188fc577484691846673dac48d75c54f79eb030e1c3",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-s390x-gnu": {
@@ -2683,8 +3179,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-s390x-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "ca9431ac327f86febe6952a8b27dab4bf61f427ee810b8864bb3fa4ab3447812",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-s390x-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "f16491a4bff9eed72ba96a8778b653ade5eade5df8d2ae5f6bb37973b3b3b044",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-x86_64-gnu": {
@@ -2699,8 +3195,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "4483afca7cfb89d31b2aa684d7a885cdc535c58de081dbb0dc0a84c28277ef6e",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "7f99e4132717dfafdeaa3bc20d3528d69f194eca7b15b589bb1ee0244af71538",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-x86_64-musl": {
@@ -2715,8 +3211,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "c541d6292f5e26fe4183a92f6d87f6942a30d3a9c67edaea83befb4c495f0e02",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "3034a8df442d5ec7bca64e71c5de4142406526b6a8a128acc80e72ff96a8971e",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-x86_64_v2-gnu": {
@@ -2731,8 +3227,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "5277a81cb19da7eab88f7dfff7131133aaa1ad40872b55e686d7ae1e21c44e68",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "34dcc79e4513974c9375ccc236a46742eab11628e6621562fba8a46fa8104352",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-x86_64_v2-musl": {
@@ -2747,8 +3243,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "b4fb9111ccd598c3e6a826fdfcf2b36f492f4d1968cbe295caa69de3d9847941",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "5604e197149fb37be4460b82e0d12c50a79eb9da8fd1fbeb6ad1cfa23b2e98f2",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-x86_64_v3-gnu": {
@@ -2763,8 +3259,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "f920911fd04de68530a803db01088de854c761b6fda535d5b2696e91795c5e78",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "b5bbc97d8e86f8319213a3932e21d5baa2dedc5a6c1c34a6daebc305c9907edf",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-x86_64_v3-musl": {
@@ -2779,8 +3275,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "686e1208496a0d76ab1c1bd7676c58178515caca72304bb28e8715d39d992f8c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "5c5aec9c91f1c6bd2701c64cd6ffb27a3da2a325147bf96226ad998d2312017a",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-x86_64_v4-gnu": {
@@ -2795,8 +3291,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "7e909c386db18316065ca7a88f89f1ed6549ac29c638dc67fc59a1abe8592e47",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "448385e9274e37b33c7001cec7864096a4f6e5c923c03c98a751904d5b43dea9",
"variant": "debug"
},
"cpython-3.12.8+debug-linux-x86_64_v4-musl": {
@@ -2811,8 +3307,8 @@
"minor": 12,
"patch": 8,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "94c9da55701f18c2a8b49e6981583c1af72ef1258c31c75c016a2766a2dd59e8",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "ed047f25b12696eca1830adb8f47226c49dcd7de74f418ca41442e60bc375023",
"variant": "debug"
},
"cpython-3.12.7-darwin-aarch64-none": {
@@ -6475,8 +6971,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "af82c85992dace78cd2682deb8e9ef41e448f66cb602f31e5b7c7af75a74bbf1",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "fc6cb8fa7ed727acdb317fb6e5149cdcd29128583e99a6dc14640a7e57a9cd34",
"variant": null
},
"cpython-3.11.11-darwin-x86_64-none": {
@@ -6491,8 +6987,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "d33b1ee09b2a9fe692cbc6e3ed0fbadb23c430233ce92e2954522c5594c6b274",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "5b036f1403725f91bd2fe5a90c5a763b14da4bc94182fb48e1a62d063de66c5e",
"variant": null
},
"cpython-3.11.11-linux-aarch64-gnu": {
@@ -6507,8 +7003,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "23fa4e8eeb3a9c7fff540b0fdd4047ae5d2e6f0caf5095e7d767ed96ccc7efe7",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "541c1d514cd2a3155f4a5805ccece9ac86f510b186e3cf1af7cfd8cf7097d0e9",
"variant": null
},
"cpython-3.11.11-linux-armv7-gnueabi": {
@@ -6523,8 +7019,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- "sha256": "d798c008dfde19f0ffdf3684f49adaf922873bee2ad1a2e5dd8d0dd2ad3a4699",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ "sha256": "01de7a51461cdb25014041c97bebbe80e1f99b1231a525aa7a2463e55347d26f",
"variant": null
},
"cpython-3.11.11-linux-armv7-gnueabihf": {
@@ -6539,8 +7035,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- "sha256": "e838902cb7d47fbf8636d30b59d27182bf818dc633eef4b47c8d28163689b5ff",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ "sha256": "0cad64bf6041f68fc960ab75e6e817cd36046331538c8e70533d709bedb6d8ef",
"variant": null
},
"cpython-3.11.11-linux-powerpc64le-gnu": {
@@ -6555,8 +7051,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "477dbc811b0f834bc3386126925d490ce734c2191a6e5022e1e0ebb346e5ddcf",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "69020a370009ab331c008f333ca6d8ff684b7b120300d195cc6fb0e6d6f641ac",
"variant": null
},
"cpython-3.11.11-linux-s390x-gnu": {
@@ -6571,8 +7067,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "4e6a9f5111d08e0421446de539224addca624865d1d1e0c5c6ce7b2382d6cf11",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "eaff12c96540a87aea624f98e439ce446afe88aca024d106aa3593f007e237d1",
"variant": null
},
"cpython-3.11.11-linux-x86_64-gnu": {
@@ -6587,8 +7083,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "a5448390ef50caaeac60c42806060d0d00488df0b61e7ab4df3b020cceb2bb73",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "56bda37ad88aaf404a8a1f77c725fde18dee6a2b8d30d361cf4b30a1f807d345",
"variant": null
},
"cpython-3.11.11-linux-x86_64-musl": {
@@ -6603,8 +7099,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "916b2d4336d9f672b5b1f0f15b0ee43504ee178244ec6656ce49de57ddefaa76",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "b9950749da7e6147cf8ad0838b9c7660a8e62d248771b3bbfef4d1f33bf2e998",
"variant": null
},
"cpython-3.11.11-linux-x86_64_v2-gnu": {
@@ -6619,8 +7115,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "267dbf5f9272bb2f8336d13b9f2468ff2b5659a5bf820afc564af49531dc676e",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "a8bd2b532d3401af71e5470eb41b85d1beb63accca9ea37c3e32cbe44d3119c6",
"variant": null
},
"cpython-3.11.11-linux-x86_64_v2-musl": {
@@ -6635,8 +7131,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "f9678be5d8366faefcabe11ce9db63a3172176e1f3d6f910ec007453fb2e114d",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "8c3961b653ea18c76dcf5e2ee05053bfdbe6353f8ba0515316f906354a200d54",
"variant": null
},
"cpython-3.11.11-linux-x86_64_v3-gnu": {
@@ -6651,8 +7147,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "3746c4c74ec610e4425b876a74161ebcd01aaf556d99017254d782e975d35f46",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "3cf193e4c8dd36bb9d1c6494f3e36fb6c2bf91a91ad724ee3ac38fa6fa958a65",
"variant": null
},
"cpython-3.11.11-linux-x86_64_v3-musl": {
@@ -6667,8 +7163,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "6662013e83d5f97c673f6b346f062d29c5a6dda36a088a0deabfd5dcf6fb9b09",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "0be47a46f66cd2d00f97a0a97cc2edede9983d856f4d6822a8ad96a574a463e0",
"variant": null
},
"cpython-3.11.11-linux-x86_64_v4-gnu": {
@@ -6683,8 +7179,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "a93ebc464644990638ae3029c9bbea6249eb90047152dfb98cbaac66da1c199e",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "9e27ab9e35c73caca1ab607364456b7e8279def51855ec73b863cafb51f2d493",
"variant": null
},
"cpython-3.11.11-linux-x86_64_v4-musl": {
@@ -6699,8 +7195,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "5d01a991ad42ad49569145d86752aa79d881d49dd9503c8304ac9a7cea0b83c8",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "b24e33285bbe050cd122e395c96420a2067449d4d27f94928d89bb2cb6d2064b",
"variant": null
},
"cpython-3.11.11-windows-i686-none": {
@@ -6715,8 +7211,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "063b3582b31d4b5c918439701ad4037cec9b90c8e2e4c55c502a9001c638ebc6",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "026d777d86ebfe687cebda919aaa6eeb2c1d0618270e19ce2996e0e74b648944",
"variant": null
},
"cpython-3.11.11-windows-x86_64-none": {
@@ -6731,8 +7227,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "f5bceb5efff7802e38638caf9dd748a88d7b67a4620d36f7511c2848b1eb83e3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "eab345a90cac05f6617250f1de33d6606fb29b627c01357033f2276480967730",
"variant": null
},
"cpython-3.11.11+debug-linux-aarch64-gnu": {
@@ -6747,8 +7243,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-aarch64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "58882518507aa6a10e25a46357287fe82be7f6aa5aed8d312de99ca3d2b8f5c3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-aarch64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "ff2c17e8540a5d521bdb9eedad09157aa11fa111c75f62f948cec965b9cb739e",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-armv7-gnueabi": {
@@ -6763,8 +7259,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
- "sha256": "12d2a6a596239f8fc6f09b68d3d7f05977e371ae624fb02dd1042ed90337f1cb",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
+ "sha256": "437f9cdbc4ec1aca9c7597929f80bc8e026a56a0f20694f6d08b547d77fa6289",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-armv7-gnueabihf": {
@@ -6779,8 +7275,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
- "sha256": "869cab18e672804220ebeb686243fd5875929d347aa57f7066ee4d382696df55",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
+ "sha256": "bed3b650e6a59665cfd890b72ab8e36f283dec796e34a2573457b0af972a4e53",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-powerpc64le-gnu": {
@@ -6795,8 +7291,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "5616c62edd0448b073d3c80d3d73d31e64996365bdf98ae9045b25961adb0bac",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "5529208bb0217666aaac11249fd2286d145a579e37c4efbf547f0368e92094df",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-s390x-gnu": {
@@ -6811,8 +7307,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-s390x-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "d05d685cdd17e3b872bfcfcba6bfe37466ed0a4b3d866250fb006efd4c87bb09",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-s390x-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "385f57308833832ae561658a06b3bc5c0c7a3b2f68853d30dc7ba363df496335",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-x86_64-gnu": {
@@ -6827,8 +7323,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "b451c624092f1a10e2557b4516687a6b681da05a153ae967d089d4e577a99590",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "04154ad54d016d5287acf37f92a3139e24ffb101156c370fb5385635a08efe59",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-x86_64-musl": {
@@ -6843,8 +7339,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "9c359bab0e03f14124860f1f5a9f82dffb87bd44ad1dd90f55aebff54d51ef6a",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "b04ed35bddf38b2293d37a9a5ca3e92fd97ad75ba62583d99536fe8335df5a18",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-x86_64_v2-gnu": {
@@ -6859,8 +7355,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "fefe7f1a3ca8becfead5a28c31ebc1e4a8edaf6d3594890308f5d0a922b0ce43",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "040a4e319e3919e7dfe09f61ca30fd585cfb6b2496fbd0ca1db6cfcaa289bfaa",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-x86_64_v2-musl": {
@@ -6875,8 +7371,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "6ea98cde291f6cbc2c23f54818093442c20d26ed0ac21a6e28cbca6c4c633de4",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "e2b8a9eaa5e70657a996ec1dfa7efbfdb3bd870e38d432087a25d6108f3f2c82",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-x86_64_v3-gnu": {
@@ -6891,8 +7387,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "885cbb2255ca8cf22254a11174e89761b12d128aebd41183a7fa606896c86250",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "c3869697b9a12c0339067d55610a64277ca74062f50643b2b511c2bcfcd45c18",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-x86_64_v3-musl": {
@@ -6907,8 +7403,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "9f4f1546b11531e09660f94d2be79ef2cf7ae60ab188cb119c074cf03a8d028e",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "c471c2021963cf725b5781e35876878f88c3305463ce0288f3e1d614db00fa9c",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-x86_64_v4-gnu": {
@@ -6923,8 +7419,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "dd6f357cef8f7c0cd2294c4cad112443fe057ecd472a5c5d6bade5b00523d35c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "9eb8b3136c27ef54a79b2d1162256c9d2100ed6baa820747a006161c4dd63d46",
"variant": "debug"
},
"cpython-3.11.11+debug-linux-x86_64_v4-musl": {
@@ -6939,8 +7435,8 @@
"minor": 11,
"patch": 11,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "a997220581f5ed9f8a5d3bd47fae6ef361cfffc84f20c1683bd95a20b6a16c0a",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "76f45a3c615c169dc7a2f671914561b25488320c57bd01f8f4692b0c1df56a87",
"variant": "debug"
},
"cpython-3.11.10-darwin-aarch64-none": {
@@ -10859,8 +11355,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "a5a1eb1f13a688621af748d552888d7b22daaddfe6b9c7069f9c955c0fcf2da3",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "b5807207ff3e99436049ef8912dfc5f553bf8b1fa332cf233805784f925fea76",
"variant": null
},
"cpython-3.10.16-darwin-x86_64-none": {
@@ -10875,8 +11371,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "27bc1b56a5268f79a9eb9bf79f8281b336f8bfffc361032f41811bc3c58cdb1d",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "fbd4c16651165764a1b2bcc60b2092d3b9b807108cdb17ae82abeeb3ec598175",
"variant": null
},
"cpython-3.10.16-linux-aarch64-gnu": {
@@ -10891,8 +11387,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "85d1ba2ada13fdbeedbaad24e1871d49766be1f4ba6cdee3c16ca7c4197cca92",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "ef4e55c684a87ce2bd3e5b38700da512340c98307b57bd4da7cc80fb6afcec79",
"variant": null
},
"cpython-3.10.16-linux-armv7-gnueabi": {
@@ -10907,8 +11403,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- "sha256": "61f76e2d924b4c6cdd706d322bdda9634bec4d41e50071a03c7e1f7a5178aef2",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ "sha256": "3ccaef3b620e988d6cc72b6fe386fa5fdd98bf26fb24b8e1b74b33d94d4c2c69",
"variant": null
},
"cpython-3.10.16-linux-armv7-gnueabihf": {
@@ -10923,8 +11419,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- "sha256": "68ff1232aeba789195f6061c6c0e2e6868c4a0c8547f2aa7a873c4226bf04a3a",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ "sha256": "32fdc81d839c3dc289324a71a207f64feb1804353d68a5b7a0d7bd1dbdf205ac",
"variant": null
},
"cpython-3.10.16-linux-powerpc64le-gnu": {
@@ -10939,8 +11435,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "57282eba8bccf723af7edf0e018407ea5a035deed9daa5fe684c980d4dd2aebc",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "4f90917977d089b3a3a7d67130737f9ba28baea2d6508f35da92ca103f3b3551",
"variant": null
},
"cpython-3.10.16-linux-s390x-gnu": {
@@ -10955,8 +11451,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "d2048f722562ff30f268e89310f9e567508d3dd6e071ced4c04b4a4aceb6236c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "77c8370579ed0dc38ffc351010acfb5b3bc6663e91230c8d462d8a323c382863",
"variant": null
},
"cpython-3.10.16-linux-x86_64-gnu": {
@@ -10971,8 +11467,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "ec5ff780e53c9af0399086f17299a88e1dd3de707940eab76fa6ef15642edcbd",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "3ba89e564ace5536a62783362c3aaf8a18d882007d9f202aff26cd8d0de0b581",
"variant": null
},
"cpython-3.10.16-linux-x86_64-musl": {
@@ -10987,8 +11483,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "9a6f91e3166e36aceb1dfc1f2246f4253b62de7ca0ee0534282e0ded229ab1f2",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "f6734667688ee41ada182024f5bf369c764060a44905a17fa1df5dfa3dc76ae7",
"variant": null
},
"cpython-3.10.16-linux-x86_64_v2-gnu": {
@@ -11003,8 +11499,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "99e51551b920a73066ca0e6b4139c1e47cecb0bd97711d8efaba819c9c3cf5ed",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "d04b15f020598b111ad1b4db757a2e26106944a6d58e1241b129636e127929e0",
"variant": null
},
"cpython-3.10.16-linux-x86_64_v2-musl": {
@@ -11019,8 +11515,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "bcd8a5c316e1e6727bb891d1b0ac58f3cf0b492a1ca55db4d7b54a8655a3c52c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "eea24e9df73f15183f34f69cc1a4db30013cb4c9f0d884987018a2b9d23ee984",
"variant": null
},
"cpython-3.10.16-linux-x86_64_v3-gnu": {
@@ -11035,8 +11531,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "bb7cc15b0870979a101d887ec6ba5ed7ac38063a1a02201b709218a32f6151fa",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "c889ba100de9bfcab46950c1db000c66a8f0cc24e9de028e8223490a903b7865",
"variant": null
},
"cpython-3.10.16-linux-x86_64_v3-musl": {
@@ -11051,8 +11547,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "37b96735ec9776fb40a17d22761d9ed5b4d42ff891cfb3d8ef9ac08411e9051a",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "f45527ffb0238d4918f9b76ec59469ae32e2820956ecb9d454d41fd843e7b37c",
"variant": null
},
"cpython-3.10.16-linux-x86_64_v4-gnu": {
@@ -11067,8 +11563,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "344b34b0c8abd2e536c4b16ab38773e4f4b6c490000a4df162d9181fb396de7c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "c0d770782f2176657c8fe2aaef3a58d61ce975c794510424ec8c9d9629ce0b32",
"variant": null
},
"cpython-3.10.16-linux-x86_64_v4-musl": {
@@ -11083,8 +11579,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "be3471934543269246ce9b5e33552d9480fffa038296a5c7875d6ca194ddda5d",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "625fa12389d3bc1e08e477e95547a81bc1bc5aa3152043b53e266a6d865dac4c",
"variant": null
},
"cpython-3.10.16-windows-i686-none": {
@@ -11099,8 +11595,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "56462270fd467a3cb2a353a6b6b7c8cd7b2734513dfa87638313db6e769407af",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "4af78f6d3a9e423e5dd47be8ca84027a9fd0d0aee48f2e1cbccdb24e7980deb7",
"variant": null
},
"cpython-3.10.16-windows-x86_64-none": {
@@ -11115,8 +11611,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "6121810d6899d1bfd89a1c3b7fba3ece4402a7f49772a3ebef6b4c707bcf22ec",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "df76a5d696485937e51cf9d2ebf8a2d559d4cb064d3e721714f6470eb35d90cc",
"variant": null
},
"cpython-3.10.16+debug-linux-aarch64-gnu": {
@@ -11131,8 +11627,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-aarch64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "c8271d1a53368d685fa1ea831a052c8ffc9be4ea7438304e04a27948bdd98675",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-aarch64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "34e70f8befedec64ea2c2b42a4c065389c7ce0a6d213ecc62161db6181a56abd",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-armv7-gnueabi": {
@@ -11147,8 +11643,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
- "sha256": "6de8c505025663bb6c1de9c277cde36e9b03f03d82e363d353ed34c6f74c5c45",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
+ "sha256": "d8dda934f097fcb7141434e696401abc4ce9b9608d2966833096ad4edbeb028d",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-armv7-gnueabihf": {
@@ -11163,8 +11659,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
- "sha256": "bcd7f6ce32cecee8997be8330beca24e6895f055da6ea96d053b815718e94d85",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
+ "sha256": "a604ff286a3710debde1b0525064cb1e77ca5db7147972f74cad2bcf24f56109",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-powerpc64le-gnu": {
@@ -11179,8 +11675,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "d8ea2bfeaf07584c7896d40e8adf6d18cc7d4a60002fce0399bdbf65d95bd085",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "3bc82283a1d70645f3f7a076a68e58915da533d4d7c527e13111fe5b6edb5b1a",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-s390x-gnu": {
@@ -11195,8 +11691,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-s390x-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "5ab813240d9fa536ccbd19c9de4820e58467c2f223830fa555cfb63e528a3818",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-s390x-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "8951d8d0fe0ea3f59ee6d2bc995d158083ef687e3e2283cb3eda2368b134503f",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-x86_64-gnu": {
@@ -11211,8 +11707,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "72ccfb128407ddd4406c8fe09caf5049bd38afc60788bf4f635aa89847c67b97",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "8e50a2deeb93c7da887bc0ccfd64b52db6fde29f6472d253e136184d38e9a393",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-x86_64-musl": {
@@ -11227,8 +11723,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "cd5532164297159b66e697b59bd66d321a164088f6e0c5870a17da0f9a86f5db",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "c66094b325b79252e36bfa558d3665a5cacebd0ae71a2e4363f11dd91bf50fc7",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-x86_64_v2-gnu": {
@@ -11243,8 +11739,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "e509c3ea9b395e53001e319251932c050118de5ed1116155372c3953d0dbbf90",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "a790bff174552301991abe4a24d58b27db112384c588a19fdd13ced074cb4864",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-x86_64_v2-musl": {
@@ -11259,8 +11755,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "c46c7cec00605ab090b0c61059fdefcd80503b9763fd59344312d19bdaae15a5",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "e2a4e094509e4772a530eee3b55b08092172ea42f1b05b730b95aa843e7db0b1",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-x86_64_v3-gnu": {
@@ -11275,8 +11771,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "d20e5e6069de6c00d104b7887e4e11e072cfdbd015fc8851d78857af4f3894a2",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "57acf73e7e7a33094daf94ef3570b33373460980bb9e9e526f7b8e43b7c38c7e",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-x86_64_v3-musl": {
@@ -11291,8 +11787,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "871e66c1c0a1c25bfbee6f89a58f2e2fc361f44654d9af27a3be8722b5f507a8",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "514586b119da1640acaa1c8cf14ede4457bc7fd1ae7b1634905653e58332c460",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-x86_64_v4-gnu": {
@@ -11307,8 +11803,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "9478741cd46afe9ddebd07b4f022b2c4ed1fee6e24289ebfbf99c53a79d07b46",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "065db4b882e3750e0721a7cf69e370aaef2d91defb9fbde33d82a0a1d93a98fa",
"variant": "debug"
},
"cpython-3.10.16+debug-linux-x86_64_v4-musl": {
@@ -11323,8 +11819,8 @@
"minor": 10,
"patch": 16,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "2f3a87d8015e5e551ac1637b11418fbc1eeef08a7828169edaceedf17d2db65b",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "4e6f9dd48ae8193b928a71b20559d4a44cffb6693d22810ecd20b9a04e888f7d",
"variant": "debug"
},
"cpython-3.10.15-darwin-aarch64-none": {
@@ -16939,8 +17435,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "f0628c6dee878610c1e3da924c8d930c6948e85c56e75b95b3d437e902acf782",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "65de6878752c9603772e0e4543dfa66e287d770c013eed14a6530d30f901585b",
"variant": null
},
"cpython-3.9.21-darwin-x86_64-none": {
@@ -16955,8 +17451,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- "sha256": "ed5c87541ea92d5dd66a6187dbec4d43d395d6908f7f42e650e12e62bf6fe145",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ "sha256": "d0936e25d48a471ac7bcbb64e8ed02bc493a7e5d46ed879a580c006db5bb75ef",
"variant": null
},
"cpython-3.9.21-linux-aarch64-gnu": {
@@ -16971,8 +17467,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "72c1ce8791b78719080fc00575bc96f21de1024955a56d00b151eeb774804bc9",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "59044e65adff98e98ea61fc68a71299722fccf9c162f1908c8d53b94e994039c",
"variant": null
},
"cpython-3.9.21-linux-armv7-gnueabi": {
@@ -16987,8 +17483,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- "sha256": "8dcfda0951f25bc5be61ef244f8892a2015ba4512bcabcf37005480683f6e72b",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ "sha256": "ecdf124007832d1cbb5092c4b5232f9c8ea88c7cd1abd47ea9703028f785a88f",
"variant": null
},
"cpython-3.9.21-linux-armv7-gnueabihf": {
@@ -17003,8 +17499,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- "sha256": "f6bac804b844fc2ad75e98bfa4ba5fb2df17dd484a6f8b7b9fda7e55e4de283b",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ "sha256": "874aa9b7b4a45512f0b8e90020cc3b38d7e1e5c21fead24e67a57ebcfb5d1707",
"variant": null
},
"cpython-3.9.21-linux-powerpc64le-gnu": {
@@ -17019,8 +17515,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "eff34eb5c7314c98382a8028647c3e0c788c48aab12879282feae2e7e176af6c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "5fa9f333333cc09e5602fd89d3ce6b9ad9b4e2ffd52d3dc29719a7194c2a7491",
"variant": null
},
"cpython-3.9.21-linux-s390x-gnu": {
@@ -17035,8 +17531,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "a1c252593e364e7fdb99f48b233311c54001a6e993057948a232ead94b10329e",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "cbd9a8e187d18fe370e5f71f4ef77c4e28813de8bef8018e35d95cb2c2f545a6",
"variant": null
},
"cpython-3.9.21-linux-x86_64-gnu": {
@@ -17051,8 +17547,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "e0ce3038ef0e779791f62ac64ed08bfaf4e09bc48ff15a82038cef2009124cbf",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "8df8d0484cda57420c2cb9ab1c805b06b3e0b01fd519f08bda0617282d2b68b2",
"variant": null
},
"cpython-3.9.21-linux-x86_64-musl": {
@@ -17067,8 +17563,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "57ba961d7cd48a4473000a0fb10581094cd5fd4cd33558b3ddc8ae6856097e80",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "7b1774e3db1e8e8d5f4eaafe2ba3f7427a7caa6955a6b74e05eb9673bd08ba4f",
"variant": null
},
"cpython-3.9.21-linux-x86_64_v2-gnu": {
@@ -17083,8 +17579,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "a96090f2eb47952b3abb6815864d7c4477bbf23d5d2559c4514140c674c8835b",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "800218e9d232e2e7d734922ddda6770911895fd9bc1e08e9c6cb96fa01c46257",
"variant": null
},
"cpython-3.9.21-linux-x86_64_v2-musl": {
@@ -17099,8 +17595,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "fb308474d3d148fbe4223704d47f4397a85317d1000dd9978f2cd97c7992a931",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "6c7b7304ea033e38e1b47d16ca784bee00f800e4c9977bcac41fbd106638652f",
"variant": null
},
"cpython-3.9.21-linux-x86_64_v3-gnu": {
@@ -17115,8 +17611,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "b9d39c72d6f503e7fb759c32310fbf1cb402189c2bb26f1d25608b8c1d8a41b6",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "c5c2ddfb7d259c607e180a97ad1a5c2bca62a4b1bd43b51b0ef7a3fb029b5387",
"variant": null
},
"cpython-3.9.21-linux-x86_64_v3-musl": {
@@ -17131,8 +17627,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "6dd47b0f86888312cee676a4ac63ddd1e217009317e8f0c6dcb82d681a56277b",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "08099c07425761f9cd100a7d0752bf66e228075941560e9afebe65a44ad33534",
"variant": null
},
"cpython-3.9.21-linux-x86_64_v4-gnu": {
@@ -17147,8 +17643,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- "sha256": "4f6ca60512658efeb4b73afd8b86883948370ed02081af2f722b2c63e08fc1ec",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ "sha256": "b4bafc45b439532bd6c9fe32410b91ed17152f38711b4df428746fd5314f1fb7",
"variant": null
},
"cpython-3.9.21-linux-x86_64_v4-musl": {
@@ -17163,8 +17659,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- "sha256": "3d39de9f4c52465a546e2bed637e20b1811ba8d85b1400dad28dbb2db4af3c2f",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ "sha256": "cca7dba1d42006b97e87b0da20bf2a7ad82909b7cb7a033649a2ea3363cb1fc4",
"variant": null
},
"cpython-3.9.21-windows-i686-none": {
@@ -17179,8 +17675,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "b0c1a25cd1c07bdf7d3d861d39527eaff66363a28eb3306463d8ffc134d55743",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "9c950f9f6185b488c0d57a2d5d00bcdf6dbeefcb72c62b4ff83634ef280a08f7",
"variant": null
},
"cpython-3.9.21-windows-x86_64-none": {
@@ -17195,8 +17691,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- "sha256": "acd70e2f5596d8e24a06d47394b15d326fc12c26662a8cc018e3635fb3025897",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ "sha256": "8522829a1617039b56b5f2744c972203fdb8897e9092b26cc59cdb1e996119c4",
"variant": null
},
"cpython-3.9.21+debug-linux-aarch64-gnu": {
@@ -17211,8 +17707,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-aarch64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "5aa07292964dc64bddec6f1c4f8d08e0a8737ac2f3a0fa0df5d5adb018126f39",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-aarch64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "d43465f8514ac4166766deb52e3b115d7a14cbe3245968dae9e55255b398623c",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-armv7-gnueabi": {
@@ -17227,8 +17723,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
- "sha256": "feb7a8bd2adbb8a041d32cfa5180271997817be6d27c2a02688f4afaf8da07a4",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-armv7-unknown-linux-gnueabi-debug-full.tar.zst",
+ "sha256": "86763a083b629fe4d1e366ff95d9eecb8b1dcf62cf3e618b90405959cd6c7057",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-armv7-gnueabihf": {
@@ -17243,8 +17739,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
- "sha256": "1513e3114c94a2322800a35eadfa9fed72d237f8c6fd48c5b2df4cfb25bebeb0",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-armv7-unknown-linux-gnueabihf-debug-full.tar.zst",
+ "sha256": "e881f54eef0aaaa7e5778f81acf7c53edc3393eb86f9b531d64e37697f67b94e",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-powerpc64le-gnu": {
@@ -17259,8 +17755,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "3e3e2c3676816328cbfc6a7fcffb4fa644c2c30133484104742c75d7e6c4b284",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-ppc64le-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "2b9c4a88af78a1d0f0b550da738be658b91454df929504ce73153c658861959f",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-s390x-gnu": {
@@ -17275,8 +17771,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-s390x-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "36685ccf8a4f3adb8e0e9f7ab1b5ea0d681b0ee407e4f82fae2c2c8d43c2534c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-s390x-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "18fc0ae17824a95487a5b81221c9e857e1f839729923cc9aad36ba6e77910d85",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-x86_64-gnu": {
@@ -17291,8 +17787,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "9d1c3af4ba47e3ff1f8c47c574b22f3fb1ed1f71a26ec668d5b1bbafd83cf73f",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "75c8268c33fa84d4c7a27a00fc7119932a93d1404b354ec64543ee595bb61f24",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-x86_64-musl": {
@@ -17307,8 +17803,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "adb778b6fa75576d6d044869c03e9c7c8c6ba6fcead0c74ccf273ff4651f7f8c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "462180c309753cf6e70297d08f938a371b5e0052f7ec2ea702b652c59fab60b8",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-x86_64_v2-gnu": {
@@ -17323,8 +17819,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "5f61e51a1472cfda28b25e6ad37e93a10a35010d58b13e642eb5ec6bd0e2ac8c",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v2-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "db3f06dd3e8820c0a898ccd676224ae7a34386be446f2cf0c3d7c3f32b12f96c",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-x86_64_v2-musl": {
@@ -17339,8 +17835,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "87ed1b3450896098e21c54b3e9094fe46cc154d96ee710f54395dc67c2dd5797",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v2-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "13dc373140b2e6430ec8508e7340843f3a4e86b43f36af23a2e4591e73282d54",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-x86_64_v3-gnu": {
@@ -17355,8 +17851,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "969996c06722faf4360a383dea039ae740e514219b9f322f45049ee578c77961",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v3-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "84aca4167d03a9d704d4377b7313466fbab2e5bb11fc3c3e77a98e16a8260252",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-x86_64_v3-musl": {
@@ -17371,8 +17867,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "d16628417b266648e0ce95f90a38d2d1833ef13a8c1a7760e63814a0b8499a68",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v3-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "5ad0883b1832ba9522a32b815dcf3693bc5234b1e617f7816f626397c3c420cc",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-x86_64_v4-gnu": {
@@ -17387,8 +17883,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
- "sha256": "d1d6f4ff07e59d89654abc92d349f9086faec559c99124831063d6882f8e73f2",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v4-unknown-linux-gnu-debug-full.tar.zst",
+ "sha256": "20d5a79dda4bc1e3f7780af7ad88fc08590ad5332336c6870c7e7f79450242ae",
"variant": "debug"
},
"cpython-3.9.21+debug-linux-x86_64_v4-musl": {
@@ -17403,8 +17899,8 @@
"minor": 9,
"patch": 21,
"prerelease": "",
- "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
- "sha256": "37bf2eea32c081c9cb4f66cfca66793960e8262836a05b521a16c1e492f9f2ec",
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v4-unknown-linux-musl-debug-full.tar.zst",
+ "sha256": "2579017e1f3cd5cd2a4556c989ece393d26b479266d3d3916c2cd69ae0a93240",
"variant": "debug"
},
"cpython-3.9.20-darwin-aarch64-none": {
diff --git a/crates/uv-python/src/downloads.inc b/crates/uv-python/src/downloads.inc
index 056125b91b40..d6a2331bda20 100644
--- a/crates/uv-python/src/downloads.inc
+++ b/crates/uv-python/src/downloads.inc
@@ -8,6 +8,402 @@ use crate::PythonVariant;
use uv_pep440::{Prerelease, PrereleaseKind};
pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Darwin(None)),
+ libc: Libc::None,
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("4d474a12d52c316f0b68790fc1f8d747cc665f3d9a4d44c8f6757b8e4aeed497")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Darwin(None)),
+ libc: Libc::None,
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("f5da54ddec8866113cb2809e1dec0ec0f4617a149b21945da7b8c8f5ccab8e5e")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("2796c6573a5359c88cb8c8b2daa561c7377ad7876f2fe45983fb977b1d10e208")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv7),
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnueabi),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ sha256: Some("de6d76889576fdfb63a6f0df3df651bed4c9586a6c4c352a3d5dc2d202837d83")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv7),
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnueabihf),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ sha256: Some("820f889a25e0ceea42742b3496ea7ffddf323a61db4a97c3712554b254650de9")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Powerpc64le,
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("e4595b35c5d0040527b9fe32ae53a3ad223ec931ccd579d0d5dd6235eaf81378")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::S390x,
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("0a632b7493756ea057f0037a0794bb3bcaa32816fd57dde9dd6103dddc0099f8")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("f143d48894e9c865a520982f28a575f138623b5ca14ca082f126171ccba46696")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: Some(ArchVariant::V2),
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("7aed85dc32191b6601abff3e55f1933795df97fada0982130d186f612b745407")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: Some(ArchVariant::V3),
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("d10663344c4bd115744afda54616053a005b51565ac9f383ff8c2bc097b20c2a")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: Some(ArchVariant::V4),
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Default
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("091ba9cf1fe4810fc5fbf5513d5bec4bff255bbebba4605ea3825f8f4bb90d1d")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Darwin(None)),
+ libc: Libc::None,
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("81a500eb67385a03e7da7add8dac34a52dcb7ca391b2a4faa493f7ebc8b4ee78")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Darwin(None)),
+ libc: Libc::None,
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("36a393f8d51d565e60ab3471c67c1dd029dc0f9bfb69bc0a890dc60a5764c0df")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-aarch64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("2af2e24d0bc0760c0e5ba2a3250c5a94624a3059d234d2bc922abb5904ca1a35")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv7),
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnueabi),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("5370785843b4cebd4d3a22d74d8c9d26179cb31ad438dcb17332922f5fc99889")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv7),
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnueabihf),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("7572e0ef1cf8bde126c88a372f015af5d106cf507e4f2461379af5aa74b4806c")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::Powerpc64le,
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("b727b1f3cd424e9fed726871223c64cca306e9a3074da3fe588aec2dd18f78a6")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::S390x,
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("9de3a8dde0b43b1d69369b8d64f9b8174bee1a979e975fb69ed10d11875bac54")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: None,
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("f4f151237b4eb6c6b2a5ed34776f6621bf84f88e9195fe5fe4a44a4446bb2ba8")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: Some(ArchVariant::V2),
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("3b9bb813870f259c73a3400f8de211c3df03d3544d82b47a9785bb14174107ee")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: Some(ArchVariant::V3),
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("2f94f0c5df33e27d5e3f8e53cb96a78cf5f2453961ea496b5b72bb41ae855cc3")
+ },
+ ManagedPythonDownload {
+ key: PythonInstallationKey {
+ major: 3,
+ minor: 14,
+ patch: 0,
+ prerelease: Some(Prerelease { kind: PrereleaseKind::Alpha, number: 3 }),
+ implementation: LenientImplementationName::Known(ImplementationName::CPython),
+ arch: Arch{
+ family: target_lexicon::Architecture::X86_64,
+ variant: Some(ArchVariant::V4),
+ },
+ os: Os(target_lexicon::OperatingSystem::Linux),
+ libc: Libc::Some(target_lexicon::Environment::Gnu),
+ variant: PythonVariant::Freethreaded
+ },
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.14.0a3%2B20250106-x86_64_v4-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("ba9e511b020df7e04b5f78bb13d7cbc0776e4bddb41101fe26f4bfdaf7b2cd6a")
+ },
ManagedPythonDownload {
key: PythonInstallationKey {
major: 3,
@@ -23,8 +419,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("25ca42cfac68cfa6bd6aa6c950cb76c950dca56e01ff00458120d3f62967a498")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("26b2ceded19b2eac3f0ae59d895f5032b541270bc65288c0ea36c6d8ae25e234")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -41,8 +437,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("cf3cfc41dc3017732d448f7273ba9a77a5629e3605459feff756922721c410fd")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("d1207ac02ae14a8714a5c15277396719421cd8c60184f4961905bcc6d7978c83")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -59,8 +455,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("c801a80c40ad36d7e3d38d2d550bb64b39af0efef7081a4b1de3e4a43ece5c0d")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("3c720649b2af9873262429074826ef0e35f98140e38df365b36a668573a86cf0")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -77,8 +473,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabi),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- sha256: Some("8c32b8a7a210a108ba3bf5391ecba48d30b5794c2d27783fdcec50b95797157e")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ sha256: Some("660a036eced9c1052a80e152f0cfe08dde43cbc38ca73c6b969920e9382dfbe8")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -95,8 +491,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabihf),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- sha256: Some("e854912d185dfd6f1e8606cb4a74a95a7535a7c95c58b5be78f4014296b488be")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ sha256: Some("48e0ee03b779be8485f2f74728b2047708626af14e2c6711a384040afcbb8ad5")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -113,8 +509,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("dc519bd25ee659db1b5f1f05e342fdc9c74c348b49f793ca3536f33e189002b0")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("8408e87648118b626d79ccf1b1aa0cbf427442c46b13983f1fa07bc4b7d2cb7c")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -131,8 +527,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("f15df22d793733a457115292289091fbf51d64af042800b1218daa2a0498105f")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("2c6649a03c11582b3b3de95ccc25ecec39e7407ad60ac0f274ef48260070d97f")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -149,8 +545,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("05b3bbc97d113b64f4a0dab680e0dbe73e9f59ce8304312ef64d4b6c2822c520")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("726a608e2867c2867caa2cf3f668cb15018456070eee32375675f190946650bc")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -167,8 +563,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("5bfb969703d2f26c26e3a31b07145d40c7a3910052fdc5f95a199868908c78e3")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("a7c27836ff102e92d0c604ab5d06a0f9c474c3d87a01689f477c99e3ce4b8a67")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -185,8 +581,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("07af19e001b8bce915296e2f0aaa32314515864d735175d52d604f8f8fef55ad")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("6778cc7eab5e9ee64b880025300dbf36f93bded39a23681e5977004fcecd9cfa")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -203,8 +599,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("27be1c4a64b3b1ace70f3e72662405c393b5e50690516b755f56c65582235e24")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("212486199b7fd545be66e363051b93297e044353ea035e7dbe94657cafa23e83")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -221,8 +617,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("d56109dd712199b25477154a93eb68b867ad46d5049c07869f8161c5c9271fab")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("e0a7e81a06987129aebc5cd28b1acbc2f5dfb615cb675da37ac8462dbd421ef5")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -239,8 +635,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("4949fd6a337418c9eb7e55871619502b96e8be712711f61414f62a161456b83c")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("a827eb8b5f736fb060a154a85091f800de320a412204c11e0e34208ebc32f707")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -257,8 +653,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("410460370f55da3b4778f89acafc3ec02d2aa390fb9b48f6b5dbdccfd60d39ac")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("5d7631269cef71e59c5f0bb90c45dca550252c6a54f1b5ec5297fb44351ef94d")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -275,8 +671,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("aa0316647307516ee1028055f0df98f05656033f1968d6a935dd1a34dde955b7")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("f3f43199919c40d974f6c616b5e8ce820af085c0218fb2415fecbba85c15a9c1")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -293,8 +689,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("082907fea39786100190137cdcf2d2e3672a33ef90e88ad0b669ba0c5205ec7d")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("972858c2d658daa2bd7b8c6bbfe81c5252990323fb64acee2eb0b033d9ad9042")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -311,8 +707,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("af37b475224eb9d37aa90e68094bbc5f9186dd421eb0fdff59f24c34a1eed4f0")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("45c77db3fefd2ea1ef1c90c8595edfd6d12a53bc5c6cbaa5a2dd5b4a29a045ec")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -329,8 +725,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
- sha256: Some("b7044c2d584eb2261edb18db58a51b8b164e3cf2101a650f7be6d850a2859474")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("49f7bd481b69a99fbbd444bf03e078c7bcdc30f656042cb2cb72d6dd258425e1")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -347,8 +743,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
- sha256: Some("ccff425786a59f387d449eeff7d8afeab2d11cf2aa3631b5777e7d697f60bacd")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("a11eb407678ac127269e3d7aff658a94b14ab42cb11e2eaa057d6d7398c486ba")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -365,8 +761,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-aarch64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
- sha256: Some("5d0b6ab67f02f343348560e32664d5cf4343aa016f34334fc178fe976e5ea74b")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-aarch64-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("f8910a48a7ce3587f21bd080c963a49441ac8c621d38424e3e69ca8dcc65b242")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -383,8 +779,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabi),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst",
- sha256: Some("4373948ca535d2c011ddc42e9d7d6b415ac1c2495d2c8ec414cc22042ae0811c")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabi-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("ba624a019bf9ae15d4ea271537e0e0a913463f2a1c7975c39fda87c1208a7977")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -401,8 +797,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabihf),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst",
- sha256: Some("bbfb13cdf815bceffdf3b22d2992ddd24f09deb3cd3721285438151733c027db")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-armv7-unknown-linux-gnueabihf-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("87cd5e03afba6164009d3001cd97dcba613a23619801bcf4358dae28e36011c7")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -419,8 +815,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
- sha256: Some("9f82f0e05d37362be651f51a5028979b0409c0b4b3ceecd207b8b4539b8c72ef")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-ppc64le-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("b9f94f840b390a68e6c34cb0ae56be3a14c067cbdc34144f6edddeefda59391c")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -437,8 +833,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
- sha256: Some("5b6bacbeb90bac09799f08f9937f811ab87c18e2654f9827f4ef2cb7832d8920")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-s390x-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
+ sha256: Some("8ce257291bd99bdbfe93688febf5a5fe8b986370df72801b0414386bea4bc99d")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -455,8 +851,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
- sha256: Some("065368d84d0d271cf081cf8a3599a0bfdcab00d45ddf3cde487dc5f901992ab5")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("080133624e16068a8d74698fa673ce6f71914ca4d836a760758f383895f4e6b3")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -473,8 +869,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
- sha256: Some("196aa926332003687eb8167e55d5761439890a763dc3f0a0758355af10979f9f")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v2-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("4d8121f8e42f18da69c8106eca6612b5edfb39ab4684af588c6626f413c78819")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -491,8 +887,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
- sha256: Some("65176446a0aa53f8cdbcb967d3e70d09e6bb21b058657461041611dc0f6ac111")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v3-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("c756e498571375568928507fbb17bca22e8c02f22cb39fb10efa3bd3c745dc1f")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -509,8 +905,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64_v4-unknown-linux-gnu-freethreaded%2Blto-full.tar.zst",
- sha256: Some("a3f43b7b6073ef7a932638c3fde128c9db306c26655ec0cdaf13af9e15f1f0cf")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64_v4-unknown-linux-gnu-freethreaded%2Bpgo%2Blto-full.tar.zst",
+ sha256: Some("96866696116e5b886eca6052db15da38bb7ab07dce0148fdbc171f53b7da79ae")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -527,8 +923,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst",
- sha256: Some("957da31c99b168d15116af5e1f3c285a5e0da9ed2c6440aa7080f49118c0931a")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-i686-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst",
+ sha256: Some("0455a9866b1b02055022e1dd9237875533c57e7bbcd8e7993da9ab16f719f1a8")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -545,8 +941,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Freethreaded
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.13.1%2B20241219-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst",
- sha256: Some("e34828f6ae08e252b07a277f0021658963cddca8a858ae858397c1e5898d7e2a")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.13.1%2B20250106-x86_64-pc-windows-msvc-freethreaded%2Bpgo-full.tar.zst",
+ sha256: Some("e2c63cd840a808705f5dd72251d2e07d70ed497c2a36d151b7701ea2c68f8bc6")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1715,8 +2111,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("abe1de2494bb8b243fd507944f4d50292848fa00685d5288c858a72623a16635")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("3a31b4f82d589a0ff6efac3cdc1e55284cb67645470b78c5aa50f156c10a93d2")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1733,8 +2129,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("867c1af10f204224b571f8f2593fc9eb580fe0c2376224d1096ebe855ad8c722")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("00e2d29ce8a688f5d2f192718e6f87e4aedae88ac2a0958011b8af9a49e049a7")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1751,8 +2147,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("fb983ec85952513f5f013674fcbf4306b1a142c50fcfd914c2c3f00c61a874b0")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("29df00dc1435f536c50396d1753fb507f81a81c93a04a7c24b145c9ad68798f8")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1769,8 +2165,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabi),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- sha256: Some("0567907c0147753b54683ffdd3559d53aa4f10b203f067628586bdc545dddffb")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ sha256: Some("d648de42934e775c78fc73638d35d021c9bf7306d03c505e10a270e70439bc9e")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1787,8 +2183,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabihf),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- sha256: Some("d970da6a727a069ae765ae067ca93e369cf9881e5140e28f5030f6314595dd9b")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ sha256: Some("fc1bf99d6c64386438d83e1cee031b2764c801e5480d7c221c5625f48bd7381a")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1805,8 +2201,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("462b028ce314eb57ced802ce7f9459520365ce5d635d7ae24d2d532b78c9ee12")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("58b09124abbdd4408cafc6e93d7df29e21e346f6dde4df38b75dee3ef5f3ef60")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1823,8 +2219,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("6791e6572b6f412fab38e6032dac87c37d6870b12c598f17c76c230678a7a86d")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("f0b61f319a1e9d2dc03a086df2b7c4721bcdbcfd2dbf4fe24d7cb3fea75fb2bb")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1841,8 +2237,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("698e53b264a9bcd35cfa15cd680c4d78b0878fa529838844b5ffd0cd661d6bc2")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("5591da823631467e204baa520ee379ba09b5699b2e2c322c456c44cb2c2e60eb")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1859,8 +2255,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("451c02f24d1883c2f8ed80914ca4d8567dbe429888332e7cc6cf597e8a7c2555")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("6f931dbe2058e3dfa1d5f381a501d3726ed2191ad464167904dd7b198cb852d2")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1877,8 +2273,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("9cf1273ae2fdbbbf308bd81ad2d0b384bec963097a83f8a6455bb187a7714d82")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("71fa45cf960cafd0097f4825d4539833077031e3152a439ab681c27a61665cdf")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1895,8 +2291,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("630e0131c947a82b342cc8115e85a98a70bb1ebb1580c88b01e5c1dc61d25814")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("8c6430da267171cff9ac741e98d6c64785860af93f530df818ed7dcf57444d7c")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1913,8 +2309,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("07319b611e2563ef5281ab4233c78bab1eca02be498fec636a2233c46dd81334")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("3337c050775285e757406bcac24f221383958f75edb1e2ed923b0abdfd5ee350")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1931,8 +2327,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("81769694bda1d525a9d9b7f493e71ae816ce939e208561c5b8b1362d6a484ed8")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("fad5ae4f20f39d0aadf42d7d79030eb5a3b56543808a5adba7020753718cc431")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1949,8 +2345,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("4e83c898973be409cf242c6dfd33a2576433f36f58f4000d6f120cc436edb5cb")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("f6f08b06acfded7fecdc62885e8ccc999cd2984dc64d792c8f8cf0a481a1623b")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1967,8 +2363,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("d0c84e94534042361f715a8e1cf9139745ab6804ab0b78289b4dd13c533c9930")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("f5275e0573b082b3e75d424354eefe95ddcc85b7a550f6818d1fd03c5b1b9569")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -1985,8 +2381,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("cbdf8fca5d3a43e8fe06d2d56f969dbc46051439828211f73047b4800c9c5ed6")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("6af6bf6a7981cbe086b394c0d56c95e66d660f9b2dbfe452831990fe2da751c3")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -2003,8 +2399,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.12.8%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("1a702b3463cf87ec0d2e33902a47e95456053b0178fe96bd673c1dbb554f5d15")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.12.8%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("2c6af885033be26a8a2ac2e069aac4889c53ab9d212fdffe0d7c8157b66a3d14")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4361,8 +4757,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("af82c85992dace78cd2682deb8e9ef41e448f66cb602f31e5b7c7af75a74bbf1")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("fc6cb8fa7ed727acdb317fb6e5149cdcd29128583e99a6dc14640a7e57a9cd34")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4379,8 +4775,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("d33b1ee09b2a9fe692cbc6e3ed0fbadb23c430233ce92e2954522c5594c6b274")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("5b036f1403725f91bd2fe5a90c5a763b14da4bc94182fb48e1a62d063de66c5e")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4397,8 +4793,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("23fa4e8eeb3a9c7fff540b0fdd4047ae5d2e6f0caf5095e7d767ed96ccc7efe7")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("541c1d514cd2a3155f4a5805ccece9ac86f510b186e3cf1af7cfd8cf7097d0e9")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4415,8 +4811,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabi),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- sha256: Some("d798c008dfde19f0ffdf3684f49adaf922873bee2ad1a2e5dd8d0dd2ad3a4699")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ sha256: Some("01de7a51461cdb25014041c97bebbe80e1f99b1231a525aa7a2463e55347d26f")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4433,8 +4829,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabihf),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- sha256: Some("e838902cb7d47fbf8636d30b59d27182bf818dc633eef4b47c8d28163689b5ff")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ sha256: Some("0cad64bf6041f68fc960ab75e6e817cd36046331538c8e70533d709bedb6d8ef")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4451,8 +4847,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("477dbc811b0f834bc3386126925d490ce734c2191a6e5022e1e0ebb346e5ddcf")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("69020a370009ab331c008f333ca6d8ff684b7b120300d195cc6fb0e6d6f641ac")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4469,8 +4865,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("4e6a9f5111d08e0421446de539224addca624865d1d1e0c5c6ce7b2382d6cf11")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("eaff12c96540a87aea624f98e439ce446afe88aca024d106aa3593f007e237d1")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4487,8 +4883,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("a5448390ef50caaeac60c42806060d0d00488df0b61e7ab4df3b020cceb2bb73")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("56bda37ad88aaf404a8a1f77c725fde18dee6a2b8d30d361cf4b30a1f807d345")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4505,8 +4901,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("916b2d4336d9f672b5b1f0f15b0ee43504ee178244ec6656ce49de57ddefaa76")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("b9950749da7e6147cf8ad0838b9c7660a8e62d248771b3bbfef4d1f33bf2e998")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4523,8 +4919,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("267dbf5f9272bb2f8336d13b9f2468ff2b5659a5bf820afc564af49531dc676e")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("a8bd2b532d3401af71e5470eb41b85d1beb63accca9ea37c3e32cbe44d3119c6")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4541,8 +4937,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("f9678be5d8366faefcabe11ce9db63a3172176e1f3d6f910ec007453fb2e114d")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("8c3961b653ea18c76dcf5e2ee05053bfdbe6353f8ba0515316f906354a200d54")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4559,8 +4955,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("3746c4c74ec610e4425b876a74161ebcd01aaf556d99017254d782e975d35f46")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("3cf193e4c8dd36bb9d1c6494f3e36fb6c2bf91a91ad724ee3ac38fa6fa958a65")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4577,8 +4973,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("6662013e83d5f97c673f6b346f062d29c5a6dda36a088a0deabfd5dcf6fb9b09")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("0be47a46f66cd2d00f97a0a97cc2edede9983d856f4d6822a8ad96a574a463e0")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4595,8 +4991,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("a93ebc464644990638ae3029c9bbea6249eb90047152dfb98cbaac66da1c199e")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("9e27ab9e35c73caca1ab607364456b7e8279def51855ec73b863cafb51f2d493")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4613,8 +5009,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("5d01a991ad42ad49569145d86752aa79d881d49dd9503c8304ac9a7cea0b83c8")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("b24e33285bbe050cd122e395c96420a2067449d4d27f94928d89bb2cb6d2064b")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4631,8 +5027,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("063b3582b31d4b5c918439701ad4037cec9b90c8e2e4c55c502a9001c638ebc6")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("026d777d86ebfe687cebda919aaa6eeb2c1d0618270e19ce2996e0e74b648944")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -4649,8 +5045,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.11.11%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("f5bceb5efff7802e38638caf9dd748a88d7b67a4620d36f7511c2848b1eb83e3")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.11.11%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("eab345a90cac05f6617250f1de33d6606fb29b627c01357033f2276480967730")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7187,8 +7583,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("a5a1eb1f13a688621af748d552888d7b22daaddfe6b9c7069f9c955c0fcf2da3")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("b5807207ff3e99436049ef8912dfc5f553bf8b1fa332cf233805784f925fea76")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7205,8 +7601,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("27bc1b56a5268f79a9eb9bf79f8281b336f8bfffc361032f41811bc3c58cdb1d")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("fbd4c16651165764a1b2bcc60b2092d3b9b807108cdb17ae82abeeb3ec598175")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7223,8 +7619,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("85d1ba2ada13fdbeedbaad24e1871d49766be1f4ba6cdee3c16ca7c4197cca92")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("ef4e55c684a87ce2bd3e5b38700da512340c98307b57bd4da7cc80fb6afcec79")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7241,8 +7637,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabi),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- sha256: Some("61f76e2d924b4c6cdd706d322bdda9634bec4d41e50071a03c7e1f7a5178aef2")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ sha256: Some("3ccaef3b620e988d6cc72b6fe386fa5fdd98bf26fb24b8e1b74b33d94d4c2c69")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7259,8 +7655,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabihf),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- sha256: Some("68ff1232aeba789195f6061c6c0e2e6868c4a0c8547f2aa7a873c4226bf04a3a")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ sha256: Some("32fdc81d839c3dc289324a71a207f64feb1804353d68a5b7a0d7bd1dbdf205ac")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7277,8 +7673,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("57282eba8bccf723af7edf0e018407ea5a035deed9daa5fe684c980d4dd2aebc")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("4f90917977d089b3a3a7d67130737f9ba28baea2d6508f35da92ca103f3b3551")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7295,8 +7691,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("d2048f722562ff30f268e89310f9e567508d3dd6e071ced4c04b4a4aceb6236c")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("77c8370579ed0dc38ffc351010acfb5b3bc6663e91230c8d462d8a323c382863")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7313,8 +7709,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("ec5ff780e53c9af0399086f17299a88e1dd3de707940eab76fa6ef15642edcbd")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("3ba89e564ace5536a62783362c3aaf8a18d882007d9f202aff26cd8d0de0b581")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7331,8 +7727,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("9a6f91e3166e36aceb1dfc1f2246f4253b62de7ca0ee0534282e0ded229ab1f2")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("f6734667688ee41ada182024f5bf369c764060a44905a17fa1df5dfa3dc76ae7")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7349,8 +7745,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("99e51551b920a73066ca0e6b4139c1e47cecb0bd97711d8efaba819c9c3cf5ed")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("d04b15f020598b111ad1b4db757a2e26106944a6d58e1241b129636e127929e0")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7367,8 +7763,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("bcd8a5c316e1e6727bb891d1b0ac58f3cf0b492a1ca55db4d7b54a8655a3c52c")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("eea24e9df73f15183f34f69cc1a4db30013cb4c9f0d884987018a2b9d23ee984")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7385,8 +7781,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("bb7cc15b0870979a101d887ec6ba5ed7ac38063a1a02201b709218a32f6151fa")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("c889ba100de9bfcab46950c1db000c66a8f0cc24e9de028e8223490a903b7865")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7403,8 +7799,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("37b96735ec9776fb40a17d22761d9ed5b4d42ff891cfb3d8ef9ac08411e9051a")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("f45527ffb0238d4918f9b76ec59469ae32e2820956ecb9d454d41fd843e7b37c")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7421,8 +7817,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("344b34b0c8abd2e536c4b16ab38773e4f4b6c490000a4df162d9181fb396de7c")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("c0d770782f2176657c8fe2aaef3a58d61ce975c794510424ec8c9d9629ce0b32")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7439,8 +7835,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("be3471934543269246ce9b5e33552d9480fffa038296a5c7875d6ca194ddda5d")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("625fa12389d3bc1e08e477e95547a81bc1bc5aa3152043b53e266a6d865dac4c")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7457,8 +7853,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("56462270fd467a3cb2a353a6b6b7c8cd7b2734513dfa87638313db6e769407af")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("4af78f6d3a9e423e5dd47be8ca84027a9fd0d0aee48f2e1cbccdb24e7980deb7")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -7475,8 +7871,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.10.16%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("6121810d6899d1bfd89a1c3b7fba3ece4402a7f49772a3ebef6b4c707bcf22ec")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.10.16%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("df76a5d696485937e51cf9d2ebf8a2d559d4cb064d3e721714f6470eb35d90cc")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11111,8 +11507,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-aarch64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("f0628c6dee878610c1e3da924c8d930c6948e85c56e75b95b3d437e902acf782")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-aarch64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("65de6878752c9603772e0e4543dfa66e287d770c013eed14a6530d30f901585b")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11129,8 +11525,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-apple-darwin-install_only_stripped.tar.gz",
- sha256: Some("ed5c87541ea92d5dd66a6187dbec4d43d395d6908f7f42e650e12e62bf6fe145")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-apple-darwin-install_only_stripped.tar.gz",
+ sha256: Some("d0936e25d48a471ac7bcbb64e8ed02bc493a7e5d46ed879a580c006db5bb75ef")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11147,8 +11543,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("72c1ce8791b78719080fc00575bc96f21de1024955a56d00b151eeb774804bc9")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("59044e65adff98e98ea61fc68a71299722fccf9c162f1908c8d53b94e994039c")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11165,8 +11561,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabi),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
- sha256: Some("8dcfda0951f25bc5be61ef244f8892a2015ba4512bcabcf37005480683f6e72b")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz",
+ sha256: Some("ecdf124007832d1cbb5092c4b5232f9c8ea88c7cd1abd47ea9703028f785a88f")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11183,8 +11579,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnueabihf),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
- sha256: Some("f6bac804b844fc2ad75e98bfa4ba5fb2df17dd484a6f8b7b9fda7e55e4de283b")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-armv7-unknown-linux-gnueabihf-install_only_stripped.tar.gz",
+ sha256: Some("874aa9b7b4a45512f0b8e90020cc3b38d7e1e5c21fead24e67a57ebcfb5d1707")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11201,8 +11597,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("eff34eb5c7314c98382a8028647c3e0c788c48aab12879282feae2e7e176af6c")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-ppc64le-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("5fa9f333333cc09e5602fd89d3ce6b9ad9b4e2ffd52d3dc29719a7194c2a7491")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11219,8 +11615,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("a1c252593e364e7fdb99f48b233311c54001a6e993057948a232ead94b10329e")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-s390x-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("cbd9a8e187d18fe370e5f71f4ef77c4e28813de8bef8018e35d95cb2c2f545a6")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11237,8 +11633,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("e0ce3038ef0e779791f62ac64ed08bfaf4e09bc48ff15a82038cef2009124cbf")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("8df8d0484cda57420c2cb9ab1c805b06b3e0b01fd519f08bda0617282d2b68b2")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11255,8 +11651,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("57ba961d7cd48a4473000a0fb10581094cd5fd4cd33558b3ddc8ae6856097e80")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("7b1774e3db1e8e8d5f4eaafe2ba3f7427a7caa6955a6b74e05eb9673bd08ba4f")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11273,8 +11669,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("a96090f2eb47952b3abb6815864d7c4477bbf23d5d2559c4514140c674c8835b")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v2-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("800218e9d232e2e7d734922ddda6770911895fd9bc1e08e9c6cb96fa01c46257")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11291,8 +11687,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("fb308474d3d148fbe4223704d47f4397a85317d1000dd9978f2cd97c7992a931")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v2-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("6c7b7304ea033e38e1b47d16ca784bee00f800e4c9977bcac41fbd106638652f")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11309,8 +11705,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("b9d39c72d6f503e7fb759c32310fbf1cb402189c2bb26f1d25608b8c1d8a41b6")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("c5c2ddfb7d259c607e180a97ad1a5c2bca62a4b1bd43b51b0ef7a3fb029b5387")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11327,8 +11723,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("6dd47b0f86888312cee676a4ac63ddd1e217009317e8f0c6dcb82d681a56277b")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v3-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("08099c07425761f9cd100a7d0752bf66e228075941560e9afebe65a44ad33534")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11345,8 +11741,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Gnu),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
- sha256: Some("4f6ca60512658efeb4b73afd8b86883948370ed02081af2f722b2c63e08fc1ec")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v4-unknown-linux-gnu-install_only_stripped.tar.gz",
+ sha256: Some("b4bafc45b439532bd6c9fe32410b91ed17152f38711b4df428746fd5314f1fb7")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11363,8 +11759,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::Some(target_lexicon::Environment::Musl),
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
- sha256: Some("3d39de9f4c52465a546e2bed637e20b1811ba8d85b1400dad28dbb2db4af3c2f")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64_v4-unknown-linux-musl-install_only_stripped.tar.gz",
+ sha256: Some("cca7dba1d42006b97e87b0da20bf2a7ad82909b7cb7a033649a2ea3363cb1fc4")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11381,8 +11777,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-i686-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("b0c1a25cd1c07bdf7d3d861d39527eaff66363a28eb3306463d8ffc134d55743")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-i686-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("9c950f9f6185b488c0d57a2d5d00bcdf6dbeefcb72c62b4ff83634ef280a08f7")
},
ManagedPythonDownload {
key: PythonInstallationKey {
@@ -11399,8 +11795,8 @@ pub(crate) const PYTHON_DOWNLOADS: &[ManagedPythonDownload] = &[
libc: Libc::None,
variant: PythonVariant::Default
},
- url: "https://github.com/astral-sh/python-build-standalone/releases/download/20241219/cpython-3.9.21%2B20241219-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
- sha256: Some("acd70e2f5596d8e24a06d47394b15d326fc12c26662a8cc018e3635fb3025897")
+ url: "https://github.com/astral-sh/python-build-standalone/releases/download/20250106/cpython-3.9.21%2B20250106-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
+ sha256: Some("8522829a1617039b56b5f2744c972203fdb8897e9092b26cc59cdb1e996119c4")
},
ManagedPythonDownload {
key: PythonInstallationKey {
diff --git a/crates/uv-python/template-download-metadata.py b/crates/uv-python/template-download-metadata.py
index 4be2d0ef98bb..86bb14966df5 100755
--- a/crates/uv-python/template-download-metadata.py
+++ b/crates/uv-python/template-download-metadata.py
@@ -98,7 +98,8 @@ def prepare_prerelease(prerelease: str) -> str:
if not (match := PRERELEASE_PATTERN.match(prerelease)):
raise ValueError(f"Invalid prerelease: {prerelease!r}")
kind, number = match.groups()
- return f"Some(Prerelease {{ kind: PrereleaseKind::{kind.capitalize()}, number: {number} }})"
+ kind_mapping = {"a": "Alpha", "b": "Beta", "rc": "Rc"}
+ return f"Some(Prerelease {{ kind: PrereleaseKind::{kind_mapping[kind]}, number: {number} }})"
def prepare_value(value: dict) -> dict:
From 0d57d298e68999be63fd0183fd0723686a6ec32a Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Mon, 6 Jan 2025 17:41:43 -0500
Subject: [PATCH 042/135] Avoid downgrading packages when `--upgrade` is
provided (#10097)
## Summary
When `--upgrade` is provided, we should retain already-installed
packages _if_ they're newer than whatever is available from the
registry.
Closes https://github.com/astral-sh/uv/issues/10089.
---
.../uv-configuration/src/package_options.rs | 9 ++
crates/uv-installer/src/plan.rs | 6 +-
crates/uv-requirements/src/upgrade.rs | 5 +
crates/uv-resolver/src/candidate_selector.rs | 77 +++++++++++---
crates/uv-resolver/src/exclusions.rs | 43 ++------
crates/uv/tests/it/pip_install.rs | 100 ++++++++++++++++--
6 files changed, 179 insertions(+), 61 deletions(-)
diff --git a/crates/uv-configuration/src/package_options.rs b/crates/uv-configuration/src/package_options.rs
index 5215425811ef..ac39383862b6 100644
--- a/crates/uv-configuration/src/package_options.rs
+++ b/crates/uv-configuration/src/package_options.rs
@@ -47,6 +47,15 @@ impl Reinstall {
matches!(self, Self::All)
}
+ /// Returns `true` if the specified package should be reinstalled.
+ pub fn contains(&self, package_name: &PackageName) -> bool {
+ match &self {
+ Self::None => false,
+ Self::All => true,
+ Self::Packages(packages) => packages.contains(package_name),
+ }
+ }
+
/// Combine a set of [`Reinstall`] values.
#[must_use]
pub fn combine(self, other: Self) -> Self {
diff --git a/crates/uv-installer/src/plan.rs b/crates/uv-installer/src/plan.rs
index bbbd227fdd82..4207e3bc1711 100644
--- a/crates/uv-installer/src/plan.rs
+++ b/crates/uv-installer/src/plan.rs
@@ -66,11 +66,7 @@ impl<'a> Planner<'a> {
for dist in self.resolution.distributions() {
// Check if the package should be reinstalled.
- let reinstall = match reinstall {
- Reinstall::None => false,
- Reinstall::All => true,
- Reinstall::Packages(packages) => packages.contains(dist.name()),
- };
+ let reinstall = reinstall.contains(dist.name());
// Check if installation of a binary version of the package should be allowed.
let no_binary = build_options.no_binary_package(dist.name());
diff --git a/crates/uv-requirements/src/upgrade.rs b/crates/uv-requirements/src/upgrade.rs
index 7f7556e67d74..ed6d9a747461 100644
--- a/crates/uv-requirements/src/upgrade.rs
+++ b/crates/uv-requirements/src/upgrade.rs
@@ -68,6 +68,11 @@ pub fn read_lock_requirements(
install_path: &Path,
upgrade: &Upgrade,
) -> Result {
+ // As an optimization, skip iterating over the lockfile is we're upgrading all packages anyway.
+ if upgrade.is_all() {
+ return Ok(LockedRequirements::default());
+ }
+
let mut preferences = Vec::new();
let mut git = Vec::new();
diff --git a/crates/uv-resolver/src/candidate_selector.rs b/crates/uv-resolver/src/candidate_selector.rs
index 7f2db96a6c6f..3104b38a73f9 100644
--- a/crates/uv-resolver/src/candidate_selector.rs
+++ b/crates/uv-resolver/src/candidate_selector.rs
@@ -1,6 +1,7 @@
+use std::fmt::{Display, Formatter};
+
use itertools::Itertools;
use pubgrub::Range;
-use std::fmt::{Display, Formatter};
use tracing::{debug, trace};
use uv_configuration::IndexStrategy;
@@ -83,17 +84,25 @@ impl CandidateSelector {
index: Option<&'a IndexUrl>,
env: &ResolverEnvironment,
) -> Option> {
- let is_excluded = exclusions.contains(package_name);
-
- // Check for a preference from a lockfile or a previous fork that satisfies the range and
- // is allowed.
+ let reinstall = exclusions.reinstall(package_name);
+ let upgrade = exclusions.upgrade(package_name);
+
+ // If we have a preference (e.g., from a lockfile), search for a version matching that
+ // preference.
+ //
+ // If `--reinstall` is provided, we should omit any already-installed packages from here,
+ // since we can't reinstall already-installed packages.
+ //
+ // If `--upgrade` is provided, we should still search for a matching preference. In
+ // practice, preferences should be empty if `--upgrade` is provided, but it's the caller's
+ // responsibility to ensure that.
if let Some(preferred) = self.get_preferred(
package_name,
range,
version_maps,
preferences,
installed_packages,
- is_excluded,
+ reinstall,
index,
env,
) {
@@ -101,19 +110,52 @@ impl CandidateSelector {
return Some(preferred);
}
- // Check for a locally installed distribution that satisfies the range and is allowed.
- if !is_excluded {
- if let Some(installed) = Self::get_installed(package_name, range, installed_packages) {
+ // If we don't have a preference, find an already-installed distribution that satisfies the
+ // range.
+ let installed = if reinstall {
+ None
+ } else {
+ Self::get_installed(package_name, range, installed_packages)
+ };
+
+ // If we're not upgrading, we should prefer the already-installed distribution.
+ if !upgrade {
+ if let Some(installed) = installed {
+ trace!(
+ "Using installed {} {} that satisfies {range}",
+ installed.name,
+ installed.version
+ );
+ return Some(installed);
+ }
+ }
+
+ // Otherwise, find the best candidate from the version maps.
+ let compatible = self.select_no_preference(package_name, range, version_maps, env);
+
+ // Cross-reference against the already-installed distribution.
+ //
+ // If the already-installed version is _more_ compatible than the best candidate
+ // from the version maps, use the installed version.
+ if let Some(installed) = installed {
+ if compatible.as_ref().is_none_or(|compatible| {
+ let highest = self.use_highest_version(package_name, env);
+ if highest {
+ installed.version() >= compatible.version()
+ } else {
+ installed.version() <= compatible.version()
+ }
+ }) {
trace!(
- "Using preference {} {} from installed package",
+ "Using installed {} {} that satisfies {range}",
installed.name,
- installed.version,
+ installed.version
);
return Some(installed);
}
}
- self.select_no_preference(package_name, range, version_maps, env)
+ compatible
}
/// If the package has a preference, an existing version from an existing lockfile or a version
@@ -132,7 +174,7 @@ impl CandidateSelector {
version_maps: &'a [VersionMap],
preferences: &'a Preferences,
installed_packages: &'a InstalledPackages,
- is_excluded: bool,
+ reinstall: bool,
index: Option<&'a IndexUrl>,
env: &ResolverEnvironment,
) -> Option> {
@@ -159,7 +201,7 @@ impl CandidateSelector {
range,
version_maps,
installed_packages,
- is_excluded,
+ reinstall,
env,
)
}
@@ -172,7 +214,7 @@ impl CandidateSelector {
range: &Range,
version_maps: &'a [VersionMap],
installed_packages: &'a InstalledPackages,
- is_excluded: bool,
+ reinstall: bool,
env: &ResolverEnvironment,
) -> Option> {
for (marker, version) in preferences {
@@ -181,8 +223,9 @@ impl CandidateSelector {
continue;
}
- // Check for a locally installed distribution that matches the preferred version.
- if !is_excluded {
+ // Check for a locally installed distribution that matches the preferred version, unless
+ // we have to reinstall, in which case we can't reuse an already-installed distribution.
+ if !reinstall {
let installed_dists = installed_packages.get_packages(package_name);
match installed_dists.as_slice() {
[] => {}
diff --git a/crates/uv-resolver/src/exclusions.rs b/crates/uv-resolver/src/exclusions.rs
index 2222584c9216..24cb7da4298a 100644
--- a/crates/uv-resolver/src/exclusions.rs
+++ b/crates/uv-resolver/src/exclusions.rs
@@ -1,48 +1,27 @@
-use rustc_hash::FxHashSet;
use uv_configuration::{Reinstall, Upgrade};
use uv_pep508::PackageName;
/// Tracks locally installed packages that should not be selected during resolution.
#[derive(Debug, Default, Clone)]
-pub enum Exclusions {
- #[default]
- None,
- /// Exclude some local packages from consideration, e.g. from `--reinstall-package foo --upgrade-package bar`
- Some(FxHashSet),
- /// Exclude all local packages from consideration, e.g. from `--reinstall` or `--upgrade`
- All,
+pub struct Exclusions {
+ reinstall: Reinstall,
+ upgrade: Upgrade,
}
impl Exclusions {
pub fn new(reinstall: Reinstall, upgrade: Upgrade) -> Self {
- if upgrade.is_all() || reinstall.is_all() {
- Self::All
- } else {
- let mut exclusions: FxHashSet =
- if let Reinstall::Packages(packages) = reinstall {
- FxHashSet::from_iter(packages)
- } else {
- FxHashSet::default()
- };
+ Self { reinstall, upgrade }
+ }
- if let Upgrade::Packages(packages) = upgrade {
- exclusions.extend(packages.into_keys());
- };
+ pub fn reinstall(&self, package: &PackageName) -> bool {
+ self.reinstall.contains(package)
+ }
- if exclusions.is_empty() {
- Self::None
- } else {
- Self::Some(exclusions)
- }
- }
+ pub fn upgrade(&self, package: &PackageName) -> bool {
+ self.upgrade.contains(package)
}
- /// Returns true if the package is excluded and a local distribution should not be used.
pub fn contains(&self, package: &PackageName) -> bool {
- match self {
- Self::None => false,
- Self::Some(packages) => packages.contains(package),
- Self::All => true,
- }
+ self.reinstall(package) || self.upgrade(package)
}
}
diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs
index ba7134271147..cdd067ee5c85 100644
--- a/crates/uv/tests/it/pip_install.rs
+++ b/crates/uv/tests/it/pip_install.rs
@@ -2617,6 +2617,93 @@ fn no_deps_editable() {
context.assert_command("import aiohttp").failure();
}
+/// Avoid downgrading already-installed packages when `--upgrade` is provided.
+#[test]
+fn install_no_downgrade() -> Result<()> {
+ let context = TestContext::new("3.12");
+
+ // Create a local package named `idna`.
+ let idna = context.temp_dir.child("idna");
+ idna.child("pyproject.toml").write_str(indoc! {r#"
+ [project]
+ name = "idna"
+ version = "1000"
+ requires-python = ">=3.12"
+ dependencies = []
+
+ [build-system]
+ requires = ["setuptools>=42"]
+ build-backend = "setuptools.build_meta"
+ "#})?;
+
+ // Install the local `idna`.
+ uv_snapshot!(context.filters(), context.pip_install()
+ .arg("./idna"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 1 package in [TIME]
+ Prepared 1 package in [TIME]
+ Installed 1 package in [TIME]
+ + idna==1000 (from file://[TEMP_DIR]/idna)
+ "###
+ );
+
+ // Install `anyio`, which depends on `idna`.
+ uv_snapshot!(context.filters(), context.pip_install()
+ .arg("anyio"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 3 packages in [TIME]
+ Prepared 2 packages in [TIME]
+ Installed 2 packages in [TIME]
+ + anyio==4.3.0
+ + sniffio==1.3.1
+ "###
+ );
+
+ // Install `anyio` with `--upgrade`, which should retain the local `idna`.
+ uv_snapshot!(context.filters(), context.pip_install()
+ .arg("-U")
+ .arg("anyio"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 3 packages in [TIME]
+ Audited 3 packages in [TIME]
+ "###
+ );
+
+ // Install `anyio` with `--reinstall`, which should downgrade `idna`.
+ uv_snapshot!(context.filters(), context.pip_install()
+ .arg("--reinstall")
+ .arg("anyio"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 3 packages in [TIME]
+ Prepared 3 packages in [TIME]
+ Uninstalled 3 packages in [TIME]
+ Installed 3 packages in [TIME]
+ ~ anyio==4.3.0
+ - idna==1000 (from file://[TEMP_DIR]/idna)
+ + idna==3.6
+ ~ sniffio==1.3.1
+ "###
+ );
+
+ Ok(())
+}
+
/// Upgrade a package.
#[test]
fn install_upgrade() {
@@ -5311,14 +5398,13 @@ fn already_installed_local_path_dependent() {
.arg("--no-index")
.arg("--find-links")
.arg(build_vendor_links_url()), @r###"
- success: false
- exit_code: 1
+ success: true
+ exit_code: 0
----- stdout -----
----- stderr -----
- × No solution found when resolving dependencies:
- ╰─▶ Because first-local was not found in the provided package locations and second-local==0.1.0 depends on first-local, we can conclude that second-local==0.1.0 cannot be used.
- And because only second-local==0.1.0 is available and you require second-local, we can conclude that your requirements are unsatisfiable.
+ Resolved 2 packages in [TIME]
+ Audited 2 packages in [TIME]
"###
);
@@ -5477,8 +5563,8 @@ fn already_installed_local_version_of_remote_package() {
----- stdout -----
----- stderr -----
- Resolved 3 packages in [TIME]
- Audited 3 packages in [TIME]
+ Resolved 1 package in [TIME]
+ Audited 1 package in [TIME]
"###
);
From eb6ad9a4fad70fc083e26dab2d9639841f0356c2 Mon Sep 17 00:00:00 2001
From: Zanie Blue
Date: Mon, 6 Jan 2025 18:04:25 -0600
Subject: [PATCH 043/135] Bump version to 0.5.15 (#10337)
---
CHANGELOG.md | 34 +++++++++++++++++++++++++++
Cargo.lock | 4 ++--
crates/uv-version/Cargo.toml | 2 +-
crates/uv/Cargo.toml | 2 +-
docs/getting-started/installation.md | 4 ++--
docs/guides/integration/docker.md | 8 +++----
docs/guides/integration/github.md | 2 +-
docs/guides/integration/pre-commit.md | 6 ++---
pyproject.toml | 2 +-
9 files changed, 49 insertions(+), 15 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81289b3a7f5d..fdb00540c29a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,39 @@
# Changelog
+## 0.5.15
+
+### Python
+
+The managed Python distributions have been updated, including:
+
+- Python 3.14.0a3 support on macOS and Linux
+- Performance improvements
+- Fixes to SQLite feature detection
+
+See the [`python-build-standalone` release notes](https://github.com/astral-sh/python-build-standalone/releases/tag/20250106) for more details.
+
+### Enhancements
+
+- Respect `FORCE_COLOR` environment variable ([#10315](https://github.com/astral-sh/uv/pull/10315))
+
+### Performance
+
+- Avoid generating unused hashes during `uv lock` ([#10307](https://github.com/astral-sh/uv/pull/10307))
+- Visit source distributions before wheels ([#10291](https://github.com/astral-sh/uv/pull/10291))
+
+### Bug fixes
+
+- Avoid downgrading packages when `--upgrade` is provided ([#10097](https://github.com/astral-sh/uv/pull/10097))
+- Extract supported architectures from wheel tags ([#10179](https://github.com/astral-sh/uv/pull/10179))
+- Redact new index credentials in `uv add` ([#10329](https://github.com/astral-sh/uv/pull/10329))
+
+### Documentation
+
+- Clarify `exclude-newer` only allows full timestamps in settings documentation ([#9135](https://github.com/astral-sh/uv/pull/9135))
+- Tweak script `--no-project` comment ([#10331](https://github.com/astral-sh/uv/pull/10331))
+- Update copyright year ([#10297](https://github.com/astral-sh/uv/pull/10297))
+- Add instructinos for installing with Scoop ([#10332](https://github.com/astral-sh/uv/pull/10332))
+
## 0.5.14
### Enhancements
diff --git a/Cargo.lock b/Cargo.lock
index 5c026b5775aa..3024fffafe54 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4454,7 +4454,7 @@ checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
[[package]]
name = "uv"
-version = "0.5.14"
+version = "0.5.15"
dependencies = [
"anstream",
"anyhow",
@@ -5661,7 +5661,7 @@ dependencies = [
[[package]]
name = "uv-version"
-version = "0.5.14"
+version = "0.5.15"
[[package]]
name = "uv-virtualenv"
diff --git a/crates/uv-version/Cargo.toml b/crates/uv-version/Cargo.toml
index e12b273d3561..a5b7f368d991 100644
--- a/crates/uv-version/Cargo.toml
+++ b/crates/uv-version/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uv-version"
-version = "0.5.14"
+version = "0.5.15"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml
index 2ba13f820f4e..4467f9947dae 100644
--- a/crates/uv/Cargo.toml
+++ b/crates/uv/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uv"
-version = "0.5.14"
+version = "0.5.15"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
index 705afb7e66ee..420034834873 100644
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -25,7 +25,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:
```console
- $ curl -LsSf https://astral.sh/uv/0.5.14/install.sh | sh
+ $ curl -LsSf https://astral.sh/uv/0.5.15/install.sh | sh
```
=== "Windows"
@@ -41,7 +41,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:
```console
- $ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.5.14/install.ps1 | iex"
+ $ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.5.15/install.ps1 | iex"
```
!!! tip
diff --git a/docs/guides/integration/docker.md b/docs/guides/integration/docker.md
index 1b6e92be361e..71a4621d9f04 100644
--- a/docs/guides/integration/docker.md
+++ b/docs/guides/integration/docker.md
@@ -21,7 +21,7 @@ $ docker run ghcr.io/astral-sh/uv --help
uv provides a distroless Docker image including the `uv` binary. The following tags are published:
- `ghcr.io/astral-sh/uv:latest`
-- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.5.14`
+- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.5.15`
- `ghcr.io/astral-sh/uv:{major}.{minor}`, e.g., `ghcr.io/astral-sh/uv:0.5` (the latest patch
version)
@@ -62,7 +62,7 @@ In addition, uv publishes the following images:
As with the distroless image, each image is published with uv version tags as
`ghcr.io/astral-sh/uv:{major}.{minor}.{patch}-{base}` and
-`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.5.14-alpine`.
+`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.5.15-alpine`.
For more details, see the [GitHub Container](https://github.com/astral-sh/uv/pkgs/container/uv)
page.
@@ -100,13 +100,13 @@ Note this requires `curl` to be available.
In either case, it is best practice to pin to a specific uv version, e.g., with:
```dockerfile
-COPY --from=ghcr.io/astral-sh/uv:0.5.14 /uv /uvx /bin/
+COPY --from=ghcr.io/astral-sh/uv:0.5.15 /uv /uvx /bin/
```
Or, with the installer:
```dockerfile
-ADD https://astral.sh/uv/0.5.14/install.sh /uv-installer.sh
+ADD https://astral.sh/uv/0.5.15/install.sh /uv-installer.sh
```
### Installing a project
diff --git a/docs/guides/integration/github.md b/docs/guides/integration/github.md
index 022b71315941..d09fb6a7b2dd 100644
--- a/docs/guides/integration/github.md
+++ b/docs/guides/integration/github.md
@@ -40,7 +40,7 @@ jobs:
uses: astral-sh/setup-uv@v5
with:
# Install a specific version of uv.
- version: "0.5.14"
+ version: "0.5.15"
```
## Setting up Python
diff --git a/docs/guides/integration/pre-commit.md b/docs/guides/integration/pre-commit.md
index 3eb5257e95c6..2694bef5ce70 100644
--- a/docs/guides/integration/pre-commit.md
+++ b/docs/guides/integration/pre-commit.md
@@ -29,7 +29,7 @@ To compile requirements via pre-commit, add the following to the `.pre-commit-co
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.14
+ rev: 0.5.15
hooks:
# Compile requirements
- id: pip-compile
@@ -41,7 +41,7 @@ To compile alternative files, modify `args` and `files`:
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.14
+ rev: 0.5.15
hooks:
# Compile requirements
- id: pip-compile
@@ -54,7 +54,7 @@ To run the hook over multiple files at the same time:
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.14
+ rev: 0.5.15
hooks:
# Compile requirements
- id: pip-compile
diff --git a/pyproject.toml b/pyproject.toml
index 3b34adc0d525..cc8cc9715d1b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "maturin"
[project]
name = "uv"
-version = "0.5.14"
+version = "0.5.15"
description = "An extremely fast Python package and project manager, written in Rust."
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
requires-python = ">=3.8"
From 180a1381615c502e5000cd6ba88b8cf397ef3066 Mon Sep 17 00:00:00 2001
From: konsti
Date: Tue, 7 Jan 2025 10:55:06 +0100
Subject: [PATCH 044/135] Deactivate tracing for choose version (#10351)
---
crates/uv-resolver/Cargo.toml | 3 +++
crates/uv-resolver/src/resolver/mod.rs | 5 ++++-
crates/uv/Cargo.toml | 1 +
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/crates/uv-resolver/Cargo.toml b/crates/uv-resolver/Cargo.toml
index 6c9381e67849..c67ab035c370 100644
--- a/crates/uv-resolver/Cargo.toml
+++ b/crates/uv-resolver/Cargo.toml
@@ -67,3 +67,6 @@ url = { workspace = true }
[dev-dependencies]
insta = { version = "1.40.0" }
toml = { workspace = true }
+
+[features]
+tracing-durations-export = []
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index 270940ee16e6..22fabda36b93 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -1013,7 +1013,10 @@ impl ResolverState
Date: Tue, 7 Jan 2025 10:55:37 +0100
Subject: [PATCH 045/135] Add `Send + 'static` bounds to cache deserialization
(#10352)
---
crates/uv-client/src/cached_client.rs | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/crates/uv-client/src/cached_client.rs b/crates/uv-client/src/cached_client.rs
index dc521f5cf3db..3ee9e1cfc25c 100644
--- a/crates/uv-client/src/cached_client.rs
+++ b/crates/uv-client/src/cached_client.rs
@@ -40,7 +40,7 @@ pub trait Cacheable: Sized {
///
/// Typical use of this is for wrapper types used to provide blanket trait
/// impls without hitting overlapping impl problems.
- type Target;
+ type Target: Send + 'static;
/// Deserialize a value from bytes aligned to a 16-byte boundary.
fn from_aligned_bytes(bytes: AlignedVec) -> Result;
@@ -58,7 +58,7 @@ pub(crate) struct SerdeCacheable {
inner: T,
}
-impl Cacheable for SerdeCacheable {
+impl Cacheable for SerdeCacheable {
type Target = T;
fn from_aligned_bytes(bytes: AlignedVec) -> Result {
@@ -79,7 +79,7 @@ impl Cacheable for SerdeCacheable {
/// All `OwnedArchive` values are cacheable.
impl Cacheable for OwnedArchive
where
- A: rkyv::Archive + for<'a> rkyv::Serialize>,
+ A: rkyv::Archive + for<'a> rkyv::Serialize> + Send + 'static,
A::Archived: rkyv::Portable
+ rkyv::Deserialize
+ for<'a> rkyv::bytecheck::CheckBytes>,
@@ -214,7 +214,7 @@ impl CachedClient {
/// allowed to make subsequent requests, e.g. through the uncached client.
#[instrument(skip_all)]
pub async fn get_serde<
- Payload: Serialize + DeserializeOwned + 'static,
+ Payload: Serialize + DeserializeOwned + Send + 'static,
CallBackError: std::error::Error + 'static,
Callback,
CallbackReturn,
@@ -342,7 +342,7 @@ impl CachedClient {
/// Make a request without checking whether the cache is fresh.
pub async fn skip_cache<
- Payload: Serialize + DeserializeOwned + 'static,
+ Payload: Serialize + DeserializeOwned + Send + 'static,
CallBackError: std::error::Error + 'static,
Callback,
CallbackReturn,
@@ -569,7 +569,7 @@ impl CachedClient {
/// Perform a [`CachedClient::get_serde`] request with a default retry strategy.
#[instrument(skip_all)]
pub async fn get_serde_with_retry<
- Payload: Serialize + DeserializeOwned + 'static,
+ Payload: Serialize + DeserializeOwned + Send + 'static,
CallBackError: std::error::Error + 'static,
Callback,
CallbackReturn,
@@ -648,7 +648,7 @@ impl CachedClient {
///
/// See:
pub async fn skip_cache_with_retry<
- Payload: Serialize + DeserializeOwned + 'static,
+ Payload: Serialize + DeserializeOwned + Send + 'static,
CallBackError: std::error::Error + 'static,
Callback,
CallbackReturn,
From 671e93816a3c638389d5f601c1ea8b1ce0e6d73e Mon Sep 17 00:00:00 2001
From: Andrew Tulloch
Date: Tue, 7 Jan 2025 05:02:33 -0800
Subject: [PATCH 046/135] Buffer file reads in `serde_json::from_reader`
(#10341)
## Summary
https://docs.rs/serde_json/latest/serde_json/fn.from_reader.html
suggests that
> When reading from a source against which short reads are not
efficient, such as a
[File](https://doc.rust-lang.org/std/fs/struct.File.html), you will want
to apply your own buffering because serde_json will not buffer the
input. See
[std::io::BufReader](https://doc.rust-lang.org/std/io/struct.BufReader.html).
Without this buffering, we observe a sequence of single byte reads which
can be quite inefficient depending on the underlying filesystem.
This adds buffering with `std::io::BufReader` to resolve this.
## Test Plan
Unit tests cover this code.
---
crates/uv-distribution-types/src/installed.rs | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/crates/uv-distribution-types/src/installed.rs b/crates/uv-distribution-types/src/installed.rs
index 1d4cf966216b..4bd528643a5a 100644
--- a/crates/uv-distribution-types/src/installed.rs
+++ b/crates/uv-distribution-types/src/installed.rs
@@ -1,4 +1,5 @@
use std::borrow::Cow;
+use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::str::FromStr;
@@ -309,7 +310,8 @@ impl InstalledDist {
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(err) => return Err(err.into()),
};
- let direct_url = serde_json::from_reader::(file)?;
+ let direct_url =
+ serde_json::from_reader::, DirectUrl>(BufReader::new(file))?;
Ok(Some(direct_url))
}
@@ -321,7 +323,8 @@ impl InstalledDist {
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(err) => return Err(err.into()),
};
- let cache_info = serde_json::from_reader::(file)?;
+ let cache_info =
+ serde_json::from_reader::, CacheInfo>(BufReader::new(file))?;
Ok(Some(cache_info))
}
From 788df24460fc1f8ec5ad7e98b4783f25938f3a67 Mon Sep 17 00:00:00 2001
From: csteiner <47841949+clintonsteiner@users.noreply.github.com>
Date: Tue, 7 Jan 2025 07:46:44 -0600
Subject: [PATCH 047/135] docs: github actions example: remove unneeded install
step (#10356)
* Previously had uv python install, then uv sync --all-extras --dev
* If we are going to use sync for dev dependencies, then the install
step is unneccessary
## Summary
## Test Plan
---
docs/guides/integration/github.md | 3 ---
1 file changed, 3 deletions(-)
diff --git a/docs/guides/integration/github.md b/docs/guides/integration/github.md
index d09fb6a7b2dd..cbc726ac7915 100644
--- a/docs/guides/integration/github.md
+++ b/docs/guides/integration/github.md
@@ -182,9 +182,6 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v5
- - name: Set up Python
- run: uv python install
-
- name: Install the project
run: uv sync --all-extras --dev
From 0fcccb89949c5835777fde7284b44b6d9c879d83 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Tue, 7 Jan 2025 08:47:05 -0500
Subject: [PATCH 048/135] Accept requirements in `uv remove` (#10338)
## Summary
This allows, e.g., `uv remove flask[dotenv]` to remove `flask`. Like
`pip install` and `uv pip install`, the content after the package name
has no effect.
Closes https://github.com/astral-sh/uv/issues/9764.
---
crates/uv-cli/src/lib.rs | 2 +-
crates/uv-settings/src/settings.rs | 4 ++-
crates/uv/src/settings.rs | 6 ++++
crates/uv/tests/it/edit.rs | 53 ++++++++++++++++++++++++++++++
4 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs
index ade2d43ac685..6419f3213625 100644
--- a/crates/uv-cli/src/lib.rs
+++ b/crates/uv-cli/src/lib.rs
@@ -3272,7 +3272,7 @@ pub struct AddArgs {
pub struct RemoveArgs {
/// The names of the dependencies to remove (e.g., `ruff`).
#[arg(required = true)]
- pub packages: Vec,
+ pub packages: Vec>,
/// Remove the packages from the development dependency group.
///
diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs
index 6f645ebc6436..6dd61ea380cd 100644
--- a/crates/uv-settings/src/settings.rs
+++ b/crates/uv-settings/src/settings.rs
@@ -1,6 +1,8 @@
-use serde::{Deserialize, Serialize};
use std::{fmt::Debug, num::NonZeroUsize, path::PathBuf};
+
+use serde::{Deserialize, Serialize};
use url::Url;
+
use uv_cache_info::CacheKey;
use uv_configuration::{
ConfigSettings, IndexStrategy, KeyringProviderType, PackageNameSpecifier, RequiredVersion,
diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs
index fff0dabb6e1b..dd327cf666a4 100644
--- a/crates/uv/src/settings.rs
+++ b/crates/uv/src/settings.rs
@@ -5,6 +5,7 @@ use std::process;
use std::str::FromStr;
use url::Url;
+
use uv_cache::{CacheArgs, Refresh};
use uv_cli::comma::CommaSeparatedRequirements;
use uv_cli::{
@@ -1251,6 +1252,11 @@ impl RemoveSettings {
.map(|fs| fs.install_mirrors.clone())
.unwrap_or_default();
+ let packages = packages
+ .into_iter()
+ .map(|requirement| requirement.name)
+ .collect();
+
Self {
locked,
frozen,
diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs
index 9deb6039022e..aabfa2435ffb 100644
--- a/crates/uv/tests/it/edit.rs
+++ b/crates/uv/tests/it/edit.rs
@@ -8386,3 +8386,56 @@ fn add_no_indent() -> Result<()> {
});
Ok(())
}
+
+/// Accept requirements, not just package names, in `uv remove`.
+#[test]
+fn remove_requirement() -> Result<()> {
+ let context = TestContext::new("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.12"
+ dependencies = ["flask"]
+
+ [build-system]
+ requires = ["setuptools>=42"]
+ build-backend = "setuptools.build_meta"
+ "#})?;
+
+ uv_snapshot!(context.filters(), context.remove().arg("flask[dotenv]"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 1 package in [TIME]
+ Prepared 1 package in [TIME]
+ Installed 1 package in [TIME]
+ + project==0.1.0 (from file://[TEMP_DIR]/)
+ "###);
+
+ let pyproject_toml = context.read("pyproject.toml");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ pyproject_toml, @r###"
+ [project]
+ name = "project"
+ version = "0.1.0"
+ requires-python = ">=3.12"
+ dependencies = []
+
+ [build-system]
+ requires = ["setuptools>=42"]
+ build-backend = "setuptools.build_meta"
+ "###
+ );
+ });
+
+ Ok(())
+}
From d8fcf2a58f18a5c88e4c1fd4f9f346ceda0fd2e8 Mon Sep 17 00:00:00 2001
From: konsti
Date: Tue, 7 Jan 2025 14:50:13 +0100
Subject: [PATCH 049/135] Simplify `requirements_for_extra` (#10347)
Ref https://github.com/astral-sh/uv/issues/10344
Not a performance optimization, but the function had become too large.
No logic changes, just code moving around. Looks slightly better when
ignoring whitespace changes.
It's still too complex but i haven't found an apt simplification.
---
crates/uv-resolver/src/resolver/mod.rs | 285 ++++++++++++++-----------
1 file changed, 156 insertions(+), 129 deletions(-)
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index 22fabda36b93..ca3097a64c2c 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -1908,33 +1908,170 @@ impl ResolverState,
+ env: &ResolverEnvironment,
+ python_requirement: &PythonRequirement,
+ ) -> bool {
+ let python_marker = python_requirement.to_marker_tree();
+ // If the requirement would not be selected with any Python version
+ // supported by the root, skip it.
+ if python_marker.is_disjoint(requirement.marker) {
+ trace!(
+ "skipping {requirement} because of Requires-Python: {requires_python}",
+ requires_python = python_requirement.target(),
+ );
+ return false;
+ }
+
+ // If we're in a fork in universal mode, ignore any dependency that isn't part of
+ // this fork (but will be part of another fork).
+ if !env.included_by_marker(requirement.marker) {
+ trace!("skipping {requirement} because of {env}");
+ return false;
+ }
+
+ // If the requirement isn't relevant for the current platform, skip it.
+ match extra {
+ Some(source_extra) => {
+ // Only include requirements that are relevant for the current extra.
+ if requirement.evaluate_markers(env.marker_environment(), &[]) {
+ return false;
+ }
+ if !requirement
+ .evaluate_markers(env.marker_environment(), slice::from_ref(source_extra))
+ {
+ return false;
+ }
+ if !env.included_by_group(ConflictItemRef::from((&requirement.name, source_extra)))
+ {
+ return false;
+ }
+ }
+ None => {
+ if !requirement.evaluate_markers(env.marker_environment(), &[]) {
+ return false;
+ }
+ }
+ }
+
+ true
+ }
+
+ /// The constraints applicable to the requirement, filtered by Python version, the markers of
+ /// this fork and the requested extra.
+ fn constraints_for_requirement<'data, 'parameters>(
+ &'data self,
+ requirement: Cow<'data, Requirement>,
+ extra: Option<&'parameters ExtraName>,
+ env: &'parameters ResolverEnvironment,
+ python_requirement: &'parameters PythonRequirement,
+ ) -> impl Iterator- > + 'parameters
+ where
+ 'data: 'parameters,
+ {
+ self.constraints
+ .get(&requirement.name)
+ .into_iter()
+ .flatten()
+ .filter_map(move |constraint| {
// If the requirement would not be selected with any Python version
// supported by the root, skip it.
- if python_marker.is_disjoint(requirement.marker) {
- trace!(
- "skipping {requirement} because of Requires-Python: {requires_python}",
- requires_python = python_requirement.target(),
- );
- return None;
- }
+ let constraint = if constraint.marker.is_true() {
+ // Additionally, if the requirement is `requests ; sys_platform == 'darwin'`
+ // and the constraint is `requests ; python_version == '3.6'`, the
+ // constraint should only apply when _both_ markers are true.
+ if requirement.marker.is_true() {
+ Cow::Borrowed(constraint)
+ } else {
+ let mut marker = constraint.marker;
+ marker.and(requirement.marker);
+
+ if marker.is_false() {
+ trace!(
+ "skipping {constraint} because of disjoint markers: `{}` vs. `{}`",
+ constraint.marker.try_to_string().unwrap(),
+ requirement.marker.try_to_string().unwrap(),
+ );
+ return None;
+ }
+
+ Cow::Owned(Requirement {
+ name: constraint.name.clone(),
+ extras: constraint.extras.clone(),
+ groups: constraint.groups.clone(),
+ source: constraint.source.clone(),
+ origin: constraint.origin.clone(),
+ marker,
+ })
+ }
+ } else {
+ let requires_python = python_requirement.target();
+ let python_marker = python_requirement.to_marker_tree();
+
+ let mut marker = constraint.marker;
+ marker.and(requirement.marker);
+
+ if marker.is_false() {
+ trace!(
+ "skipping {constraint} because of disjoint markers: `{}` vs. `{}`",
+ constraint.marker.try_to_string().unwrap(),
+ requirement.marker.try_to_string().unwrap(),
+ );
+ return None;
+ }
+
+ // Additionally, if the requirement is `requests ; sys_platform == 'darwin'`
+ // and the constraint is `requests ; python_version == '3.6'`, the
+ // constraint should only apply when _both_ markers are true.
+ if python_marker.is_disjoint(marker) {
+ trace!(
+ "skipping constraint {requirement} because of Requires-Python: {requires_python}"
+ );
+ return None;
+ }
+
+ if marker == constraint.marker {
+ Cow::Borrowed(constraint)
+ } else {
+ Cow::Owned(Requirement {
+ name: constraint.name.clone(),
+ extras: constraint.extras.clone(),
+ groups: constraint.groups.clone(),
+ source: constraint.source.clone(),
+ origin: constraint.origin.clone(),
+ marker,
+ })
+ }
+ };
// If we're in a fork in universal mode, ignore any dependency that isn't part of
// this fork (but will be part of another fork).
- if !env.included_by_marker(requirement.marker) {
- trace!("skipping {requirement} because of {env}");
+ if !env.included_by_marker(constraint.marker) {
+ trace!("skipping {constraint} because of {env}");
return None;
}
- // If the requirement isn't relevant for the current platform, skip it.
+ // If the constraint isn't relevant for the current platform, skip it.
match extra {
Some(source_extra) => {
- // Only include requirements that are relevant for the current extra.
- if requirement.evaluate_markers(env.marker_environment(), &[]) {
- return None;
- }
- if !requirement.evaluate_markers(
+ if !constraint.evaluate_markers(
env.marker_environment(),
slice::from_ref(source_extra),
) {
@@ -1947,123 +2084,13 @@ impl ResolverState {
- if !requirement.evaluate_markers(env.marker_environment(), &[]) {
+ if !constraint.evaluate_markers(env.marker_environment(), &[]) {
return None;
}
}
}
- Some(requirement)
- })
- .flat_map(move |requirement| {
- iter::once(requirement.clone()).chain(
- self.constraints
- .get(&requirement.name)
- .into_iter()
- .flatten()
- .filter_map(move |constraint| {
- // If the requirement would not be selected with any Python version
- // supported by the root, skip it.
- let constraint = if constraint.marker.is_true() {
- // Additionally, if the requirement is `requests ; sys_platform == 'darwin'`
- // and the constraint is `requests ; python_version == '3.6'`, the
- // constraint should only apply when _both_ markers are true.
- if requirement.marker.is_true() {
- Cow::Borrowed(constraint)
- } else {
- let mut marker = constraint.marker;
- marker.and(requirement.marker);
-
- if marker.is_false() {
- trace!(
- "skipping {constraint} because of disjoint markers: `{}` vs. `{}`",
- constraint.marker.try_to_string().unwrap(),
- requirement.marker.try_to_string().unwrap(),
- );
- return None;
- }
-
- Cow::Owned(Requirement {
- name: constraint.name.clone(),
- extras: constraint.extras.clone(),
- groups: constraint.groups.clone(),
- source: constraint.source.clone(),
- origin: constraint.origin.clone(),
- marker,
- })
- }
- } else {
- let requires_python = python_requirement.target();
- let python_marker = python_requirement.to_marker_tree();
-
- let mut marker = constraint.marker;
- marker.and(requirement.marker);
-
- if marker.is_false() {
- trace!(
- "skipping {constraint} because of disjoint markers: `{}` vs. `{}`",
- constraint.marker.try_to_string().unwrap(),
- requirement.marker.try_to_string().unwrap(),
- );
- return None;
- }
-
- // Additionally, if the requirement is `requests ; sys_platform == 'darwin'`
- // and the constraint is `requests ; python_version == '3.6'`, the
- // constraint should only apply when _both_ markers are true.
- if python_marker.is_disjoint(marker) {
- trace!(
- "skipping constraint {requirement} because of Requires-Python: {requires_python}"
- );
- return None;
- }
-
- if marker == constraint.marker {
- Cow::Borrowed(constraint)
- } else {
- Cow::Owned(Requirement {
- name: constraint.name.clone(),
- extras: constraint.extras.clone(),
- groups: constraint.groups.clone(),
- source: constraint.source.clone(),
- origin: constraint.origin.clone(),
- marker,
- })
- }
- };
-
- // If we're in a fork in universal mode, ignore any dependency that isn't part of
- // this fork (but will be part of another fork).
- if !env.included_by_marker(constraint.marker) {
- trace!("skipping {constraint} because of {env}");
- return None;
- }
-
- // If the constraint isn't relevant for the current platform, skip it.
- match extra {
- Some(source_extra) => {
- if !constraint.evaluate_markers(
- env.marker_environment(),
- slice::from_ref(source_extra),
- ) {
- return None;
- }
- if !env.included_by_group(
- ConflictItemRef::from((&requirement.name, source_extra)),
- ) {
- return None;
- }
- }
- None => {
- if !constraint.evaluate_markers(env.marker_environment(), &[]) {
- return None;
- }
- }
- }
-
- Some(constraint)
- })
- )
+ Some(constraint)
})
}
From 3dc481b0633bfa710e68a172dcb91839aa97f1c8 Mon Sep 17 00:00:00 2001
From: konsti
Date: Tue, 7 Jan 2025 14:51:08 +0100
Subject: [PATCH 050/135] Speed up file pins (#10346)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Ref https://github.com/astral-sh/uv/issues/10344
Avoid the nested hashmap, file pinning is called in the version
selection hot loop.
```
$ hyperfine --warmup 1 --prepare "uv venv -p 3.12" "./uv-2 pip compile scripts/requirements/airflow.in" "./uv-1 pip compile scripts/requirements/airflow.in"
Finished `profiling` profile [optimized + debuginfo] target(s) in 0.12s
Benchmark 1: ./uv-2 pip compile scripts/requirements/airflow.in
Time (mean ± σ): 420.1 ms ± 4.7 ms [User: 585.4 ms, System: 195.1 ms]
Range (min … max): 413.1 ms … 429.2 ms 10 runs
Benchmark 2: ./uv-1 pip compile scripts/requirements/airflow.in
Time (mean ± σ): 473.0 ms ± 4.9 ms [User: 654.4 ms, System: 209.4 ms]
Range (min … max): 468.0 ms … 481.1 ms 10 runs
Summary
./uv-2 pip compile scripts/requirements/airflow.in ran
1.13 ± 0.02 times faster than ./uv-1 pip compile scripts/requirements/airflow.in
```
---
crates/uv-resolver/src/pins.rs | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/crates/uv-resolver/src/pins.rs b/crates/uv-resolver/src/pins.rs
index 73623f92c9a1..b7e96f9b8f9f 100644
--- a/crates/uv-resolver/src/pins.rs
+++ b/crates/uv-resolver/src/pins.rs
@@ -10,15 +10,14 @@ use crate::candidate_selector::Candidate;
/// For example, given `Flask==3.0.0`, the [`FilePins`] would contain a mapping from `Flask` to
/// `3.0.0` to the specific wheel or source distribution archive that was pinned for that version.
#[derive(Clone, Debug, Default)]
-pub(crate) struct FilePins(FxHashMap>);
+pub(crate) struct FilePins(FxHashMap<(PackageName, uv_pep440::Version), ResolvedDist>);
impl FilePins {
/// Pin a candidate package.
pub(crate) fn insert(&mut self, candidate: &Candidate, dist: &CompatibleDist) {
- self.0.entry(candidate.name().clone()).or_default().insert(
- candidate.version().clone(),
- dist.for_installation().to_owned(),
- );
+ self.0
+ .entry((candidate.name().clone(), candidate.version().clone()))
+ .or_insert_with(|| dist.for_installation().to_owned());
}
/// Return the pinned file for the given package name and version, if it exists.
@@ -27,6 +26,7 @@ impl FilePins {
name: &PackageName,
version: &uv_pep440::Version,
) -> Option<&ResolvedDist> {
- self.0.get(name)?.get(version)
+ // Inserts are common while reads are rare, so the clone here is overall faster.
+ self.0.get(&(name.clone(), version.clone()))
}
}
From c6ac121ed05599470368efb5c9771ac18bcaf5d6 Mon Sep 17 00:00:00 2001
From: konsti
Date: Tue, 7 Jan 2025 14:58:36 +0100
Subject: [PATCH 051/135] Refactor batch prefetch (#10349)
---
Cargo.lock | 1 -
crates/uv-resolver/Cargo.toml | 1 -
.../src/resolver/batch_prefetch.rs | 247 +++++++++++-------
3 files changed, 150 insertions(+), 99 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 3024fffafe54..cf1ce2226037 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5470,7 +5470,6 @@ dependencies = [
name = "uv-resolver"
version = "0.0.1"
dependencies = [
- "anyhow",
"clap",
"dashmap",
"either",
diff --git a/crates/uv-resolver/Cargo.toml b/crates/uv-resolver/Cargo.toml
index c67ab035c370..62706a4e6c89 100644
--- a/crates/uv-resolver/Cargo.toml
+++ b/crates/uv-resolver/Cargo.toml
@@ -38,7 +38,6 @@ uv-types = { workspace = true }
uv-warnings = { workspace = true }
uv-workspace = { workspace = true }
-anyhow = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
dashmap = { workspace = true }
either = { workspace = true }
diff --git a/crates/uv-resolver/src/resolver/batch_prefetch.rs b/crates/uv-resolver/src/resolver/batch_prefetch.rs
index b8e6e95331cd..dd63fc60fbb2 100644
--- a/crates/uv-resolver/src/resolver/batch_prefetch.rs
+++ b/crates/uv-resolver/src/resolver/batch_prefetch.rs
@@ -1,7 +1,8 @@
use std::cmp::min;
+use std::sync::Arc;
use itertools::Itertools;
-use pubgrub::{Range, Term};
+use pubgrub::{Range, Ranges, Term};
use rustc_hash::FxHashMap;
use tokio::sync::mpsc::Sender;
use tracing::{debug, trace};
@@ -41,10 +42,19 @@ enum BatchPrefetchStrategy {
/// Note that these all heuristics that could totally prefetch lots of irrelevant versions.
#[derive(Clone)]
pub(crate) struct BatchPrefetcher {
- // Internal types.
+ // Types to determine whether we need to prefetch.
tried_versions: FxHashMap,
last_prefetch: FxHashMap,
- // Shared (e.g., `Arc`) types.
+ // Types to execute the prefetch.
+ prefetch_runner: BatchPrefetcherRunner,
+}
+
+/// The types that are needed for running the batch prefetching after we determined that we need to
+/// prefetch.
+///
+/// These types are shared (e.g., `Arc`) so they can be cheaply cloned and moved between threads.
+#[derive(Clone)]
+pub(crate) struct BatchPrefetcherRunner {
capabilities: IndexCapabilities,
index: InMemoryIndex,
request_sink: Sender,
@@ -59,9 +69,11 @@ impl BatchPrefetcher {
Self {
tried_versions: FxHashMap::default(),
last_prefetch: FxHashMap::default(),
- capabilities,
- index,
- request_sink,
+ prefetch_runner: BatchPrefetcherRunner {
+ capabilities,
+ index,
+ request_sink,
+ },
}
}
@@ -76,7 +88,7 @@ impl BatchPrefetcher {
python_requirement: &PythonRequirement,
selector: &CandidateSelector,
env: &ResolverEnvironment,
- ) -> anyhow::Result<(), ResolveError> {
+ ) -> Result<(), ResolveError> {
let PubGrubPackageInner::Package {
name,
extra: None,
@@ -95,25 +107,116 @@ impl BatchPrefetcher {
// This is immediate, we already fetched the version map.
let versions_response = if let Some(index) = index {
- self.index
+ self.prefetch_runner
+ .index
.explicit()
.wait_blocking(&(name.clone(), index.clone()))
.ok_or_else(|| ResolveError::UnregisteredTask(name.to_string()))?
} else {
- self.index
+ self.prefetch_runner
+ .index
.implicit()
.wait_blocking(name)
.ok_or_else(|| ResolveError::UnregisteredTask(name.to_string()))?
};
- let VersionsResponse::Found(ref version_map) = *versions_response else {
- return Ok(());
- };
-
- let mut phase = BatchPrefetchStrategy::Compatible {
+ let phase = BatchPrefetchStrategy::Compatible {
compatible: current_range.clone(),
previous: version.clone(),
};
+
+ self.last_prefetch.insert(name.clone(), num_tried);
+
+ self.prefetch_runner.send_prefetch(
+ name,
+ unchangeable_constraints,
+ total_prefetch,
+ &versions_response,
+ phase,
+ python_requirement,
+ selector,
+ env,
+ )?;
+
+ Ok(())
+ }
+
+ /// Each time we tried a version for a package, we register that here.
+ pub(crate) fn version_tried(&mut self, package: &PubGrubPackage) {
+ // Only track base packages, no virtual packages from extras.
+ let PubGrubPackageInner::Package {
+ name,
+ extra: None,
+ dev: None,
+ marker: MarkerTree::TRUE,
+ } = &**package
+ else {
+ return;
+ };
+ *self.tried_versions.entry(name.clone()).or_default() += 1;
+ }
+
+ /// After 5, 10, 20, 40 tried versions, prefetch that many versions to start early but not
+ /// too aggressive. Later we schedule the prefetch of 50 versions every 20 versions, this gives
+ /// us a good buffer until we see prefetch again and is high enough to saturate the task pool.
+ fn should_prefetch(&self, next: &PubGrubPackage) -> (usize, bool) {
+ let PubGrubPackageInner::Package {
+ name,
+ extra: None,
+ dev: None,
+ marker: MarkerTree::TRUE,
+ } = &**next
+ else {
+ return (0, false);
+ };
+
+ let num_tried = self.tried_versions.get(name).copied().unwrap_or_default();
+ let previous_prefetch = self.last_prefetch.get(name).copied().unwrap_or_default();
+ let do_prefetch = (num_tried >= 5 && previous_prefetch < 5)
+ || (num_tried >= 10 && previous_prefetch < 10)
+ || (num_tried >= 20 && previous_prefetch < 20)
+ || (num_tried >= 20 && num_tried - previous_prefetch >= 20);
+ (num_tried, do_prefetch)
+ }
+
+ /// Log stats about how many versions we tried.
+ ///
+ /// Note that they may be inflated when we count the same version repeatedly during
+ /// backtracking.
+ pub(crate) fn log_tried_versions(&self) {
+ let total_versions: usize = self.tried_versions.values().sum();
+ let mut tried_versions: Vec<_> = self.tried_versions.iter().collect();
+ tried_versions.sort_by(|(p1, c1), (p2, c2)| {
+ c1.cmp(c2)
+ .reverse()
+ .then(p1.to_string().cmp(&p2.to_string()))
+ });
+ let counts = tried_versions
+ .iter()
+ .map(|(package, count)| format!("{package} {count}"))
+ .join(", ");
+ debug!("Tried {total_versions} versions: {counts}");
+ }
+}
+
+impl BatchPrefetcherRunner {
+ /// Given that the conditions for prefetching are met, find the versions to prefetch and
+ /// send the prefetch requests.
+ fn send_prefetch(
+ &self,
+ name: &PackageName,
+ unchangeable_constraints: Option<&Term>>,
+ total_prefetch: usize,
+ versions_response: &Arc,
+ mut phase: BatchPrefetchStrategy,
+ python_requirement: &PythonRequirement,
+ selector: &CandidateSelector,
+ env: &ResolverEnvironment,
+ ) -> Result<(), ResolveError> {
+ let VersionsResponse::Found(ref version_map) = &**versions_response else {
+ return Ok(());
+ };
+
let mut prefetch_count = 0;
for _ in 0..total_prefetch {
let candidate = match phase {
@@ -148,7 +251,7 @@ impl BatchPrefetcher {
// If we have constraints from root, don't go beyond those. Example: We are
// prefetching for foo 1.60 and have a dependency for `foo>=1.50`, so we should
// only prefetch 1.60 to 1.50, knowing 1.49 will always be rejected.
- if let Some(unchangeable_constraints) = unchangeable_constraints {
+ if let Some(unchangeable_constraints) = &unchangeable_constraints {
range = match unchangeable_constraints {
Term::Positive(constraints) => range.intersection(constraints),
Term::Negative(negative_constraints) => {
@@ -189,33 +292,9 @@ impl BatchPrefetcher {
}
// Avoid prefetching for distributions that don't satisfy the Python requirement.
- match dist {
- CompatibleDist::InstalledDist(_) => {}
- CompatibleDist::SourceDist { sdist, .. }
- | CompatibleDist::IncompatibleWheel { sdist, .. } => {
- // Source distributions must meet both the _target_ Python version and the
- // _installed_ Python version (to build successfully).
- if let Some(requires_python) = sdist.file.requires_python.as_ref() {
- if !python_requirement
- .installed()
- .is_contained_by(requires_python)
- {
- continue;
- }
- if !python_requirement.target().is_contained_by(requires_python) {
- continue;
- }
- }
- }
- CompatibleDist::CompatibleWheel { wheel, .. } => {
- // Wheels must meet the _target_ Python version.
- if let Some(requires_python) = wheel.file.requires_python.as_ref() {
- if !python_requirement.target().is_contained_by(requires_python) {
- continue;
- }
- }
- }
- };
+ if !satisfies_python(dist, python_requirement) {
+ continue;
+ }
let dist = dist.for_resolution();
@@ -242,64 +321,38 @@ impl BatchPrefetcher {
_ => debug!("Prefetched {prefetch_count} `{name}` versions"),
}
- self.last_prefetch.insert(name.clone(), num_tried);
Ok(())
}
+}
- /// Each time we tried a version for a package, we register that here.
- pub(crate) fn version_tried(&mut self, package: &PubGrubPackage) {
- // Only track base packages, no virtual packages from extras.
- let PubGrubPackageInner::Package {
- name,
- extra: None,
- dev: None,
- marker: MarkerTree::TRUE,
- } = &**package
- else {
- return;
- };
- *self.tried_versions.entry(name.clone()).or_default() += 1;
- }
-
- /// After 5, 10, 20, 40 tried versions, prefetch that many versions to start early but not
- /// too aggressive. Later we schedule the prefetch of 50 versions every 20 versions, this gives
- /// us a good buffer until we see prefetch again and is high enough to saturate the task pool.
- fn should_prefetch(&self, next: &PubGrubPackage) -> (usize, bool) {
- let PubGrubPackageInner::Package {
- name,
- extra: None,
- dev: None,
- marker: MarkerTree::TRUE,
- } = &**next
- else {
- return (0, false);
- };
-
- let num_tried = self.tried_versions.get(name).copied().unwrap_or_default();
- let previous_prefetch = self.last_prefetch.get(name).copied().unwrap_or_default();
- let do_prefetch = (num_tried >= 5 && previous_prefetch < 5)
- || (num_tried >= 10 && previous_prefetch < 10)
- || (num_tried >= 20 && previous_prefetch < 20)
- || (num_tried >= 20 && num_tried - previous_prefetch >= 20);
- (num_tried, do_prefetch)
+fn satisfies_python(dist: &CompatibleDist, python_requirement: &PythonRequirement) -> bool {
+ match dist {
+ CompatibleDist::InstalledDist(_) => {}
+ CompatibleDist::SourceDist { sdist, .. }
+ | CompatibleDist::IncompatibleWheel { sdist, .. } => {
+ // Source distributions must meet both the _target_ Python version and the
+ // _installed_ Python version (to build successfully).
+ if let Some(requires_python) = sdist.file.requires_python.as_ref() {
+ if !python_requirement
+ .installed()
+ .is_contained_by(requires_python)
+ {
+ return false;
+ }
+ if !python_requirement.target().is_contained_by(requires_python) {
+ return false;
+ }
+ }
+ }
+ CompatibleDist::CompatibleWheel { wheel, .. } => {
+ // Wheels must meet the _target_ Python version.
+ if let Some(requires_python) = wheel.file.requires_python.as_ref() {
+ if !python_requirement.target().is_contained_by(requires_python) {
+ return false;
+ }
+ }
+ }
}
- /// Log stats about how many versions we tried.
- ///
- /// Note that they may be inflated when we count the same version repeatedly during
- /// backtracking.
- pub(crate) fn log_tried_versions(&self) {
- let total_versions: usize = self.tried_versions.values().sum();
- let mut tried_versions: Vec<_> = self.tried_versions.iter().collect();
- tried_versions.sort_by(|(p1, c1), (p2, c2)| {
- c1.cmp(c2)
- .reverse()
- .then(p1.to_string().cmp(&p2.to_string()))
- });
- let counts = tried_versions
- .iter()
- .map(|(package, count)| format!("{package} {count}"))
- .join(", ");
- debug!("Tried {total_versions} versions: {counts}");
- }
+ true
}
From 3cbf8ab7b1c61ca652314e64e6b3338790194bc6 Mon Sep 17 00:00:00 2001
From: konsti
Date: Tue, 7 Jan 2025 15:12:12 +0100
Subject: [PATCH 052/135] Optimize `requirements_for_extra` (#10348)
---
crates/uv-configuration/src/overrides.rs | 9 ++-
crates/uv-resolver/src/resolver/mod.rs | 79 ++++++++++++++----------
2 files changed, 55 insertions(+), 33 deletions(-)
diff --git a/crates/uv-configuration/src/overrides.rs b/crates/uv-configuration/src/overrides.rs
index e690a66cbf2d..8a84188febee 100644
--- a/crates/uv-configuration/src/overrides.rs
+++ b/crates/uv-configuration/src/overrides.rs
@@ -42,7 +42,12 @@ impl Overrides {
&'a self,
requirements: impl IntoIterator
- ,
) -> impl Iterator
- > {
- requirements.into_iter().flat_map(|requirement| {
+ if self.0.is_empty() {
+ // Fast path: There are no overrides.
+ return Either::Left(requirements.into_iter().map(Cow::Borrowed));
+ }
+
+ Either::Right(requirements.into_iter().flat_map(|requirement| {
let Some(overrides) = self.get(&requirement.name) else {
// Case 1: No override(s).
return Either::Left(std::iter::once(Cow::Borrowed(requirement)));
@@ -70,6 +75,6 @@ impl Overrides {
})
},
)))
- })
+ }))
}
}
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index ca3097a64c2c..dd71b0b0d6f6 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -1802,8 +1802,15 @@ impl ResolverState>();
// Dependency groups can include the project itself, so no need to flatten recursive
@@ -1833,9 +1840,13 @@ impl ResolverState {
requirement.marker.and(marker);
@@ -1901,6 +1912,7 @@ impl ResolverState + 'parameters,
extra: Option<&'parameters ExtraName>,
env: &'parameters ResolverEnvironment,
+ python_marker: MarkerTree,
python_requirement: &'parameters PythonRequirement,
) -> impl Iterator
- > + 'parameters
where
@@ -1909,13 +1921,20 @@ impl ResolverState ResolverState,
env: &ResolverEnvironment,
+ python_marker: MarkerTree,
python_requirement: &PythonRequirement,
) -> bool {
- let python_marker = python_requirement.to_marker_tree();
- // If the requirement would not be selected with any Python version
- // supported by the root, skip it.
- if python_marker.is_disjoint(requirement.marker) {
- trace!(
- "skipping {requirement} because of Requires-Python: {requires_python}",
- requires_python = python_requirement.target(),
- );
- return false;
- }
-
- // If we're in a fork in universal mode, ignore any dependency that isn't part of
- // this fork (but will be part of another fork).
- if !env.included_by_marker(requirement.marker) {
- trace!("skipping {requirement} because of {env}");
- return false;
- }
-
// If the requirement isn't relevant for the current platform, skip it.
match extra {
Some(source_extra) => {
@@ -1971,6 +1973,23 @@ impl ResolverState ResolverState,
extra: Option<&'parameters ExtraName>,
env: &'parameters ResolverEnvironment,
+ python_marker: MarkerTree,
python_requirement: &'parameters PythonRequirement,
) -> impl Iterator
- > + 'parameters
where
@@ -2023,7 +2043,6 @@ impl ResolverState ResolverState {
- if !constraint.evaluate_markers(
- env.marker_environment(),
- slice::from_ref(source_extra),
- ) {
+ if !constraint
+ .evaluate_markers(env.marker_environment(), slice::from_ref(source_extra))
+ {
return None;
}
- if !env.included_by_group(
- ConflictItemRef::from((&requirement.name, source_extra)),
- ) {
+ if !env.included_by_group(ConflictItemRef::from((&requirement.name, source_extra)))
+ {
return None;
}
}
From 14a9008e54def81e46955cebeb443c196813be71 Mon Sep 17 00:00:00 2001
From: konsti
Date: Tue, 7 Jan 2025 15:12:18 +0100
Subject: [PATCH 053/135] Avoid overcounting versions in batch prefetcher
(#10350)
---
.../src/resolver/batch_prefetch.rs | 24 +++++++++++--------
crates/uv-resolver/src/resolver/mod.rs | 4 ++--
2 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/crates/uv-resolver/src/resolver/batch_prefetch.rs b/crates/uv-resolver/src/resolver/batch_prefetch.rs
index dd63fc60fbb2..6d35a2f97913 100644
--- a/crates/uv-resolver/src/resolver/batch_prefetch.rs
+++ b/crates/uv-resolver/src/resolver/batch_prefetch.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
use itertools::Itertools;
use pubgrub::{Range, Ranges, Term};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
use tokio::sync::mpsc::Sender;
use tracing::{debug, trace};
@@ -43,7 +43,7 @@ enum BatchPrefetchStrategy {
#[derive(Clone)]
pub(crate) struct BatchPrefetcher {
// Types to determine whether we need to prefetch.
- tried_versions: FxHashMap,
+ tried_versions: FxHashMap>,
last_prefetch: FxHashMap,
// Types to execute the prefetch.
prefetch_runner: BatchPrefetcherRunner,
@@ -142,7 +142,7 @@ impl BatchPrefetcher {
}
/// Each time we tried a version for a package, we register that here.
- pub(crate) fn version_tried(&mut self, package: &PubGrubPackage) {
+ pub(crate) fn version_tried(&mut self, package: &PubGrubPackage, version: &Version) {
// Only track base packages, no virtual packages from extras.
let PubGrubPackageInner::Package {
name,
@@ -153,7 +153,10 @@ impl BatchPrefetcher {
else {
return;
};
- *self.tried_versions.entry(name.clone()).or_default() += 1;
+ self.tried_versions
+ .entry(name.clone())
+ .or_default()
+ .insert(version.clone());
}
/// After 5, 10, 20, 40 tried versions, prefetch that many versions to start early but not
@@ -170,7 +173,7 @@ impl BatchPrefetcher {
return (0, false);
};
- let num_tried = self.tried_versions.get(name).copied().unwrap_or_default();
+ let num_tried = self.tried_versions.get(name).map_or(0, FxHashSet::len);
let previous_prefetch = self.last_prefetch.get(name).copied().unwrap_or_default();
let do_prefetch = (num_tried >= 5 && previous_prefetch < 5)
|| (num_tried >= 10 && previous_prefetch < 10)
@@ -180,12 +183,13 @@ impl BatchPrefetcher {
}
/// Log stats about how many versions we tried.
- ///
- /// Note that they may be inflated when we count the same version repeatedly during
- /// backtracking.
pub(crate) fn log_tried_versions(&self) {
- let total_versions: usize = self.tried_versions.values().sum();
- let mut tried_versions: Vec<_> = self.tried_versions.iter().collect();
+ let total_versions: usize = self.tried_versions.values().map(FxHashSet::len).sum();
+ let mut tried_versions: Vec<_> = self
+ .tried_versions
+ .iter()
+ .map(|(name, versions)| (name, versions.len()))
+ .collect();
tried_versions.sort_by(|(p1, c1), (p2, c2)| {
c1.cmp(c2)
.reverse()
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index dd71b0b0d6f6..2b3e4f69613f 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -476,8 +476,6 @@ impl ResolverState ResolverState
Date: Tue, 7 Jan 2025 09:16:23 -0500
Subject: [PATCH 054/135] uv-pep440: adds an explicit test for trailing zeros
(#10362)
Basically, this explicitly checks that parsing a `1.2.0` into a
`Version` will roundtrip back to a `1.2.0`, and that parsing a `1.2`
will roundtrip back to a `1.2`.
I think this case is included in the other tests in this module, but
this test makes the behavior more clearly intentional I think.
Ref #10345
---
crates/uv-pep440/src/version.rs | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs
index ca8919c19330..80bc262ff4d8 100644
--- a/crates/uv-pep440/src/version.rs
+++ b/crates/uv-pep440/src/version.rs
@@ -3961,4 +3961,18 @@ mod tests {
VersionBloatedDebug(self)
}
}
+
+ /// This explicitly tests that we preserve trailing zeros in a version
+ /// string. i.e., Both `1.2` and `1.2.0` round-trip, with the former
+ /// lacking a trailing zero and the latter including it.
+ #[test]
+ fn preserve_trailing_zeros() {
+ let v1: Version = "1.2.0".parse().unwrap();
+ assert_eq!(v1.release(), &[1, 2, 0]);
+ assert_eq!(v1.to_string(), "1.2.0");
+
+ let v2: Version = "1.2".parse().unwrap();
+ assert_eq!(v2.release(), &[1, 2]);
+ assert_eq!(v2.to_string(), "1.2");
+ }
}
From 373e34f5ddfcbdb2d20c81e0c6bb20e12c862d59 Mon Sep 17 00:00:00 2001
From: konsti
Date: Tue, 7 Jan 2025 15:25:32 +0100
Subject: [PATCH 055/135] Remove `[u64; 4]` from small version to move `Arc` to
full version (#10345)
---
crates/uv-distribution-types/src/lib.rs | 4 +-
crates/uv-pep440/src/version.rs | 178 +++++++++++++---------
crates/uv-pep440/src/version_ranges.rs | 6 +-
crates/uv-pep440/src/version_specifier.rs | 16 +-
crates/uv-pep508/src/marker/algebra.rs | 4 +-
crates/uv-pep508/src/marker/simplify.rs | 2 +-
crates/uv-python/src/discovery.rs | 2 +-
crates/uv/src/commands/python/list.rs | 14 +-
crates/uv/src/lib.rs | 2 +
9 files changed, 132 insertions(+), 96 deletions(-)
diff --git a/crates/uv-distribution-types/src/lib.rs b/crates/uv-distribution-types/src/lib.rs
index c9a0c48b50cd..860368cf93d1 100644
--- a/crates/uv-distribution-types/src/lib.rs
+++ b/crates/uv-distribution-types/src/lib.rs
@@ -1343,8 +1343,8 @@ mod test {
/// Ensure that we don't accidentally grow the `Dist` sizes.
#[test]
fn dist_size() {
- assert!(size_of::() <= 336, "{}", size_of::());
- assert!(size_of::() <= 336, "{}", size_of::());
+ assert!(size_of::() <= 352, "{}", size_of::());
+ assert!(size_of::() <= 352, "{}", size_of::());
assert!(
size_of::() <= 264,
"{}",
diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs
index 80bc262ff4d8..e83ef7a13f2a 100644
--- a/crates/uv-pep440/src/version.rs
+++ b/crates/uv-pep440/src/version.rs
@@ -1,4 +1,5 @@
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
+use std::ops::Deref;
use std::sync::LazyLock;
use std::{
borrow::Borrow,
@@ -267,7 +268,7 @@ impl std::fmt::Display for OperatorParseError {
)]
#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, Eq, PartialEq, PartialOrd, Ord)))]
pub struct Version {
- inner: Arc,
+ inner: VersionInner,
}
#[derive(Clone, Debug)]
@@ -278,7 +279,7 @@ pub struct Version {
#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, Eq, PartialEq, PartialOrd, Ord)))]
enum VersionInner {
Small { small: VersionSmall },
- Full { full: VersionFull },
+ Full { full: Arc },
}
impl Version {
@@ -295,9 +296,9 @@ impl Version {
R: Borrow,
{
Self {
- inner: Arc::new(VersionInner::Small {
+ inner: VersionInner::Small {
small: VersionSmall::new(),
- }),
+ },
}
.with_release(release_numbers)
}
@@ -344,7 +345,7 @@ impl Version {
/// Returns the epoch of this version.
#[inline]
pub fn epoch(&self) -> u64 {
- match *self.inner {
+ match self.inner {
VersionInner::Small { ref small } => small.epoch(),
VersionInner::Full { ref full } => full.epoch,
}
@@ -352,17 +353,44 @@ impl Version {
/// Returns the release number part of the version.
#[inline]
- pub fn release(&self) -> &[u64] {
- match *self.inner {
- VersionInner::Small { ref small } => small.release(),
- VersionInner::Full { ref full, .. } => &full.release,
- }
+ pub fn release(&self) -> Release {
+ let inner = match &self.inner {
+ VersionInner::Small { small } => {
+ // Parse out the version digits.
+ // * Bytes 6 and 7 correspond to the first release segment as a `u16`.
+ // * Bytes 5, 4 and 3 correspond to the second, third and fourth release
+ // segments, respectively.
+ match small.len {
+ 0 => ReleaseInner::Small0([]),
+ 1 => ReleaseInner::Small1([(small.repr >> 0o60) & 0xFFFF]),
+ 2 => ReleaseInner::Small2([
+ (small.repr >> 0o60) & 0xFFFF,
+ (small.repr >> 0o50) & 0xFF,
+ ]),
+ 3 => ReleaseInner::Small3([
+ (small.repr >> 0o60) & 0xFFFF,
+ (small.repr >> 0o50) & 0xFF,
+ (small.repr >> 0o40) & 0xFF,
+ ]),
+ 4 => ReleaseInner::Small4([
+ (small.repr >> 0o60) & 0xFFFF,
+ (small.repr >> 0o50) & 0xFF,
+ (small.repr >> 0o40) & 0xFF,
+ (small.repr >> 0o30) & 0xFF,
+ ]),
+ _ => unreachable!("{}", small.len),
+ }
+ }
+ VersionInner::Full { full } => ReleaseInner::Full(&full.release),
+ };
+
+ Release { inner }
}
/// Returns the pre-release part of this version, if it exists.
#[inline]
pub fn pre(&self) -> Option {
- match *self.inner {
+ match self.inner {
VersionInner::Small { ref small } => small.pre(),
VersionInner::Full { ref full } => full.pre,
}
@@ -371,7 +399,7 @@ impl Version {
/// Returns the post-release part of this version, if it exists.
#[inline]
pub fn post(&self) -> Option {
- match *self.inner {
+ match self.inner {
VersionInner::Small { ref small } => small.post(),
VersionInner::Full { ref full } => full.post,
}
@@ -380,7 +408,7 @@ impl Version {
/// Returns the dev-release part of this version, if it exists.
#[inline]
pub fn dev(&self) -> Option {
- match *self.inner {
+ match self.inner {
VersionInner::Small { ref small } => small.dev(),
VersionInner::Full { ref full } => full.dev,
}
@@ -389,7 +417,7 @@ impl Version {
/// Returns the local segments in this version, if any exist.
#[inline]
pub fn local(&self) -> LocalVersionSlice {
- match *self.inner {
+ match self.inner {
VersionInner::Small { ref small } => small.local_slice(),
VersionInner::Full { ref full } => full.local.as_slice(),
}
@@ -402,7 +430,7 @@ impl Version {
/// like `1.0a1`, `1.0dev0`, etc.
#[inline]
pub fn min(&self) -> Option {
- match *self.inner {
+ match self.inner {
VersionInner::Small { ref small } => small.min(),
VersionInner::Full { ref full } => full.min,
}
@@ -415,7 +443,7 @@ impl Version {
/// like `1.0.post1`, `1.0+local`, etc.
#[inline]
pub fn max(&self) -> Option {
- match *self.inner {
+ match self.inner {
VersionInner::Small { ref small } => small.max(),
VersionInner::Full { ref full } => full.max,
}
@@ -453,7 +481,7 @@ impl Version {
/// last number in the release component.
#[inline]
fn push_release(&mut self, n: u64) {
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.push_release(n) {
return;
}
@@ -467,10 +495,10 @@ impl Version {
/// since all versions should have at least one release number.
#[inline]
fn clear_release(&mut self) {
- match Arc::make_mut(&mut self.inner) {
+ match &mut self.inner {
VersionInner::Small { ref mut small } => small.clear_release(),
VersionInner::Full { ref mut full } => {
- full.release.clear();
+ Arc::make_mut(full).release.clear();
}
}
}
@@ -479,7 +507,7 @@ impl Version {
#[inline]
#[must_use]
pub fn with_epoch(mut self, value: u64) -> Self {
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.set_epoch(value) {
return self;
}
@@ -492,7 +520,7 @@ impl Version {
#[inline]
#[must_use]
pub fn with_pre(mut self, value: Option) -> Self {
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.set_pre(value) {
return self;
}
@@ -505,7 +533,7 @@ impl Version {
#[inline]
#[must_use]
pub fn with_post(mut self, value: Option) -> Self {
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.set_post(value) {
return self;
}
@@ -518,7 +546,7 @@ impl Version {
#[inline]
#[must_use]
pub fn with_dev(mut self, value: Option) -> Self {
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.set_dev(value) {
return self;
}
@@ -546,7 +574,7 @@ impl Version {
match value {
LocalVersion::Segments(segments) => self.with_local_segments(segments),
LocalVersion::Max => {
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.set_local(LocalVersion::Max) {
return self;
}
@@ -564,7 +592,7 @@ impl Version {
#[inline]
#[must_use]
pub fn without_local(mut self) -> Self {
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.set_local(LocalVersion::empty()) {
return self;
}
@@ -605,7 +633,7 @@ impl Version {
pub fn with_min(mut self, value: Option) -> Self {
debug_assert!(!self.is_pre(), "min is not allowed on pre-release versions");
debug_assert!(!self.is_dev(), "min is not allowed on dev versions");
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.set_min(value) {
return self;
}
@@ -627,7 +655,7 @@ impl Version {
"max is not allowed on post-release versions"
);
debug_assert!(!self.is_dev(), "max is not allowed on dev versions");
- if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) {
+ if let VersionInner::Small { ref mut small } = &mut self.inner {
if small.set_max(value) {
return self;
}
@@ -639,10 +667,10 @@ impl Version {
/// Convert this version to a "full" representation in-place and return a
/// mutable borrow to the full type.
fn make_full(&mut self) -> &mut VersionFull {
- if let VersionInner::Small { ref small } = *self.inner {
+ if let VersionInner::Small { ref small } = self.inner {
let full = VersionFull {
epoch: small.epoch(),
- release: small.release().to_vec(),
+ release: self.release().to_vec(),
min: small.min(),
max: small.max(),
pre: small.pre(),
@@ -651,11 +679,13 @@ impl Version {
local: small.local(),
};
*self = Self {
- inner: Arc::new(VersionInner::Full { full }),
+ inner: VersionInner::Full {
+ full: Arc::new(full),
+ },
};
}
- match Arc::make_mut(&mut self.inner) {
- VersionInner::Full { ref mut full } => full,
+ match &mut self.inner {
+ VersionInner::Full { ref mut full } => Arc::make_mut(full),
VersionInner::Small { .. } => unreachable!(),
}
}
@@ -679,7 +709,7 @@ impl Version {
}
}
- match compare_release(self.release(), other.release()) {
+ match compare_release(&self.release(), &other.release()) {
Ordering::Less => {
return Ordering::Less;
}
@@ -800,7 +830,7 @@ impl Ord for Version {
/// < 1.0 < 1.0.post456.dev34 < 1.0.post456
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
- match (&*self.inner, &*other.inner) {
+ match (&self.inner, &other.inner) {
(VersionInner::Small { small: small1 }, VersionInner::Small { small: small2 }) => {
small1.repr.cmp(&small2.repr)
}
@@ -820,7 +850,7 @@ impl FromStr for Version {
}
}
-/// A "small" representation of a version.
+/// A small representation of a version.
///
/// This representation is used for a (very common) subset of versions: the
/// set of all versions with ~small numbers and no local component. The
@@ -901,27 +931,6 @@ impl FromStr for Version {
)]
#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, Eq, PartialEq, PartialOrd, Ord)))]
struct VersionSmall {
- /// The representation discussed above.
- repr: u64,
- /// The `u64` numbers in the release component.
- ///
- /// These are *only* used to implement the public API `Version::release`
- /// method. This is necessary in order to provide a `&[u64]` to the caller.
- /// If we didn't need the public API, or could re-work it, then we could
- /// get rid of this extra storage. (Which is indeed duplicative of what is
- /// stored in `repr`.) Note that this uses `u64` not because it can store
- /// bigger numbers than what's in `repr` (it can't), but so that it permits
- /// us to return a `&[u64]`.
- ///
- /// I believe there is only one way to get rid of this extra storage:
- /// change the public API so that it doesn't return a `&[u64]`. Instead,
- /// we'd return a new type that conceptually represents a `&[u64]`, but may
- /// use a different representation based on what kind of `Version` it came
- /// from. The downside of this approach is that one loses the flexibility
- /// of a simple `&[u64]`. (Which, at time of writing, is taken advantage of
- /// in several places via slice patterns.) But, if we needed to change it,
- /// we could do it without losing expressivity, but losing convenience.
- release: [u64; 4],
/// The number of segments in the release component.
///
/// Strictly speaking, this isn't necessary since `1.2` is considered
@@ -930,6 +939,8 @@ struct VersionSmall {
/// places somewhat exposes internal details, since the "full" version
/// representation would not do that.
len: u8,
+ /// The representation discussed above.
+ repr: u64,
}
impl VersionSmall {
@@ -979,7 +990,6 @@ impl VersionSmall {
fn new() -> Self {
Self {
repr: Self::SUFFIX_NONE << Self::SUFFIX_VERSION_BIT_LEN,
- release: [0, 0, 0, 0],
len: 0,
}
}
@@ -999,15 +1009,9 @@ impl VersionSmall {
true
}
- #[inline]
- fn release(&self) -> &[u64] {
- &self.release[..usize::from(self.len)]
- }
-
#[inline]
fn clear_release(&mut self) {
self.repr &= !Self::SUFFIX_RELEASE_MASK;
- self.release = [0, 0, 0, 0];
self.len = 0;
}
@@ -1018,7 +1022,6 @@ impl VersionSmall {
return false;
}
self.repr |= n << 48;
- self.release[0] = n;
self.len = 1;
true
} else {
@@ -1030,7 +1033,6 @@ impl VersionSmall {
}
let shift = 48 - (usize::from(self.len) * 8);
self.repr |= n << shift;
- self.release[usize::from(self.len)] = n;
self.len += 1;
true
}
@@ -1416,6 +1418,41 @@ impl FromStr for VersionPattern {
}
}
+/// Release digits of a [`Version`].
+///
+/// Lifetime and indexing workaround to allow accessing the release as `&[u64]` even though the
+/// digits may be stored in a compressed representation.
+pub struct Release<'a> {
+ inner: ReleaseInner<'a>,
+}
+
+enum ReleaseInner<'a> {
+ // The small versions unpacked into larger u64 values.
+ // We're storing at most 4 u64 plus determinant for the duration of the release call on the
+ // stack, without heap allocation.
+ Small0([u64; 0]),
+ Small1([u64; 1]),
+ Small2([u64; 2]),
+ Small3([u64; 3]),
+ Small4([u64; 4]),
+ Full(&'a [u64]),
+}
+
+impl Deref for Release<'_> {
+ type Target = [u64];
+
+ fn deref(&self) -> &Self::Target {
+ match &self.inner {
+ ReleaseInner::Small0(v) => v,
+ ReleaseInner::Small1(v) => v,
+ ReleaseInner::Small2(v) => v,
+ ReleaseInner::Small3(v) => v,
+ ReleaseInner::Small4(v) => v,
+ ReleaseInner::Full(v) => v,
+ }
+ }
+}
+
/// An optional pre-release modifier and number applied to a version.
#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
#[cfg_attr(
@@ -1768,15 +1805,10 @@ impl<'a> Parser<'a> {
| (u64::from(release[2]) << 32)
| (u64::from(release[3]) << 24)
| (VersionSmall::SUFFIX_NONE << VersionSmall::SUFFIX_VERSION_BIT_LEN),
- release: [
- u64::from(release[0]),
- u64::from(release[1]),
- u64::from(release[2]),
- u64::from(release[3]),
- ],
+
len,
};
- let inner = Arc::new(VersionInner::Small { small });
+ let inner = VersionInner::Small { small };
let version = Version { inner };
Some(VersionPattern {
version,
@@ -3945,7 +3977,7 @@ mod tests {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Version")
.field("epoch", &self.0.epoch())
- .field("release", &self.0.release())
+ .field("release", &&*self.0.release())
.field("pre", &self.0.pre())
.field("post", &self.0.post())
.field("dev", &self.0.dev())
diff --git a/crates/uv-pep440/src/version_ranges.rs b/crates/uv-pep440/src/version_ranges.rs
index 7a9a4b055eed..526dce967e9a 100644
--- a/crates/uv-pep440/src/version_ranges.rs
+++ b/crates/uv-pep440/src/version_ranges.rs
@@ -43,7 +43,8 @@ impl From for Ranges {
})
.complement(),
Operator::TildeEqual => {
- let [rest @ .., last, _] = version.release() else {
+ let release = version.release();
+ let [rest @ .., last, _] = &*release else {
unreachable!("~= must have at least two segments");
};
let upper = Version::new(rest.iter().chain([&(last + 1)]))
@@ -160,7 +161,8 @@ pub fn release_specifier_to_range(specifier: VersionSpecifier) -> Ranges {
- let [rest @ .., last, _] = version.release() else {
+ let release = version.release();
+ let [rest @ .., last, _] = &*release else {
unreachable!("~= must have at least two segments");
};
let upper = Version::new(rest.iter().chain([&(last + 1)]));
diff --git a/crates/uv-pep440/src/version_specifier.rs b/crates/uv-pep440/src/version_specifier.rs
index 30cdc76243b8..91e7171e56f4 100644
--- a/crates/uv-pep440/src/version_specifier.rs
+++ b/crates/uv-pep440/src/version_specifier.rs
@@ -86,7 +86,7 @@ impl VersionSpecifiers {
// Ex) [3.7, 3.8), (3.8, 3.9] -> >=3.7,!=3.8.*,<=3.9
(Bound::Excluded(prev), Bound::Included(lower))
if prev.release().len() == 2
- && lower.release() == [prev.release()[0], prev.release()[1] + 1] =>
+ && *lower.release() == [prev.release()[0], prev.release()[1] + 1] =>
{
specifiers.push(VersionSpecifier::not_equals_star_version(prev.clone()));
}
@@ -415,7 +415,7 @@ impl VersionSpecifier {
// `v >= 3.7 && v < 3.8` is equivalent to `v == 3.7.*`
(Bound::Included(v1), Bound::Excluded(v2))
if v1.release().len() == 2
- && v2.release() == [v1.release()[0], v1.release()[1] + 1] =>
+ && *v2.release() == [v1.release()[0], v1.release()[1] + 1] =>
{
(
Some(VersionSpecifier::equals_star_version(v1.clone())),
@@ -484,7 +484,7 @@ impl VersionSpecifier {
.version
.release()
.iter()
- .zip(other.release())
+ .zip(&*other.release())
.all(|(this, other)| this == other)
}
#[allow(deprecated)]
@@ -501,7 +501,7 @@ impl VersionSpecifier {
|| !this
.release()
.iter()
- .zip(version.release())
+ .zip(&*version.release())
.all(|(this, other)| this == other)
}
Operator::TildeEqual => {
@@ -516,7 +516,7 @@ impl VersionSpecifier {
if !this.release()[..this.release().len() - 1]
.iter()
- .zip(other.release())
+ .zip(&*other.release())
.all(|(this, other)| this == other)
{
return false;
@@ -530,7 +530,7 @@ impl VersionSpecifier {
Operator::GreaterThanEqual => Self::greater_than(&this, &other) || other >= this,
Operator::LessThan => {
Self::less_than(&this, &other)
- && !(version::compare_release(this.release(), other.release())
+ && !(version::compare_release(&this.release(), &other.release())
== Ordering::Equal
&& other.any_prerelease())
}
@@ -549,7 +549,7 @@ impl VersionSpecifier {
// not match 3.1.dev0, but should match 3.0.dev0).
if !this.any_prerelease()
&& other.is_pre()
- && version::compare_release(this.release(), other.release()) == Ordering::Equal
+ && version::compare_release(&this.release(), &other.release()) == Ordering::Equal
{
return false;
}
@@ -562,7 +562,7 @@ impl VersionSpecifier {
return true;
}
- if version::compare_release(this.release(), other.release()) == Ordering::Equal {
+ if version::compare_release(&this.release(), &other.release()) == Ordering::Equal {
// This special case is here so that, unless the specifier itself
// includes is a post-release version, that we do not accept
// post-release versions for the version mentioned in the specifier
diff --git a/crates/uv-pep508/src/marker/algebra.rs b/crates/uv-pep508/src/marker/algebra.rs
index 0c1bdbce200e..089e22432176 100644
--- a/crates/uv-pep508/src/marker/algebra.rs
+++ b/crates/uv-pep508/src/marker/algebra.rs
@@ -1508,7 +1508,7 @@ fn normalize_specifier(specifier: VersionSpecifier) -> VersionSpecifier {
// for `python_version` to fully simplify any ranges, such as `python_version > '3.9' or python_version <= '3.9'`,
// which is always `true` for `python_version`. For `python_full_version` however, this decision
// is a semantic change.
- let mut release = version.release();
+ let mut release = &*version.release();
// Strip any trailing `0`s.
//
@@ -1583,7 +1583,7 @@ fn python_version_to_full_version(specifier: VersionSpecifier) -> Result specifier,
})
} else {
- let &[major, minor, ..] = specifier.version().release() else {
+ let [major, minor, ..] = *specifier.version().release() else {
unreachable!()
};
diff --git a/crates/uv-pep508/src/marker/simplify.rs b/crates/uv-pep508/src/marker/simplify.rs
index 35a33b6ce4b8..ea868c3cfe4f 100644
--- a/crates/uv-pep508/src/marker/simplify.rs
+++ b/crates/uv-pep508/src/marker/simplify.rs
@@ -352,7 +352,7 @@ fn star_range_inequality(range: &Ranges) -> Option {
match (b1, b2) {
((Bound::Unbounded, Bound::Excluded(v1)), (Bound::Included(v2), Bound::Unbounded))
if v1.release().len() == 2
- && v2.release() == [v1.release()[0], v1.release()[1] + 1] =>
+ && *v2.release() == [v1.release()[0], v1.release()[1] + 1] =>
{
Some(VersionSpecifier::not_equals_star_version(v1.clone()))
}
diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs
index d7210ab4d4bc..879bb8b240ef 100644
--- a/crates/uv-python/src/discovery.rs
+++ b/crates/uv-python/src/discovery.rs
@@ -2220,7 +2220,7 @@ impl FromStr for VersionRequest {
};
// Cast the release components into u8s since that's what we use in `VersionRequest`
- let Ok(release) = try_into_u8_slice(version.release()) else {
+ let Ok(release) = try_into_u8_slice(&version.release()) else {
return Err(Error::InvalidVersionRequest(s.to_string()));
};
diff --git a/crates/uv/src/commands/python/list.rs b/crates/uv/src/commands/python/list.rs
index 899028801d4a..44951c3483fb 100644
--- a/crates/uv/src/commands/python/list.rs
+++ b/crates/uv/src/commands/python/list.rs
@@ -132,11 +132,11 @@ pub(crate) async fn list(
// Only show the latest patch version for each download unless all were requested
if !matches!(kind, Kind::System) {
- if let [major, minor, ..] = key.version().release() {
+ if let [major, minor, ..] = *key.version().release() {
if !seen_minor.insert((
*key.os(),
- *major,
- *minor,
+ major,
+ minor,
key.variant(),
key.implementation(),
*key.arch(),
@@ -147,12 +147,12 @@ pub(crate) async fn list(
}
}
}
- if let [major, minor, patch] = key.version().release() {
+ if let [major, minor, patch] = *key.version().release() {
if !seen_patch.insert((
*key.os(),
- *major,
- *minor,
- *patch,
+ major,
+ minor,
+ patch,
key.variant(),
key.implementation(),
*key.arch(),
diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs
index 9e7be8ce3796..7381b97729a0 100644
--- a/crates/uv/src/lib.rs
+++ b/crates/uv/src/lib.rs
@@ -11,6 +11,7 @@ use anstream::eprintln;
use anyhow::{bail, Context, Result};
use clap::error::{ContextKind, ContextValue};
use clap::{CommandFactory, Parser};
+use futures::FutureExt;
use owo_colors::OwoColorize;
use settings::PipTreeSettings;
use tokio::task::spawn_blocking;
@@ -1705,6 +1706,7 @@ async fn run_project(
printer,
globals.preview,
)
+ .boxed_local()
.await
}
}
From 043bdcec8bef56d09ab18932495fc92fa0e3f028 Mon Sep 17 00:00:00 2001
From: Andrew Gallant
Date: Tue, 7 Jan 2025 10:11:41 -0500
Subject: [PATCH 056/135] uv-pep440: fix bad merge (#10368)
This happened as a result of #10345 and #10362 being merged
independently. The latter used the old `Version::release` API, but the
former changed the `Version::release` API. This PR tweaks the new test
to use the new API (i.e., force a deref on the proxy type).
---
crates/uv-pep440/src/version.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs
index e83ef7a13f2a..c69a26a75e17 100644
--- a/crates/uv-pep440/src/version.rs
+++ b/crates/uv-pep440/src/version.rs
@@ -4000,11 +4000,11 @@ mod tests {
#[test]
fn preserve_trailing_zeros() {
let v1: Version = "1.2.0".parse().unwrap();
- assert_eq!(v1.release(), &[1, 2, 0]);
+ assert_eq!(&*v1.release(), &[1, 2, 0]);
assert_eq!(v1.to_string(), "1.2.0");
let v2: Version = "1.2".parse().unwrap();
- assert_eq!(v2.release(), &[1, 2]);
+ assert_eq!(&*v2.release(), &[1, 2]);
assert_eq!(v2.to_string(), "1.2");
}
}
From c8b3e8523cb70d61354adedc8cf303770d170189 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Tue, 7 Jan 2025 11:00:32 -0500
Subject: [PATCH 057/135] Re-enable `zlib-ng` on x86 platforms (#10365)
## Summary
Closes https://github.com/astral-sh/uv/issues/10363.
---
Cargo.lock | 31 ++++++++++++++++---
.../uv-performance-flate2-backend/Cargo.toml | 9 +++++-
2 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index cf1ce2226037..7a16a53e5a04 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -631,6 +631,15 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+[[package]]
+name = "cmake"
+version = "0.1.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "codspeed"
version = "2.7.2"
@@ -1026,7 +1035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -1101,6 +1110,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
dependencies = [
"crc32fast",
+ "libz-ng-sys",
"libz-rs-sys",
"miniz_oxide",
]
@@ -1887,7 +1897,7 @@ dependencies = [
"portable-atomic",
"portable-atomic-util",
"serde",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2007,6 +2017,16 @@ dependencies = [
"redox_syscall 0.5.8",
]
+[[package]]
+name = "libz-ng-sys"
+version = "1.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4436751a01da56f1277f323c80d584ffad94a3d14aecd959dd0dff75aa73a438"
+dependencies = [
+ "cmake",
+ "libc",
+]
+
[[package]]
name = "libz-rs-sys"
version = "0.4.1"
@@ -2765,7 +2785,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -3202,7 +3222,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -3771,7 +3791,7 @@ dependencies = [
"getrandom",
"once_cell",
"rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -5262,6 +5282,7 @@ name = "uv-performance-flate2-backend"
version = "0.1.0"
dependencies = [
"flate2",
+ "libz-ng-sys",
]
[[package]]
diff --git a/crates/uv-performance-flate2-backend/Cargo.toml b/crates/uv-performance-flate2-backend/Cargo.toml
index b82083d39081..7f137bfe652f 100644
--- a/crates/uv-performance-flate2-backend/Cargo.toml
+++ b/crates/uv-performance-flate2-backend/Cargo.toml
@@ -7,5 +7,12 @@ edition = "2021"
[lib]
doctest = false
-[dependencies]
+# Use `zlib-ng` on x86_64 targets, apart from FreeBSD.
+[target.'cfg(all(target_arch = "x86_64", not(target_os = "freebsd")))'.dependencies]
+flate2 = { version = "1.0.28", default-features = false, features = ["zlib-ng"] }
+# See: https://github.com/rust-lang/libz-sys/issues/225
+libz-ng-sys = { version = "<1.1.20" }
+
+# Use `zlib-rs` everywhere else.
+[target.'cfg(not(all(target_arch = "x86_64", not(target_os = "freebsd"))))'.dependencies]
flate2 = { version = "1.0.28", default-features = false, features = ["zlib-rs"] }
From a2a2662d43df593b7eb0fd38cc65706de9a58844 Mon Sep 17 00:00:00 2001
From: Kevin Marchais
Date: Tue, 7 Jan 2025 18:07:44 +0100
Subject: [PATCH 058/135] Fix ruff linting warnings from generated template
files for extension modules (#10371)
## Summary
This PR fixes two ruff linting issues in the generated template files
when using: `uv init --build-backend` for extension modules.
1. Removes unnecessary `from __future__ import annotations` imports from
generated .pyi files
([PYI044](https://docs.astral.sh/ruff/rules/future-annotations-in-stub/))
2. Adds missing blank line after `hello_from_bin` import to comply with
isort formatting
([I001](https://docs.astral.sh/ruff/rules/unsorted-imports/))
## Test Plan
```bash
cargo run -- init --build-backend scikit-build-core example-ext
uvx ruff check example-ext --select ALL
cargo run -- init --build-backend maturin example-ext
uvx ruff check example-ext --select ALL
```
## Remaining warnings
There are still warnings remainings in the generated `__init__.py`
files:
- [D104](https://docs.astral.sh/ruff/rules/undocumented-public-package/)
Missing docstring in public package
-
[D103](https://docs.astral.sh/ruff/rules/undocumented-public-function/)
Missing docstring in public function
- [T201](https://docs.astral.sh/ruff/rules/print/) `print` found
---
crates/uv/src/commands/project/init.rs | 4 ++--
crates/uv/tests/it/init.rs | 12 ++++--------
docs/concepts/projects/init.md | 1 +
3 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs
index 88e33a41b2bf..faa3302493d3 100644
--- a/crates/uv/src/commands/project/init.rs
+++ b/crates/uv/src/commands/project/init.rs
@@ -1055,6 +1055,7 @@ fn generate_package_scripts(
indoc::formatdoc! {r#"
from {module_name}._core import hello_from_bin
+
def hello() -> str:
return hello_from_bin()
"#}
@@ -1062,6 +1063,7 @@ fn generate_package_scripts(
indoc::formatdoc! {r#"
from {module_name}._core import hello_from_bin
+
def main() -> None:
print(hello_from_bin())
"#}
@@ -1069,8 +1071,6 @@ fn generate_package_scripts(
// .pyi file for binary script
let pyi_contents = indoc::indoc! {r"
- from __future__ import annotations
-
def hello_from_bin() -> str: ...
"};
diff --git a/crates/uv/tests/it/init.rs b/crates/uv/tests/it/init.rs
index 969aa8d3a33e..9157d2f671a8 100644
--- a/crates/uv/tests/it/init.rs
+++ b/crates/uv/tests/it/init.rs
@@ -2764,6 +2764,7 @@ fn init_app_build_backend_maturin() -> Result<()> {
init, @r###"
from foo._core import hello_from_bin
+
def main() -> None:
print(hello_from_bin())
"###
@@ -2776,8 +2777,6 @@ fn init_app_build_backend_maturin() -> Result<()> {
}, {
assert_snapshot!(
pyi_contents, @r###"
- from __future__ import annotations
-
def hello_from_bin() -> str: ...
"###
);
@@ -2894,6 +2893,7 @@ fn init_app_build_backend_scikit() -> Result<()> {
init, @r###"
from foo._core import hello_from_bin
+
def main() -> None:
print(hello_from_bin())
"###
@@ -2906,8 +2906,6 @@ fn init_app_build_backend_scikit() -> Result<()> {
}, {
assert_snapshot!(
pyi_contents, @r###"
- from __future__ import annotations
-
def hello_from_bin() -> str: ...
"###
);
@@ -3017,6 +3015,7 @@ fn init_lib_build_backend_maturin() -> Result<()> {
init, @r###"
from foo._core import hello_from_bin
+
def hello() -> str:
return hello_from_bin()
"###
@@ -3029,8 +3028,6 @@ fn init_lib_build_backend_maturin() -> Result<()> {
}, {
assert_snapshot!(
pyi_contents, @r###"
- from __future__ import annotations
-
def hello_from_bin() -> str: ...
"###
);
@@ -3144,6 +3141,7 @@ fn init_lib_build_backend_scikit() -> Result<()> {
init, @r###"
from foo._core import hello_from_bin
+
def hello() -> str:
return hello_from_bin()
"###
@@ -3156,8 +3154,6 @@ fn init_lib_build_backend_scikit() -> Result<()> {
}, {
assert_snapshot!(
pyi_contents, @r###"
- from __future__ import annotations
-
def hello_from_bin() -> str: ...
"###
);
diff --git a/docs/concepts/projects/init.md b/docs/concepts/projects/init.md
index 30c4ee4bad2a..a237af7f57e1 100644
--- a/docs/concepts/projects/init.md
+++ b/docs/concepts/projects/init.md
@@ -279,6 +279,7 @@ And the Python module imports it:
```python title="src/example_ext/__init__.py"
from example_ext._core import hello_from_bin
+
def main() -> None:
print(hello_from_bin())
```
From 2ae0ed3b355ca317cfa1ae1375fdcf54b980e371 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Tue, 7 Jan 2025 13:04:34 -0500
Subject: [PATCH 059/135] Re-enable zlib-ng on all platforms (except s390x,
PowerPC, and FreeBSD) (#10370)
PowerPC seems to build without errors if we upgrade `zlib-ng`, but
upgrading `zlib-ng` causes Windows to break
(https://github.com/rust-lang/libz-sys/issues/225), and Cargo doesn't
let us include two different versions.
s390x fails because it can't find `stfle`. It's possible that we could
fix this by by upgrading our manylinux version and/or by upgrading GCC
(which may necessitate upgrading our manylinux version), but I don't
know if it's fixable without one of those things? And it's not worth
bumping compatibility for that reason. \cc @konstin
---
.../uv-performance-flate2-backend/Cargo.lock | 42 +++++++++++++++++++
.../uv-performance-flate2-backend/Cargo.toml | 6 +--
2 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/crates/uv-performance-flate2-backend/Cargo.lock b/crates/uv-performance-flate2-backend/Cargo.lock
index 718b75bef8b4..3799c814f998 100644
--- a/crates/uv-performance-flate2-backend/Cargo.lock
+++ b/crates/uv-performance-flate2-backend/Cargo.lock
@@ -8,12 +8,30 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+[[package]]
+name = "cc"
+version = "1.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
+dependencies = [
+ "shlex",
+]
+
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "cmake"
+version = "0.1.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "crc32fast"
version = "1.4.2"
@@ -30,10 +48,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
dependencies = [
"crc32fast",
+ "libz-ng-sys",
"libz-rs-sys",
"miniz_oxide",
]
+[[package]]
+name = "libc"
+version = "0.2.169"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+
+[[package]]
+name = "libz-ng-sys"
+version = "1.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4436751a01da56f1277f323c80d584ffad94a3d14aecd959dd0dff75aa73a438"
+dependencies = [
+ "cmake",
+ "libc",
+]
+
[[package]]
name = "libz-rs-sys"
version = "0.4.1"
@@ -52,11 +87,18 @@ dependencies = [
"adler2",
]
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
[[package]]
name = "uv-performance-flate2-backend"
version = "0.1.0"
dependencies = [
"flate2",
+ "libz-ng-sys",
]
[[package]]
diff --git a/crates/uv-performance-flate2-backend/Cargo.toml b/crates/uv-performance-flate2-backend/Cargo.toml
index 7f137bfe652f..61249be4e328 100644
--- a/crates/uv-performance-flate2-backend/Cargo.toml
+++ b/crates/uv-performance-flate2-backend/Cargo.toml
@@ -7,12 +7,12 @@ edition = "2021"
[lib]
doctest = false
-# Use `zlib-ng` on x86_64 targets, apart from FreeBSD.
-[target.'cfg(all(target_arch = "x86_64", not(target_os = "freebsd")))'.dependencies]
+# Use `zlib-ng` on all supported platforms.
+[target.'cfg(not(any(target_arch = "s390x", target_arch = "powerpc64", target_os = "freebsd")))'.dependencies]
flate2 = { version = "1.0.28", default-features = false, features = ["zlib-ng"] }
# See: https://github.com/rust-lang/libz-sys/issues/225
libz-ng-sys = { version = "<1.1.20" }
# Use `zlib-rs` everywhere else.
-[target.'cfg(not(all(target_arch = "x86_64", not(target_os = "freebsd"))))'.dependencies]
+[target.'cfg(any(target_arch = "s390x", target_arch = "powerpc64", target_os = "freebsd"))'.dependencies]
flate2 = { version = "1.0.28", default-features = false, features = ["zlib-rs"] }
From 1ee17afd792e9263b349ef97a45b25ea8b66174c Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Tue, 7 Jan 2025 13:17:42 -0500
Subject: [PATCH 060/135] Avoid enforcing project-level required version for
`uv self` (#10374)
## Summary
Closes https://github.com/astral-sh/uv/issues/10355.
---
crates/uv/src/lib.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs
index 7381b97729a0..ca854b0adb49 100644
--- a/crates/uv/src/lib.rs
+++ b/crates/uv/src/lib.rs
@@ -119,7 +119,7 @@ async fn run(mut cli: Cli) -> Result {
Some(FilesystemOptions::from_file(config_file)?)
} else if deprecated_isolated || cli.top_level.no_config {
None
- } else if matches!(&*cli.command, Commands::Tool(_)) {
+ } else if matches!(&*cli.command, Commands::Tool(_) | Commands::Self_(_)) {
// For commands that operate at the user-level, ignore local configuration.
FilesystemOptions::user()?.combine(FilesystemOptions::system()?)
} else if let Ok(workspace) =
From 4c0922ee1b576ea2de484f843148f07658b27a27 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Tue, 7 Jan 2025 13:50:31 -0500
Subject: [PATCH 061/135] Add AWS Lambda integration guide (#10278)
## Summary
This includes instructions to:
- Deploy a standalone application.
- Deploy an application that depends on local libraries (a workspace).
- Deploy via Docker.
- Deploy via zip.
There's an accompanying Git repository here:
https://github.com/astral-sh/uv-aws-lambda-example.
Closes https://github.com/astral-sh/uv/issues/8935.
---
docs/guides/integration/aws-lambda.md | 416 ++++++++++++++++++++++++++
docs/guides/integration/index.md | 1 +
mkdocs.template.yml | 1 +
pyproject.toml | 1 +
4 files changed, 419 insertions(+)
create mode 100644 docs/guides/integration/aws-lambda.md
diff --git a/docs/guides/integration/aws-lambda.md b/docs/guides/integration/aws-lambda.md
new file mode 100644
index 000000000000..115d94bd5cb9
--- /dev/null
+++ b/docs/guides/integration/aws-lambda.md
@@ -0,0 +1,416 @@
+# Using uv with AWS Lambda
+
+[AWS Lambda](https://aws.amazon.com/lambda/) is a serverless computing service that lets you run
+code without provisioning or managing servers.
+
+You can use uv with AWS Lambda to manage your Python dependencies, build your deployment package,
+and deploy your Lambda functions.
+
+!!! tip
+
+ Check out the [`uv-aws-lambda-example`](https://github.com/astral-sh/uv-aws-lambda-example) project for
+ an example of best practices when using uv to deploy an application to AWS Lambda.
+
+## Getting started
+
+To start, assume we have a minimal FastAPI application with the following structure:
+
+```plaintext
+project
+├── pyproject.toml
+└── app
+ ├── __init__.py
+ └── main.py
+```
+
+Where the `pyproject.toml` contains:
+
+```toml title="pyproject.toml"
+[project]
+name = "uv-aws-lambda-example"
+version = "0.1.0"
+requires-python = ">=3.13"
+dependencies = [
+ # FastAPI is a modern web framework for building APIs with Python.
+ "fastapi",
+ # Mangum is a library that adapts ASGI applications to AWS Lambda and API Gateway.
+ "mangum",
+]
+
+[dependency-groups]
+dev = [
+ # In development mode, include the FastAPI development server.
+ "fastapi[standard]>=0.115",
+]
+```
+
+And the `main.py` file contains:
+
+```python title="app/main.py"
+import logging
+
+from fastapi import FastAPI
+from mangum import Mangum
+
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+
+app = FastAPI()
+handler = Mangum(app)
+
+
+@app.get("/")
+async def root() -> str:
+ return "Hello, world!"
+```
+
+We can run this application locally with:
+
+```console
+$ uv run fastapi dev
+```
+
+From there, opening http://127.0.0.1:8000/ in a web browser will display "Hello, world!"
+
+## Deploying a Docker image
+
+To deploy to AWS Lambda, we need to build a container image that includes the application code and
+dependencies in a single output directory.
+
+We'll follow the principles outlined in the [Docker guide](./docker.md) (in particular, a
+multi-stage build) to ensure that the final image is as small and cache-friendly as possible.
+
+In the first stage, we'll populate a single directory with all application code and dependencies. In
+the second stage, we'll copy this directory over to the final image, omitting the build tools and
+other unnecessary files.
+
+```dockerfile title="Dockerfile"
+FROM ghcr.io/astral-sh/uv:0.5.15 AS uv
+
+# First, bundle the dependencies into the task root.
+FROM public.ecr.aws/lambda/python:3.13 AS builder
+
+# Enable bytecode compilation.
+ENV UV_COMPILE_BYTECODE=1
+
+# Enable copy mode to support bind mount caching.
+ENV UV_LINK_MODE=copy
+
+# Bundle the dependencies into the Lambda task root via `uv pip install --target`.
+#
+# Omit any local packages (`--no-emit-workspace`) and development dependencies (`--no-dev`).
+# This ensures that the Docker layer cache is only invalidated when the `pyproject.toml` or `uv.lock`
+# files change, but remains robust to changes in the application code.
+RUN --mount=from=uv,source=/uv,target=/bin/uv \
+ --mount=type=cache,target=/root/.cache/uv \
+ --mount=type=bind,source=uv.lock,target=uv.lock \
+ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
+ uv export --frozen --no-emit-workspace --no-dev --no-editable -o requirements.txt && \
+ uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
+
+FROM public.ecr.aws/lambda/python:3.13
+
+# Copy the runtime dependencies from the builder stage.
+COPY --from=builder ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT}
+
+# Copy the application code.
+COPY ./app ${LAMBDA_TASK_ROOT}/app
+
+# Set the AWS Lambda handler.
+CMD ["app.main.handler"]
+```
+
+!!! tip
+
+ To deploy to ARM-based AWS Lambda runtimes, replace `public.ecr.aws/lambda/python:3.13` with `public.ecr.aws/lambda/python:3.13-arm64`.
+
+We can build the image with, e.g.:
+
+```console
+$ uv lock
+$ docker build -t fastapi-app .
+```
+
+The core benefits of this Dockerfile structure are as follows:
+
+1. **Minimal image size.** By using a multi-stage build, we can ensure that the final image only
+ includes the application code and dependencies. For example, the uv binary itself is not included
+ in the final image.
+2. **Maximal cache reuse.** By installing application dependencies separately from the application
+ code, we can ensure that the Docker layer cache is only invalidated when the dependencies change.
+
+Concretely, rebuilding the image after modifying the application source code can reuse the cached
+layers, resulting in millisecond builds:
+
+```console
+ => [internal] load build definition from Dockerfile 0.0s
+ => => transferring dockerfile: 1.31kB 0.0s
+ => [internal] load metadata for public.ecr.aws/lambda/python:3.13 0.3s
+ => [internal] load metadata for ghcr.io/astral-sh/uv:latest 0.3s
+ => [internal] load .dockerignore 0.0s
+ => => transferring context: 106B 0.0s
+ => [uv 1/1] FROM ghcr.io/astral-sh/uv:latest@sha256:ea61e006cfec0e8d81fae901ad703e09d2c6cf1aa58abcb6507d124b50286f 0.0s
+ => [builder 1/2] FROM public.ecr.aws/lambda/python:3.13@sha256:f5b51b377b80bd303fe8055084e2763336ea8920d12955b23ef 0.0s
+ => [internal] load build context 0.0s
+ => => transferring context: 185B 0.0s
+ => CACHED [builder 2/2] RUN --mount=from=uv,source=/uv,target=/bin/uv --mount=type=cache,target=/root/.cache/u 0.0s
+ => CACHED [stage-2 2/3] COPY --from=builder /var/task /var/task 0.0s
+ => CACHED [stage-2 3/3] COPY ./app /var/task 0.0s
+ => exporting to image 0.0s
+ => => exporting layers 0.0s
+ => => writing image sha256:6f8f9ef715a7cda466b677a9df4046ebbb90c8e88595242ade3b4771f547652d 0.0
+```
+
+After building, we can push the image to
+[Elastic Container Registry (ECR)](https://aws.amazon.com/ecr/) with, e.g.:
+
+```console
+$ aws ecr get-login-password --region region | docker login --username AWS --password-stdin aws_account_id.dkr.ecr.region.amazonaws.com
+$ docker tag fastapi-app:latest aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest
+$ docker push aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest
+```
+
+Finally, we can deploy the image to AWS Lambda using the AWS Management Console or the AWS CLI,
+e.g.:
+
+```console
+$ aws lambda create-function \
+ --function-name myFunction \
+ --package-type Image \
+ --code ImageUri=aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest \
+ --role arn:aws:iam::111122223333:role/my-lambda-role
+```
+
+Where the
+[execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html#permissions-executionrole-api)
+is created via:
+
+```console
+$ aws iam create-role \
+ --role-name my-lambda-role \
+ --assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
+```
+
+Or, update an existing function with:
+
+```console
+$ aws lambda update-function-code \
+ --function-name myFunction \
+ --image-uri aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest \
+ --publish
+```
+
+For details, see the
+[AWS Lambda documentation](https://docs.aws.amazon.com/lambda/latest/dg/python-image.html).
+
+### Workspace support
+
+If a project includes local dependencies (e.g., via
+[Workspaces](../../concepts/projects/workspaces.md), those too must be included in the deployment
+package.
+
+We'll start by extending the above example to include a dependency on a locally-developed library
+named `library`.
+
+First, we'll create the library itself:
+
+```console
+$ uv init --lib library
+$ uv add ./library
+```
+
+Running `uv init` within the `project` directory will automatically convert `project` to a workspace
+and add `library` as a workspace member:
+
+```toml title="pyproject.toml"
+[project]
+name = "uv-aws-lambda-example"
+version = "0.1.0"
+requires-python = ">=3.13"
+dependencies = [
+ # FastAPI is a modern web framework for building APIs with Python.
+ "fastapi",
+ # A local library.
+ "library",
+ # Mangum is a library that adapts ASGI applications to AWS Lambda and API Gateway.
+ "mangum",
+]
+
+[dependency-groups]
+dev = [
+ # In development mode, include the FastAPI development server.
+ "fastapi[standard]",
+]
+
+[tool.uv.workspace]
+members = ["library"]
+
+[tool.uv.sources]
+lib = { workspace = true }
+```
+
+By default, `uv init --lib` will create a package that exports a `hello` function. We'll modify the
+application source code to call that function:
+
+```python title="app/main.py"
+import logging
+
+from fastapi import FastAPI
+from mangum import Mangum
+
+from library import hello
+
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+
+app = FastAPI()
+handler = Mangum(app)
+
+
+@app.get("/")
+async def root() -> str:
+ return hello()
+```
+
+We can run the modified application locally with:
+
+```console
+$ uv run fastapi dev
+```
+
+And confirm that opening http://127.0.0.1:8000/ in a web browser displays, "Hello from library!"
+(instead of "Hello, World!")
+
+Finally, we'll update the Dockerfile to include the local library in the deployment package:
+
+```dockerfile title="Dockerfile"
+FROM ghcr.io/astral-sh/uv:0.5.15 AS uv
+
+# First, bundle the dependencies into the task root.
+FROM public.ecr.aws/lambda/python:3.13 AS builder
+
+# Enable bytecode compilation.
+ENV UV_COMPILE_BYTECODE=1
+
+# Enable copy mode to support bind mount caching.
+ENV UV_LINK_MODE=copy
+
+# Bundle the dependencies into the Lambda task root via `uv pip install --target`.
+#
+# Omit any local packages (`--no-emit-workspace`) and development dependencies (`--no-dev`).
+# This ensures that the Docker layer cache is only invalidated when the `pyproject.toml` or `uv.lock`
+# files change, but remains robust to changes in the application code.
+RUN --mount=from=uv,source=/uv,target=/bin/uv \
+ --mount=type=cache,target=/root/.cache/uv \
+ --mount=type=bind,source=uv.lock,target=uv.lock \
+ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
+ uv export --frozen --no-emit-workspace --no-dev --no-editable -o requirements.txt && \
+ uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
+
+# If you have a workspace, copy it over and install it too.
+#
+# By omitting `--no-emit-workspace`, `library` will be copied into the task root. Using a separate
+# `RUN` command ensures that all third-party dependencies are cached separately and remain
+# robust to changes in the workspace.
+RUN --mount=from=uv,source=/uv,target=/bin/uv \
+ --mount=type=cache,target=/root/.cache/uv \
+ --mount=type=bind,source=uv.lock,target=uv.lock \
+ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
+ --mount=type=bind,source=library,target=library \
+ uv export --frozen --no-dev --no-editable -o requirements.txt && \
+ uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
+
+FROM public.ecr.aws/lambda/python:3.13
+
+# Copy the runtime dependencies from the builder stage.
+COPY --from=builder ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT}
+
+# Copy the application code.
+COPY ./app ${LAMBDA_TASK_ROOT}/app
+
+# Set the AWS Lambda handler.
+CMD ["app.main.handler"]
+```
+
+!!! tip
+
+ To deploy to ARM-based AWS Lambda runtimes, replace `public.ecr.aws/lambda/python:3.13` with `public.ecr.aws/lambda/python:3.13-arm64`.
+
+From there, we can build and deploy the updated image as before.
+
+## Deploying a zip archive
+
+AWS Lambda also supports deployment via zip archives. For simple applications, zip archives can be a
+more straightforward and efficient deployment method than Docker images; however, zip archives are
+limited to
+[250 MB](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-update)
+in size.
+
+Returning to the FastAPI example, we can bundle the application dependencies into a local directory
+for AWS Lambda via:
+
+```console
+$ uv export --frozen --no-dev --no-editable -o requirements.txt
+$ uv pip install \
+ --no-compile-bytecode \
+ --python-platform x86_64-manylinux2014 \
+ --python-version 3.13 \
+ --target packages \
+ -r requirements.txt
+```
+
+!!! tip
+
+ To deploy to ARM-based AWS Lambda runtimes, replace `x86_64-manylinux2014` with `aarch64-manylinux2014`.
+
+Following the
+[AWS Lambda documentation](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html), we can
+then bundle these dependencies into a zip as follows:
+
+```console
+$ cd packages
+$ zip -r ../package.zip .
+```
+
+Finally, we can add the application code to the zip archive:
+
+```console
+$ cd ..
+$ zip -r package.zip app
+```
+
+We can then deploy the zip archive to AWS Lambda via the AWS Management Console or the AWS CLI,
+e.g.:
+
+```console
+$ aws lambda create-function \
+ --function-name myFunction \
+ --runtime python3.13 \
+ --zip-file fileb://package.zip
+ --handler app.main.handler \
+ --role arn:aws:iam::111122223333:role/service-role/my-lambda-role
+```
+
+Where the
+[execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html#permissions-executionrole-api)
+is created via:
+
+```console
+$ aws iam create-role \
+ --role-name my-lambda-role \
+ --assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
+```
+
+Or, update an existing function with:
+
+```console
+$ aws lambda update-function-code \
+ --function-name myFunction \
+ --zip-file fileb://package.zip
+```
+
+!!! note
+
+ By default, the AWS Management Console assumes a Lambda entrypoint of `lambda_function.lambda_handler`.
+ If your application uses a different entrypoint, you'll need to modify it in the AWS Management Console.
+ For example, the above FastAPI application uses `app.main.handler`.
diff --git a/docs/guides/integration/index.md b/docs/guides/integration/index.md
index 3e00d7bf26c7..209ec358a95c 100644
--- a/docs/guides/integration/index.md
+++ b/docs/guides/integration/index.md
@@ -10,6 +10,7 @@ Learn how to integrate uv with other software:
- [Using with alternative package indexes](./alternative-indexes.md)
- [Installing PyTorch](./pytorch.md)
- [Building a FastAPI application](./fastapi.md)
+- [Using with AWS Lambda](./aws-lambda.md)
Or, explore the [concept documentation](../../concepts/index.md) for comprehensive breakdown of each
feature.
diff --git a/mkdocs.template.yml b/mkdocs.template.yml
index 0753abbdbdd8..ef1ca20d3402 100644
--- a/mkdocs.template.yml
+++ b/mkdocs.template.yml
@@ -112,6 +112,7 @@ nav:
- FastAPI: guides/integration/fastapi.md
- Alternative indexes: guides/integration/alternative-indexes.md
- Dependency bots: guides/integration/dependency-bots.md
+ - AWS Lambda: guides/integration/aws-lambda.md
- Concepts:
- concepts/index.md
- Projects:
diff --git a/pyproject.toml b/pyproject.toml
index cc8cc9715d1b..0ef523cf6cf3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -83,6 +83,7 @@ version_files = [
"docs/guides/integration/docker.md",
"docs/guides/integration/pre-commit.md",
"docs/guides/integration/github.md",
+ "docs/guides/integration/aws-lambda.md",
]
[tool.mypy]
From fa305bd244380cb36dc46f258106753eb974c284 Mon Sep 17 00:00:00 2001
From: konsti
Date: Wed, 8 Jan 2025 11:55:06 +0100
Subject: [PATCH 062/135] Fix `invalid_platform` test on main (#10388)
This test started failing on main.
I don't understand why this changed (there was a new release but exclude-newer is supposed to exclude those), but the error message improved.
---
crates/uv/tests/it/pip_compile.rs | 21 ++-------------------
1 file changed, 2 insertions(+), 19 deletions(-)
diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs
index f4ae05acd51c..87a3264068f3 100644
--- a/crates/uv/tests/it/pip_compile.rs
+++ b/crates/uv/tests/it/pip_compile.rs
@@ -13940,25 +13940,8 @@ fn invalid_platform() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because only the following versions of open3d are available:
- open3d==0.8.0.0
- open3d==0.9.0.0
- open3d==0.10.0.0
- open3d==0.10.0.1
- open3d==0.11.0
- open3d==0.11.1
- open3d==0.11.2
- open3d==0.12.0
- open3d==0.13.0
- open3d==0.14.1
- open3d==0.15.1
- open3d==0.15.2
- open3d==0.16.0
- open3d==0.16.1
- open3d==0.17.0
- open3d==0.18.0
- and open3d<=0.15.2 has no wheels with a matching Python ABI tag, we can conclude that open3d<=0.15.2 cannot be used.
- And because open3d>=0.16.0 has no wheels with a matching platform tag and you require open3d, we can conclude that your requirements are unsatisfiable.
+ ╰─▶ Because only open3d<=0.18.0 is available and open3d<=0.15.2 has no wheels with a matching Python ABI tag, we can conclude that open3d<=0.15.2 cannot be used.
+ And because open3d>=0.16.0,<=0.18.0 has no wheels with a matching platform tag and you require open3d, we can conclude that your requirements are unsatisfiable.
"###);
Ok(())
From 68adadf806ffca48d1542a89766d642ef38aee9f Mon Sep 17 00:00:00 2001
From: konsti
Date: Wed, 8 Jan 2025 12:42:25 +0100
Subject: [PATCH 063/135] Improve file pinning comments (#10387)
---
crates/uv-resolver/src/pins.rs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/crates/uv-resolver/src/pins.rs b/crates/uv-resolver/src/pins.rs
index b7e96f9b8f9f..6d0d88f655ca 100644
--- a/crates/uv-resolver/src/pins.rs
+++ b/crates/uv-resolver/src/pins.rs
@@ -12,11 +12,14 @@ use crate::candidate_selector::Candidate;
#[derive(Clone, Debug, Default)]
pub(crate) struct FilePins(FxHashMap<(PackageName, uv_pep440::Version), ResolvedDist>);
+// Inserts are common (every time we select a version) while reads are rare (converting the
+// final resolution).
impl FilePins {
/// Pin a candidate package.
pub(crate) fn insert(&mut self, candidate: &Candidate, dist: &CompatibleDist) {
self.0
.entry((candidate.name().clone(), candidate.version().clone()))
+ // Avoid the expensive clone when a version is selected again.
.or_insert_with(|| dist.for_installation().to_owned());
}
@@ -26,7 +29,6 @@ impl FilePins {
name: &PackageName,
version: &uv_pep440::Version,
) -> Option<&ResolvedDist> {
- // Inserts are common while reads are rare, so the clone here is overall faster.
self.0.get(&(name.clone(), version.clone()))
}
}
From 15c81e9a02c1b783fe5b241f21cdbd16cd250046 Mon Sep 17 00:00:00 2001
From: konsti
Date: Wed, 8 Jan 2025 14:33:39 +0100
Subject: [PATCH 064/135] Force a niche into `VersionSmall` (#10385)
---
crates/uv-pep440/src/version.rs | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs
index c69a26a75e17..7eeb2ad198e1 100644
--- a/crates/uv-pep440/src/version.rs
+++ b/crates/uv-pep440/src/version.rs
@@ -1,4 +1,5 @@
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
+use std::num::NonZero;
use std::ops::Deref;
use std::sync::LazyLock;
use std::{
@@ -933,14 +934,13 @@ impl FromStr for Version {
struct VersionSmall {
/// The number of segments in the release component.
///
- /// Strictly speaking, this isn't necessary since `1.2` is considered
- /// equivalent to `1.2.0.0`. But in practice it's nice to be able
- /// to truncate the zero components. And always filling out to 4
- /// places somewhat exposes internal details, since the "full" version
- /// representation would not do that.
+ /// PEP 440 considers `1.2` equivalent to `1.2.0.0`, but we want to preserve trailing zeroes
+ /// in roundtrips, as the "full" version representation also does.
len: u8,
/// The representation discussed above.
repr: u64,
+ /// Force a niche into the aligned type so the [`Version`] enum is two words instead of three.
+ _force_niche: NonZero,
}
impl VersionSmall {
@@ -989,6 +989,7 @@ impl VersionSmall {
#[inline]
fn new() -> Self {
Self {
+ _force_niche: NonZero::::MIN,
repr: Self::SUFFIX_NONE << Self::SUFFIX_VERSION_BIT_LEN,
len: 0,
}
@@ -1800,6 +1801,7 @@ impl<'a> Parser<'a> {
*release.get_mut(usize::from(len))? = cur;
len += 1;
let small = VersionSmall {
+ _force_niche: NonZero::::MIN,
repr: (u64::from(release[0]) << 48)
| (u64::from(release[1]) << 40)
| (u64::from(release[2]) << 32)
@@ -4007,4 +4009,10 @@ mod tests {
assert_eq!(&*v2.release(), &[1, 2]);
assert_eq!(v2.to_string(), "1.2");
}
+
+ #[test]
+ fn type_size() {
+ assert_eq!(size_of::(), size_of::() * 2);
+ assert_eq!(size_of::(), size_of::() * 2);
+ }
}
From c5583b326f843a444778b014cfc59524950321e8 Mon Sep 17 00:00:00 2001
From: konsti
Date: Wed, 8 Jan 2025 15:33:19 +0100
Subject: [PATCH 065/135] Shrink `Dist` from 352 to 288 bytes (#10389)
Found this when looking at #10385. Since we're constructing a lot of
`Dist`s, we should keep it small.
---
crates/uv-client/tests/it/remote_metadata.rs | 2 +-
crates/uv-dev/src/wheel_metadata.rs | 2 +-
crates/uv-distribution-types/src/lib.rs | 8 ++++----
crates/uv-resolver/src/lock/mod.rs | 2 +-
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/crates/uv-client/tests/it/remote_metadata.rs b/crates/uv-client/tests/it/remote_metadata.rs
index 0570bba4d720..72bc7322238a 100644
--- a/crates/uv-client/tests/it/remote_metadata.rs
+++ b/crates/uv-client/tests/it/remote_metadata.rs
@@ -21,7 +21,7 @@ async fn remote_metadata_with_and_without_cache() -> Result<()> {
let filename = WheelFilename::from_str(url.rsplit_once('/').unwrap().1)?;
let dist = BuiltDist::DirectUrl(DirectUrlBuiltDist {
filename,
- location: Url::parse(url).unwrap(),
+ location: Box::new(Url::parse(url).unwrap()),
url: VerbatimUrl::from_str(url).unwrap(),
});
let capabilities = IndexCapabilities::default();
diff --git a/crates/uv-dev/src/wheel_metadata.rs b/crates/uv-dev/src/wheel_metadata.rs
index 421709810237..9ef55dae16a2 100644
--- a/crates/uv-dev/src/wheel_metadata.rs
+++ b/crates/uv-dev/src/wheel_metadata.rs
@@ -33,7 +33,7 @@ pub(crate) async fn wheel_metadata(args: WheelMetadataArgs) -> Result<()> {
.wheel_metadata(
&BuiltDist::DirectUrl(DirectUrlBuiltDist {
filename,
- location: archive.url,
+ location: Box::new(archive.url),
url: args.url,
}),
&capabilities,
diff --git a/crates/uv-distribution-types/src/lib.rs b/crates/uv-distribution-types/src/lib.rs
index 860368cf93d1..81e9d53a697e 100644
--- a/crates/uv-distribution-types/src/lib.rs
+++ b/crates/uv-distribution-types/src/lib.rs
@@ -250,7 +250,7 @@ pub struct DirectUrlBuiltDist {
/// `https://example.org/packages/flask-3.0.0-py3-none-any.whl`
pub filename: WheelFilename,
/// The URL without the subdirectory fragment.
- pub location: Url,
+ pub location: Box,
/// The URL as it was provided by the user.
pub url: VerbatimUrl,
}
@@ -363,7 +363,7 @@ impl Dist {
Ok(Self::Built(BuiltDist::DirectUrl(DirectUrlBuiltDist {
filename,
- location,
+ location: Box::new(location),
url,
})))
}
@@ -1343,8 +1343,8 @@ mod test {
/// Ensure that we don't accidentally grow the `Dist` sizes.
#[test]
fn dist_size() {
- assert!(size_of::() <= 352, "{}", size_of::());
- assert!(size_of::() <= 352, "{}", size_of::());
+ assert!(size_of::() <= 288, "{}", size_of::());
+ assert!(size_of::() <= 288, "{}", size_of::());
assert!(
size_of::() <= 264,
"{}",
diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs
index 49f95bb7909e..c0255804641c 100644
--- a/crates/uv-resolver/src/lock/mod.rs
+++ b/crates/uv-resolver/src/lock/mod.rs
@@ -1820,7 +1820,7 @@ impl Package {
});
let direct_dist = DirectUrlBuiltDist {
filename,
- location: url.clone(),
+ location: Box::new(url.clone()),
url: VerbatimUrl::from_url(url),
};
let built_dist = BuiltDist::DirectUrl(direct_dist);
From 333f03f11206d22c3e26cd55da4c59bc0f5b551b Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 10:29:32 -0500
Subject: [PATCH 066/135] Bump version to v0.5.16 (#10395)
---
CHANGELOG.md | 28 +++++++++++++++++++++++++++
Cargo.lock | 4 ++--
crates/uv-version/Cargo.toml | 2 +-
crates/uv/Cargo.toml | 2 +-
docs/getting-started/installation.md | 4 ++--
docs/guides/integration/aws-lambda.md | 4 ++--
docs/guides/integration/docker.md | 8 ++++----
docs/guides/integration/github.md | 2 +-
docs/guides/integration/pre-commit.md | 6 +++---
pyproject.toml | 2 +-
10 files changed, 45 insertions(+), 17 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fdb00540c29a..7e4af1bff5e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,33 @@
# Changelog
+## 0.5.16
+
+### Enhancements
+
+- Accept full requirements in `uv remove` ([#10338](https://github.com/astral-sh/uv/pull/10338))
+
+### Performance
+
+- Avoid over-counting versions in batch prefetcher ([#10350](https://github.com/astral-sh/uv/pull/10350))
+- Deactivate tracing for version-choosing ([#10351](https://github.com/astral-sh/uv/pull/10351))
+- Force a niche into `VersionSmall` ([#10385](https://github.com/astral-sh/uv/pull/10385))
+- Optimize `requirements_for_extra` ([#10348](https://github.com/astral-sh/uv/pull/10348))
+- Re-enable `zlib-ng` on x86 platforms ([#10365](https://github.com/astral-sh/uv/pull/10365))
+- Re-enable zlib-ng on all platforms (except s390x, PowerPC, and FreeBSD) ([#10370](https://github.com/astral-sh/uv/pull/10370))
+- Remove `[u64; 4]` from small version to move `Arc` to full version ([#10345](https://github.com/astral-sh/uv/pull/10345))
+- Shrink `Dist` from 352 to 288 bytes ([#10389](https://github.com/astral-sh/uv/pull/10389))
+- Speed up file pins by removing nested hash map ([#10346](https://github.com/astral-sh/uv/pull/10346))
+- Buffer file reads in `serde_json::from_reader` ([#10341](https://github.com/astral-sh/uv/pull/10341))
+
+### Bug fixes
+
+- Avoid enforcing project-level required version for `uv self` ([#10374](https://github.com/astral-sh/uv/pull/10374))
+- Fix Ruff linting warnings from generated template files for extension modules ([#10371](https://github.com/astral-sh/uv/pull/10371))
+
+### Documentation
+
+- Add AWS Lambda integration guide ([#10278](https://github.com/astral-sh/uv/pull/10278))
+
## 0.5.15
### Python
diff --git a/Cargo.lock b/Cargo.lock
index 7a16a53e5a04..2bd7f5ae5032 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4474,7 +4474,7 @@ checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
[[package]]
name = "uv"
-version = "0.5.15"
+version = "0.5.16"
dependencies = [
"anstream",
"anyhow",
@@ -5681,7 +5681,7 @@ dependencies = [
[[package]]
name = "uv-version"
-version = "0.5.15"
+version = "0.5.16"
[[package]]
name = "uv-virtualenv"
diff --git a/crates/uv-version/Cargo.toml b/crates/uv-version/Cargo.toml
index a5b7f368d991..bfc8226167fd 100644
--- a/crates/uv-version/Cargo.toml
+++ b/crates/uv-version/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uv-version"
-version = "0.5.15"
+version = "0.5.16"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml
index e91587c8ffd5..5516afcdb635 100644
--- a/crates/uv/Cargo.toml
+++ b/crates/uv/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uv"
-version = "0.5.15"
+version = "0.5.16"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
index 420034834873..d631f330479d 100644
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -25,7 +25,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:
```console
- $ curl -LsSf https://astral.sh/uv/0.5.15/install.sh | sh
+ $ curl -LsSf https://astral.sh/uv/0.5.16/install.sh | sh
```
=== "Windows"
@@ -41,7 +41,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:
```console
- $ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.5.15/install.ps1 | iex"
+ $ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.5.16/install.ps1 | iex"
```
!!! tip
diff --git a/docs/guides/integration/aws-lambda.md b/docs/guides/integration/aws-lambda.md
index 115d94bd5cb9..d8cd7983bb94 100644
--- a/docs/guides/integration/aws-lambda.md
+++ b/docs/guides/integration/aws-lambda.md
@@ -85,7 +85,7 @@ the second stage, we'll copy this directory over to the final image, omitting th
other unnecessary files.
```dockerfile title="Dockerfile"
-FROM ghcr.io/astral-sh/uv:0.5.15 AS uv
+FROM ghcr.io/astral-sh/uv:0.5.16 AS uv
# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
@@ -284,7 +284,7 @@ And confirm that opening http://127.0.0.1:8000/ in a web browser displays, "Hell
Finally, we'll update the Dockerfile to include the local library in the deployment package:
```dockerfile title="Dockerfile"
-FROM ghcr.io/astral-sh/uv:0.5.15 AS uv
+FROM ghcr.io/astral-sh/uv:0.5.16 AS uv
# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
diff --git a/docs/guides/integration/docker.md b/docs/guides/integration/docker.md
index 71a4621d9f04..834e93c46f54 100644
--- a/docs/guides/integration/docker.md
+++ b/docs/guides/integration/docker.md
@@ -21,7 +21,7 @@ $ docker run ghcr.io/astral-sh/uv --help
uv provides a distroless Docker image including the `uv` binary. The following tags are published:
- `ghcr.io/astral-sh/uv:latest`
-- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.5.15`
+- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.5.16`
- `ghcr.io/astral-sh/uv:{major}.{minor}`, e.g., `ghcr.io/astral-sh/uv:0.5` (the latest patch
version)
@@ -62,7 +62,7 @@ In addition, uv publishes the following images:
As with the distroless image, each image is published with uv version tags as
`ghcr.io/astral-sh/uv:{major}.{minor}.{patch}-{base}` and
-`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.5.15-alpine`.
+`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.5.16-alpine`.
For more details, see the [GitHub Container](https://github.com/astral-sh/uv/pkgs/container/uv)
page.
@@ -100,13 +100,13 @@ Note this requires `curl` to be available.
In either case, it is best practice to pin to a specific uv version, e.g., with:
```dockerfile
-COPY --from=ghcr.io/astral-sh/uv:0.5.15 /uv /uvx /bin/
+COPY --from=ghcr.io/astral-sh/uv:0.5.16 /uv /uvx /bin/
```
Or, with the installer:
```dockerfile
-ADD https://astral.sh/uv/0.5.15/install.sh /uv-installer.sh
+ADD https://astral.sh/uv/0.5.16/install.sh /uv-installer.sh
```
### Installing a project
diff --git a/docs/guides/integration/github.md b/docs/guides/integration/github.md
index cbc726ac7915..26204abd1f0c 100644
--- a/docs/guides/integration/github.md
+++ b/docs/guides/integration/github.md
@@ -40,7 +40,7 @@ jobs:
uses: astral-sh/setup-uv@v5
with:
# Install a specific version of uv.
- version: "0.5.15"
+ version: "0.5.16"
```
## Setting up Python
diff --git a/docs/guides/integration/pre-commit.md b/docs/guides/integration/pre-commit.md
index 2694bef5ce70..6d963f8a43f3 100644
--- a/docs/guides/integration/pre-commit.md
+++ b/docs/guides/integration/pre-commit.md
@@ -29,7 +29,7 @@ To compile requirements via pre-commit, add the following to the `.pre-commit-co
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.15
+ rev: 0.5.16
hooks:
# Compile requirements
- id: pip-compile
@@ -41,7 +41,7 @@ To compile alternative files, modify `args` and `files`:
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.15
+ rev: 0.5.16
hooks:
# Compile requirements
- id: pip-compile
@@ -54,7 +54,7 @@ To run the hook over multiple files at the same time:
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.15
+ rev: 0.5.16
hooks:
# Compile requirements
- id: pip-compile
diff --git a/pyproject.toml b/pyproject.toml
index 0ef523cf6cf3..843193109c26 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "maturin"
[project]
name = "uv"
-version = "0.5.15"
+version = "0.5.16"
description = "An extremely fast Python package and project manager, written in Rust."
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
requires-python = ">=3.8"
From 4c161d284bac47104db1ccd2e73daeb2e4d0d182 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 12:38:17 -0500
Subject: [PATCH 067/135] Respect `requires-python` when installing tools
(#10401)
## Summary
This PR revives https://github.com/astral-sh/uv/pull/7827 to improve
tool resolutions such that, if the resolution fails, and the selected
interpreter doesn't match the required Python version from the solve, we
attempt to re-solve with a newly-discovered interpreter that _does_
match the required Python version.
For now, we attempt to choose a Python interpreter that's greater than
the inferred `requires-python`, but compatible with the same Python
minor. This helps avoid successive failures for cases like Posting,
where choosing Python 3.13 fails because it has a dependency that lacks
source distributions and doesn't publish any Python 3.13 wheels. We
should further improve the strategy to solve _that_ case too, but this
is at least the more conservative option...
In short, if you do `uv tool instal posting`, and we find Python 3.8 on
your machine, we'll detect that `requires-python: >=3.11`, then search
for the latest Python 3.11 interpreter and re-resolve.
Closes https://github.com/astral-sh/uv/issues/6381.
Closes https://github.com/astral-sh/uv/issues/10282.
## Test Plan
The following should succeed:
```
cargo run python uninstall --all
cargo run python install 3.8
cargo run tool install posting
```
In the logs, we see:
```
...
DEBUG No compatible version found for: posting
DEBUG Refining interpreter with: Python >=3.11, <3.12
DEBUG Searching for Python >=3.11, <3.12 in managed installations or search path
DEBUG Searching for managed installations at `/Users/crmarsh/.local/share/uv/python`
DEBUG Skipping incompatible managed installation `cpython-3.8.20-macos-aarch64-none`
DEBUG Found `cpython-3.13.1-macos-aarch64-none` at `/opt/homebrew/bin/python3` (search path)
DEBUG Skipping interpreter at `/opt/homebrew/opt/python@3.13/bin/python3.13` from search path: does not satisfy request `>=3.11, <3.12`
DEBUG Found `cpython-3.11.7-macos-aarch64-none` at `/opt/homebrew/bin/python3.11` (search path)
DEBUG Re-resolving with Python 3.11.7
DEBUG Using request timeout of 30s
DEBUG Solving with installed Python version: 3.11.7
DEBUG Solving with target Python version: >=3.11.7
DEBUG Adding direct dependency: posting*
DEBUG Searching for a compatible version of posting (*)
...
```
---
crates/uv-requirements/src/specification.rs | 2 +-
crates/uv-resolver/src/error.rs | 28 +++++
crates/uv-resolver/src/requires_python.rs | 66 ++++++-----
crates/uv/src/commands/project/environment.rs | 4 +-
crates/uv/src/commands/project/mod.rs | 2 +-
crates/uv/src/commands/project/run.rs | 4 +-
crates/uv/src/commands/tool/common.rs | 110 ++++++++++++++++--
crates/uv/src/commands/tool/install.rs | 84 ++++++++++---
crates/uv/src/commands/tool/run.rs | 80 +++++++++++--
crates/uv/src/lib.rs | 4 +-
10 files changed, 320 insertions(+), 64 deletions(-)
diff --git a/crates/uv-requirements/src/specification.rs b/crates/uv-requirements/src/specification.rs
index 61c5513e1079..2c8ec42a8651 100644
--- a/crates/uv-requirements/src/specification.rs
+++ b/crates/uv-requirements/src/specification.rs
@@ -50,7 +50,7 @@ use uv_workspace::pyproject::PyProjectToml;
use crate::RequirementsSource;
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
pub struct RequirementsSpecification {
/// The name of the project specifying requirements.
pub project: Option,
diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs
index 2cd333c40a82..de9c15646a9a 100644
--- a/crates/uv-resolver/src/error.rs
+++ b/crates/uv-resolver/src/error.rs
@@ -22,6 +22,7 @@ use crate::fork_urls::ForkUrls;
use crate::prerelease::AllowPrerelease;
use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner, PubGrubReportFormatter};
use crate::python_requirement::PythonRequirement;
+use crate::requires_python::LowerBound;
use crate::resolution::ConflictingDistributionError;
use crate::resolver::{
MetadataUnavailable, ResolverEnvironment, UnavailablePackage, UnavailableReason,
@@ -294,6 +295,33 @@ impl NoSolutionError {
strip(derivation_tree).expect("derivation tree should contain at least one term")
}
+ /// Given a [`DerivationTree`], identify the largest required Python version that is missing.
+ pub fn find_requires_python(&self) -> LowerBound {
+ fn find(derivation_tree: &ErrorTree, minimum: &mut LowerBound) {
+ match derivation_tree {
+ DerivationTree::Derived(derived) => {
+ find(derived.cause1.as_ref(), minimum);
+ find(derived.cause2.as_ref(), minimum);
+ }
+ DerivationTree::External(External::FromDependencyOf(.., package, version)) => {
+ if let PubGrubPackageInner::Python(_) = &**package {
+ if let Some((lower, ..)) = version.bounding_range() {
+ let lower = LowerBound::new(lower.cloned());
+ if lower > *minimum {
+ *minimum = lower;
+ }
+ }
+ }
+ }
+ DerivationTree::External(_) => {}
+ }
+ }
+
+ let mut minimum = LowerBound::default();
+ find(&self.error, &mut minimum);
+ minimum
+ }
+
/// Initialize a [`NoSolutionHeader`] for this error.
pub fn header(&self) -> NoSolutionHeader {
NoSolutionHeader::new(self.env.clone())
diff --git a/crates/uv-resolver/src/requires_python.rs b/crates/uv-resolver/src/requires_python.rs
index 7c6b3b99b7aa..9fbaa36af390 100644
--- a/crates/uv-resolver/src/requires_python.rs
+++ b/crates/uv-resolver/src/requires_python.rs
@@ -586,6 +586,17 @@ impl SimplifiedMarkerTree {
pub struct LowerBound(Bound);
impl LowerBound {
+ /// Initialize a [`LowerBound`] with the given bound.
+ ///
+ /// These bounds use release-only semantics when comparing versions.
+ pub fn new(bound: Bound) -> Self {
+ Self(match bound {
+ Bound::Included(version) => Bound::Included(version.only_release()),
+ Bound::Excluded(version) => Bound::Excluded(version.only_release()),
+ Bound::Unbounded => Bound::Unbounded,
+ })
+ }
+
/// Return the [`LowerBound`] truncated to the major and minor version.
fn major_minor(&self) -> Self {
match &self.0 {
@@ -600,6 +611,15 @@ impl LowerBound {
Bound::Unbounded => Self(Bound::Unbounded),
}
}
+
+ /// Returns `true` if the lower bound contains the given version.
+ pub fn contains(&self, version: &Version) -> bool {
+ match self.0 {
+ Bound::Included(ref bound) => bound <= version,
+ Bound::Excluded(ref bound) => bound < version,
+ Bound::Unbounded => true,
+ }
+ }
}
impl PartialOrd for LowerBound {
@@ -668,19 +688,6 @@ impl Default for LowerBound {
}
}
-impl LowerBound {
- /// Initialize a [`LowerBound`] with the given bound.
- ///
- /// These bounds use release-only semantics when comparing versions.
- pub fn new(bound: Bound) -> Self {
- Self(match bound {
- Bound::Included(version) => Bound::Included(version.only_release()),
- Bound::Excluded(version) => Bound::Excluded(version.only_release()),
- Bound::Unbounded => Bound::Unbounded,
- })
- }
-}
-
impl Deref for LowerBound {
type Target = Bound;
@@ -699,6 +706,17 @@ impl From for Bound {
pub struct UpperBound(Bound);
impl UpperBound {
+ /// Initialize a [`UpperBound`] with the given bound.
+ ///
+ /// These bounds use release-only semantics when comparing versions.
+ pub fn new(bound: Bound) -> Self {
+ Self(match bound {
+ Bound::Included(version) => Bound::Included(version.only_release()),
+ Bound::Excluded(version) => Bound::Excluded(version.only_release()),
+ Bound::Unbounded => Bound::Unbounded,
+ })
+ }
+
/// Return the [`UpperBound`] truncated to the major and minor version.
fn major_minor(&self) -> Self {
match &self.0 {
@@ -721,6 +739,15 @@ impl UpperBound {
Bound::Unbounded => Self(Bound::Unbounded),
}
}
+
+ /// Returns `true` if the upper bound contains the given version.
+ pub fn contains(&self, version: &Version) -> bool {
+ match self.0 {
+ Bound::Included(ref bound) => bound >= version,
+ Bound::Excluded(ref bound) => bound > version,
+ Bound::Unbounded => true,
+ }
+ }
}
impl PartialOrd for UpperBound {
@@ -787,19 +814,6 @@ impl Default for UpperBound {
}
}
-impl UpperBound {
- /// Initialize a [`UpperBound`] with the given bound.
- ///
- /// These bounds use release-only semantics when comparing versions.
- pub fn new(bound: Bound) -> Self {
- Self(match bound {
- Bound::Included(version) => Bound::Included(version.only_release()),
- Bound::Excluded(version) => Bound::Excluded(version.only_release()),
- Bound::Unbounded => Bound::Unbounded,
- })
- }
-}
-
impl Deref for UpperBound {
type Target = Bound;
diff --git a/crates/uv/src/commands/project/environment.rs b/crates/uv/src/commands/project/environment.rs
index e4dd28ec2c01..e6b696518e39 100644
--- a/crates/uv/src/commands/project/environment.rs
+++ b/crates/uv/src/commands/project/environment.rs
@@ -29,7 +29,7 @@ impl CachedEnvironment {
/// interpreter.
pub(crate) async fn get_or_create(
spec: EnvironmentSpecification<'_>,
- interpreter: Interpreter,
+ interpreter: &Interpreter,
settings: &ResolverInstallerSettings,
state: &SharedState,
resolve: Box,
@@ -56,7 +56,7 @@ impl CachedEnvironment {
"Caching via interpreter: `{}`",
interpreter.sys_executable().display()
);
- interpreter
+ interpreter.clone()
};
// Resolve the requirements with the interpreter.
diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs
index 0c96a5b52ac2..c738eba18661 100644
--- a/crates/uv/src/commands/project/mod.rs
+++ b/crates/uv/src/commands/project/mod.rs
@@ -1086,7 +1086,7 @@ pub(crate) async fn resolve_names(
Ok(requirements)
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub(crate) struct EnvironmentSpecification<'lock> {
/// The requirements to include in the environment.
requirements: RequirementsSpecification,
diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs
index e08ecb95ff37..ac4e37694461 100644
--- a/crates/uv/src/commands/project/run.rs
+++ b/crates/uv/src/commands/project/run.rs
@@ -296,7 +296,7 @@ pub(crate) async fn run(
RequirementsSpecification::from_overrides(requirements, constraints, overrides);
let result = CachedEnvironment::get_or_create(
EnvironmentSpecification::from(spec),
- interpreter,
+ &interpreter,
&settings,
&state,
if show_resolution {
@@ -852,7 +852,7 @@ pub(crate) async fn run(
lock.as_ref()
.map(|(lock, install_path)| (lock, install_path.as_ref())),
),
- base_interpreter.clone(),
+ &base_interpreter,
&settings,
&state,
if show_resolution {
diff --git a/crates/uv/src/commands/tool/common.rs b/crates/uv/src/commands/tool/common.rs
index 495e7467a161..3e2f283eb75b 100644
--- a/crates/uv/src/commands/tool/common.rs
+++ b/crates/uv/src/commands/tool/common.rs
@@ -1,25 +1,32 @@
-use std::fmt::Write;
-use std::{collections::BTreeSet, ffi::OsString};
-
use anyhow::{bail, Context};
use itertools::Itertools;
use owo_colors::OwoColorize;
+use std::collections::Bound;
+use std::fmt::Write;
+use std::{collections::BTreeSet, ffi::OsString};
use tracing::{debug, warn};
-
+use uv_cache::Cache;
+use uv_client::BaseClientBuilder;
use uv_distribution_types::{InstalledDist, Name};
#[cfg(unix)]
use uv_fs::replace_symlink;
use uv_fs::Simplified;
use uv_installer::SitePackages;
+use uv_pep440::{Version, VersionSpecifier, VersionSpecifiers};
use uv_pep508::PackageName;
use uv_pypi_types::Requirement;
-use uv_python::PythonEnvironment;
-use uv_settings::ToolOptions;
+use uv_python::{
+ EnvironmentPreference, Interpreter, PythonDownloads, PythonEnvironment, PythonInstallation,
+ PythonPreference, PythonRequest, PythonVariant, VersionRequest,
+};
+use uv_settings::{PythonInstallMirrors, ToolOptions};
use uv_shell::Shell;
use uv_tool::{entrypoint_paths, tool_executable_dir, InstalledTools, Tool, ToolEntrypoint};
use uv_warnings::warn_user;
-use crate::commands::ExitStatus;
+use crate::commands::project::ProjectError;
+use crate::commands::reporters::PythonDownloadReporter;
+use crate::commands::{pip, ExitStatus};
use crate::printer::Printer;
/// Return all packages which contain an executable with the given name.
@@ -61,6 +68,95 @@ pub(crate) fn remove_entrypoints(tool: &Tool) {
}
}
+/// Given a no-solution error and the [`Interpreter`] that was used during the solve, attempt to
+/// discover an alternate [`Interpreter`] that satisfies the `requires-python` constraint.
+pub(crate) async fn refine_interpreter(
+ interpreter: &Interpreter,
+ python_request: Option<&PythonRequest>,
+ err: &pip::operations::Error,
+ client_builder: &BaseClientBuilder<'_>,
+ reporter: &PythonDownloadReporter,
+ install_mirrors: &PythonInstallMirrors,
+ python_preference: PythonPreference,
+ python_downloads: PythonDownloads,
+ cache: &Cache,
+) -> anyhow::Result, ProjectError> {
+ let pip::operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(ref no_solution_err)) =
+ err
+ else {
+ return Ok(None);
+ };
+
+ // Infer the `requires-python` constraint from the error.
+ let requires_python = no_solution_err.find_requires_python();
+
+ // If the existing interpreter already satisfies the `requires-python` constraint, we don't need
+ // to refine it. We'd expect to fail again anyway.
+ if requires_python.contains(interpreter.python_version()) {
+ return Ok(None);
+ }
+
+ // If the user passed a `--python` request, and the refined interpreter is incompatible, we
+ // can't use it.
+ if let Some(python_request) = python_request {
+ if !python_request.satisfied(interpreter, cache) {
+ return Ok(None);
+ }
+ }
+
+ // We want an interpreter that's as close to the required version as possible. If we choose the
+ // "latest" Python, we risk choosing a version that lacks wheels for the tool's requirements
+ // (assuming those requirements don't publish source distributions).
+ //
+ // TODO(charlie): Solve for the Python version iteratively (or even, within the resolver
+ // itself). The current strategy can also fail if the tool's requirements have greater
+ // `requires-python` constraints, and we didn't see them in the initial solve. It can also fail
+ // if the tool's requirements don't publish wheels for this interpreter version, though that's
+ // rarer.
+ let lower_bound = match requires_python.as_ref() {
+ Bound::Included(version) => VersionSpecifier::greater_than_equal_version(version.clone()),
+ Bound::Excluded(version) => VersionSpecifier::greater_than_version(version.clone()),
+ Bound::Unbounded => unreachable!("`requires-python` should never be unbounded"),
+ };
+
+ let upper_bound = match requires_python.as_ref() {
+ Bound::Included(version) => {
+ let major = version.release().first().copied().unwrap_or(0);
+ let minor = version.release().get(1).copied().unwrap_or(0);
+ VersionSpecifier::less_than_version(Version::new([major, minor + 1]))
+ }
+ Bound::Excluded(version) => {
+ let major = version.release().first().copied().unwrap_or(0);
+ let minor = version.release().get(1).copied().unwrap_or(0);
+ VersionSpecifier::less_than_version(Version::new([major, minor + 1]))
+ }
+ Bound::Unbounded => unreachable!("`requires-python` should never be unbounded"),
+ };
+
+ let python_request = PythonRequest::Version(VersionRequest::Range(
+ VersionSpecifiers::from_iter([lower_bound, upper_bound]),
+ PythonVariant::default(),
+ ));
+
+ debug!("Refining interpreter with: {python_request}");
+
+ let interpreter = PythonInstallation::find_or_download(
+ Some(&python_request),
+ EnvironmentPreference::OnlySystem,
+ python_preference,
+ python_downloads,
+ client_builder,
+ cache,
+ Some(reporter),
+ install_mirrors.python_install_mirror.as_deref(),
+ install_mirrors.pypy_install_mirror.as_deref(),
+ )
+ .await?
+ .into_interpreter();
+
+ Ok(Some(interpreter))
+}
+
/// Installs tool executables for a given package and handles any conflicts.
pub(crate) fn install_executables(
environment: &PythonEnvironment,
diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs
index f36d9a19bf5f..958dd30bcb50 100644
--- a/crates/uv/src/commands/tool/install.rs
+++ b/crates/uv/src/commands/tool/install.rs
@@ -29,12 +29,10 @@ use crate::commands::project::{
resolve_environment, resolve_names, sync_environment, update_environment,
EnvironmentSpecification, ProjectError,
};
-use crate::commands::tool::common::remove_entrypoints;
+use crate::commands::tool::common::{install_executables, refine_interpreter, remove_entrypoints};
use crate::commands::tool::Target;
use crate::commands::ExitStatus;
-use crate::commands::{
- diagnostics, reporters::PythonDownloadReporter, tool::common::install_executables,
-};
+use crate::commands::{diagnostics, reporters::PythonDownloadReporter};
use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings;
@@ -448,10 +446,12 @@ pub(crate) async fn install(
environment
} else {
+ let spec = EnvironmentSpecification::from(spec);
+
// If we're creating a new environment, ensure that we can resolve the requirements prior
// to removing any existing tools.
- let resolution = match resolve_environment(
- EnvironmentSpecification::from(spec),
+ let resolution = resolve_environment(
+ spec.clone(),
&interpreter,
settings.as_ref().into(),
&state,
@@ -464,15 +464,71 @@ pub(crate) async fn install(
printer,
preview,
)
- .await
- {
+ .await;
+
+ // If the resolution failed, retry with the inferred `requires-python` constraint.
+ let resolution = match resolution {
Ok(resolution) => resolution,
- Err(ProjectError::Operation(err)) => {
- return diagnostics::OperationDiagnostic::default()
- .report(err)
- .map_or(Ok(ExitStatus::Failure), |err| Err(err.into()))
- }
- Err(err) => return Err(err.into()),
+ Err(err) => match err {
+ ProjectError::Operation(err) => {
+ // If the resolution failed due to the discovered interpreter not satisfying the
+ // `requires-python` constraint, we can try to refine the interpreter.
+ //
+ // For example, if we discovered a Python 3.8 interpreter on the user's machine,
+ // but the tool requires Python 3.10 or later, we can try to download a
+ // Python 3.10 interpreter and re-resolve.
+ let Some(interpreter) = refine_interpreter(
+ &interpreter,
+ python_request.as_ref(),
+ &err,
+ &client_builder,
+ &reporter,
+ &install_mirrors,
+ python_preference,
+ python_downloads,
+ &cache,
+ )
+ .await
+ .ok()
+ .flatten() else {
+ return diagnostics::OperationDiagnostic::default()
+ .report(err)
+ .map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
+ };
+
+ debug!(
+ "Re-resolving with Python {} (`{}`)",
+ interpreter.python_version(),
+ interpreter.sys_executable().display()
+ );
+
+ match resolve_environment(
+ spec,
+ &interpreter,
+ settings.as_ref().into(),
+ &state,
+ Box::new(DefaultResolveLogger),
+ connectivity,
+ concurrency,
+ native_tls,
+ allow_insecure_host,
+ &cache,
+ printer,
+ preview,
+ )
+ .await
+ {
+ Ok(resolution) => resolution,
+ Err(ProjectError::Operation(err)) => {
+ return diagnostics::OperationDiagnostic::default()
+ .report(err)
+ .map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
+ }
+ Err(err) => return Err(err.into()),
+ }
+ }
+ err => return Err(err.into()),
+ },
};
let environment = installed_tools.create_environment(&from.name, interpreter)?;
diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs
index 8d97bc10485d..bdeb735ece4f 100644
--- a/crates/uv/src/commands/tool/run.rs
+++ b/crates/uv/src/commands/tool/run.rs
@@ -37,11 +37,10 @@ use crate::commands::pip::loggers::{
};
use crate::commands::project::{resolve_names, EnvironmentSpecification, ProjectError};
use crate::commands::reporters::PythonDownloadReporter;
+use crate::commands::tool::common::{matching_packages, refine_interpreter};
use crate::commands::tool::Target;
use crate::commands::ExitStatus;
-use crate::commands::{
- diagnostics, project::environment::CachedEnvironment, tool::common::matching_packages,
-};
+use crate::commands::{diagnostics, project::environment::CachedEnvironment};
use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings;
@@ -610,20 +609,20 @@ async fn get_or_create_environment(
}
// Create a `RequirementsSpecification` from the resolved requirements, to avoid re-resolving.
- let spec = RequirementsSpecification {
+ let spec = EnvironmentSpecification::from(RequirementsSpecification {
requirements: requirements
.into_iter()
.map(UnresolvedRequirementSpecification::from)
.collect(),
..spec
- };
+ });
// TODO(zanieb): When implementing project-level tools, discover the project and check if it has the tool.
// TODO(zanieb): Determine if we should layer on top of the project environment if it is present.
- let environment = CachedEnvironment::get_or_create(
- EnvironmentSpecification::from(spec),
- interpreter,
+ let result = CachedEnvironment::get_or_create(
+ spec.clone(),
+ &interpreter,
settings,
&state,
if show_resolution {
@@ -645,7 +644,70 @@ async fn get_or_create_environment(
printer,
preview,
)
- .await?;
+ .await;
+
+ let environment = match result {
+ Ok(environment) => environment,
+ Err(err) => match err {
+ ProjectError::Operation(err) => {
+ // If the resolution failed due to the discovered interpreter not satisfying the
+ // `requires-python` constraint, we can try to refine the interpreter.
+ //
+ // For example, if we discovered a Python 3.8 interpreter on the user's machine,
+ // but the tool requires Python 3.10 or later, we can try to download a
+ // Python 3.10 interpreter and re-resolve.
+ let Some(interpreter) = refine_interpreter(
+ &interpreter,
+ python_request.as_ref(),
+ &err,
+ &client_builder,
+ &reporter,
+ &install_mirrors,
+ python_preference,
+ python_downloads,
+ cache,
+ )
+ .await
+ .ok()
+ .flatten() else {
+ return Err(err.into());
+ };
+
+ debug!(
+ "Re-resolving with Python {} (`{}`)",
+ interpreter.python_version(),
+ interpreter.sys_executable().display()
+ );
+
+ CachedEnvironment::get_or_create(
+ spec,
+ &interpreter,
+ settings,
+ &state,
+ if show_resolution {
+ Box::new(DefaultResolveLogger)
+ } else {
+ Box::new(SummaryResolveLogger)
+ },
+ if show_resolution {
+ Box::new(DefaultInstallLogger)
+ } else {
+ Box::new(SummaryInstallLogger)
+ },
+ installer_metadata,
+ connectivity,
+ concurrency,
+ native_tls,
+ allow_insecure_host,
+ cache,
+ printer,
+ preview,
+ )
+ .await?
+ }
+ err => return Err(err),
+ },
+ };
Ok((from, environment.into()))
}
diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs
index ca854b0adb49..2797093f2b4c 100644
--- a/crates/uv/src/lib.rs
+++ b/crates/uv/src/lib.rs
@@ -939,7 +939,7 @@ async fn run(mut cli: Cli) -> Result {
)
.collect::>();
- commands::tool_run(
+ Box::pin(commands::tool_run(
args.command,
args.from,
&requirements,
@@ -959,7 +959,7 @@ async fn run(mut cli: Cli) -> Result {
cache,
printer,
globals.preview,
- )
+ ))
.await
}
Commands::Tool(ToolNamespace {
From 2f7f9ea571f4fa8d2eefbfa4a95f6bc8fd18175c Mon Sep 17 00:00:00 2001
From: Aria Desires
Date: Wed, 8 Jan 2025 13:12:29 -0500
Subject: [PATCH 068/135] improve shell compatibility of venv activate scripts
(#10397)
The shellcheck action we uses misses some files, so they fell out of
spec for what we support. This PR first and foremost adds them to the
scanning list, and then fixes the issues found.
Fixes #7480
---
.github/workflows/ci.yml | 1 +
crates/uv-virtualenv/src/activator/activate | 21 +++++++++++++--------
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fce3bceaa495..736aaffd2833 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -95,6 +95,7 @@ jobs:
version: ${{ env.SHELLCHECK_VERSION }}
severity: style
check_together: "yes"
+ additional_files: activate
cargo-clippy:
timeout-minutes: 10
diff --git a/crates/uv-virtualenv/src/activator/activate b/crates/uv-virtualenv/src/activator/activate
index 5a49d9e48596..011eac5abb82 100644
--- a/crates/uv-virtualenv/src/activator/activate
+++ b/crates/uv-virtualenv/src/activator/activate
@@ -31,9 +31,12 @@ if [ -n "${BASH_VERSION:+x}" ] ; then
exit 33
fi
elif [ -n "${ZSH_VERSION:+x}" ] ; then
- SCRIPT_PATH="${(%):-%x}"
+ # we use eval in these paths to "hide" the fact that these branches
+ # genuinely don't work when run in the wrong shell. Any shell or
+ # linter that eagerly checks them would be right to complain!
+ eval 'SCRIPT_PATH="${(%):-%x}"'
elif [ -n "${KSH_VERSION:+x}" ] ; then
- SCRIPT_PATH="${.sh.file}"
+ eval 'SCRIPT_PATH="${.sh.file}"'
fi
deactivate () {
@@ -41,12 +44,12 @@ deactivate () {
# reset old environment variables
# ! [ -z ${VAR+_} ] returns true if VAR is declared at all
- if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then
+ if [ -n "${_OLD_VIRTUAL_PATH:+_}" ] ; then
PATH="$_OLD_VIRTUAL_PATH"
export PATH
unset _OLD_VIRTUAL_PATH
fi
- if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then
+ if [ -n "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then
PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
@@ -57,7 +60,7 @@ deactivate () {
# we made may not be respected
hash -r 2>/dev/null
- if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then
+ if [ -n "${_OLD_VIRTUAL_PS1+_}" ] ; then
PS1="$_OLD_VIRTUAL_PS1"
export PS1
unset _OLD_VIRTUAL_PS1
@@ -75,7 +78,7 @@ deactivate () {
deactivate nondestructive
VIRTUAL_ENV='{{ VIRTUAL_ENV_DIR }}'
-if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then
+if { [ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]; } && command -v cygpath &> /dev/null ; then
VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV")
fi
export VIRTUAL_ENV
@@ -84,6 +87,8 @@ _OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/{{ BIN_NAME }}:$PATH"
export PATH
+# this file is templated, so the constant comparison here actually varies
+# shellcheck disable=SC2050
if [ "x{{ VIRTUAL_PROMPT }}" != x ] ; then
VIRTUAL_ENV_PROMPT="({{ VIRTUAL_PROMPT }}) "
else
@@ -92,7 +97,7 @@ fi
export VIRTUAL_ENV_PROMPT
# unset PYTHONHOME if set
-if ! [ -z "${PYTHONHOME+_}" ] ; then
+if [ -n "${PYTHONHOME+_}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME"
unset PYTHONHOME
fi
@@ -104,7 +109,7 @@ if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then
fi
# Make sure to unalias pydoc if it's already there
-alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true
+{ alias pydoc 2>/dev/null >/dev/null && unalias pydoc; } || true
pydoc () {
python -m pydoc "$@"
From 5d33b94c42209533b1f2a369087ed1efbf895d26 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 13:36:53 -0500
Subject: [PATCH 069/135] Add support for locking PEP 723 scripts (#10135)
## Summary
You can now run `uv lock --script main.py` to lock a given script
(though as of this PR, the script itself isn't used anywhere).
Closes https://github.com/astral-sh/uv/issues/6318.
---
crates/uv-cli/src/lib.rs | 7 +
crates/uv/src/commands/project/lock.rs | 63 +++--
crates/uv/src/commands/project/lock_target.rs | 114 +++++++-
crates/uv/src/lib.rs | 18 +-
crates/uv/src/settings.rs | 3 +
crates/uv/tests/it/lock.rs | 248 ++++++++++++++++++
docs/reference/cli.md | 4 +
7 files changed, 434 insertions(+), 23 deletions(-)
diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs
index 6419f3213625..b829483189a5 100644
--- a/crates/uv-cli/src/lib.rs
+++ b/crates/uv-cli/src/lib.rs
@@ -3108,6 +3108,13 @@ pub struct LockArgs {
#[arg(long, conflicts_with = "check_exists", conflicts_with = "check")]
pub dry_run: bool,
+ /// Lock the specified Python script, rather than the current project.
+ ///
+ /// If provided, uv will lock the script (based on its inline metadata table, in adherence with
+ /// PEP 723) to a `.lock` file adjacent to the script itself.
+ #[arg(long)]
+ pub script: Option,
+
#[command(flatten)]
pub resolver: ResolverArgs,
diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs
index 083aa940fc5e..f33e24d29131 100644
--- a/crates/uv/src/commands/project/lock.rs
+++ b/crates/uv/src/commands/project/lock.rs
@@ -32,6 +32,7 @@ use uv_resolver::{
FlatIndex, InMemoryIndex, Lock, Options, OptionsBuilder, PythonRequirement, RequiresPython,
ResolverEnvironment, ResolverManifest, SatisfiesResult, UniversalMarker,
};
+use uv_scripts::{Pep723ItemRef, Pep723Script};
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy};
use uv_warnings::{warn_user, warn_user_once};
@@ -39,7 +40,7 @@ use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceMember};
use crate::commands::pip::loggers::{DefaultResolveLogger, ResolveLogger, SummaryResolveLogger};
use crate::commands::project::lock_target::LockTarget;
-use crate::commands::project::{ProjectError, ProjectInterpreter};
+use crate::commands::project::{ProjectError, ProjectInterpreter, ScriptInterpreter};
use crate::commands::reporters::ResolverReporter;
use crate::commands::{diagnostics, pip, ExitStatus};
use crate::printer::Printer;
@@ -80,6 +81,7 @@ pub(crate) async fn lock(
python: Option,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
+ script: Option,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
connectivity: Connectivity,
@@ -92,29 +94,52 @@ pub(crate) async fn lock(
preview: PreviewMode,
) -> anyhow::Result {
// Find the project requirements.
- let workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?;
+ let workspace;
+ let target = if let Some(script) = script.as_ref() {
+ LockTarget::Script(script)
+ } else {
+ workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?;
+ LockTarget::Workspace(&workspace)
+ };
// Determine the lock mode.
let interpreter;
let mode = if frozen {
LockMode::Frozen
} else {
- interpreter = ProjectInterpreter::discover(
- &workspace,
- project_dir,
- python.as_deref().map(PythonRequest::parse),
- python_preference,
- python_downloads,
- connectivity,
- native_tls,
- allow_insecure_host,
- &install_mirrors,
- no_config,
- cache,
- printer,
- )
- .await?
- .into_interpreter();
+ interpreter = match target {
+ LockTarget::Workspace(workspace) => ProjectInterpreter::discover(
+ workspace,
+ project_dir,
+ python.as_deref().map(PythonRequest::parse),
+ python_preference,
+ python_downloads,
+ connectivity,
+ native_tls,
+ allow_insecure_host,
+ &install_mirrors,
+ no_config,
+ cache,
+ printer,
+ )
+ .await?
+ .into_interpreter(),
+ LockTarget::Script(script) => ScriptInterpreter::discover(
+ Pep723ItemRef::Script(script),
+ python.as_deref().map(PythonRequest::parse),
+ python_preference,
+ python_downloads,
+ connectivity,
+ native_tls,
+ allow_insecure_host,
+ &install_mirrors,
+ no_config,
+ cache,
+ printer,
+ )
+ .await?
+ .into_interpreter(),
+ };
if locked {
LockMode::Locked(&interpreter)
@@ -131,7 +156,7 @@ pub(crate) async fn lock(
// Perform the lock operation.
match do_safe_lock(
mode,
- (&workspace).into(),
+ target,
settings.as_ref(),
LowerBound::Warn,
&state,
diff --git a/crates/uv/src/commands/project/lock_target.rs b/crates/uv/src/commands/project/lock_target.rs
index 6b1dee8271e8..2d2cc0197adb 100644
--- a/crates/uv/src/commands/project/lock_target.rs
+++ b/crates/uv/src/commands/project/lock_target.rs
@@ -1,12 +1,16 @@
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
+use itertools::Either;
+
use uv_configuration::{LowerBound, SourceStrategy};
+use uv_distribution::LoweredRequirement;
use uv_distribution_types::IndexLocations;
use uv_normalize::{GroupName, PackageName};
use uv_pep508::RequirementOrigin;
use uv_pypi_types::{Conflicts, Requirement, SupportedEnvironments, VerbatimParsedUrl};
use uv_resolver::{Lock, LockVersion, RequiresPython, VERSION};
+use uv_scripts::Pep723Script;
use uv_workspace::dependency_groups::DependencyGroupError;
use uv_workspace::{Workspace, WorkspaceMember};
@@ -16,6 +20,7 @@ use crate::commands::project::{find_requires_python, ProjectError};
#[derive(Debug, Copy, Clone)]
pub(crate) enum LockTarget<'lock> {
Workspace(&'lock Workspace),
+ Script(&'lock Pep723Script),
}
impl<'lock> From<&'lock Workspace> for LockTarget<'lock> {
@@ -24,12 +29,19 @@ impl<'lock> From<&'lock Workspace> for LockTarget<'lock> {
}
}
+impl<'lock> From<&'lock Pep723Script> for LockTarget<'lock> {
+ fn from(script: &'lock Pep723Script) -> Self {
+ LockTarget::Script(script)
+ }
+}
+
impl<'lock> LockTarget<'lock> {
/// Return the set of requirements that are attached to the target directly, as opposed to being
/// attached to any members within the target.
pub(crate) fn requirements(self) -> Vec> {
match self {
Self::Workspace(workspace) => workspace.requirements(),
+ Self::Script(script) => script.metadata.dependencies.clone().unwrap_or_default(),
}
}
@@ -37,6 +49,16 @@ impl<'lock> LockTarget<'lock> {
pub(crate) fn overrides(self) -> Vec> {
match self {
Self::Workspace(workspace) => workspace.overrides(),
+ Self::Script(script) => script
+ .metadata
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.override_dependencies.as_ref())
+ .into_iter()
+ .flatten()
+ .cloned()
+ .collect(),
}
}
@@ -44,6 +66,16 @@ impl<'lock> LockTarget<'lock> {
pub(crate) fn constraints(self) -> Vec> {
match self {
Self::Workspace(workspace) => workspace.constraints(),
+ Self::Script(script) => script
+ .metadata
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.constraint_dependencies.as_ref())
+ .into_iter()
+ .flatten()
+ .cloned()
+ .collect(),
}
}
@@ -57,20 +89,23 @@ impl<'lock> LockTarget<'lock> {
> {
match self {
Self::Workspace(workspace) => workspace.dependency_groups(),
+ Self::Script(_) => Ok(BTreeMap::new()),
}
}
/// Returns the set of all members within the target.
pub(crate) fn members_requirements(self) -> impl Iterator
- + 'lock {
match self {
- Self::Workspace(workspace) => workspace.members_requirements(),
+ Self::Workspace(workspace) => Either::Left(workspace.members_requirements()),
+ Self::Script(_) => Either::Right(std::iter::empty()),
}
}
/// Returns the set of all dependency groups within the target.
pub(crate) fn group_requirements(self) -> impl Iterator
- + 'lock {
match self {
- Self::Workspace(workspace) => workspace.group_requirements(),
+ Self::Workspace(workspace) => Either::Left(workspace.group_requirements()),
+ Self::Script(_) => Either::Right(std::iter::empty()),
}
}
@@ -90,6 +125,7 @@ impl<'lock> LockTarget<'lock> {
members
}
+ Self::Script(_) => Vec::new(),
}
}
@@ -97,6 +133,10 @@ impl<'lock> LockTarget<'lock> {
pub(crate) fn packages(self) -> &'lock BTreeMap {
match self {
Self::Workspace(workspace) => workspace.packages(),
+ Self::Script(_) => {
+ static EMPTY: BTreeMap = BTreeMap::new();
+ &EMPTY
+ }
}
}
@@ -104,6 +144,10 @@ impl<'lock> LockTarget<'lock> {
pub(crate) fn environments(self) -> Option<&'lock SupportedEnvironments> {
match self {
Self::Workspace(workspace) => workspace.environments(),
+ Self::Script(_) => {
+ // TODO(charlie): Add support for environments in scripts.
+ None
+ }
}
}
@@ -111,6 +155,7 @@ impl<'lock> LockTarget<'lock> {
pub(crate) fn conflicts(self) -> Conflicts {
match self {
Self::Workspace(workspace) => workspace.conflicts(),
+ Self::Script(_) => Conflicts::empty(),
}
}
@@ -118,6 +163,11 @@ impl<'lock> LockTarget<'lock> {
pub(crate) fn requires_python(self) -> Option {
match self {
Self::Workspace(workspace) => find_requires_python(workspace),
+ Self::Script(script) => script
+ .metadata
+ .requires_python
+ .as_ref()
+ .map(RequiresPython::from_specifiers),
}
}
@@ -125,13 +175,24 @@ impl<'lock> LockTarget<'lock> {
pub(crate) fn install_path(self) -> &'lock Path {
match self {
Self::Workspace(workspace) => workspace.install_path(),
+ Self::Script(script) => script.path.parent().unwrap(),
}
}
/// Return the path to the lockfile.
pub(crate) fn lock_path(self) -> PathBuf {
match self {
+ // `uv.lock`
Self::Workspace(workspace) => workspace.install_path().join("uv.lock"),
+ // `script.py.lock`
+ Self::Script(script) => {
+ let mut file_name = match script.path.file_name() {
+ Some(f) => f.to_os_string(),
+ None => panic!("Script path has no file name"),
+ };
+ file_name.push(".lock");
+ script.path.with_file_name(file_name)
+ }
}
}
@@ -223,6 +284,55 @@ impl<'lock> LockTarget<'lock> {
.map(|requirement| requirement.with_origin(RequirementOrigin::Workspace))
.collect::>())
}
+ Self::Script(script) => {
+ // Collect any `tool.uv.index` from the script.
+ let empty = Vec::default();
+ let indexes = match sources {
+ SourceStrategy::Enabled => script
+ .metadata
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.top_level.index.as_deref())
+ .unwrap_or(&empty),
+ SourceStrategy::Disabled => &empty,
+ };
+
+ // Collect any `tool.uv.sources` from the script.
+ let empty = BTreeMap::default();
+ let sources = match sources {
+ SourceStrategy::Enabled => script
+ .metadata
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.sources.as_ref())
+ .unwrap_or(&empty),
+ SourceStrategy::Disabled => &empty,
+ };
+
+ Ok(requirements
+ .into_iter()
+ .flat_map(|requirement| {
+ let requirement_name = requirement.name.clone();
+ LoweredRequirement::from_non_workspace_requirement(
+ requirement,
+ script.path.parent().unwrap(),
+ sources,
+ indexes,
+ locations,
+ LowerBound::Allow,
+ )
+ .map(move |requirement| match requirement {
+ Ok(requirement) => Ok(requirement.into_inner()),
+ Err(err) => Err(uv_distribution::MetadataError::LoweringError(
+ requirement_name.clone(),
+ Box::new(err),
+ )),
+ })
+ })
+ .collect::>()?)
+ }
}
}
}
diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs
index 2797093f2b4c..66ca098ffcc5 100644
--- a/crates/uv/src/lib.rs
+++ b/crates/uv/src/lib.rs
@@ -185,6 +185,12 @@ async fn run(mut cli: Cli) -> Result {
script: Some(script),
..
}) = &**command
+ {
+ Pep723Script::read(&script).await?.map(Pep723Item::Script)
+ } else if let ProjectCommand::Lock(uv_cli::LockArgs {
+ script: Some(script),
+ ..
+ }) = &**command
{
Pep723Script::read(&script).await?.map(Pep723Item::Script)
} else {
@@ -1508,7 +1514,14 @@ async fn run_project(
.combine(Refresh::from(args.settings.upgrade.clone())),
);
- commands::lock(
+ // Unwrap the script.
+ let script = script.map(|script| match script {
+ Pep723Item::Script(script) => script,
+ Pep723Item::Stdin(_) => unreachable!("`uv lock` does not support stdin"),
+ Pep723Item::Remote(_) => unreachable!("`uv lock` does not support remote files"),
+ });
+
+ Box::pin(commands::lock(
project_dir,
args.locked,
args.frozen,
@@ -1516,6 +1529,7 @@ async fn run_project(
args.python,
args.install_mirrors,
args.settings,
+ script,
globals.python_preference,
globals.python_downloads,
globals.connectivity,
@@ -1526,7 +1540,7 @@ async fn run_project(
&cache,
printer,
globals.preview,
- )
+ ))
.await
}
ProjectCommand::Add(args) => {
diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs
index dd327cf666a4..e4d030ceb542 100644
--- a/crates/uv/src/settings.rs
+++ b/crates/uv/src/settings.rs
@@ -1023,6 +1023,7 @@ pub(crate) struct LockSettings {
pub(crate) locked: bool,
pub(crate) frozen: bool,
pub(crate) dry_run: bool,
+ pub(crate) script: Option,
pub(crate) python: Option,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
@@ -1037,6 +1038,7 @@ impl LockSettings {
check,
check_exists,
dry_run,
+ script,
resolver,
build,
refresh,
@@ -1052,6 +1054,7 @@ impl LockSettings {
locked: check,
frozen: check_exists,
dry_run,
+ script,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs
index f783a696581d..d4039ab4bf5c 100644
--- a/crates/uv/tests/it/lock.rs
+++ b/crates/uv/tests/it/lock.rs
@@ -21360,6 +21360,254 @@ fn lock_missing_git_prefix() -> Result<()> {
Ok(())
}
+#[test]
+fn lock_script() -> Result<()> {
+ let context = TestContext::new("3.12");
+
+ let script = context.temp_dir.child("script.py");
+ script.write_str(indoc! { r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "anyio",
+ # ]
+ # ///
+
+ import anyio
+ "#
+ })?;
+
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 3 packages in [TIME]
+ "###);
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [{ name = "anyio" }]
+
+ [[package]]
+ name = "anyio"
+ version = "4.3.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "idna" },
+ { name = "sniffio" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", size = 85584 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "sniffio"
+ version = "1.3.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+ ]
+ "###
+ );
+ });
+
+ // Re-run with `--locked`.
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py").arg("--locked"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 3 packages in [TIME]
+ "###);
+
+ // Modify the script metadata.
+ script.write_str(indoc! { r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "anyio",
+ # "iniconfig",
+ # ]
+ # ///
+
+ import anyio
+ "#
+ })?;
+
+ // Re-run with `--locked`.
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py").arg("--locked"), @r###"
+ success: false
+ exit_code: 2
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 4 packages in [TIME]
+ error: The lockfile at `uv.lock` needs to be updated, but `--locked` was provided. To update the lockfile, run `uv lock`.
+ "###);
+
+ Ok(())
+}
+
+#[test]
+fn lock_script_path() -> Result<()> {
+ let context = TestContext::new("3.12");
+
+ let script = context.temp_dir.child("script.py");
+ script.write_str(indoc! { r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "anyio",
+ # "child",
+ # ]
+ #
+ # [tool.uv.sources]
+ # child = { path = "child" }
+ # ///
+
+ import anyio
+ "#
+ })?;
+
+ let child = context.temp_dir.child("child");
+ fs_err::create_dir_all(&child)?;
+
+ let pyproject_toml = child.child("pyproject.toml");
+ pyproject_toml.write_str(
+ r#"
+ [project]
+ name = "child"
+ version = "0.1.0"
+ requires-python = ">=3.11"
+ dependencies = ["iniconfig"]
+
+ [build-system]
+ requires = ["setuptools>=42"]
+ build-backend = "setuptools.build_meta"
+ "#,
+ )?;
+
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 5 packages in [TIME]
+ "###);
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [
+ { name = "anyio" },
+ { name = "child", directory = "child" },
+ ]
+
+ [[package]]
+ name = "anyio"
+ version = "4.3.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "idna" },
+ { name = "sniffio" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", size = 85584 },
+ ]
+
+ [[package]]
+ name = "child"
+ version = "0.1.0"
+ source = { directory = "child" }
+ dependencies = [
+ { name = "iniconfig" },
+ ]
+
+ [package.metadata]
+ requires-dist = [{ name = "iniconfig" }]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "iniconfig"
+ version = "2.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+ ]
+
+ [[package]]
+ name = "sniffio"
+ version = "1.3.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+ ]
+ "###
+ );
+ });
+
+ // Re-run with `--locked`.
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py").arg("--locked"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 5 packages in [TIME]
+ "###);
+
+ Ok(())
+}
+
#[test]
fn lock_pytorch_cpu() -> Result<()> {
let context = TestContext::new("3.12");
diff --git a/docs/reference/cli.md b/docs/reference/cli.md
index ae723000047c..b673ce64bc3b 100644
--- a/docs/reference/cli.md
+++ b/docs/reference/cli.md
@@ -2135,6 +2135,10 @@ uv lock [OPTIONS]
lowest-direct
: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
+--script
scriptLock the specified Python script, rather than the current project.
+
+If provided, uv will lock the script (based on its inline metadata table, in adherence with PEP 723) to a .lock
file adjacent to the script itself.
+
--upgrade
, -U
Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh
--upgrade-package
, -P
upgrade-packageAllow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package
From ad09070dd7ae9cfa7c609a42f07af1df8e6d8b18 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 8 Jan 2025 18:39:26 +0000
Subject: [PATCH 070/135] Update Rust crate petgraph to 0.7.1 (#10317)
---
Cargo.lock | 8 ++++----
Cargo.toml | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 2bd7f5ae5032..3a8e8184e2df 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1099,9 +1099,9 @@ dependencies = [
[[package]]
name = "fixedbitset"
-version = "0.4.2"
+version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flate2"
@@ -2500,9 +2500,9 @@ dependencies = [
[[package]]
name = "petgraph"
-version = "0.6.5"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [
"fixedbitset",
"indexmap",
diff --git a/Cargo.toml b/Cargo.toml
index a4df23b68568..bae813d11bda 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -126,7 +126,7 @@ nix = { version = "0.29.0" }
owo-colors = { version = "4.1.0" }
path-slash = { version = "0.2.1" }
pathdiff = { version = "0.2.1" }
-petgraph = { version = "0.6.5" }
+petgraph = { version = "0.7.1" }
platform-info = { version = "2.0.3" }
proc-macro2 = { version = "1.0.86" }
procfs = { version = "0.17.0", default-features = false, features = ["flate2"] }
From 68bfa5b5b8d32257fabcf0de9897437ba6088d64 Mon Sep 17 00:00:00 2001
From: Zanie Blue
Date: Wed, 8 Jan 2025 12:46:24 -0600
Subject: [PATCH 071/135] Avoid Docker rate limits by logging into DockerHub
(#10400)
The latest release flaked failing to fetch the buildx image, which is
reportedly due to rate limits. Last I checked, DockerHub enforces much
stricter limits on unauthenticated requests. I added a bot account and a
corresponding read-only token.
---
.github/workflows/build-docker.yml | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml
index 9ef0052342ff..843d80e4b2df 100644
--- a/.github/workflows/build-docker.yml
+++ b/.github/workflows/build-docker.yml
@@ -37,6 +37,12 @@ jobs:
with:
submodules: recursive
+ # Login to DockerHub first, to avoid rate-limiting
+ - uses: docker/login-action@v3
+ with:
+ username: astralshbot
+ password: ${{ secrets.DOCKERHUB_TOKEN_RO }}
+
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
@@ -108,6 +114,12 @@ jobs:
- docker-build
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
steps:
+ # Login to DockerHub first, to avoid rate-limiting
+ - uses: docker/login-action@v3
+ with:
+ username: astralshbot
+ password: ${{ secrets.DOCKERHUB_TOKEN_RO }}
+
- name: Download digests
uses: actions/download-artifact@v4
with:
@@ -180,6 +192,12 @@ jobs:
- python:3.9-slim-bookworm,python3.9-bookworm-slim
- python:3.8-slim-bookworm,python3.8-bookworm-slim
steps:
+ # Login to DockerHub first, to avoid rate-limiting
+ - uses: docker/login-action@v3
+ with:
+ username: astralshbot
+ password: ${{ secrets.DOCKERHUB_TOKEN_RO }}
+
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
@@ -266,6 +284,12 @@ jobs:
- docker-publish-extra
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
steps:
+ # Login to DockerHub first, to avoid rate-limiting
+ - uses: docker/login-action@v3
+ with:
+ username: astralshbot
+ password: ${{ secrets.DOCKERHUB_TOKEN_RO }}
+
- name: Download digests
uses: actions/download-artifact@v4
with:
From 391ab757b85ad6c2c8955f39ecc578e70de25c62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Burak=20Varl=C4=B1?=
Date: Wed, 8 Jan 2025 19:09:20 +0000
Subject: [PATCH 072/135] Add `ls` alias to `uv {tool, python, pip} list`
(#10240)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
This PR adds `ls` alias to `uv {tool, python, pip} list` for
convenience.
Not sure if folks previously discussed this or have any opinion on
having aliases – but I have a muscle memory for `ls` for listing things
in commands I'm using (like `docker images ls`, `zellij ls`, `helm ls`
etc.) and thought having `ls` alias for `list` command would be useful.
## Test Plan
I simply compiled `uv` and manually checked `./target/release/uv {tool,
python, pip} ls`.
---
crates/uv-cli/src/lib.rs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs
index b829483189a5..c798bec075d7 100644
--- a/crates/uv-cli/src/lib.rs
+++ b/crates/uv-cli/src/lib.rs
@@ -613,7 +613,8 @@ pub enum PipCommand {
/// List, in tabular format, packages installed in an environment.
#[command(
after_help = "Use `uv help pip list` for more details.",
- after_long_help = ""
+ after_long_help = "",
+ alias = "ls"
)]
List(PipListArgs),
/// Show information about one or more installed packages.
@@ -3724,6 +3725,7 @@ pub enum ToolCommand {
#[command(alias = "update")]
Upgrade(ToolUpgradeArgs),
/// List installed tools.
+ #[command(alias = "ls")]
List(ToolListArgs),
/// Uninstall a tool.
Uninstall(ToolUninstallArgs),
@@ -4203,6 +4205,7 @@ pub enum PythonCommand {
/// Use `--all-versions` to view all available patch versions.
///
/// Use `--only-installed` to omit available downloads.
+ #[command(alias = "ls")]
List(PythonListArgs),
/// Download and install Python versions.
From e22b728e3f39816c21cb4e597f51da53dc82ce82 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 15:34:45 -0500
Subject: [PATCH 073/135] Respect PEP 723 script lockfiles in uv run (#10136)
## Summary
If a script has a lockfile, then `uv run` will now reuse it.
Closes https://github.com/astral-sh/uv/issues/7483.
Closes https://github.com/astral-sh/uv/issues/9688.
---
crates/uv-configuration/src/dev.rs | 4 +-
crates/uv-scripts/src/lib.rs | 20 ++
crates/uv/src/commands/project/add.rs | 1 +
crates/uv/src/commands/project/environment.rs | 138 ++++++--
.../uv/src/commands/project/install_target.rs | 136 +++++++-
crates/uv/src/commands/project/mod.rs | 4 +-
crates/uv/src/commands/project/run.rs | 298 +++++++++++-------
crates/uv/src/commands/project/sync.rs | 133 +-------
crates/uv/src/commands/tool/run.rs | 4 +-
crates/uv/tests/it/run.rs | 187 +++++++++++
10 files changed, 658 insertions(+), 267 deletions(-)
diff --git a/crates/uv-configuration/src/dev.rs b/crates/uv-configuration/src/dev.rs
index 80ae4f0640e4..cce967ba1f89 100644
--- a/crates/uv-configuration/src/dev.rs
+++ b/crates/uv-configuration/src/dev.rs
@@ -316,7 +316,7 @@ impl From for DevGroupsSpecification {
/// The manifest of `dependency-groups` to include, taking into account the user-provided
/// [`DevGroupsSpecification`] and the project-specific default groups.
-#[derive(Debug, Clone)]
+#[derive(Debug, Default, Clone)]
pub struct DevGroupsManifest {
/// The specification for the development dependencies.
pub(crate) spec: DevGroupsSpecification,
@@ -347,7 +347,7 @@ impl DevGroupsManifest {
}
/// Returns `true` if the group was enabled by default.
- pub fn default(&self, group: &GroupName) -> bool {
+ pub fn is_default(&self, group: &GroupName) -> bool {
if self.spec.contains(group) {
// If the group was explicitly requested, then it wasn't enabled by default.
false
diff --git a/crates/uv-scripts/src/lib.rs b/crates/uv-scripts/src/lib.rs
index cdcccc96e613..2f812622747b 100644
--- a/crates/uv-scripts/src/lib.rs
+++ b/crates/uv-scripts/src/lib.rs
@@ -54,6 +54,14 @@ impl Pep723Item {
Self::Remote(_) => None,
}
}
+
+ /// Return the PEP 723 script, if any.
+ pub fn as_script(&self) -> Option<&Pep723Script> {
+ match self {
+ Self::Script(script) => Some(script),
+ _ => None,
+ }
+ }
}
/// A reference to a PEP 723 item.
@@ -234,6 +242,18 @@ impl Pep723Script {
Ok(())
}
+
+ /// Return the [`Sources`] defined in the PEP 723 metadata.
+ pub fn sources(&self) -> &BTreeMap {
+ static EMPTY: BTreeMap = BTreeMap::new();
+
+ self.metadata
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.sources.as_ref())
+ .unwrap_or(&EMPTY)
+ }
}
/// PEP 723 metadata as parsed from a `script` comment block.
diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs
index c721f1527d06..017ebd5c8043 100644
--- a/crates/uv/src/commands/project/add.rs
+++ b/crates/uv/src/commands/project/add.rs
@@ -592,6 +592,7 @@ pub(crate) async fn add(
Target::Project(project, environment) => (project, environment),
// If `--script`, exit early. There's no reason to lock and sync.
Target::Script(script, _) => {
+ // TODO(charlie): Lock the script, if a lockfile already exists.
writeln!(
printer.stderr(),
"Updated `{}`",
diff --git a/crates/uv/src/commands/project/environment.rs b/crates/uv/src/commands/project/environment.rs
index e6b696518e39..0654ec6bc953 100644
--- a/crates/uv/src/commands/project/environment.rs
+++ b/crates/uv/src/commands/project/environment.rs
@@ -1,6 +1,7 @@
use tracing::debug;
use crate::commands::pip::loggers::{InstallLogger, ResolveLogger};
+use crate::commands::project::install_target::InstallTarget;
use crate::commands::project::{
resolve_environment, sync_environment, EnvironmentSpecification, ProjectError,
};
@@ -9,10 +10,13 @@ use crate::settings::ResolverInstallerSettings;
use uv_cache::{Cache, CacheBucket};
use uv_cache_key::{cache_digest, hash_digest};
use uv_client::Connectivity;
-use uv_configuration::{Concurrency, PreviewMode, TrustedHost};
+use uv_configuration::{
+ Concurrency, DevGroupsManifest, ExtrasSpecification, InstallOptions, PreviewMode, TrustedHost,
+};
use uv_dispatch::SharedState;
use uv_distribution_types::{Name, Resolution};
use uv_python::{Interpreter, PythonEnvironment};
+use uv_resolver::Installable;
/// A [`PythonEnvironment`] stored in the cache.
#[derive(Debug)]
@@ -25,9 +29,8 @@ impl From for PythonEnvironment {
}
impl CachedEnvironment {
- /// Get or create an [`CachedEnvironment`] based on a given set of requirements and a base
- /// interpreter.
- pub(crate) async fn get_or_create(
+ /// Get or create an [`CachedEnvironment`] based on a given set of requirements.
+ pub(crate) async fn from_spec(
spec: EnvironmentSpecification<'_>,
interpreter: &Interpreter,
settings: &ResolverInstallerSettings,
@@ -43,21 +46,7 @@ impl CachedEnvironment {
printer: Printer,
preview: PreviewMode,
) -> Result {
- // When caching, always use the base interpreter, rather than that of the virtual
- // environment.
- let interpreter = if let Some(interpreter) = interpreter.to_base_interpreter(cache)? {
- debug!(
- "Caching via base interpreter: `{}`",
- interpreter.sys_executable().display()
- );
- interpreter
- } else {
- debug!(
- "Caching via interpreter: `{}`",
- interpreter.sys_executable().display()
- );
- interpreter.clone()
- };
+ let interpreter = Self::base_interpreter(interpreter, cache)?;
// Resolve the requirements with the interpreter.
let resolution = Resolution::from(
@@ -78,6 +67,93 @@ impl CachedEnvironment {
.await?,
);
+ Self::from_resolution(
+ resolution,
+ interpreter,
+ settings,
+ state,
+ install,
+ installer_metadata,
+ connectivity,
+ concurrency,
+ native_tls,
+ allow_insecure_host,
+ cache,
+ printer,
+ preview,
+ )
+ .await
+ }
+
+ /// Get or create an [`CachedEnvironment`] based on a given [`InstallTarget`].
+ pub(crate) async fn from_lock(
+ target: InstallTarget<'_>,
+ extras: &ExtrasSpecification,
+ dev: &DevGroupsManifest,
+ install_options: InstallOptions,
+ settings: &ResolverInstallerSettings,
+ interpreter: &Interpreter,
+ state: &SharedState,
+ install: Box,
+ installer_metadata: bool,
+ connectivity: Connectivity,
+ concurrency: Concurrency,
+ native_tls: bool,
+ allow_insecure_host: &[TrustedHost],
+ cache: &Cache,
+ printer: Printer,
+ preview: PreviewMode,
+ ) -> Result {
+ let interpreter = Self::base_interpreter(interpreter, cache)?;
+
+ // Determine the tags, markers, and interpreter to use for resolution.
+ let tags = interpreter.tags()?;
+ let marker_env = interpreter.resolver_marker_environment();
+
+ // Read the lockfile.
+ let resolution = target.to_resolution(
+ &marker_env,
+ tags,
+ extras,
+ dev,
+ &settings.build_options,
+ &install_options,
+ )?;
+
+ Self::from_resolution(
+ resolution,
+ interpreter,
+ settings,
+ state,
+ install,
+ installer_metadata,
+ connectivity,
+ concurrency,
+ native_tls,
+ allow_insecure_host,
+ cache,
+ printer,
+ preview,
+ )
+ .await
+ }
+
+ /// Get or create an [`CachedEnvironment`] based on a given [`Resolution`].
+ pub(crate) async fn from_resolution(
+ resolution: Resolution,
+ interpreter: Interpreter,
+ settings: &ResolverInstallerSettings,
+ state: &SharedState,
+ install: Box,
+ installer_metadata: bool,
+ connectivity: Connectivity,
+ concurrency: Concurrency,
+ native_tls: bool,
+ allow_insecure_host: &[TrustedHost],
+ cache: &Cache,
+ printer: Printer,
+ preview: PreviewMode,
+ ) -> Result {
// Hash the resolution by hashing the generated lockfile.
// TODO(charlie): If the resolution contains any mutable metadata (like a path or URL
// dependency), skip this step.
@@ -144,4 +220,28 @@ impl CachedEnvironment {
pub(crate) fn into_interpreter(self) -> Interpreter {
self.0.into_interpreter()
}
+
+ /// Return the [`Interpreter`] to use for the cached environment, based on a given
+ /// [`Interpreter`].
+ ///
+ /// When caching, always use the base interpreter, rather than that of the virtual
+ /// environment.
+ fn base_interpreter(
+ interpreter: &Interpreter,
+ cache: &Cache,
+ ) -> Result {
+ if let Some(interpreter) = interpreter.to_base_interpreter(cache)? {
+ debug!(
+ "Caching via base interpreter: `{}`",
+ interpreter.sys_executable().display()
+ );
+ Ok(interpreter)
+ } else {
+ debug!(
+ "Caching via interpreter: `{}`",
+ interpreter.sys_executable().display()
+ );
+ Ok(interpreter.clone())
+ }
+ }
}
diff --git a/crates/uv/src/commands/project/install_target.rs b/crates/uv/src/commands/project/install_target.rs
index 69999d4d77cf..78d88640960f 100644
--- a/crates/uv/src/commands/project/install_target.rs
+++ b/crates/uv/src/commands/project/install_target.rs
@@ -1,9 +1,14 @@
+use std::borrow::Cow;
use std::path::Path;
+use std::str::FromStr;
use itertools::Either;
use uv_normalize::PackageName;
+use uv_pypi_types::{LenientRequirement, VerbatimParsedUrl};
use uv_resolver::{Installable, Lock, Package};
+use uv_scripts::Pep723Script;
+use uv_workspace::pyproject::{DependencyGroupSpecifier, Source, Sources, ToolUvSources};
use uv_workspace::Workspace;
/// A target that can be installed from a lockfile.
@@ -25,6 +30,11 @@ pub(crate) enum InstallTarget<'lock> {
workspace: &'lock Workspace,
lock: &'lock Lock,
},
+ /// A PEP 723 script.
+ Script {
+ script: &'lock Pep723Script,
+ lock: &'lock Lock,
+ },
}
impl<'lock> Installable<'lock> for InstallTarget<'lock> {
@@ -33,6 +43,7 @@ impl<'lock> Installable<'lock> for InstallTarget<'lock> {
Self::Project { workspace, .. } => workspace.install_path(),
Self::Workspace { workspace, .. } => workspace.install_path(),
Self::NonProjectWorkspace { workspace, .. } => workspace.install_path(),
+ Self::Script { script, .. } => script.path.parent().unwrap(),
}
}
@@ -41,24 +52,28 @@ impl<'lock> Installable<'lock> for InstallTarget<'lock> {
Self::Project { lock, .. } => lock,
Self::Workspace { lock, .. } => lock,
Self::NonProjectWorkspace { lock, .. } => lock,
+ Self::Script { lock, .. } => lock,
}
}
fn roots(&self) -> impl Iterator- {
match self {
- Self::Project { name, .. } => Either::Right(Either::Left(std::iter::once(*name))),
- Self::NonProjectWorkspace { lock, .. } => Either::Left(lock.members().iter()),
+ Self::Project { name, .. } => Either::Left(Either::Left(std::iter::once(*name))),
+ Self::NonProjectWorkspace { lock, .. } => {
+ Either::Left(Either::Right(lock.members().iter()))
+ }
Self::Workspace { lock, .. } => {
// Identify the workspace members.
//
// The members are encoded directly in the lockfile, unless the workspace contains a
// single member at the root, in which case, we identify it by its source.
if lock.members().is_empty() {
- Either::Right(Either::Right(lock.root().into_iter().map(Package::name)))
+ Either::Right(Either::Left(lock.root().into_iter().map(Package::name)))
} else {
- Either::Left(lock.members().iter())
+ Either::Left(Either::Right(lock.members().iter()))
}
}
+ Self::Script { .. } => Either::Right(Either::Right(std::iter::empty())),
}
}
@@ -67,17 +82,120 @@ impl<'lock> Installable<'lock> for InstallTarget<'lock> {
Self::Project { name, .. } => Some(name),
Self::Workspace { .. } => None,
Self::NonProjectWorkspace { .. } => None,
+ Self::Script { .. } => None,
}
}
}
impl<'lock> InstallTarget<'lock> {
- /// Return the [`Workspace`] of the target.
- pub(crate) fn workspace(&self) -> &'lock Workspace {
+ /// Return an iterator over all [`Sources`] defined by the target.
+ pub(crate) fn sources(&self) -> impl Iterator
- {
+ match self {
+ Self::Project { workspace, .. }
+ | Self::Workspace { workspace, .. }
+ | Self::NonProjectWorkspace { workspace, .. } => {
+ Either::Left(workspace.sources().values().flat_map(Sources::iter).chain(
+ workspace.packages().values().flat_map(|member| {
+ member
+ .pyproject_toml()
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.sources.as_ref())
+ .map(ToolUvSources::inner)
+ .into_iter()
+ .flat_map(|sources| sources.values().flat_map(Sources::iter))
+ }),
+ ))
+ }
+ Self::Script { script, .. } => {
+ Either::Right(script.sources().values().flat_map(Sources::iter))
+ }
+ }
+ }
+
+ /// Return an iterator over all requirements defined by the target.
+ pub(crate) fn requirements(
+ &self,
+ ) -> impl Iterator
- >> {
match self {
- Self::Project { workspace, .. } => workspace,
- Self::Workspace { workspace, .. } => workspace,
- Self::NonProjectWorkspace { workspace, .. } => workspace,
+ Self::Project { workspace, .. }
+ | Self::Workspace { workspace, .. }
+ | Self::NonProjectWorkspace { workspace, .. } => {
+ Either::Left(
+ // Iterate over the non-member requirements in the workspace.
+ workspace
+ .requirements()
+ .into_iter()
+ .map(Cow::Owned)
+ .chain(workspace.dependency_groups().ok().into_iter().flat_map(
+ |dependency_groups| {
+ dependency_groups.into_values().flatten().map(Cow::Owned)
+ },
+ ))
+ .chain(workspace.packages().values().flat_map(|member| {
+ // Iterate over all dependencies in each member.
+ let dependencies = member
+ .pyproject_toml()
+ .project
+ .as_ref()
+ .and_then(|project| project.dependencies.as_ref())
+ .into_iter()
+ .flatten();
+ let optional_dependencies = member
+ .pyproject_toml()
+ .project
+ .as_ref()
+ .and_then(|project| project.optional_dependencies.as_ref())
+ .into_iter()
+ .flat_map(|optional| optional.values())
+ .flatten();
+ let dependency_groups = member
+ .pyproject_toml()
+ .dependency_groups
+ .as_ref()
+ .into_iter()
+ .flatten()
+ .flat_map(|(_, dependencies)| {
+ dependencies.iter().filter_map(|specifier| {
+ if let DependencyGroupSpecifier::Requirement(requirement) =
+ specifier
+ {
+ Some(requirement)
+ } else {
+ None
+ }
+ })
+ });
+ let dev_dependencies = member
+ .pyproject_toml()
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.dev_dependencies.as_ref())
+ .into_iter()
+ .flatten();
+ dependencies
+ .chain(optional_dependencies)
+ .chain(dependency_groups)
+ .filter_map(|requires_dist| {
+ LenientRequirement::::from_str(requires_dist)
+ .map(uv_pep508::Requirement::from)
+ .map(Cow::Owned)
+ .ok()
+ })
+ .chain(dev_dependencies.map(Cow::Borrowed))
+ })),
+ )
+ }
+ Self::Script { script, .. } => Either::Right(
+ script
+ .metadata
+ .dependencies
+ .iter()
+ .flatten()
+ .map(Cow::Borrowed),
+ ),
}
}
}
diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs
index c738eba18661..1f85ec11a1d4 100644
--- a/crates/uv/src/commands/project/mod.rs
+++ b/crates/uv/src/commands/project/mod.rs
@@ -272,7 +272,7 @@ impl std::fmt::Display for ConflictError {
self.conflicts
.iter()
.map(|conflict| match conflict {
- ConflictPackage::Group(ref group) if self.dev.default(group) =>
+ ConflictPackage::Group(ref group) if self.dev.is_default(group) =>
format!("`{group}` (enabled by default)"),
ConflictPackage::Group(ref group) => format!("`{group}`"),
ConflictPackage::Extra(..) => unreachable!(),
@@ -291,7 +291,7 @@ impl std::fmt::Display for ConflictError {
.map(|(i, conflict)| {
let conflict = match conflict {
ConflictPackage::Extra(ref extra) => format!("extra `{extra}`"),
- ConflictPackage::Group(ref group) if self.dev.default(group) => {
+ ConflictPackage::Group(ref group) if self.dev.is_default(group) => {
format!("group `{group}` (enabled by default)")
}
ConflictPackage::Group(ref group) => format!("group `{group}`"),
diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs
index ac4e37694461..27a2e22b1bcb 100644
--- a/crates/uv/src/commands/project/run.rs
+++ b/crates/uv/src/commands/project/run.rs
@@ -17,8 +17,8 @@ use uv_cache::Cache;
use uv_cli::ExternalCommand;
use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{
- Concurrency, DevGroupsSpecification, EditableMode, ExtrasSpecification, GroupsSpecification,
- InstallOptions, LowerBound, PreviewMode, SourceStrategy, TrustedHost,
+ Concurrency, DevGroupsManifest, DevGroupsSpecification, EditableMode, ExtrasSpecification,
+ GroupsSpecification, InstallOptions, LowerBound, PreviewMode, SourceStrategy, TrustedHost,
};
use uv_dispatch::SharedState;
use uv_distribution::LoweredRequirement;
@@ -201,109 +201,57 @@ pub(crate) async fn run(
.await?
.into_interpreter();
- // Determine the working directory for the script.
- let script_dir = match &script {
- Pep723Item::Script(script) => std::path::absolute(&script.path)?
- .parent()
- .expect("script path has no parent")
- .to_owned(),
- Pep723Item::Stdin(..) | Pep723Item::Remote(..) => std::env::current_dir()?,
- };
- let script = script.into_metadata();
-
- // Install the script requirements, if necessary. Otherwise, use an isolated environment.
- if let Some(dependencies) = script.dependencies {
- // Collect any `tool.uv.index` from the script.
- let empty = Vec::default();
- let script_indexes = match settings.sources {
- SourceStrategy::Enabled => script
- .tool
- .as_ref()
- .and_then(|tool| tool.uv.as_ref())
- .and_then(|uv| uv.top_level.index.as_deref())
- .unwrap_or(&empty),
- SourceStrategy::Disabled => &empty,
- };
+ // If a lockfile already exists, lock the script.
+ if let Some(target) = script
+ .as_script()
+ .map(LockTarget::from)
+ .filter(|target| target.lock_path().is_file())
+ {
+ debug!("Found existing lockfile for script");
- // Collect any `tool.uv.sources` from the script.
- let empty = BTreeMap::default();
- let script_sources = match settings.sources {
- SourceStrategy::Enabled => script
- .tool
- .as_ref()
- .and_then(|tool| tool.uv.as_ref())
- .and_then(|uv| uv.sources.as_ref())
- .unwrap_or(&empty),
- SourceStrategy::Disabled => &empty,
+ // Determine the lock mode.
+ let mode = if frozen {
+ LockMode::Frozen
+ } else if locked {
+ LockMode::Locked(&interpreter)
+ } else {
+ LockMode::Write(&interpreter)
};
- let requirements = dependencies
- .into_iter()
- .flat_map(|requirement| {
- LoweredRequirement::from_non_workspace_requirement(
- requirement,
- script_dir.as_ref(),
- script_sources,
- script_indexes,
- &settings.index_locations,
- LowerBound::Allow,
- )
- .map_ok(LoweredRequirement::into_inner)
- })
- .collect::>()?;
- let constraints = script
- .tool
- .as_ref()
- .and_then(|tool| tool.uv.as_ref())
- .and_then(|uv| uv.constraint_dependencies.as_ref())
- .into_iter()
- .flatten()
- .cloned()
- .flat_map(|requirement| {
- LoweredRequirement::from_non_workspace_requirement(
- requirement,
- script_dir.as_ref(),
- script_sources,
- script_indexes,
- &settings.index_locations,
- LowerBound::Allow,
- )
- .map_ok(LoweredRequirement::into_inner)
- })
- .collect::, _>>()?;
- let overrides = script
- .tool
- .as_ref()
- .and_then(|tool| tool.uv.as_ref())
- .and_then(|uv| uv.override_dependencies.as_ref())
- .into_iter()
- .flatten()
- .cloned()
- .flat_map(|requirement| {
- LoweredRequirement::from_non_workspace_requirement(
- requirement,
- script_dir.as_ref(),
- script_sources,
- script_indexes,
- &settings.index_locations,
- LowerBound::Allow,
- )
- .map_ok(LoweredRequirement::into_inner)
- })
- .collect::, _>>()?;
-
- let spec =
- RequirementsSpecification::from_overrides(requirements, constraints, overrides);
- let result = CachedEnvironment::get_or_create(
- EnvironmentSpecification::from(spec),
- &interpreter,
- &settings,
+ // Generate a lockfile.
+ let lock = project::lock::do_safe_lock(
+ mode,
+ target,
+ settings.as_ref().into(),
+ LowerBound::Allow,
&state,
if show_resolution {
Box::new(DefaultResolveLogger)
} else {
Box::new(SummaryResolveLogger)
},
+ connectivity,
+ concurrency,
+ native_tls,
+ allow_insecure_host,
+ cache,
+ printer,
+ preview,
+ )
+ .await?
+ .into_lock();
+
+ let result = CachedEnvironment::from_lock(
+ InstallTarget::Script {
+ script: script.as_script().unwrap(),
+ lock: &lock,
+ },
+ &ExtrasSpecification::default(),
+ &DevGroupsManifest::default(),
+ InstallOptions::default(),
+ &settings,
+ &interpreter,
+ &state,
if show_resolution {
Box::new(DefaultInstallLogger)
} else {
@@ -332,19 +280,151 @@ pub(crate) async fn run(
Some(environment.into_interpreter())
} else {
- // Create a virtual environment.
- temp_dir = cache.venv_dir()?;
- let environment = uv_virtualenv::create_venv(
- temp_dir.path(),
- interpreter,
- uv_virtualenv::Prompt::None,
- false,
- false,
- false,
- false,
- )?;
+ // Determine the working directory for the script.
+ let script_dir = match &script {
+ Pep723Item::Script(script) => std::path::absolute(&script.path)?
+ .parent()
+ .expect("script path has no parent")
+ .to_owned(),
+ Pep723Item::Stdin(..) | Pep723Item::Remote(..) => std::env::current_dir()?,
+ };
+ let script = script.into_metadata();
+
+ // Install the script requirements, if necessary. Otherwise, use an isolated environment.
+ if let Some(dependencies) = script.dependencies {
+ // Collect any `tool.uv.index` from the script.
+ let empty = Vec::default();
+ let script_indexes = match settings.sources {
+ SourceStrategy::Enabled => script
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.top_level.index.as_deref())
+ .unwrap_or(&empty),
+ SourceStrategy::Disabled => &empty,
+ };
- Some(environment.into_interpreter())
+ // Collect any `tool.uv.sources` from the script.
+ let empty = BTreeMap::default();
+ let script_sources = match settings.sources {
+ SourceStrategy::Enabled => script
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.sources.as_ref())
+ .unwrap_or(&empty),
+ SourceStrategy::Disabled => &empty,
+ };
+
+ let requirements = dependencies
+ .into_iter()
+ .flat_map(|requirement| {
+ LoweredRequirement::from_non_workspace_requirement(
+ requirement,
+ script_dir.as_ref(),
+ script_sources,
+ script_indexes,
+ &settings.index_locations,
+ LowerBound::Allow,
+ )
+ .map_ok(LoweredRequirement::into_inner)
+ })
+ .collect::>()?;
+ let constraints = script
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.constraint_dependencies.as_ref())
+ .into_iter()
+ .flatten()
+ .cloned()
+ .flat_map(|requirement| {
+ LoweredRequirement::from_non_workspace_requirement(
+ requirement,
+ script_dir.as_ref(),
+ script_sources,
+ script_indexes,
+ &settings.index_locations,
+ LowerBound::Allow,
+ )
+ .map_ok(LoweredRequirement::into_inner)
+ })
+ .collect::, _>>()?;
+ let overrides = script
+ .tool
+ .as_ref()
+ .and_then(|tool| tool.uv.as_ref())
+ .and_then(|uv| uv.override_dependencies.as_ref())
+ .into_iter()
+ .flatten()
+ .cloned()
+ .flat_map(|requirement| {
+ LoweredRequirement::from_non_workspace_requirement(
+ requirement,
+ script_dir.as_ref(),
+ script_sources,
+ script_indexes,
+ &settings.index_locations,
+ LowerBound::Allow,
+ )
+ .map_ok(LoweredRequirement::into_inner)
+ })
+ .collect::, _>>()?;
+
+ let spec =
+ RequirementsSpecification::from_overrides(requirements, constraints, overrides);
+ let result = CachedEnvironment::from_spec(
+ EnvironmentSpecification::from(spec),
+ &interpreter,
+ &settings,
+ &state,
+ if show_resolution {
+ Box::new(DefaultResolveLogger)
+ } else {
+ Box::new(SummaryResolveLogger)
+ },
+ if show_resolution {
+ Box::new(DefaultInstallLogger)
+ } else {
+ Box::new(SummaryInstallLogger)
+ },
+ installer_metadata,
+ connectivity,
+ concurrency,
+ native_tls,
+ allow_insecure_host,
+ cache,
+ printer,
+ preview,
+ )
+ .await;
+
+ let environment = match result {
+ Ok(resolution) => resolution,
+ Err(ProjectError::Operation(err)) => {
+ return diagnostics::OperationDiagnostic::with_context("script")
+ .report(err)
+ .map_or(Ok(ExitStatus::Failure), |err| Err(err.into()))
+ }
+ Err(err) => return Err(err.into()),
+ };
+
+ Some(environment.into_interpreter())
+ } else {
+ // Create a virtual environment.
+ temp_dir = cache.venv_dir()?;
+ let environment = uv_virtualenv::create_venv(
+ temp_dir.path(),
+ interpreter,
+ uv_virtualenv::Prompt::None,
+ false,
+ false,
+ false,
+ false,
+ )?;
+
+ Some(environment.into_interpreter())
+ }
}
} else {
None
@@ -847,7 +927,7 @@ pub(crate) async fn run(
Some(spec) => {
debug!("Syncing ephemeral requirements");
- let result = CachedEnvironment::get_or_create(
+ let result = CachedEnvironment::from_spec(
EnvironmentSpecification::from(spec).with_lock(
lock.as_ref()
.map(|(lock, install_path)| (lock, install_path.as_ref())),
diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs
index 78a3595c3ff6..755a4314c3b7 100644
--- a/crates/uv/src/commands/project/sync.rs
+++ b/crates/uv/src/commands/project/sync.rs
@@ -1,6 +1,4 @@
-use std::borrow::Cow;
use std::path::Path;
-use std::str::FromStr;
use anyhow::{Context, Result};
use itertools::Itertools;
@@ -18,16 +16,14 @@ use uv_distribution_types::{
};
use uv_installer::SitePackages;
use uv_normalize::PackageName;
-use uv_pep508::{MarkerTree, Requirement, VersionOrUrl};
-use uv_pypi_types::{
- LenientRequirement, ParsedArchiveUrl, ParsedGitUrl, ParsedUrl, VerbatimParsedUrl,
-};
+use uv_pep508::{MarkerTree, VersionOrUrl};
+use uv_pypi_types::{ParsedArchiveUrl, ParsedGitUrl, ParsedUrl};
use uv_python::{PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest};
use uv_resolver::{FlatIndex, Installable};
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user;
-use uv_workspace::pyproject::{DependencyGroupSpecifier, Source, Sources, ToolUvSources};
+use uv_workspace::pyproject::Source;
use uv_workspace::{DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace};
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
@@ -368,7 +364,7 @@ pub(super) async fn do_sync(
}
// Populate credentials from the workspace.
- store_credentials_from_workspace(target.workspace());
+ store_credentials_from_workspace(target);
// Initialize the registry client.
let client = RegistryClientBuilder::new(cache.clone())
@@ -526,9 +522,9 @@ fn apply_editable_mode(resolution: Resolution, editable: EditableMode) -> Resolu
///
/// These credentials can come from any of `tool.uv.sources`, `tool.uv.dev-dependencies`,
/// `project.dependencies`, and `project.optional-dependencies`.
-fn store_credentials_from_workspace(workspace: &Workspace) {
- // Iterate over any sources in the workspace root.
- for source in workspace.sources().values().flat_map(Sources::iter) {
+fn store_credentials_from_workspace(target: InstallTarget<'_>) {
+ // Iterate over any sources in the target.
+ for source in target.sources() {
match source {
Source::Git { git, .. } => {
uv_git::store_credentials_from_url(git);
@@ -540,29 +536,8 @@ fn store_credentials_from_workspace(workspace: &Workspace) {
}
}
- // Iterate over any dependencies defined in the workspace root.
- for requirement in &workspace.requirements() {
- let Some(VersionOrUrl::Url(url)) = &requirement.version_or_url else {
- continue;
- };
- match &url.parsed_url {
- ParsedUrl::Git(ParsedGitUrl { url, .. }) => {
- uv_git::store_credentials_from_url(url.repository());
- }
- ParsedUrl::Archive(ParsedArchiveUrl { url, .. }) => {
- uv_auth::store_credentials_from_url(url);
- }
- _ => {}
- }
- }
-
- // Iterate over any dependency groups defined in the workspace root.
- for requirement in workspace
- .dependency_groups()
- .ok()
- .iter()
- .flat_map(|groups| groups.values().flat_map(|group| group.iter()))
- {
+ // Iterate over any dependencies defined in the target.
+ for requirement in target.requirements() {
let Some(VersionOrUrl::Url(url)) = &requirement.version_or_url else {
continue;
};
@@ -576,94 +551,4 @@ fn store_credentials_from_workspace(workspace: &Workspace) {
_ => {}
}
}
-
- // Iterate over each workspace member.
- for member in workspace.packages().values() {
- // Iterate over the `tool.uv.sources`.
- for source in member
- .pyproject_toml()
- .tool
- .as_ref()
- .and_then(|tool| tool.uv.as_ref())
- .and_then(|uv| uv.sources.as_ref())
- .map(ToolUvSources::inner)
- .iter()
- .flat_map(|sources| sources.values().flat_map(Sources::iter))
- {
- match source {
- Source::Git { git, .. } => {
- uv_git::store_credentials_from_url(git);
- }
- Source::Url { url, .. } => {
- uv_auth::store_credentials_from_url(url);
- }
- _ => {}
- }
- }
-
- // Iterate over all dependencies.
- let dependencies = member
- .pyproject_toml()
- .project
- .as_ref()
- .and_then(|project| project.dependencies.as_ref())
- .into_iter()
- .flatten();
- let optional_dependencies = member
- .pyproject_toml()
- .project
- .as_ref()
- .and_then(|project| project.optional_dependencies.as_ref())
- .into_iter()
- .flat_map(|optional| optional.values())
- .flatten();
- let dependency_groups = member
- .pyproject_toml()
- .dependency_groups
- .as_ref()
- .into_iter()
- .flatten()
- .flat_map(|(_, dependencies)| {
- dependencies.iter().filter_map(|specifier| {
- if let DependencyGroupSpecifier::Requirement(requirement) = specifier {
- Some(requirement)
- } else {
- None
- }
- })
- });
- let dev_dependencies = member
- .pyproject_toml()
- .tool
- .as_ref()
- .and_then(|tool| tool.uv.as_ref())
- .and_then(|uv| uv.dev_dependencies.as_ref())
- .into_iter()
- .flatten();
-
- for requirement in dependencies
- .chain(optional_dependencies)
- .chain(dependency_groups)
- .filter_map(|requires_dist| {
- LenientRequirement::::from_str(requires_dist)
- .map(Requirement::from)
- .map(Cow::Owned)
- .ok()
- })
- .chain(dev_dependencies.map(Cow::Borrowed))
- {
- let Some(VersionOrUrl::Url(url)) = &requirement.version_or_url else {
- continue;
- };
- match &url.parsed_url {
- ParsedUrl::Git(ParsedGitUrl { url, .. }) => {
- uv_git::store_credentials_from_url(url.repository());
- }
- ParsedUrl::Archive(ParsedArchiveUrl { url, .. }) => {
- uv_auth::store_credentials_from_url(url);
- }
- _ => {}
- }
- }
- }
}
diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs
index bdeb735ece4f..79a7a30dab3e 100644
--- a/crates/uv/src/commands/tool/run.rs
+++ b/crates/uv/src/commands/tool/run.rs
@@ -620,7 +620,7 @@ async fn get_or_create_environment(
// TODO(zanieb): When implementing project-level tools, discover the project and check if it has the tool.
// TODO(zanieb): Determine if we should layer on top of the project environment if it is present.
- let result = CachedEnvironment::get_or_create(
+ let result = CachedEnvironment::from_spec(
spec.clone(),
&interpreter,
settings,
@@ -679,7 +679,7 @@ async fn get_or_create_environment(
interpreter.sys_executable().display()
);
- CachedEnvironment::get_or_create(
+ CachedEnvironment::from_spec(
spec,
&interpreter,
settings,
diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs
index b330ba9cac62..fd506a7a2853 100644
--- a/crates/uv/tests/it/run.rs
+++ b/crates/uv/tests/it/run.rs
@@ -324,6 +324,9 @@ fn run_pep723_script() -> Result<()> {
Resolved 1 package in [TIME]
"###);
+ // But neither invocation should create a lockfile.
+ assert!(!context.temp_dir.child("main.py.lock").exists());
+
// Otherwise, the script requirements should _not_ be available, but the project requirements
// should.
let test_non_script = context.temp_dir.child("main.py");
@@ -774,6 +777,190 @@ fn run_pep723_script_overrides() -> Result<()> {
Ok(())
}
+/// Run a PEP 723-compatible script with a lockfile.
+#[test]
+fn run_pep723_script_lock() -> Result<()> {
+ let context = TestContext::new("3.12");
+
+ let test_script = context.temp_dir.child("main.py");
+ test_script.write_str(indoc! { r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "iniconfig",
+ # ]
+ # ///
+
+ import iniconfig
+
+ print("Hello, world!")
+ "#
+ })?;
+
+ // Explicitly lock the script.
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg("main.py"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 1 package in [TIME]
+ "###);
+
+ let lock = context.read("main.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [{ name = "iniconfig" }]
+
+ [[package]]
+ name = "iniconfig"
+ version = "2.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+ ]
+ "###
+ );
+ });
+
+ uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+ Hello, world!
+
+ ----- stderr -----
+ Reading inline script metadata from `main.py`
+ Resolved 1 package in [TIME]
+ Prepared 1 package in [TIME]
+ Installed 1 package in [TIME]
+ + iniconfig==2.0.0
+ "###);
+
+ // Modify the metadata.
+ test_script.write_str(indoc! { r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "anyio",
+ # ]
+ # ///
+
+ import anyio
+
+ print("Hello, world!")
+ "#
+ })?;
+
+ // Re-running the script with `--locked` should error.
+ uv_snapshot!(context.filters(), context.run().arg("--locked").arg("main.py"), @r###"
+ success: false
+ exit_code: 2
+ ----- stdout -----
+
+ ----- stderr -----
+ Reading inline script metadata from `main.py`
+ Resolved 3 packages in [TIME]
+ error: The lockfile at `uv.lock` needs to be updated, but `--locked` was provided. To update the lockfile, run `uv lock`.
+ "###);
+
+ // Re-running the script with `--frozen` should also error, but at runtime.
+ uv_snapshot!(context.filters(), context.run().arg("--frozen").arg("main.py"), @r###"
+ success: false
+ exit_code: 1
+ ----- stdout -----
+
+ ----- stderr -----
+ Reading inline script metadata from `main.py`
+ warning: `--frozen` is a no-op for Python scripts with inline metadata, which always run in isolation
+ Traceback (most recent call last):
+ File "[TEMP_DIR]/main.py", line 8, in
+ import anyio
+ ModuleNotFoundError: No module named 'anyio'
+ "###);
+
+ // Re-running the script should update the lockfile.
+ uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+ Hello, world!
+
+ ----- stderr -----
+ Reading inline script metadata from `main.py`
+ Resolved 3 packages in [TIME]
+ Prepared 3 packages in [TIME]
+ Installed 3 packages in [TIME]
+ + anyio==4.3.0
+ + idna==3.6
+ + sniffio==1.3.1
+ "###);
+
+ let lock = context.read("main.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [{ name = "anyio" }]
+
+ [[package]]
+ name = "anyio"
+ version = "4.3.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "idna" },
+ { name = "sniffio" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", size = 85584 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "sniffio"
+ version = "1.3.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+ ]
+ "###
+ );
+ });
+
+ Ok(())
+}
+
/// With `managed = false`, we should avoid installing the project itself.
#[test]
fn run_managed_false() -> Result<()> {
From 31b2d3f9885071a5d6ca1379aee2dbb9bdad9a6a Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 16:19:12 -0500
Subject: [PATCH 074/135] Update PEP 723 lockfile in `uv add --script` (#10145)
## Summary
`uv add --script main.py anyio` will now update the lockfile, _if_ it
already exists. (If no such lockfile exists, the behavior is unchanged.)
---
crates/uv-cli/src/lib.rs | 7 +-
crates/uv-scripts/src/lib.rs | 6 +-
crates/uv/src/commands/project/add.rs | 293 ++++++++-----
crates/uv/src/commands/project/lock_target.rs | 4 +-
crates/uv/src/commands/project/mod.rs | 3 +
crates/uv/src/commands/project/remove.rs | 2 +-
crates/uv/tests/it/edit.rs | 391 ++++++++++++++++++
7 files changed, 585 insertions(+), 121 deletions(-)
diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs
index c798bec075d7..817d35fdfec6 100644
--- a/crates/uv-cli/src/lib.rs
+++ b/crates/uv-cli/src/lib.rs
@@ -3257,7 +3257,12 @@ pub struct AddArgs {
/// a new one will be created and added to the script. When executed via `uv run`,
/// uv will create a temporary environment for the script with all inline
/// dependencies installed.
- #[arg(long, conflicts_with = "dev", conflicts_with = "optional")]
+ #[arg(
+ long,
+ conflicts_with = "dev",
+ conflicts_with = "optional",
+ conflicts_with = "package"
+ )]
pub script: Option,
/// The Python interpreter to use for resolving and syncing.
diff --git a/crates/uv-scripts/src/lib.rs b/crates/uv-scripts/src/lib.rs
index 2f812622747b..a17398e278a0 100644
--- a/crates/uv-scripts/src/lib.rs
+++ b/crates/uv-scripts/src/lib.rs
@@ -230,7 +230,7 @@ impl Pep723Script {
}
/// Replace the existing metadata in the file with new metadata and write the updated content.
- pub async fn write(&self, metadata: &str) -> Result<(), Pep723Error> {
+ pub fn write(&self, metadata: &str) -> Result<(), io::Error> {
let content = format!(
"{}{}{}",
self.prelude,
@@ -238,7 +238,7 @@ impl Pep723Script {
self.postlude
);
- fs_err::tokio::write(&self.path, content).await?;
+ fs_err::write(&self.path, content)?;
Ok(())
}
@@ -307,7 +307,7 @@ impl Pep723Metadata {
}
impl FromStr for Pep723Metadata {
- type Err = Pep723Error;
+ type Err = toml::de::Error;
/// Parse `Pep723Metadata` from a raw TOML string.
fn from_str(raw: &str) -> Result {
diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs
index 017ebd5c8043..99378e28074c 100644
--- a/crates/uv/src/commands/project/add.rs
+++ b/crates/uv/src/commands/project/add.rs
@@ -1,12 +1,14 @@
-use anyhow::{bail, Context, Result};
-use itertools::Itertools;
-use owo_colors::OwoColorize;
-use rustc_hash::{FxBuildHasher, FxHashMap};
use std::collections::hash_map::Entry;
use std::fmt::Write;
use std::io;
use std::path::{Path, PathBuf};
+use std::str::FromStr;
use std::sync::Arc;
+
+use anyhow::{bail, Context, Result};
+use itertools::Itertools;
+use owo_colors::OwoColorize;
+use rustc_hash::{FxBuildHasher, FxHashMap};
use tracing::debug;
use url::Url;
@@ -29,7 +31,7 @@ use uv_pypi_types::{redact_credentials, ParsedUrl, RequirementSource, VerbatimPa
use uv_python::{Interpreter, PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest};
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
use uv_resolver::FlatIndex;
-use uv_scripts::{Pep723ItemRef, Pep723Script};
+use uv_scripts::{Pep723ItemRef, Pep723Metadata, Pep723Script};
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user_once;
@@ -173,7 +175,7 @@ pub(crate) async fn add(
.await?
.into_interpreter();
- Target::Script(script, Box::new(interpreter))
+ AddTarget::Script(script, Box::new(interpreter))
} else {
// Find the project in the workspace.
let project = if let Some(package) = package {
@@ -221,7 +223,7 @@ pub(crate) async fn add(
.await?
.into_interpreter();
- Target::Project(project, Box::new(PythonTarget::Interpreter(interpreter)))
+ AddTarget::Project(project, Box::new(PythonTarget::Interpreter(interpreter)))
} else {
// Discover or create the virtual environment.
let venv = project::get_or_init_environment(
@@ -239,7 +241,7 @@ pub(crate) async fn add(
)
.await?;
- Target::Project(project, Box::new(PythonTarget::Environment(venv)))
+ AddTarget::Project(project, Box::new(PythonTarget::Environment(venv)))
}
};
@@ -361,7 +363,7 @@ pub(crate) async fn add(
// If any of the requirements are self-dependencies, bail.
if matches!(dependency_type, DependencyType::Production) {
- if let Target::Project(project, _) = &target {
+ if let AddTarget::Project(project, _) = &target {
if let Some(project_name) = project.project_name() {
for requirement in &requirements {
if requirement.name == *project_name {
@@ -389,10 +391,10 @@ pub(crate) async fn add(
// Add the requirements to the `pyproject.toml` or script.
let mut toml = match &target {
- Target::Script(script, _) => {
+ AddTarget::Script(script, _) => {
PyProjectTomlMut::from_toml(&script.metadata.raw, DependencyTarget::Script)
}
- Target::Project(project, _) => PyProjectTomlMut::from_toml(
+ AddTarget::Project(project, _) => PyProjectTomlMut::from_toml(
&project.pyproject_toml().raw,
DependencyTarget::PyProjectToml,
),
@@ -405,10 +407,10 @@ pub(crate) async fn add(
requirement.extras.dedup();
let (requirement, source) = match target {
- Target::Script(_, _) | Target::Project(_, _) if raw_sources => {
+ AddTarget::Script(_, _) | AddTarget::Project(_, _) if raw_sources => {
(uv_pep508::Requirement::from(requirement), None)
}
- Target::Script(ref script, _) => {
+ AddTarget::Script(ref script, _) => {
let script_path = std::path::absolute(&script.path)?;
let script_dir = script_path.parent().expect("script path has no parent");
resolve_requirement(
@@ -422,7 +424,7 @@ pub(crate) async fn add(
script_dir,
)?
}
- Target::Project(ref project, _) => {
+ AddTarget::Project(ref project, _) => {
let workspace = project
.workspace()
.packages()
@@ -566,33 +568,17 @@ pub(crate) async fn add(
let content = toml.to_string();
// Save the modified `pyproject.toml` or script.
- let modified = match &target {
- Target::Script(script, _) => {
- if content == script.metadata.raw {
- debug!("No changes to dependencies; skipping update");
- false
- } else {
- script.write(&content).await?;
- true
- }
- }
- Target::Project(project, _) => {
- if content == *project.pyproject_toml().raw {
- debug!("No changes to dependencies; skipping update");
- false
- } else {
- let pyproject_path = project.root().join("pyproject.toml");
- fs_err::write(pyproject_path, &content)?;
- true
- }
- }
- };
+ let modified = target.write(&content)?;
- let (project, environment) = match target {
- Target::Project(project, environment) => (project, environment),
- // If `--script`, exit early. There's no reason to lock and sync.
- Target::Script(script, _) => {
- // TODO(charlie): Lock the script, if a lockfile already exists.
+ // If `--frozen`, exit early. There's no reason to lock and sync, since we don't need a `uv.lock`
+ // to exist at all.
+ if frozen {
+ return Ok(ExitStatus::Success);
+ }
+
+ // If we're modifying a script, and lockfile doesn't exist, don't create it.
+ if let AddTarget::Script(ref script, _) = target {
+ if !LockTarget::from(script).lock_path().is_file() {
writeln!(
printer.stderr(),
"Updated `{}`",
@@ -600,39 +586,20 @@ pub(crate) async fn add(
)?;
return Ok(ExitStatus::Success);
}
- };
-
- // If `--frozen`, exit early. There's no reason to lock and sync, and we don't need a `uv.lock`
- // to exist at all.
- if frozen {
- return Ok(ExitStatus::Success);
}
// Store the content prior to any modifications.
- let project_root = project.root().to_path_buf();
- let workspace_root = project.workspace().install_path().clone();
- let existing_pyproject_toml = project.pyproject_toml().as_ref().to_vec();
- let existing_uv_lock = LockTarget::from(project.workspace()).read_bytes().await?;
+ let snapshot = target.snapshot().await?;
// Update the `pypackage.toml` in-memory.
- let project = project
- .with_pyproject_toml(toml::from_str(&content).map_err(ProjectError::PyprojectTomlParse)?)
- .ok_or(ProjectError::PyprojectTomlUpdate)?;
+ let target = target.update(&content)?;
// Set the Ctrl-C handler to revert changes on exit.
let _ = ctrlc::set_handler({
- let project_root = project_root.clone();
- let workspace_root = workspace_root.clone();
- let existing_pyproject_toml = existing_pyproject_toml.clone();
- let existing_uv_lock = existing_uv_lock.clone();
+ let snapshot = snapshot.clone();
move || {
if modified {
- let _ = revert(
- &project_root,
- &workspace_root,
- &existing_pyproject_toml,
- existing_uv_lock.as_deref(),
- );
+ let _ = snapshot.revert();
}
#[allow(clippy::exit, clippy::cast_possible_wrap)]
@@ -645,10 +612,9 @@ pub(crate) async fn add(
});
match lock_and_sync(
- project,
+ target,
&mut toml,
&edits,
- &environment,
state,
locked,
&dependency_type,
@@ -668,12 +634,7 @@ pub(crate) async fn add(
Ok(()) => Ok(ExitStatus::Success),
Err(err) => {
if modified {
- let _ = revert(
- &project_root,
- &workspace_root,
- &existing_pyproject_toml,
- existing_uv_lock.as_deref(),
- );
+ let _ = snapshot.revert();
}
match err {
ProjectError::Operation(err) => diagnostics::OperationDiagnostic::with_hint(format!("If you want to add the package regardless of the failed resolution, provide the `{}` flag to skip locking and syncing.", "--frozen".green()))
@@ -688,10 +649,9 @@ pub(crate) async fn add(
/// Re-lock and re-sync the project after a series of edits.
#[allow(clippy::fn_params_excessive_bools)]
async fn lock_and_sync(
- mut project: VirtualProject,
+ mut target: AddTarget,
toml: &mut PyProjectTomlMut,
edits: &[DependencyEdit],
- environment: &PythonTarget,
state: SharedState,
locked: bool,
dependency_type: &DependencyType,
@@ -706,15 +666,13 @@ async fn lock_and_sync(
printer: Printer,
preview: PreviewMode,
) -> Result<(), ProjectError> {
- let mode = if locked {
- LockMode::Locked(environment.interpreter())
- } else {
- LockMode::Write(environment.interpreter())
- };
-
let mut lock = project::lock::do_safe_lock(
- mode,
- project.workspace().into(),
+ if locked {
+ LockMode::Locked(target.interpreter())
+ } else {
+ LockMode::Write(target.interpreter())
+ },
+ (&target).into(),
settings.into(),
LowerBound::default(),
&state,
@@ -811,17 +769,13 @@ async fn lock_and_sync(
let content = toml.to_string();
// Write the updated `pyproject.toml` to disk.
- fs_err::write(project.root().join("pyproject.toml"), &content)?;
+ target.write(&content)?;
// Update the `pypackage.toml` in-memory.
- project = project
- .with_pyproject_toml(
- toml::from_str(&content).map_err(ProjectError::PyprojectTomlParse)?,
- )
- .ok_or(ProjectError::PyprojectTomlUpdate)?;
+ target = target.update(&content)?;
// Invalidate the project metadata.
- if let VirtualProject::Project(ref project) = project {
+ if let AddTarget::Project(VirtualProject::Project(ref project), _) = target {
let url = Url::from_file_path(project.project_root())
.expect("project root is a valid URL");
let version_id = VersionId::from_url(&url);
@@ -832,8 +786,12 @@ async fn lock_and_sync(
// If the file was modified, we have to lock again, though the only expected change is
// the addition of the minimum version specifiers.
lock = project::lock::do_safe_lock(
- mode,
- project.workspace().into(),
+ if locked {
+ LockMode::Locked(target.interpreter())
+ } else {
+ LockMode::Write(target.interpreter())
+ },
+ (&target).into(),
settings.into(),
LowerBound::default(),
&state,
@@ -851,7 +809,12 @@ async fn lock_and_sync(
}
}
- let PythonTarget::Environment(venv) = environment else {
+ let AddTarget::Project(project, environment) = target else {
+ // If we're not adding to a project, exit early.
+ return Ok(());
+ };
+
+ let PythonTarget::Environment(venv) = &*environment else {
// If we're not syncing, exit early.
return Ok(());
};
@@ -918,25 +881,6 @@ async fn lock_and_sync(
Ok(())
}
-/// Revert the changes to the `pyproject.toml` and `uv.lock`, if necessary.
-fn revert(
- project_root: &Path,
- workspace_root: &Path,
- pyproject_toml: &[u8],
- uv_lock: Option<&[u8]>,
-) -> Result<(), io::Error> {
- debug!("Reverting changes to `pyproject.toml`");
- let () = fs_err::write(project_root.join("pyproject.toml"), pyproject_toml)?;
- if let Some(uv_lock) = uv_lock.as_ref() {
- debug!("Reverting changes to `uv.lock`");
- let () = fs_err::write(workspace_root.join("uv.lock"), uv_lock)?;
- } else {
- debug!("Removing `uv.lock`");
- let () = fs_err::remove_file(workspace_root.join("uv.lock"))?;
- }
- Ok(())
-}
-
/// Augment a user-provided requirement by attaching any specification data that was provided
/// separately from the requirement itself (e.g., `--branch main`).
fn augment_requirement(
@@ -1047,8 +991,8 @@ fn resolve_requirement(
}
/// Represents the destination where dependencies are added, either to a project or a script.
-#[derive(Debug)]
-enum Target {
+#[derive(Debug, Clone)]
+enum AddTarget {
/// A PEP 723 script, with inline metadata.
Script(Pep723Script, Box),
@@ -1056,7 +1000,16 @@ enum Target {
Project(VirtualProject, Box),
}
-impl Target {
+impl<'lock> From<&'lock AddTarget> for LockTarget<'lock> {
+ fn from(value: &'lock AddTarget) -> Self {
+ match value {
+ AddTarget::Script(script, _) => LockTarget::Script(script),
+ AddTarget::Project(project, _) => LockTarget::Workspace(project.workspace()),
+ }
+ }
+}
+
+impl AddTarget {
/// Returns the [`Interpreter`] for the target.
fn interpreter(&self) -> &Interpreter {
match self {
@@ -1064,10 +1017,122 @@ impl Target {
Self::Project(_, venv) => venv.interpreter(),
}
}
+
+ /// Write the updated content to the target.
+ ///
+ /// Returns `true` if the content was modified.
+ fn write(&self, content: &str) -> Result {
+ match self {
+ Self::Script(script, _) => {
+ if content == script.metadata.raw {
+ debug!("No changes to dependencies; skipping update");
+ Ok(false)
+ } else {
+ script.write(content)?;
+ Ok(true)
+ }
+ }
+ Self::Project(project, _) => {
+ if content == project.pyproject_toml().raw {
+ debug!("No changes to dependencies; skipping update");
+ Ok(false)
+ } else {
+ let pyproject_path = project.root().join("pyproject.toml");
+ fs_err::write(pyproject_path, content)?;
+ Ok(true)
+ }
+ }
+ }
+ }
+
+ /// Update the target in-memory to incorporate the new content.
+ #[allow(clippy::result_large_err)]
+ fn update(self, content: &str) -> Result {
+ match self {
+ Self::Script(mut script, interpreter) => {
+ script.metadata = Pep723Metadata::from_str(content)
+ .map_err(ProjectError::Pep723ScriptTomlParse)?;
+ Ok(Self::Script(script, interpreter))
+ }
+ Self::Project(project, venv) => {
+ let project = project
+ .with_pyproject_toml(
+ toml::from_str(content).map_err(ProjectError::PyprojectTomlParse)?,
+ )
+ .ok_or(ProjectError::PyprojectTomlUpdate)?;
+ Ok(Self::Project(project, venv))
+ }
+ }
+ }
+
+ /// Take a snapshot of the target.
+ async fn snapshot(&self) -> Result {
+ // Read the lockfile into memory.
+ let target = match self {
+ Self::Script(script, _) => LockTarget::from(script),
+ Self::Project(project, _) => LockTarget::Workspace(project.workspace()),
+ };
+ let lock = target.read_bytes().await?;
+
+ // Clone the target.
+ match self {
+ Self::Script(script, _) => Ok(AddTargetSnapshot::Script(script.clone(), lock)),
+ Self::Project(project, _) => Ok(AddTargetSnapshot::Project(project.clone(), lock)),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+enum AddTargetSnapshot {
+ Script(Pep723Script, Option>),
+ Project(VirtualProject, Option>),
+}
+
+impl AddTargetSnapshot {
+ /// Write the snapshot back to disk (e.g., to a `pyproject.toml` and `uv.lock`).
+ fn revert(&self) -> Result<(), io::Error> {
+ match self {
+ Self::Script(script, lock) => {
+ // Write the PEP 723 script back to disk.
+ debug!("Reverting changes to PEP 723 script block");
+ script.write(&script.metadata.raw)?;
+
+ // Write the lockfile back to disk.
+ let target = LockTarget::from(script);
+ if let Some(lock) = lock {
+ debug!("Reverting changes to `uv.lock`");
+ fs_err::write(target.lock_path(), lock)?;
+ } else {
+ debug!("Removing `uv.lock`");
+ fs_err::remove_file(target.lock_path())?;
+ }
+ Ok(())
+ }
+ Self::Project(project, lock) => {
+ // Write the `pyproject.toml` back to disk.
+ debug!("Reverting changes to `pyproject.toml`");
+ fs_err::write(
+ project.root().join("pyproject.toml"),
+ project.pyproject_toml().as_ref(),
+ )?;
+
+ // Write the lockfile back to disk.
+ let target = LockTarget::from(project.workspace());
+ if let Some(lock) = lock {
+ debug!("Reverting changes to `uv.lock`");
+ fs_err::write(target.lock_path(), lock)?;
+ } else {
+ debug!("Removing `uv.lock`");
+ fs_err::remove_file(target.lock_path())?;
+ }
+ Ok(())
+ }
+ }
+ }
}
/// A Python [`Interpreter`] or [`PythonEnvironment`] for a project.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
enum PythonTarget {
Interpreter(Interpreter),
diff --git a/crates/uv/src/commands/project/lock_target.rs b/crates/uv/src/commands/project/lock_target.rs
index 2d2cc0197adb..ba3e9727b88c 100644
--- a/crates/uv/src/commands/project/lock_target.rs
+++ b/crates/uv/src/commands/project/lock_target.rs
@@ -235,11 +235,11 @@ impl<'lock> LockTarget<'lock> {
}
/// Read the lockfile from the workspace as bytes.
- pub(crate) async fn read_bytes(self) -> Result>, ProjectError> {
+ pub(crate) async fn read_bytes(self) -> Result>, std::io::Error> {
match fs_err::tokio::read(self.lock_path()).await {
Ok(encoded) => Ok(Some(encoded)),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
- Err(err) => Err(err.into()),
+ Err(err) => Err(err),
}
}
diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs
index 1f85ec11a1d4..57cf4d9c2fab 100644
--- a/crates/uv/src/commands/project/mod.rs
+++ b/crates/uv/src/commands/project/mod.rs
@@ -167,6 +167,9 @@ pub(crate) enum ProjectError {
#[error("Failed to update `pyproject.toml`")]
PyprojectTomlUpdate,
+ #[error("Failed to parse PEP 723 script metadata")]
+ Pep723ScriptTomlParse(#[source] toml::de::Error),
+
#[error(transparent)]
DependencyGroup(#[from] DependencyGroupError),
diff --git a/crates/uv/src/commands/project/remove.rs b/crates/uv/src/commands/project/remove.rs
index 5a669b74c3cf..415115aa358a 100644
--- a/crates/uv/src/commands/project/remove.rs
+++ b/crates/uv/src/commands/project/remove.rs
@@ -164,7 +164,7 @@ pub(crate) async fn remove(
// Save the modified dependencies.
match &target {
Target::Script(script) => {
- script.write(&toml.to_string()).await?;
+ script.write(&toml.to_string())?;
}
Target::Project(project) => {
let pyproject_path = project.root().join("pyproject.toml");
diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs
index aabfa2435ffb..fc5942223606 100644
--- a/crates/uv/tests/it/edit.rs
+++ b/crates/uv/tests/it/edit.rs
@@ -4929,6 +4929,10 @@ fn add_script() -> Result<()> {
"###
);
});
+
+ // Adding to a script without a lockfile shouldn't create a lockfile.
+ assert!(!context.temp_dir.join("script.py.lock").exists());
+
Ok(())
}
@@ -5330,6 +5334,393 @@ fn remove_repeated() -> Result<()> {
Ok(())
}
+/// Add to a PEP 732 script with a lockfile.
+#[test]
+fn add_script_lock() -> Result<()> {
+ let context = TestContext::new("3.12");
+
+ let script = context.temp_dir.child("script.py");
+ script.write_str(indoc! {r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "requests<3",
+ # "rich",
+ # ]
+ # ///
+
+ import requests
+ from rich.pretty import pprint
+
+ resp = requests.get("https://peps.python.org/api/peps.json")
+ data = resp.json()
+ pprint([(k, v["title"]) for k, v in data.items()][:10])
+ "#})?;
+
+ // Explicitly lock the script.
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 9 packages in [TIME]
+ "###);
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [
+ { name = "requests", specifier = "<3" },
+ { name = "rich" },
+ ]
+
+ [[package]]
+ name = "certifi"
+ version = "2024.2.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/71/da/e94e26401b62acd6d91df2b52954aceb7f561743aa5ccc32152886c76c96/certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", size = 164886 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1", size = 163774 },
+ ]
+
+ [[package]]
+ name = "charset-normalizer"
+ version = "3.3.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
+ { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
+ { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
+ { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
+ { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
+ { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
+ { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
+ { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
+ { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
+ { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
+ { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
+ { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
+ { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
+ { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
+ { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
+ { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
+ { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
+ { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
+ { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
+ { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
+ { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
+ { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
+ { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
+ { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
+ { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
+ { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
+ { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
+ { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
+ { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
+ { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
+ { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "markdown-it-py"
+ version = "3.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "mdurl" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
+ ]
+
+ [[package]]
+ name = "mdurl"
+ version = "0.1.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
+ ]
+
+ [[package]]
+ name = "pygments"
+ version = "2.17.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 },
+ ]
+
+ [[package]]
+ name = "requests"
+ version = "2.31.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
+ ]
+
+ [[package]]
+ name = "rich"
+ version = "13.7.1"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 },
+ ]
+
+ [[package]]
+ name = "urllib3"
+ version = "2.2.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", size = 291020 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 },
+ ]
+ "###
+ );
+ });
+
+ // Adding to a locked script should update the lockfile.
+ uv_snapshot!(context.filters(), context.add().arg("anyio").arg("--script").arg("script.py"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 11 packages in [TIME]
+ "###);
+
+ let script_content = context.read("script.py");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ script_content, @r###"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "anyio>=4.3.0",
+ # "requests<3",
+ # "rich",
+ # ]
+ # ///
+
+ import requests
+ from rich.pretty import pprint
+
+ resp = requests.get("https://peps.python.org/api/peps.json")
+ data = resp.json()
+ pprint([(k, v["title"]) for k, v in data.items()][:10])
+ "###
+ );
+ });
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [
+ { name = "anyio", specifier = ">=4.3.0" },
+ { name = "requests", specifier = "<3" },
+ { name = "rich" },
+ ]
+
+ [[package]]
+ name = "anyio"
+ version = "4.3.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "idna" },
+ { name = "sniffio" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", size = 85584 },
+ ]
+
+ [[package]]
+ name = "certifi"
+ version = "2024.2.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/71/da/e94e26401b62acd6d91df2b52954aceb7f561743aa5ccc32152886c76c96/certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", size = 164886 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1", size = 163774 },
+ ]
+
+ [[package]]
+ name = "charset-normalizer"
+ version = "3.3.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
+ { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
+ { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
+ { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
+ { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
+ { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
+ { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
+ { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
+ { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
+ { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
+ { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
+ { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
+ { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
+ { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
+ { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
+ { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
+ { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
+ { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
+ { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
+ { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
+ { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
+ { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
+ { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
+ { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
+ { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
+ { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
+ { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
+ { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
+ { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
+ { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
+ { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "markdown-it-py"
+ version = "3.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "mdurl" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
+ ]
+
+ [[package]]
+ name = "mdurl"
+ version = "0.1.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
+ ]
+
+ [[package]]
+ name = "pygments"
+ version = "2.17.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 },
+ ]
+
+ [[package]]
+ name = "requests"
+ version = "2.31.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
+ ]
+
+ [[package]]
+ name = "rich"
+ version = "13.7.1"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 },
+ ]
+
+ [[package]]
+ name = "sniffio"
+ version = "1.3.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+ ]
+
+ [[package]]
+ name = "urllib3"
+ version = "2.2.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", size = 291020 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 },
+ ]
+ "###
+ );
+ });
+
+ Ok(())
+}
+
/// Remove from a PEP732 script,
#[test]
fn remove_script() -> Result<()> {
From 9d5779b68c88dd67aeafbbf37feb951e86bffdd9 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 16:32:46 -0500
Subject: [PATCH 075/135] Add `--script` support to `uv tree` for PEP 723
scripts (#10159)
## Summary
You can now run `uv tree --script main.py` to show the dependency tree
for a given script. If a lockfile doesn't exist, it will create one.
Closes https://github.com/astral-sh/uv/issues/7328.
---
crates/uv-cli/src/lib.rs | 8 +
crates/uv/src/commands/project/mod.rs | 8 +
crates/uv/src/commands/project/tree.rs | 54 +++-
crates/uv/src/lib.rs | 18 +-
crates/uv/src/settings.rs | 4 +
crates/uv/tests/it/tree.rs | 400 ++++++++++++++++++++++++-
docs/reference/cli.md | 4 +
7 files changed, 483 insertions(+), 13 deletions(-)
diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs
index 817d35fdfec6..1ec02fa391d5 100644
--- a/crates/uv-cli/src/lib.rs
+++ b/crates/uv-cli/src/lib.rs
@@ -3437,6 +3437,14 @@ pub struct TreeArgs {
#[command(flatten)]
pub resolver: ResolverArgs,
+ /// Show the dependency tree the specified PEP 723 Python script, rather than the current
+ /// project.
+ ///
+ /// If provided, uv will resolve the dependencies based on its inline metadata table, in
+ /// adherence with PEP 723.
+ #[arg(long)]
+ pub script: Option,
+
/// The Python version to use when filtering the tree.
///
/// For example, pass `--python-version 3.10` to display the dependencies
diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs
index 57cf4d9c2fab..4f3c1416efcb 100644
--- a/crates/uv/src/commands/project/mod.rs
+++ b/crates/uv/src/commands/project/mod.rs
@@ -143,6 +143,9 @@ pub(crate) enum ProjectError {
#[error("Group `{0}` is not defined in any project's `dependency-group` table")]
MissingGroupWorkspace(GroupName),
+ #[error("PEP 723 scripts do not support dependency groups, but group `{0}` was specified")]
+ MissingGroupScript(GroupName),
+
#[error("Default group `{0}` (from `tool.uv.default-groups`) is not defined in the project's `dependency-group` table")]
MissingDefaultGroup(GroupName),
@@ -1730,6 +1733,8 @@ pub(crate) enum DependencyGroupsTarget<'env> {
Workspace(&'env Workspace),
/// The dependency groups must be defined in the target project.
Project(&'env ProjectWorkspace),
+ /// The dependency groups must be defined in the target script.
+ Script,
}
impl DependencyGroupsTarget<'_> {
@@ -1760,6 +1765,9 @@ impl DependencyGroupsTarget<'_> {
return Err(ProjectError::MissingGroupProject(group.clone()));
}
}
+ Self::Script => {
+ return Err(ProjectError::MissingGroupScript(group.clone()));
+ }
}
}
Ok(())
diff --git a/crates/uv/src/commands/project/tree.rs b/crates/uv/src/commands/project/tree.rs
index 1994862fa7fb..1c612cceabfe 100644
--- a/crates/uv/src/commands/project/tree.rs
+++ b/crates/uv/src/commands/project/tree.rs
@@ -15,6 +15,7 @@ use uv_distribution_types::IndexCapabilities;
use uv_pep508::PackageName;
use uv_python::{PythonDownloads, PythonPreference, PythonRequest, PythonVersion};
use uv_resolver::{PackageMap, TreeDisplay};
+use uv_scripts::{Pep723ItemRef, Pep723Script};
use uv_settings::PythonInstallMirrors;
use uv_workspace::{DiscoveryOptions, Workspace};
@@ -22,8 +23,10 @@ use crate::commands::pip::latest::LatestClient;
use crate::commands::pip::loggers::DefaultResolveLogger;
use crate::commands::pip::resolution_markers;
use crate::commands::project::lock::{do_safe_lock, LockMode};
+use crate::commands::project::lock_target::LockTarget;
use crate::commands::project::{
default_dependency_groups, DependencyGroupsTarget, ProjectError, ProjectInterpreter,
+ ScriptInterpreter,
};
use crate::commands::reporters::LatestVersionReporter;
use crate::commands::{diagnostics, ExitStatus};
@@ -49,6 +52,7 @@ pub(crate) async fn tree(
python: Option,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
+ script: Option,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
connectivity: Connectivity,
@@ -61,24 +65,51 @@ pub(crate) async fn tree(
preview: PreviewMode,
) -> Result {
// Find the project requirements.
- let workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?;
+ let workspace;
+ let target = if let Some(script) = script.as_ref() {
+ LockTarget::Script(script)
+ } else {
+ workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?;
+ LockTarget::Workspace(&workspace)
+ };
- // Validate that any referenced dependency groups are defined in the workspace.
+ // Validate that any referenced dependency groups are defined in the target.
if !frozen {
- let target = DependencyGroupsTarget::Workspace(&workspace);
+ let target = match &target {
+ LockTarget::Workspace(workspace) => DependencyGroupsTarget::Workspace(workspace),
+ LockTarget::Script(..) => DependencyGroupsTarget::Script,
+ };
target.validate(&dev)?;
}
// Determine the default groups to include.
- let defaults = default_dependency_groups(workspace.pyproject_toml())?;
+ let defaults = match target {
+ LockTarget::Workspace(workspace) => default_dependency_groups(workspace.pyproject_toml())?,
+ LockTarget::Script(_) => vec![],
+ };
// Find an interpreter for the project, unless `--frozen` and `--universal` are both set.
let interpreter = if frozen && universal {
None
} else {
- Some(
- ProjectInterpreter::discover(
- &workspace,
+ Some(match target {
+ LockTarget::Script(script) => ScriptInterpreter::discover(
+ Pep723ItemRef::Script(script),
+ python.as_deref().map(PythonRequest::parse),
+ python_preference,
+ python_downloads,
+ connectivity,
+ native_tls,
+ allow_insecure_host,
+ &install_mirrors,
+ no_config,
+ cache,
+ printer,
+ )
+ .await?
+ .into_interpreter(),
+ LockTarget::Workspace(workspace) => ProjectInterpreter::discover(
+ workspace,
project_dir,
python.as_deref().map(PythonRequest::parse),
python_preference,
@@ -93,7 +124,7 @@ pub(crate) async fn tree(
)
.await?
.into_interpreter(),
- )
+ })
};
// Determine the lock mode.
@@ -101,6 +132,9 @@ pub(crate) async fn tree(
LockMode::Frozen
} else if locked {
LockMode::Locked(interpreter.as_ref().unwrap())
+ } else if matches!(target, LockTarget::Script(_)) && !target.lock_path().is_file() {
+ // If we're locking a script, avoid creating a lockfile if it doesn't already exist.
+ LockMode::DryRun(interpreter.as_ref().unwrap())
} else {
LockMode::Write(interpreter.as_ref().unwrap())
};
@@ -111,7 +145,7 @@ pub(crate) async fn tree(
// Update the lockfile, if necessary.
let lock = match do_safe_lock(
mode,
- (&workspace).into(),
+ target,
settings.as_ref(),
LowerBound::Allow,
&state,
@@ -151,7 +185,7 @@ pub(crate) async fn tree(
.packages()
.iter()
.filter_map(|package| {
- let index = match package.index(workspace.install_path()) {
+ let index = match package.index(target.install_path()) {
Ok(Some(index)) => index,
Ok(None) => return None,
Err(err) => return Some(Err(err)),
diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs
index 66ca098ffcc5..e51c2afb4b5b 100644
--- a/crates/uv/src/lib.rs
+++ b/crates/uv/src/lib.rs
@@ -191,6 +191,12 @@ async fn run(mut cli: Cli) -> Result {
script: Some(script),
..
}) = &**command
+ {
+ Pep723Script::read(&script).await?.map(Pep723Item::Script)
+ } else if let ProjectCommand::Tree(uv_cli::TreeArgs {
+ script: Some(script),
+ ..
+ }) = &**command
{
Pep723Script::read(&script).await?.map(Pep723Item::Script)
} else {
@@ -1652,7 +1658,14 @@ async fn run_project(
// Initialize the cache.
let cache = cache.init()?;
- commands::tree(
+ // Unwrap the script.
+ let script = script.map(|script| match script {
+ Pep723Item::Script(script) => script,
+ Pep723Item::Stdin(_) => unreachable!("`uv tree` does not support stdin"),
+ Pep723Item::Remote(_) => unreachable!("`uv tree` does not support remote files"),
+ });
+
+ Box::pin(commands::tree(
project_dir,
args.dev,
args.locked,
@@ -1669,6 +1682,7 @@ async fn run_project(
args.python,
args.install_mirrors,
args.resolver,
+ script,
globals.python_preference,
globals.python_downloads,
globals.connectivity,
@@ -1679,7 +1693,7 @@ async fn run_project(
&cache,
printer,
globals.preview,
- )
+ ))
.await
}
ProjectCommand::Export(args) => {
diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs
index e4d030ceb542..8d6da1557432 100644
--- a/crates/uv/src/settings.rs
+++ b/crates/uv/src/settings.rs
@@ -1293,6 +1293,8 @@ pub(crate) struct TreeSettings {
pub(crate) no_dedupe: bool,
pub(crate) invert: bool,
pub(crate) outdated: bool,
+ #[allow(dead_code)]
+ pub(crate) script: Option,
pub(crate) python_version: Option,
pub(crate) python_platform: Option,
pub(crate) python: Option,
@@ -1317,6 +1319,7 @@ impl TreeSettings {
frozen,
build,
resolver,
+ script,
python_version,
python_platform,
python,
@@ -1339,6 +1342,7 @@ impl TreeSettings {
no_dedupe: tree.no_dedupe,
invert: tree.invert,
outdated: tree.outdated,
+ script,
python_version,
python_platform,
python: python.and_then(Maybe::into_option),
diff --git a/crates/uv/tests/it/tree.rs b/crates/uv/tests/it/tree.rs
index 879fe3315569..7e71fc4e2b1e 100644
--- a/crates/uv/tests/it/tree.rs
+++ b/crates/uv/tests/it/tree.rs
@@ -1,7 +1,8 @@
use anyhow::Result;
use assert_cmd::assert::OutputAssertExt;
use assert_fs::prelude::*;
-use indoc::formatdoc;
+use indoc::{formatdoc, indoc};
+use insta::assert_snapshot;
use url::Url;
use crate::common::{uv_snapshot, TestContext};
@@ -1172,3 +1173,400 @@ fn non_project_member() -> Result<()> {
Ok(())
}
+
+#[test]
+fn script() -> Result<()> {
+ let context = TestContext::new("3.12");
+
+ let script = context.temp_dir.child("script.py");
+ script.write_str(indoc! {r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "requests<3",
+ # "rich",
+ # ]
+ # ///
+
+ import requests
+ from rich.pretty import pprint
+
+ resp = requests.get("https://peps.python.org/api/peps.json")
+ data = resp.json()
+ pprint([(k, v["title"]) for k, v in data.items()][:10])
+ "#})?;
+
+ uv_snapshot!(context.filters(), context.tree().arg("--script").arg(script.path()), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+ rich v13.7.1
+ ├── markdown-it-py v3.0.0
+ │ └── mdurl v0.1.2
+ └── pygments v2.17.2
+ requests v2.31.0
+ ├── certifi v2024.2.2
+ ├── charset-normalizer v3.3.2
+ ├── idna v3.6
+ └── urllib3 v2.2.1
+
+ ----- stderr -----
+ Resolved 9 packages in [TIME]
+ "###);
+
+ // If the lockfile didn't exist already, it shouldn't be persisted to disk.
+ assert!(!context.temp_dir.child("uv.lock").exists());
+
+ // Explicitly lock the script.
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg(script.path()), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 9 packages in [TIME]
+ "###);
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [
+ { name = "requests", specifier = "<3" },
+ { name = "rich" },
+ ]
+
+ [[package]]
+ name = "certifi"
+ version = "2024.2.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/71/da/e94e26401b62acd6d91df2b52954aceb7f561743aa5ccc32152886c76c96/certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", size = 164886 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1", size = 163774 },
+ ]
+
+ [[package]]
+ name = "charset-normalizer"
+ version = "3.3.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
+ { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
+ { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
+ { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
+ { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
+ { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
+ { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
+ { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
+ { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
+ { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
+ { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
+ { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
+ { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
+ { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
+ { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
+ { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
+ { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
+ { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
+ { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
+ { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
+ { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
+ { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
+ { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
+ { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
+ { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
+ { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
+ { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
+ { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
+ { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
+ { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
+ { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "markdown-it-py"
+ version = "3.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "mdurl" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
+ ]
+
+ [[package]]
+ name = "mdurl"
+ version = "0.1.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
+ ]
+
+ [[package]]
+ name = "pygments"
+ version = "2.17.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 },
+ ]
+
+ [[package]]
+ name = "requests"
+ version = "2.31.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
+ ]
+
+ [[package]]
+ name = "rich"
+ version = "13.7.1"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 },
+ ]
+
+ [[package]]
+ name = "urllib3"
+ version = "2.2.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", size = 291020 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 },
+ ]
+ "###
+ );
+ });
+
+ // Update the dependencies.
+ script.write_str(indoc! {r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "iniconfig",
+ # "requests<3",
+ # "rich",
+ # ]
+ # ///
+
+ import requests
+ from rich.pretty import pprint
+
+ resp = requests.get("https://peps.python.org/api/peps.json")
+ data = resp.json()
+ pprint([(k, v["title"]) for k, v in data.items()][:10])
+ "#})?;
+
+ // `uv tree` should update the lockfile.
+ uv_snapshot!(context.filters(), context.tree().arg("--script").arg(script.path()), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+ rich v13.7.1
+ ├── markdown-it-py v3.0.0
+ │ └── mdurl v0.1.2
+ └── pygments v2.17.2
+ requests v2.31.0
+ ├── certifi v2024.2.2
+ ├── charset-normalizer v3.3.2
+ ├── idna v3.6
+ └── urllib3 v2.2.1
+ iniconfig v2.0.0
+
+ ----- stderr -----
+ Resolved 10 packages in [TIME]
+ "###);
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [
+ { name = "iniconfig" },
+ { name = "requests", specifier = "<3" },
+ { name = "rich" },
+ ]
+
+ [[package]]
+ name = "certifi"
+ version = "2024.2.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/71/da/e94e26401b62acd6d91df2b52954aceb7f561743aa5ccc32152886c76c96/certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", size = 164886 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1", size = 163774 },
+ ]
+
+ [[package]]
+ name = "charset-normalizer"
+ version = "3.3.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
+ { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
+ { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
+ { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
+ { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
+ { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
+ { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
+ { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
+ { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
+ { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
+ { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
+ { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
+ { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
+ { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
+ { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
+ { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
+ { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
+ { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
+ { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
+ { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
+ { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
+ { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
+ { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
+ { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
+ { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
+ { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
+ { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
+ { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
+ { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
+ { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
+ { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "iniconfig"
+ version = "2.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+ ]
+
+ [[package]]
+ name = "markdown-it-py"
+ version = "3.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "mdurl" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
+ ]
+
+ [[package]]
+ name = "mdurl"
+ version = "0.1.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
+ ]
+
+ [[package]]
+ name = "pygments"
+ version = "2.17.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 },
+ ]
+
+ [[package]]
+ name = "requests"
+ version = "2.31.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
+ ]
+
+ [[package]]
+ name = "rich"
+ version = "13.7.1"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 },
+ ]
+
+ [[package]]
+ name = "urllib3"
+ version = "2.2.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", size = 291020 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 },
+ ]
+ "###
+ );
+ });
+
+ Ok(())
+}
diff --git a/docs/reference/cli.md b/docs/reference/cli.md
index b673ce64bc3b..e8efbbea0cbf 100644
--- a/docs/reference/cli.md
+++ b/docs/reference/cli.md
@@ -2959,6 +2959,10 @@ uv tree [OPTIONS]
lowest-direct
: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
+ --script
scriptShow the dependency tree the specified PEP 723 Python script, rather than the current project.
+
+If provided, uv will resolve the dependencies based on its inline metadata table, in adherence with PEP 723.
+
--universal
Show a platform-independent dependency tree.
Shows resolved package versions for all Python versions and platforms, rather than filtering to those that are relevant for the current environment.
From 18b53c5b45459d1bba135bfe035b1ed148a835cc Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 16:48:53 -0500
Subject: [PATCH 076/135] Add `--script` support to `uv export` for PEP 723
scripts (#10160)
## Summary
You can now run `uv export --script main.py` to show the dependency tree
for a given script. If a lockfile doesn't exist, it will create one.
Closes https://github.com/astral-sh/uv/issues/8609.
Closes https://github.com/astral-sh/uv/issues/9657.
---
crates/uv-cli/src/lib.rs | 8 +
crates/uv/src/commands/project/add.rs | 4 +-
crates/uv/src/commands/project/export.rs | 161 +++++++++-----
crates/uv/src/lib.rs | 14 ++
crates/uv/src/settings.rs | 3 +
crates/uv/tests/it/export.rs | 259 +++++++++++++++++++++++
docs/reference/cli.md | 4 +
7 files changed, 400 insertions(+), 53 deletions(-)
diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs
index 1ec02fa391d5..8c0dc6f8cee9 100644
--- a/crates/uv-cli/src/lib.rs
+++ b/crates/uv-cli/src/lib.rs
@@ -3651,6 +3651,14 @@ pub struct ExportArgs {
#[command(flatten)]
pub refresh: RefreshArgs,
+ /// Export the dependencies for the specified PEP 723 Python script, rather than the current
+ /// project.
+ ///
+ /// If provided, uv will resolve the dependencies based on its inline metadata table, in
+ /// adherence with PEP 723.
+ #[arg(long, conflicts_with_all = ["all_packages", "package", "no_emit_project", "no_emit_workspace"])]
+ pub script: Option,
+
/// The Python interpreter to use during resolution.
///
/// A Python interpreter is required for building source distributions to
diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs
index 99378e28074c..950e7470af52 100644
--- a/crates/uv/src/commands/project/add.rs
+++ b/crates/uv/src/commands/project/add.rs
@@ -1003,8 +1003,8 @@ enum AddTarget {
impl<'lock> From<&'lock AddTarget> for LockTarget<'lock> {
fn from(value: &'lock AddTarget) -> Self {
match value {
- AddTarget::Script(script, _) => LockTarget::Script(script),
- AddTarget::Project(project, _) => LockTarget::Workspace(project.workspace()),
+ AddTarget::Script(script, _) => Self::Script(script),
+ AddTarget::Project(project, _) => Self::Workspace(project.workspace()),
}
}
}
diff --git a/crates/uv/src/commands/project/export.rs b/crates/uv/src/commands/project/export.rs
index eb1d0ebe3bb8..64b70d466915 100644
--- a/crates/uv/src/commands/project/export.rs
+++ b/crates/uv/src/commands/project/export.rs
@@ -16,19 +16,39 @@ use uv_dispatch::SharedState;
use uv_normalize::PackageName;
use uv_python::{PythonDownloads, PythonPreference, PythonRequest};
use uv_resolver::RequirementsTxtExport;
+use uv_scripts::{Pep723ItemRef, Pep723Script};
use uv_workspace::{DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace};
use crate::commands::pip::loggers::DefaultResolveLogger;
use crate::commands::project::install_target::InstallTarget;
use crate::commands::project::lock::{do_safe_lock, LockMode};
+use crate::commands::project::lock_target::LockTarget;
use crate::commands::project::{
default_dependency_groups, detect_conflicts, DependencyGroupsTarget, ProjectError,
- ProjectInterpreter,
+ ProjectInterpreter, ScriptInterpreter,
};
use crate::commands::{diagnostics, ExitStatus, OutputWriter};
use crate::printer::Printer;
use crate::settings::ResolverSettings;
+#[derive(Debug, Clone)]
+enum ExportTarget {
+ /// A PEP 723 script, with inline metadata.
+ Script(Pep723Script),
+
+ /// A project with a `pyproject.toml`.
+ Project(VirtualProject),
+}
+
+impl<'lock> From<&'lock ExportTarget> for LockTarget<'lock> {
+ fn from(value: &'lock ExportTarget) -> Self {
+ match value {
+ ExportTarget::Script(script) => Self::Script(script),
+ ExportTarget::Project(project) => Self::Workspace(project.workspace()),
+ }
+ }
+}
+
/// Export the project's `uv.lock` in an alternate format.
#[allow(clippy::fn_params_excessive_bools)]
pub(crate) async fn export(
@@ -46,6 +66,7 @@ pub(crate) async fn export(
locked: bool,
frozen: bool,
include_header: bool,
+ script: Option,
python: Option,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
@@ -61,74 +82,108 @@ pub(crate) async fn export(
printer: Printer,
preview: PreviewMode,
) -> Result {
- // Identify the project.
- let project = if frozen {
- VirtualProject::discover(
- project_dir,
- &DiscoveryOptions {
- members: MemberDiscovery::None,
- ..DiscoveryOptions::default()
- },
- )
- .await?
- } else if let Some(package) = package.as_ref() {
- VirtualProject::Project(
- Workspace::discover(project_dir, &DiscoveryOptions::default())
- .await?
- .with_current_project(package.clone())
- .with_context(|| format!("Package `{package}` not found in workspace"))?,
- )
+ // Identify the target.
+ let target = if let Some(script) = script {
+ ExportTarget::Script(script)
} else {
- VirtualProject::discover(project_dir, &DiscoveryOptions::default()).await?
+ let project = if frozen {
+ VirtualProject::discover(
+ project_dir,
+ &DiscoveryOptions {
+ members: MemberDiscovery::None,
+ ..DiscoveryOptions::default()
+ },
+ )
+ .await?
+ } else if let Some(package) = package.as_ref() {
+ VirtualProject::Project(
+ Workspace::discover(project_dir, &DiscoveryOptions::default())
+ .await?
+ .with_current_project(package.clone())
+ .with_context(|| format!("Package `{package}` not found in workspace"))?,
+ )
+ } else {
+ VirtualProject::discover(project_dir, &DiscoveryOptions::default()).await?
+ };
+ ExportTarget::Project(project)
};
// Validate that any referenced dependency groups are defined in the workspace.
if !frozen {
- let target = match &project {
- VirtualProject::Project(project) => {
+ let target = match &target {
+ ExportTarget::Project(VirtualProject::Project(project)) => {
if all_packages {
DependencyGroupsTarget::Workspace(project.workspace())
} else {
DependencyGroupsTarget::Project(project)
}
}
- VirtualProject::NonProject(workspace) => DependencyGroupsTarget::Workspace(workspace),
+ ExportTarget::Project(VirtualProject::NonProject(workspace)) => {
+ DependencyGroupsTarget::Workspace(workspace)
+ }
+ ExportTarget::Script(_) => DependencyGroupsTarget::Script,
};
target.validate(&dev)?;
}
// Determine the default groups to include.
- let defaults = default_dependency_groups(project.pyproject_toml())?;
+ let defaults = match &target {
+ ExportTarget::Project(project) => default_dependency_groups(project.pyproject_toml())?,
+ ExportTarget::Script(_) => vec![],
+ };
let dev = dev.with_defaults(defaults);
+ // Find an interpreter for the project, unless `--frozen` is set.
+ let interpreter = if frozen {
+ None
+ } else {
+ Some(match &target {
+ ExportTarget::Script(script) => ScriptInterpreter::discover(
+ Pep723ItemRef::Script(script),
+ python.as_deref().map(PythonRequest::parse),
+ python_preference,
+ python_downloads,
+ connectivity,
+ native_tls,
+ allow_insecure_host,
+ &install_mirrors,
+ no_config,
+ cache,
+ printer,
+ )
+ .await?
+ .into_interpreter(),
+ ExportTarget::Project(project) => ProjectInterpreter::discover(
+ project.workspace(),
+ project_dir,
+ python.as_deref().map(PythonRequest::parse),
+ python_preference,
+ python_downloads,
+ connectivity,
+ native_tls,
+ allow_insecure_host,
+ &install_mirrors,
+ no_config,
+ cache,
+ printer,
+ )
+ .await?
+ .into_interpreter(),
+ })
+ };
+
// Determine the lock mode.
- let interpreter;
let mode = if frozen {
LockMode::Frozen
+ } else if locked {
+ LockMode::Locked(interpreter.as_ref().unwrap())
+ } else if matches!(target, ExportTarget::Script(_))
+ && !LockTarget::from(&target).lock_path().is_file()
+ {
+ // If we're locking a script, avoid creating a lockfile if it doesn't already exist.
+ LockMode::DryRun(interpreter.as_ref().unwrap())
} else {
- // Find an interpreter for the project
- interpreter = ProjectInterpreter::discover(
- project.workspace(),
- project_dir,
- python.as_deref().map(PythonRequest::parse),
- python_preference,
- python_downloads,
- connectivity,
- native_tls,
- allow_insecure_host,
- &install_mirrors,
- no_config,
- cache,
- printer,
- )
- .await?
- .into_interpreter();
-
- if locked {
- LockMode::Locked(&interpreter)
- } else {
- LockMode::Write(&interpreter)
- }
+ LockMode::Write(interpreter.as_ref().unwrap())
};
// Initialize any shared state.
@@ -137,7 +192,7 @@ pub(crate) async fn export(
// Lock the project.
let lock = match do_safe_lock(
mode,
- project.workspace().into(),
+ (&target).into(),
settings.as_ref(),
LowerBound::Warn,
&state,
@@ -165,8 +220,8 @@ pub(crate) async fn export(
detect_conflicts(&lock, &extras, &dev)?;
// Identify the installation target.
- let target = match &project {
- VirtualProject::Project(project) => {
+ let target = match &target {
+ ExportTarget::Project(VirtualProject::Project(project)) => {
if all_packages {
InstallTarget::Workspace {
workspace: project.workspace(),
@@ -187,7 +242,7 @@ pub(crate) async fn export(
}
}
}
- VirtualProject::NonProject(workspace) => {
+ ExportTarget::Project(VirtualProject::NonProject(workspace)) => {
if all_packages {
InstallTarget::NonProjectWorkspace {
workspace,
@@ -207,6 +262,10 @@ pub(crate) async fn export(
}
}
}
+ ExportTarget::Script(script) => InstallTarget::Script {
+ script,
+ lock: &lock,
+ },
};
// Write the resolved dependencies to the output channel.
diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs
index e51c2afb4b5b..69d4e6f414a3 100644
--- a/crates/uv/src/lib.rs
+++ b/crates/uv/src/lib.rs
@@ -197,6 +197,12 @@ async fn run(mut cli: Cli) -> Result {
script: Some(script),
..
}) = &**command
+ {
+ Pep723Script::read(&script).await?.map(Pep723Item::Script)
+ } else if let ProjectCommand::Export(uv_cli::ExportArgs {
+ script: Some(script),
+ ..
+ }) = &**command
{
Pep723Script::read(&script).await?.map(Pep723Item::Script)
} else {
@@ -1704,6 +1710,13 @@ async fn run_project(
// Initialize the cache.
let cache = cache.init()?;
+ // Unwrap the script.
+ let script = script.map(|script| match script {
+ Pep723Item::Script(script) => script,
+ Pep723Item::Stdin(_) => unreachable!("`uv export` does not support stdin"),
+ Pep723Item::Remote(_) => unreachable!("`uv export` does not support remote files"),
+ });
+
commands::export(
project_dir,
args.format,
@@ -1719,6 +1732,7 @@ async fn run_project(
args.locked,
args.frozen,
args.include_header,
+ script,
args.python,
args.install_mirrors,
args.settings,
diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs
index 8d6da1557432..1e282657dd31 100644
--- a/crates/uv/src/settings.rs
+++ b/crates/uv/src/settings.rs
@@ -1369,6 +1369,7 @@ pub(crate) struct ExportSettings {
pub(crate) locked: bool,
pub(crate) frozen: bool,
pub(crate) include_header: bool,
+ pub(crate) script: Option,
pub(crate) python: Option,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
@@ -1409,6 +1410,7 @@ impl ExportSettings {
resolver,
build,
refresh,
+ script,
python,
} = args;
let install_mirrors = filesystem
@@ -1440,6 +1442,7 @@ impl ExportSettings {
locked,
frozen,
include_header: flag(header, no_header).unwrap_or(true),
+ script,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
diff --git a/crates/uv/tests/it/export.rs b/crates/uv/tests/it/export.rs
index 19e7a388d937..aedf471d36f7 100644
--- a/crates/uv/tests/it/export.rs
+++ b/crates/uv/tests/it/export.rs
@@ -4,6 +4,8 @@ use crate::common::{apply_filters, uv_snapshot, TestContext};
use anyhow::{Ok, Result};
use assert_cmd::assert::OutputAssertExt;
use assert_fs::prelude::*;
+use indoc::indoc;
+use insta::assert_snapshot;
use std::process::Stdio;
#[test]
@@ -2054,6 +2056,263 @@ fn export_group() -> Result<()> {
Ok(())
}
+#[test]
+fn script() -> Result<()> {
+ let context = TestContext::new("3.12");
+
+ let script = context.temp_dir.child("script.py");
+ script.write_str(indoc! {r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "anyio==2.0.0 ; sys_platform == 'win32'",
+ # "anyio==3.0.0 ; sys_platform == 'linux'"
+ # ]
+ # ///
+ "#})?;
+
+ uv_snapshot!(context.filters(), context.export().arg("--script").arg(script.path()), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+ # This file was autogenerated by uv via the following command:
+ # uv export --cache-dir [CACHE_DIR] --script [TEMP_DIR]/script.py
+ anyio==2.0.0 ; sys_platform == 'win32' \
+ --hash=sha256:0b8375c8fc665236cb4d143ea13e849eb9e074d727b1b5c27d88aba44ca8c547 \
+ --hash=sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826
+ anyio==3.0.0 ; sys_platform == 'linux' \
+ --hash=sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9 \
+ --hash=sha256:e71c3d9d72291d12056c0265d07c6bbedf92332f78573e278aeb116f24f30395
+ idna==3.6 ; sys_platform == 'linux' or sys_platform == 'win32' \
+ --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
+ --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
+ sniffio==1.3.1 ; sys_platform == 'linux' or sys_platform == 'win32' \
+ --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
+ --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
+
+ ----- stderr -----
+ Resolved 4 packages in [TIME]
+ "###);
+
+ // If the lockfile didn't exist already, it shouldn't be persisted to disk.
+ assert!(!context.temp_dir.child("uv.lock").exists());
+
+ // Explicitly lock the script.
+ uv_snapshot!(context.filters(), context.lock().arg("--script").arg(script.path()), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 4 packages in [TIME]
+ "###);
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+ resolution-markers = [
+ "sys_platform == 'win32'",
+ "sys_platform == 'linux'",
+ "sys_platform != 'linux' and sys_platform != 'win32'",
+ ]
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [
+ { name = "anyio", marker = "sys_platform == 'linux'", specifier = "==3.0.0" },
+ { name = "anyio", marker = "sys_platform == 'win32'", specifier = "==2.0.0" },
+ ]
+
+ [[package]]
+ name = "anyio"
+ version = "2.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ resolution-markers = [
+ "sys_platform == 'win32'",
+ ]
+ dependencies = [
+ { name = "idna", marker = "sys_platform == 'win32'" },
+ { name = "sniffio", marker = "sys_platform == 'win32'" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/fe/dc/daeadb9b34093d3968afcc93946ee567cd6d2b402a96c608cb160f74d737/anyio-2.0.0.tar.gz", hash = "sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826", size = 91291 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/19/10fe682e962efd1610aa41376399fc3f3e002425449b02d0fb04749bb712/anyio-2.0.0-py3-none-any.whl", hash = "sha256:0b8375c8fc665236cb4d143ea13e849eb9e074d727b1b5c27d88aba44ca8c547", size = 62675 },
+ ]
+
+ [[package]]
+ name = "anyio"
+ version = "3.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ resolution-markers = [
+ "sys_platform == 'linux'",
+ ]
+ dependencies = [
+ { name = "idna", marker = "sys_platform == 'linux'" },
+ { name = "sniffio", marker = "sys_platform == 'linux'" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/99/0d/65165f99e5f4f3b4c43a5ed9db0fb7aa655f5a58f290727a30528a87eb45/anyio-3.0.0.tar.gz", hash = "sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9", size = 116952 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/49/ebee263b69fe243bd1fd0a88bc6bb0f7732bf1794ba3273cb446351f9482/anyio-3.0.0-py3-none-any.whl", hash = "sha256:e71c3d9d72291d12056c0265d07c6bbedf92332f78573e278aeb116f24f30395", size = 72182 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "sniffio"
+ version = "1.3.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+ ]
+ "###
+ );
+ });
+
+ // Update the dependencies.
+ script.write_str(indoc! {r#"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "anyio==2.0.0 ; sys_platform == 'win32'",
+ # "anyio==3.0.0 ; sys_platform == 'linux'",
+ # "iniconfig",
+ # ]
+ # ///
+ "#})?;
+
+ // `uv tree` should update the lockfile.
+ uv_snapshot!(context.filters(), context.export().arg("--script").arg(script.path()), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+ # This file was autogenerated by uv via the following command:
+ # uv export --cache-dir [CACHE_DIR] --script [TEMP_DIR]/script.py
+ anyio==2.0.0 ; sys_platform == 'win32' \
+ --hash=sha256:0b8375c8fc665236cb4d143ea13e849eb9e074d727b1b5c27d88aba44ca8c547 \
+ --hash=sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826
+ anyio==3.0.0 ; sys_platform == 'linux' \
+ --hash=sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9 \
+ --hash=sha256:e71c3d9d72291d12056c0265d07c6bbedf92332f78573e278aeb116f24f30395
+ idna==3.6 ; sys_platform == 'linux' or sys_platform == 'win32' \
+ --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
+ --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
+ iniconfig==2.0.0 \
+ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
+ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
+ sniffio==1.3.1 ; sys_platform == 'linux' or sys_platform == 'win32' \
+ --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
+ --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
+
+ ----- stderr -----
+ Resolved 5 packages in [TIME]
+ "###);
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+ resolution-markers = [
+ "sys_platform == 'win32'",
+ "sys_platform == 'linux'",
+ "sys_platform != 'linux' and sys_platform != 'win32'",
+ ]
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [
+ { name = "anyio", marker = "sys_platform == 'linux'", specifier = "==3.0.0" },
+ { name = "anyio", marker = "sys_platform == 'win32'", specifier = "==2.0.0" },
+ { name = "iniconfig" },
+ ]
+
+ [[package]]
+ name = "anyio"
+ version = "2.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ resolution-markers = [
+ "sys_platform == 'win32'",
+ ]
+ dependencies = [
+ { name = "idna", marker = "sys_platform == 'win32'" },
+ { name = "sniffio", marker = "sys_platform == 'win32'" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/fe/dc/daeadb9b34093d3968afcc93946ee567cd6d2b402a96c608cb160f74d737/anyio-2.0.0.tar.gz", hash = "sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826", size = 91291 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/19/10fe682e962efd1610aa41376399fc3f3e002425449b02d0fb04749bb712/anyio-2.0.0-py3-none-any.whl", hash = "sha256:0b8375c8fc665236cb4d143ea13e849eb9e074d727b1b5c27d88aba44ca8c547", size = 62675 },
+ ]
+
+ [[package]]
+ name = "anyio"
+ version = "3.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ resolution-markers = [
+ "sys_platform == 'linux'",
+ ]
+ dependencies = [
+ { name = "idna", marker = "sys_platform == 'linux'" },
+ { name = "sniffio", marker = "sys_platform == 'linux'" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/99/0d/65165f99e5f4f3b4c43a5ed9db0fb7aa655f5a58f290727a30528a87eb45/anyio-3.0.0.tar.gz", hash = "sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9", size = 116952 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/49/ebee263b69fe243bd1fd0a88bc6bb0f7732bf1794ba3273cb446351f9482/anyio-3.0.0-py3-none-any.whl", hash = "sha256:e71c3d9d72291d12056c0265d07c6bbedf92332f78573e278aeb116f24f30395", size = 72182 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "iniconfig"
+ version = "2.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+ ]
+
+ [[package]]
+ name = "sniffio"
+ version = "1.3.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+ ]
+ "###
+ );
+ });
+
+ Ok(())
+}
+
#[test]
fn conflicts() -> Result<()> {
let context = TestContext::new("3.12");
diff --git a/docs/reference/cli.md b/docs/reference/cli.md
index e8efbbea0cbf..ed6db02acf7b 100644
--- a/docs/reference/cli.md
+++ b/docs/reference/cli.md
@@ -2532,6 +2532,10 @@ uv export [OPTIONS]
lowest-direct
: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
+--script
scriptExport the dependencies for the specified PEP 723 Python script, rather than the current project.
+
+If provided, uv will resolve the dependencies based on its inline metadata table, in adherence with PEP 723.
+
--upgrade
, -U
Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh
--upgrade-package
, -P
upgrade-packageAllow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package
From 53dd554919f3c570ed15b1697ff78bb8aa4f5032 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 17:05:37 -0500
Subject: [PATCH 077/135] Update PEP 723 lockfile in `uv remove --script`
(#10162)
## Summary
Counterpart to #10145 covering `uv remove` for PEP 723 scripts with
lockfiles.
---
crates/uv/src/commands/project/add.rs | 40 ++---
crates/uv/src/commands/project/remove.rs | 198 +++++++++++++++++------
crates/uv/tests/it/edit.rs | 191 +++++++++++++++++++++-
3 files changed, 357 insertions(+), 72 deletions(-)
diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs
index 950e7470af52..227bf676faed 100644
--- a/crates/uv/src/commands/project/add.rs
+++ b/crates/uv/src/commands/project/add.rs
@@ -990,9 +990,27 @@ fn resolve_requirement(
Ok((processed_requirement, source))
}
+/// A Python [`Interpreter`] or [`PythonEnvironment`] for a project.
+#[derive(Debug, Clone)]
+#[allow(clippy::large_enum_variant)]
+pub(super) enum PythonTarget {
+ Interpreter(Interpreter),
+ Environment(PythonEnvironment),
+}
+
+impl PythonTarget {
+ /// Return the [`Interpreter`] for the project.
+ fn interpreter(&self) -> &Interpreter {
+ match self {
+ Self::Interpreter(interpreter) => interpreter,
+ Self::Environment(venv) => venv.interpreter(),
+ }
+ }
+}
+
/// Represents the destination where dependencies are added, either to a project or a script.
#[derive(Debug, Clone)]
-enum AddTarget {
+pub(super) enum AddTarget {
/// A PEP 723 script, with inline metadata.
Script(Pep723Script, Box),
@@ -1011,7 +1029,7 @@ impl<'lock> From<&'lock AddTarget> for LockTarget<'lock> {
impl AddTarget {
/// Returns the [`Interpreter`] for the target.
- fn interpreter(&self) -> &Interpreter {
+ pub(super) fn interpreter(&self) -> &Interpreter {
match self {
Self::Script(_, interpreter) => interpreter,
Self::Project(_, venv) => venv.interpreter(),
@@ -1131,24 +1149,6 @@ impl AddTargetSnapshot {
}
}
-/// A Python [`Interpreter`] or [`PythonEnvironment`] for a project.
-#[derive(Debug, Clone)]
-#[allow(clippy::large_enum_variant)]
-enum PythonTarget {
- Interpreter(Interpreter),
- Environment(PythonEnvironment),
-}
-
-impl PythonTarget {
- /// Return the [`Interpreter`] for the project.
- fn interpreter(&self) -> &Interpreter {
- match self {
- Self::Interpreter(interpreter) => interpreter,
- Self::Environment(venv) => venv.interpreter(),
- }
- }
-}
-
#[derive(Debug, Clone)]
struct DependencyEdit {
dependency_type: DependencyType,
diff --git a/crates/uv/src/commands/project/remove.rs b/crates/uv/src/commands/project/remove.rs
index 415115aa358a..6c6243410db1 100644
--- a/crates/uv/src/commands/project/remove.rs
+++ b/crates/uv/src/commands/project/remove.rs
@@ -1,8 +1,11 @@
use std::fmt::Write;
+use std::io;
use std::path::Path;
+use std::str::FromStr;
use anyhow::{Context, Result};
use owo_colors::OwoColorize;
+use tracing::debug;
use uv_cache::Cache;
use uv_client::Connectivity;
@@ -15,7 +18,7 @@ use uv_fs::Simplified;
use uv_normalize::DEV_DEPENDENCIES;
use uv_pep508::PackageName;
use uv_python::{PythonDownloads, PythonPreference, PythonRequest};
-use uv_scripts::Pep723Script;
+use uv_scripts::{Pep723ItemRef, Pep723Metadata, Pep723Script};
use uv_settings::PythonInstallMirrors;
use uv_warnings::warn_user_once;
use uv_workspace::pyproject::DependencyType;
@@ -24,9 +27,13 @@ use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace};
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger};
use crate::commands::pip::operations::Modifications;
+use crate::commands::project::add::{AddTarget, PythonTarget};
use crate::commands::project::install_target::InstallTarget;
use crate::commands::project::lock::LockMode;
-use crate::commands::project::{default_dependency_groups, ProjectError};
+use crate::commands::project::lock_target::LockTarget;
+use crate::commands::project::{
+ default_dependency_groups, ProjectError, ProjectInterpreter, ScriptInterpreter,
+};
use crate::commands::{diagnostics, project, ExitStatus};
use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings;
@@ -79,7 +86,7 @@ pub(crate) async fn remove(
"`--no-sync` is a no-op for Python scripts with inline metadata, which always run in isolation"
);
}
- Target::Script(script)
+ RemoveTarget::Script(script)
} else {
// Find the project in the workspace.
let project = if let Some(package) = package {
@@ -93,14 +100,14 @@ pub(crate) async fn remove(
VirtualProject::discover(project_dir, &DiscoveryOptions::default()).await?
};
- Target::Project(project)
+ RemoveTarget::Project(project)
};
let mut toml = match &target {
- Target::Script(script) => {
+ RemoveTarget::Script(script) => {
PyProjectTomlMut::from_toml(&script.metadata.raw, DependencyTarget::Script)
}
- Target::Project(project) => PyProjectTomlMut::from_toml(
+ RemoveTarget::Project(project) => PyProjectTomlMut::from_toml(
project.pyproject_toml().raw.as_ref(),
DependencyTarget::PyProjectToml,
),
@@ -161,27 +168,20 @@ pub(crate) async fn remove(
}
}
- // Save the modified dependencies.
- match &target {
- Target::Script(script) => {
- script.write(&toml.to_string())?;
- }
- Target::Project(project) => {
- let pyproject_path = project.root().join("pyproject.toml");
- fs_err::write(pyproject_path, toml.to_string())?;
- }
- };
+ let content = toml.to_string();
+
+ // Save the modified `pyproject.toml` or script.
+ target.write(&content)?;
- // If `--frozen`, exit early. There's no reason to lock and sync, and we don't need a `uv.lock`
+ // If `--frozen`, exit early. There's no reason to lock and sync, since we don't need a `uv.lock`
// to exist at all.
if frozen {
return Ok(ExitStatus::Success);
}
- let project = match target {
- Target::Project(project) => project,
- // If `--script`, exit early. There's no reason to lock and sync.
- Target::Script(script) => {
+ // If we're modifying a script, and lockfile doesn't exist, don't create it.
+ if let RemoveTarget::Script(ref script) = target {
+ if !LockTarget::from(script).lock_path().is_file() {
writeln!(
printer.stderr(),
"Updated `{}`",
@@ -189,31 +189,80 @@ pub(crate) async fn remove(
)?;
return Ok(ExitStatus::Success);
}
- };
+ }
- // Discover or create the virtual environment.
- let venv = project::get_or_init_environment(
- project.workspace(),
- python.as_deref().map(PythonRequest::parse),
- &install_mirrors,
- python_preference,
- python_downloads,
- connectivity,
- native_tls,
- allow_insecure_host,
- no_config,
- cache,
- printer,
- )
- .await?;
+ // Update the `pypackage.toml` in-memory.
+ let target = target.update(&content)?;
+
+ // Convert to an `AddTarget` by attaching the appropriate interpreter or environment.
+ let target = match target {
+ RemoveTarget::Project(project) => {
+ if no_sync {
+ // Discover the interpreter.
+ let interpreter = ProjectInterpreter::discover(
+ project.workspace(),
+ project_dir,
+ python.as_deref().map(PythonRequest::parse),
+ python_preference,
+ python_downloads,
+ connectivity,
+ native_tls,
+ allow_insecure_host,
+ &install_mirrors,
+ no_config,
+ cache,
+ printer,
+ )
+ .await?
+ .into_interpreter();
+
+ AddTarget::Project(project, Box::new(PythonTarget::Interpreter(interpreter)))
+ } else {
+ // Discover or create the virtual environment.
+ let venv = project::get_or_init_environment(
+ project.workspace(),
+ python.as_deref().map(PythonRequest::parse),
+ &install_mirrors,
+ python_preference,
+ python_downloads,
+ connectivity,
+ native_tls,
+ allow_insecure_host,
+ no_config,
+ cache,
+ printer,
+ )
+ .await?;
+
+ AddTarget::Project(project, Box::new(PythonTarget::Environment(venv)))
+ }
+ }
+ RemoveTarget::Script(script) => {
+ let interpreter = ScriptInterpreter::discover(
+ Pep723ItemRef::Script(&script),
+ python.as_deref().map(PythonRequest::parse),
+ python_preference,
+ python_downloads,
+ connectivity,
+ native_tls,
+ allow_insecure_host,
+ &install_mirrors,
+ no_config,
+ cache,
+ printer,
+ )
+ .await?
+ .into_interpreter();
+
+ AddTarget::Script(script, Box::new(interpreter))
+ }
+ };
// Determine the lock mode.
- let mode = if frozen {
- LockMode::Frozen
- } else if locked {
- LockMode::Locked(venv.interpreter())
+ let mode = if locked {
+ LockMode::Locked(target.interpreter())
} else {
- LockMode::Write(venv.interpreter())
+ LockMode::Write(target.interpreter())
};
// Initialize any shared state.
@@ -222,7 +271,7 @@ pub(crate) async fn remove(
// Lock and sync the environment, if necessary.
let lock = match project::lock::do_safe_lock(
mode,
- project.workspace().into(),
+ (&target).into(),
settings.as_ref().into(),
LowerBound::Allow,
&state,
@@ -246,9 +295,15 @@ pub(crate) async fn remove(
Err(err) => return Err(err.into()),
};
- if no_sync {
+ let AddTarget::Project(project, environment) = target else {
+ // If we're not adding to a project, exit early.
return Ok(ExitStatus::Success);
- }
+ };
+
+ let PythonTarget::Environment(venv) = &*environment else {
+ // If we're not syncing, exit early.
+ return Ok(ExitStatus::Success);
+ };
// Perform a full sync, because we don't know what exactly is affected by the removal.
// TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here?
@@ -273,7 +328,7 @@ pub(crate) async fn remove(
match project::sync::do_sync(
target,
- &venv,
+ venv,
&extras,
&DevGroupsManifest::from_defaults(defaults),
EditableMode::Editable,
@@ -306,13 +361,62 @@ pub(crate) async fn remove(
/// Represents the destination where dependencies are added, either to a project or a script.
#[derive(Debug)]
-enum Target {
+enum RemoveTarget {
/// A PEP 723 script, with inline metadata.
Project(VirtualProject),
/// A project with a `pyproject.toml`.
Script(Pep723Script),
}
+impl RemoveTarget {
+ /// Write the updated content to the target.
+ ///
+ /// Returns `true` if the content was modified.
+ fn write(&self, content: &str) -> Result {
+ match self {
+ Self::Script(script) => {
+ if content == script.metadata.raw {
+ debug!("No changes to dependencies; skipping update");
+ Ok(false)
+ } else {
+ script.write(content)?;
+ Ok(true)
+ }
+ }
+ Self::Project(project) => {
+ if content == project.pyproject_toml().raw {
+ debug!("No changes to dependencies; skipping update");
+ Ok(false)
+ } else {
+ let pyproject_path = project.root().join("pyproject.toml");
+ fs_err::write(pyproject_path, content)?;
+ Ok(true)
+ }
+ }
+ }
+ }
+
+ /// Update the target in-memory to incorporate the new content.
+ #[allow(clippy::result_large_err)]
+ fn update(self, content: &str) -> Result {
+ match self {
+ Self::Script(mut script) => {
+ script.metadata = Pep723Metadata::from_str(content)
+ .map_err(ProjectError::Pep723ScriptTomlParse)?;
+ Ok(Self::Script(script))
+ }
+ Self::Project(project) => {
+ let project = project
+ .with_pyproject_toml(
+ toml::from_str(content).map_err(ProjectError::PyprojectTomlParse)?,
+ )
+ .ok_or(ProjectError::PyprojectTomlUpdate)?;
+ Ok(Self::Project(project))
+ }
+ }
+ }
+}
+
/// Show a hint if a dependency with the given name is present as any dependency type.
///
/// This is useful when a dependency of the user-specified type was not found, but it may be present
diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs
index fc5942223606..e3e1d8b39012 100644
--- a/crates/uv/tests/it/edit.rs
+++ b/crates/uv/tests/it/edit.rs
@@ -5334,9 +5334,9 @@ fn remove_repeated() -> Result<()> {
Ok(())
}
-/// Add to a PEP 732 script with a lockfile.
+/// Add to (and remove from) a PEP 732 script with a lockfile.
#[test]
-fn add_script_lock() -> Result<()> {
+fn add_remove_script_lock() -> Result<()> {
let context = TestContext::new("3.12");
let script = context.temp_dir.child("script.py");
@@ -5718,10 +5718,191 @@ fn add_script_lock() -> Result<()> {
);
});
+ // Removing from a locked script should update the lockfile.
+ uv_snapshot!(context.filters(), context.remove().arg("anyio").arg("--script").arg("script.py"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 9 packages in [TIME]
+ "###);
+
+ let script_content = context.read("script.py");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ script_content, @r###"
+ # /// script
+ # requires-python = ">=3.11"
+ # dependencies = [
+ # "requests<3",
+ # "rich",
+ # ]
+ # ///
+
+ import requests
+ from rich.pretty import pprint
+
+ resp = requests.get("https://peps.python.org/api/peps.json")
+ data = resp.json()
+ pprint([(k, v["title"]) for k, v in data.items()][:10])
+ "###
+ );
+ });
+
+ let lock = context.read("script.py.lock");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ lock, @r###"
+ version = 1
+ requires-python = ">=3.11"
+
+ [options]
+ exclude-newer = "2024-03-25T00:00:00Z"
+
+ [manifest]
+ requirements = [
+ { name = "requests", specifier = "<3" },
+ { name = "rich" },
+ ]
+
+ [[package]]
+ name = "certifi"
+ version = "2024.2.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/71/da/e94e26401b62acd6d91df2b52954aceb7f561743aa5ccc32152886c76c96/certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", size = 164886 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1", size = 163774 },
+ ]
+
+ [[package]]
+ name = "charset-normalizer"
+ version = "3.3.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
+ { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
+ { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
+ { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
+ { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
+ { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
+ { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
+ { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
+ { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
+ { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
+ { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
+ { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
+ { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
+ { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
+ { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
+ { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
+ { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
+ { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
+ { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
+ { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
+ { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
+ { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
+ { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
+ { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
+ { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
+ { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
+ { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
+ { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
+ { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
+ { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
+ { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
+ ]
+
+ [[package]]
+ name = "idna"
+ version = "3.6"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
+ ]
+
+ [[package]]
+ name = "markdown-it-py"
+ version = "3.0.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "mdurl" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
+ ]
+
+ [[package]]
+ name = "mdurl"
+ version = "0.1.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
+ ]
+
+ [[package]]
+ name = "pygments"
+ version = "2.17.2"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 },
+ ]
+
+ [[package]]
+ name = "requests"
+ version = "2.31.0"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
+ ]
+
+ [[package]]
+ name = "rich"
+ version = "13.7.1"
+ source = { registry = "https://pypi.org/simple" }
+ dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+ ]
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 },
+ ]
+
+ [[package]]
+ name = "urllib3"
+ version = "2.2.1"
+ source = { registry = "https://pypi.org/simple" }
+ sdist = { url = "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", size = 291020 }
+ wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", size = 121067 },
+ ]
+ "###
+ );
+ });
+
Ok(())
}
-/// Remove from a PEP732 script,
+/// Remove from a PEP 723 script.
#[test]
fn remove_script() -> Result<()> {
let context = TestContext::new("3.12");
@@ -5781,7 +5962,7 @@ fn remove_script() -> Result<()> {
Ok(())
}
-/// Remove last dependency PEP732 script
+/// Remove last dependency PEP 723 script
#[test]
fn remove_last_dep_script() -> Result<()> {
let context = TestContext::new("3.12");
@@ -5836,7 +6017,7 @@ fn remove_last_dep_script() -> Result<()> {
Ok(())
}
-/// Add a Git requirement to PEP732 script.
+/// Add a Git requirement to PEP 723 script.
#[test]
#[cfg(feature = "git")]
fn add_git_to_script() -> Result<()> {
From 2e0d7429ef3d31fa12f57dcef2dcdf9a6114ba31 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 17:12:52 -0500
Subject: [PATCH 078/135] Add Lambda layer instructions to AWS Lambda guide
(#10411)
## Summary
Closes https://github.com/astral-sh/uv/issues/10406.
---
docs/guides/integration/aws-lambda.md | 114 ++++++++++++++++++++++++--
1 file changed, 109 insertions(+), 5 deletions(-)
diff --git a/docs/guides/integration/aws-lambda.md b/docs/guides/integration/aws-lambda.md
index d8cd7983bb94..11ed1d7ea50a 100644
--- a/docs/guides/integration/aws-lambda.md
+++ b/docs/guides/integration/aws-lambda.md
@@ -1,3 +1,10 @@
+---
+title: Using uv with AWS Lambda
+description:
+ A complete guide to using uv with AWS Lambda to manage Python dependencies and deploy serverless
+ functions via Docker containers or zip archives.
+---
+
# Using uv with AWS Lambda
[AWS Lambda](https://aws.amazon.com/lambda/) is a serverless computing service that lets you run
@@ -90,9 +97,12 @@ FROM ghcr.io/astral-sh/uv:0.5.16 AS uv
# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
-# Enable bytecode compilation.
+# Enable bytecode compilation, to improve cold-start performance.
ENV UV_COMPILE_BYTECODE=1
+# Disable installer metadata, to create a deterministic layer.
+ENV UV_NO_INSTALLER_METADATA=1
+
# Enable copy mode to support bind mount caching.
ENV UV_LINK_MODE=copy
@@ -289,9 +299,12 @@ FROM ghcr.io/astral-sh/uv:0.5.16 AS uv
# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
-# Enable bytecode compilation.
+# Enable bytecode compilation, to improve cold-start performance.
ENV UV_COMPILE_BYTECODE=1
+# Disable installer metadata, to create a deterministic layer.
+ENV UV_NO_INSTALLER_METADATA=1
+
# Enable copy mode to support bind mount caching.
ENV UV_LINK_MODE=copy
@@ -352,9 +365,10 @@ for AWS Lambda via:
```console
$ uv export --frozen --no-dev --no-editable -o requirements.txt
$ uv pip install \
+ --no-installer-metadata \
--no-compile-bytecode \
--python-platform x86_64-manylinux2014 \
- --python-version 3.13 \
+ --python 3.13 \
--target packages \
-r requirements.txt
```
@@ -370,12 +384,12 @@ then bundle these dependencies into a zip as follows:
```console
$ cd packages
$ zip -r ../package.zip .
+$ cd ..
```
Finally, we can add the application code to the zip archive:
```console
-$ cd ..
$ zip -r package.zip app
```
@@ -386,7 +400,7 @@ e.g.:
$ aws lambda create-function \
--function-name myFunction \
--runtime python3.13 \
- --zip-file fileb://package.zip
+ --zip-file fileb://package.zip \
--handler app.main.handler \
--role arn:aws:iam::111122223333:role/service-role/my-lambda-role
```
@@ -414,3 +428,93 @@ $ aws lambda update-function-code \
By default, the AWS Management Console assumes a Lambda entrypoint of `lambda_function.lambda_handler`.
If your application uses a different entrypoint, you'll need to modify it in the AWS Management Console.
For example, the above FastAPI application uses `app.main.handler`.
+
+### Using a Lambda layer
+
+AWS Lambda also supports the deployment of multiple composed
+[Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/python-layers.html) when working with
+zip archives. These layers are conceptually similar to layers in a Docker image, allowing you to
+separate application code from dependencies.
+
+In particular, we can create a lambda layer for application dependencies and attach it to the Lambda
+function, separate from the application code itself. This setup can improve cold-start performance
+for application updates, as the dependencies layer can be reused across deployments.
+
+To create a Lambda layer, we'll follow similar steps, but create two separate zip archives: one for
+the application code and one for the application dependencies.
+
+First, we'll create the dependency layer. Lambda layers are expected to follow a slightly different
+structure, so we'll use `--prefix` rather than `--target`:
+
+```console
+$ uv export --frozen --no-dev --no-editable -o requirements.txt
+$ uv pip install \
+ --no-installer-metadata \
+ --no-compile-bytecode \
+ --python-platform x86_64-manylinux2014 \
+ --python 3.13 \
+ --prefix packages \
+ -r requirements.txt
+```
+
+We'll then zip the dependencies in adherence with the expected layout for Lambda layers:
+
+```console
+$ mkdir python
+$ cp -r packages/lib python/
+$ zip -r layer_content.zip python
+```
+
+!!! tip
+
+ To generate deterministic zip archives, consider passing the `-X` flag to `zip` to exclude
+ extended attributes and file system metadata.
+
+And publish the Lambda layer:
+
+```console
+$ aws lambda publish-layer-version --layer-name dependencies-layer \
+ --zip-file fileb://layer_content.zip \
+ --compatible-runtimes python3.13 \
+ --compatible-architectures "x86_64"
+```
+
+We can then create the Lambda function as in the previous example, omitting the dependencies:
+
+```console
+$ # Zip the application code.
+$ zip -r app.zip app
+
+$ # Create the Lambda function.
+$ aws lambda create-function \
+ --function-name myFunction \
+ --runtime python3.13 \
+ --zip-file fileb://app.zip \
+ --handler app.main.handler \
+ --role arn:aws:iam::111122223333:role/service-role/my-lambda-role
+```
+
+Finally, we can attach the dependencies layer to the Lambda function, using the ARN returned by the
+`publish-layer-version` step:
+
+```console
+$ aws lambda update-function-configuration --function-name myFunction \
+ --cli-binary-format raw-in-base64-out \
+ --layers "arn:aws:lambda:region:111122223333:layer:dependencies-layer:1"
+```
+
+When the application dependencies change, the layer can be updated independently of the application
+by republishing the layer and updating the Lambda function configuration:
+
+```console
+$ # Update the dependencies in the layer.
+$ aws lambda publish-layer-version --layer-name dependencies-layer \
+ --zip-file fileb://layer_content.zip \
+ --compatible-runtimes python3.13 \
+ --compatible-architectures "x86_64"
+
+$ # Update the Lambda function configuration.
+$ aws lambda update-function-configuration --function-name myFunction \
+ --cli-binary-format raw-in-base64-out \
+ --layers "arn:aws:lambda:region:111122223333:layer:dependencies-layer:2"
+```
From f65fcf23b3c01e08a0dee772c01d38c3f44848e2 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 18:06:09 -0500
Subject: [PATCH 079/135] Remove duplicated comment (#10416)
---
crates/uv-resolver/src/resolver/mod.rs | 1 -
1 file changed, 1 deletion(-)
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index 2b3e4f69613f..3bc26c62d36d 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -1039,7 +1039,6 @@ impl ResolverState {
// Dependencies on Python are only added when a package is incompatible; as such,
// we don't need to do anything here.
- // we don't need to do anything here.
Ok(None)
}
From 5551f9f3da0e74cdb790d78a1ed97545141001ab Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 18:59:13 -0500
Subject: [PATCH 080/135] Remove unnecessary `.to_string()` call (#10419)
---
crates/uv-platform-tags/src/tags.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/crates/uv-platform-tags/src/tags.rs b/crates/uv-platform-tags/src/tags.rs
index 6cfe3709db8d..9dc99c618af1 100644
--- a/crates/uv-platform-tags/src/tags.rs
+++ b/crates/uv-platform-tags/src/tags.rs
@@ -83,11 +83,11 @@ impl Tags {
pub fn new(tags: Vec<(String, String, String)>) -> Self {
let mut map = FxHashMap::default();
for (index, (py, abi, platform)) in tags.into_iter().rev().enumerate() {
- map.entry(py.to_string())
+ map.entry(py)
.or_insert(FxHashMap::default())
- .entry(abi.to_string())
+ .entry(abi)
.or_insert(FxHashMap::default())
- .entry(platform.to_string())
+ .entry(platform)
.or_insert(TagPriority::try_from(index).expect("valid tag priority"));
}
Self { map: Arc::new(map) }
From 58a81f7c4700ed79bb5870334fe0b52198f91973 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 19:33:55 -0500
Subject: [PATCH 081/135] Add `uv lock --script` to the docs (#10414)
---
docs/guides/scripts.md | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/docs/guides/scripts.md b/docs/guides/scripts.md
index 0696af57ac4e..c6d10af63fde 100644
--- a/docs/guides/scripts.md
+++ b/docs/guides/scripts.md
@@ -210,11 +210,30 @@ print(Point)
is not installed — see the documentation on [Python versions](../concepts/python-versions.md) for
more details.
+## Locking dependencies
+
+uv supports locking dependencies for PEP 723 scripts using the `uv.lock` file format. Unlike with
+projects, scripts must be explicitly locked using `uv lock`:
+
+```console
+$ uv lock --script example.py
+```
+
+Running `uv lock --script` will create a `.lock` file adjacent to the script (e.g.,
+`example.py.lock`).
+
+Once locked, subsequent operations like `uv run --script`, `uv add --script`, `uv export --script`,
+and `uv tree --script` will reuse the locked dependencies, updating the lockfile if necessary.
+
+If no such lockfile is present, commands like `uv export --script` will still function as expected,
+but will not create a lockfile.
+
## Improving reproducibility
-uv supports an `exclude-newer` field in the `tool.uv` section of inline script metadata to limit uv
-to only considering distributions released before a specific date. This is useful for improving the
-reproducibility of your script when run at a later point in time.
+In addition to locking dependencies, uv supports an `exclude-newer` field in the `tool.uv` section
+of inline script metadata to limit uv to only considering distributions released before a specific
+date. This is useful for improving the reproducibility of your script when run at a later point in
+time.
The date must be specified as an [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) timestamp
(e.g., `2006-12-02T02:07:43Z`).
From 15ec830beab1aa7f7adc13b125e8e2122fa78343 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Wed, 8 Jan 2025 21:32:09 -0500
Subject: [PATCH 082/135] Use `matches` rather than `contains` in
`requirements.txt` parsing (#10423)
---
crates/uv-requirements-txt/src/lib.rs | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/crates/uv-requirements-txt/src/lib.rs b/crates/uv-requirements-txt/src/lib.rs
index e08c09cf28cd..3c8264d53e9d 100644
--- a/crates/uv-requirements-txt/src/lib.rs
+++ b/crates/uv-requirements-txt/src/lib.rs
@@ -416,6 +416,11 @@ impl RequirementsTxt {
}
}
+/// Returns `true` if the character is a newline or a comment character.
+const fn is_terminal(c: char) -> bool {
+ matches!(c, '\n' | '\r' | '#')
+}
+
/// Parse a single entry, that is a requirement, an inclusion or a comment line
///
/// Consumes all preceding trivia (whitespace and comments). If it returns None, we've reached
@@ -436,7 +441,7 @@ fn parse_entry(
let start = s.cursor();
Ok(Some(if s.eat_if("-r") || s.eat_if("--requirement") {
- let requirements_file = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
+ let requirements_file = parse_value(content, s, |c: char| !is_terminal(c))?;
let end = s.cursor();
RequirementsTxtStatement::Requirements {
filename: requirements_file.to_string(),
@@ -444,7 +449,7 @@ fn parse_entry(
end,
}
} else if s.eat_if("-c") || s.eat_if("--constraint") {
- let constraints_file = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
+ let constraints_file = parse_value(content, s, |c: char| !is_terminal(c))?;
let end = s.cursor();
RequirementsTxtStatement::Constraint {
filename: constraints_file.to_string(),
@@ -475,7 +480,7 @@ fn parse_entry(
hashes,
})
} else if s.eat_if("-i") || s.eat_if("--index-url") {
- let given = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
+ let given = parse_value(content, s, |c: char| !is_terminal(c))?;
let expanded = expand_env_vars(given);
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
.ok()
@@ -501,7 +506,7 @@ fn parse_entry(
};
RequirementsTxtStatement::IndexUrl(url.with_given(given))
} else if s.eat_if("--extra-index-url") {
- let given = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
+ let given = parse_value(content, s, |c: char| !is_terminal(c))?;
let expanded = expand_env_vars(given);
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
.ok()
@@ -529,7 +534,7 @@ fn parse_entry(
} else if s.eat_if("--no-index") {
RequirementsTxtStatement::NoIndex
} else if s.eat_if("--find-links") || s.eat_if("-f") {
- let given = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
+ let given = parse_value(content, s, |c: char| !is_terminal(c))?;
let expanded = expand_env_vars(given);
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
.ok()
@@ -555,7 +560,7 @@ fn parse_entry(
};
RequirementsTxtStatement::FindLinks(url.with_given(given))
} else if s.eat_if("--no-binary") {
- let given = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
+ let given = parse_value(content, s, |c: char| !is_terminal(c))?;
let specifier = PackageNameSpecifier::from_str(given).map_err(|err| {
RequirementsTxtParserError::NoBinary {
source: err,
@@ -566,7 +571,7 @@ fn parse_entry(
})?;
RequirementsTxtStatement::NoBinary(NoBinary::from_pip_arg(specifier))
} else if s.eat_if("--only-binary") {
- let given = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
+ let given = parse_value(content, s, |c: char| !is_terminal(c))?;
let specifier = PackageNameSpecifier::from_str(given).map_err(|err| {
RequirementsTxtParserError::NoBinary {
source: err,
From 359ef288f8204ad504aa8b81cdd5b0517fc32bcb Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 10:47:25 -0500
Subject: [PATCH 083/135] Upgrade to the latest Ruff version (#10433)
---
scripts/benchmark/src/benchmark/resolver.py | 24 +++++++++----------
.../pypi_10k_most_dependents.ipynb | 2 +-
scripts/publish/test_publish.py | 8 +++----
scripts/scenarios/generate.py | 6 ++---
4 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/scripts/benchmark/src/benchmark/resolver.py b/scripts/benchmark/src/benchmark/resolver.py
index 923a4c313722..54ae8471944a 100644
--- a/scripts/benchmark/src/benchmark/resolver.py
+++ b/scripts/benchmark/src/benchmark/resolver.py
@@ -443,9 +443,9 @@ def resolve_incremental(
self.setup(requirements_file, cwd=cwd)
poetry_lock = os.path.join(cwd, "poetry.lock")
- assert not os.path.exists(
- poetry_lock
- ), f"Lockfile already exists at: {poetry_lock}"
+ assert not os.path.exists(poetry_lock), (
+ f"Lockfile already exists at: {poetry_lock}"
+ )
# Run a resolution, to ensure that the lockfile exists.
# TODO(charlie): Make this a `setup`.
@@ -499,9 +499,9 @@ def resolve_noop(self, requirements_file: str, *, cwd: str) -> Command | None:
self.setup(requirements_file, cwd=cwd)
poetry_lock = os.path.join(cwd, "poetry.lock")
- assert not os.path.exists(
- poetry_lock
- ), f"Lockfile already exists at: {poetry_lock}"
+ assert not os.path.exists(poetry_lock), (
+ f"Lockfile already exists at: {poetry_lock}"
+ )
# Run a resolution, to ensure that the lockfile exists.
# TODO(charlie): Make this a `setup`.
@@ -536,9 +536,9 @@ def install_cold(self, requirements_file: str, *, cwd: str) -> Command | None:
self.setup(requirements_file, cwd=cwd)
poetry_lock = os.path.join(cwd, "poetry.lock")
- assert not os.path.exists(
- poetry_lock
- ), f"Lockfile already exists at: {poetry_lock}"
+ assert not os.path.exists(poetry_lock), (
+ f"Lockfile already exists at: {poetry_lock}"
+ )
# Run a resolution, to ensure that the lockfile exists.
# TODO(charlie): Make this a `setup`.
@@ -581,9 +581,9 @@ def install_warm(self, requirements_file: str, *, cwd: str) -> Command | None:
self.setup(requirements_file, cwd=cwd)
poetry_lock = os.path.join(cwd, "poetry.lock")
- assert not os.path.exists(
- poetry_lock
- ), f"Lockfile already exists at: {poetry_lock}"
+ assert not os.path.exists(poetry_lock), (
+ f"Lockfile already exists at: {poetry_lock}"
+ )
# Run a resolution, to ensure that the lockfile exists.
subprocess.check_call(
diff --git a/scripts/popular_packages/pypi_10k_most_dependents.ipynb b/scripts/popular_packages/pypi_10k_most_dependents.ipynb
index 9dcf3c0d1b3c..6b9d401e68ca 100644
--- a/scripts/popular_packages/pypi_10k_most_dependents.ipynb
+++ b/scripts/popular_packages/pypi_10k_most_dependents.ipynb
@@ -37,7 +37,7 @@
" if i not in responses:\n",
" # https://libraries.io/api#project-search\n",
" sort = \"dependents_count\"\n",
- " url = f\"https://libraries.io/api/search?platforms=Pypi&per_page=100&page={i+1}&sort{sort}&api_key={api_key}\"\n",
+ " url = f\"https://libraries.io/api/search?platforms=Pypi&per_page=100&page={i + 1}&sort{sort}&api_key={api_key}\"\n",
" responses[i] = httpx.get(url, timeout=30.0).json()"
]
},
diff --git a/scripts/publish/test_publish.py b/scripts/publish/test_publish.py
index 55666274c1c7..f3f8e8d0fdb0 100644
--- a/scripts/publish/test_publish.py
+++ b/scripts/publish/test_publish.py
@@ -371,8 +371,8 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
):
raise RuntimeError(
f"PyPI re-upload of the same files failed: "
- f"{output.count("Uploading")} != {len(expected_filenames)}, "
- f"{output.count("already exists")} != 0\n"
+ f"{output.count('Uploading')} != {len(expected_filenames)}, "
+ f"{output.count('already exists')} != 0\n"
f"---\n{output}\n---"
)
@@ -407,8 +407,8 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
):
raise RuntimeError(
f"Re-upload with check URL failed: "
- f"{output.count("Uploading")} != 0, "
- f"{output.count("already exists")} != {len(expected_filenames)}\n"
+ f"{output.count('Uploading')} != 0, "
+ f"{output.count('already exists')} != {len(expected_filenames)}\n"
f"---\n{output}\n---"
)
diff --git a/scripts/scenarios/generate.py b/scripts/scenarios/generate.py
index 6b9c4e462115..d6db67eeb354 100755
--- a/scripts/scenarios/generate.py
+++ b/scripts/scenarios/generate.py
@@ -269,9 +269,9 @@ def update_common_mod_rs(packse_version: str):
url_matcher = re.compile(
re.escape(before_version) + '[^"]+' + re.escape(after_version)
)
- assert (
- len(url_matcher.findall(test_common)) == 1
- ), f"PACKSE_VERSION not found in {TESTS_COMMON_MOD_RS}"
+ assert len(url_matcher.findall(test_common)) == 1, (
+ f"PACKSE_VERSION not found in {TESTS_COMMON_MOD_RS}"
+ )
test_common = url_matcher.sub(build_vendor_links_url, test_common)
TESTS_COMMON_MOD_RS.write_text(test_common)
From 19589e0614ad063fed39056e4968476576a48054 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 10:50:16 -0500
Subject: [PATCH 084/135] Add meta descriptions for some guides (#10421)
## Summary
Part of: https://github.com/astral-sh/uv/issues/10418.
---
docs/guides/integration/docker.md | 7 +++++++
docs/guides/integration/fastapi.md | 7 +++++++
docs/guides/integration/jupyter.md | 7 +++++++
docs/guides/integration/pre-commit.md | 7 +++++++
4 files changed, 28 insertions(+)
diff --git a/docs/guides/integration/docker.md b/docs/guides/integration/docker.md
index 834e93c46f54..fee04193f3d1 100644
--- a/docs/guides/integration/docker.md
+++ b/docs/guides/integration/docker.md
@@ -1,3 +1,10 @@
+---
+title: Using uv in Docker
+description:
+ A complete guide to using uv in Docker to manage Python dependencies while optimizing build times
+ and image size via multi-stage builds, intermediate layers, and more.
+---
+
# Using uv in Docker
## Getting started
diff --git a/docs/guides/integration/fastapi.md b/docs/guides/integration/fastapi.md
index 654b2ed14265..6693f46d3c6b 100644
--- a/docs/guides/integration/fastapi.md
+++ b/docs/guides/integration/fastapi.md
@@ -1,3 +1,10 @@
+---
+title: Using uv with FastAPI
+description:
+ A guide to using uv with FastAPI to manage Python dependencies, run applications, and deploy with
+ Docker.
+---
+
# Using uv with FastAPI
[FastAPI](https://github.com/fastapi/fastapi) is a modern, high-performance Python web framework.
diff --git a/docs/guides/integration/jupyter.md b/docs/guides/integration/jupyter.md
index 358a090d49b6..f1292a58123c 100644
--- a/docs/guides/integration/jupyter.md
+++ b/docs/guides/integration/jupyter.md
@@ -1,3 +1,10 @@
+---
+title: Using uv with Jupyter
+description:
+ A complete guide to using uv with Jupyter notebooks for interactive computing, data analysis, and
+ visualization, including kernel management and virtual environment integration.
+---
+
# Using uv with Jupyter
The [Jupyter](https://jupyter.org/) notebook is a popular tool for interactive computing, data
diff --git a/docs/guides/integration/pre-commit.md b/docs/guides/integration/pre-commit.md
index 6d963f8a43f3..e60c1785465b 100644
--- a/docs/guides/integration/pre-commit.md
+++ b/docs/guides/integration/pre-commit.md
@@ -1,3 +1,10 @@
+---
+title: Using uv with pre-commit
+description:
+ A guide to using uv with pre-commit to automatically update lock files, export requirements, and
+ compile requirements files.
+---
+
# Using uv in pre-commit
An official pre-commit hook is provided at
From 201726cda527340bd3becf84547f3dc36f5098a2 Mon Sep 17 00:00:00 2001
From: FishAlchemist <48265002+FishAlchemist@users.noreply.github.com>
Date: Fri, 10 Jan 2025 01:41:04 +0800
Subject: [PATCH 085/135] docs: Clarify build system specific features usage.
(#10261)
## Summary
Since there are occasional inquiries about how to configure UV for
build-system specific features, I want to raise awareness that users
should refer to the documentation of the build system they are using for
relevant settings.
## Test Plan
Run docs service in local.
https://github.com/astral-sh/uv/pull/10261/commits/9821d58d35dce6bbafe4bdadd9031afba0f787a5
![image](https://github.com/user-attachments/assets/3c07ac15-a562-40e2-9289-204c0975261f)
---------
Signed-off-by: FishAlchemist <48265002+FishAlchemist@users.noreply.github.com>
Co-authored-by: Zanie Blue
---
docs/concepts/projects/config.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/docs/concepts/projects/config.md b/docs/concepts/projects/config.md
index 3fa7c088890c..6e1f84632a3e 100644
--- a/docs/concepts/projects/config.md
+++ b/docs/concepts/projects/config.md
@@ -119,6 +119,18 @@ with the default build system.
installable. Similarly, if you add a dependency on a local package or install it with `uv pip`,
uv will always attempt to build and install it.
+### Build system options
+
+Build systems are used to power the following features:
+
+- Including or excluding files from distributions
+- Editable install behavior
+- Dynamic project metadata
+- Compilation of native code
+- Vendoring shared libraries
+
+To configure these features, refer to the documentation of your chosen build system.
+
## Project packaging
As discussed in [build systems](#build-systems), a Python project must be built to be installed.
From a0494bb059992df44abcb37e0c492a56c53159d2 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 12:45:20 -0500
Subject: [PATCH 086/135] Fetch concurrently for non-first-match index
strategies (#10432)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
On a basic test, this speeds up cold resolution by about 25%:
```
❯ hyperfine "uv lock --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match --upgrade --no-cache" "../target/release/uv lock --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match --upgrade --no-cache" --warmup 10 --runs 30
Benchmark 1: uv lock --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match --upgrade --no-cache
Time (mean ± σ): 585.8 ms ± 28.2 ms [User: 149.7 ms, System: 97.4 ms]
Range (min … max): 541.5 ms … 654.8 ms 30 runs
Benchmark 2: ../target/release/uv lock --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match --upgrade --no-cache
Time (mean ± σ): 468.3 ms ± 52.0 ms [User: 131.7 ms, System: 76.9 ms]
Range (min … max): 380.2 ms … 607.0 ms 30 runs
Summary
../target/release/uv lock --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match --upgrade --no-cache ran
1.25 ± 0.15 times faster than uv lock --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match --upgrade --no-cache
```
Given:
```toml
[project]
name = "foo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12.0"
dependencies = [
"black>=24.10.0",
"django>=5.1.4",
"flask>=3.1.0",
"requests>=2.32.3",
]
```
And:
```shell
hyperfine "uv lock --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match --upgrade --no-cache" "../target/release/uv lock --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match --upgrade --no-cache" --warmup 10 --runs 30
```
Closes https://github.com/astral-sh/uv/issues/10429.
---
crates/uv-client/src/registry_client.rs | 117 +++++++++++++++++-------
1 file changed, 83 insertions(+), 34 deletions(-)
diff --git a/crates/uv-client/src/registry_client.rs b/crates/uv-client/src/registry_client.rs
index da8c3b18c427..d9812253a2d4 100644
--- a/crates/uv-client/src/registry_client.rs
+++ b/crates/uv-client/src/registry_client.rs
@@ -1,14 +1,15 @@
-use async_http_range_reader::AsyncHttpRangeReader;
-use futures::{FutureExt, TryStreamExt};
-use http::HeaderMap;
-use itertools::Either;
-use reqwest::{Client, Response, StatusCode};
-use reqwest_middleware::ClientWithMiddleware;
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
+
+use async_http_range_reader::AsyncHttpRangeReader;
+use futures::{FutureExt, StreamExt, TryStreamExt};
+use http::HeaderMap;
+use itertools::Either;
+use reqwest::{Client, Response, StatusCode};
+use reqwest_middleware::ClientWithMiddleware;
use tracing::{info_span, instrument, trace, warn, Instrument};
use url::Url;
@@ -247,38 +248,86 @@ impl RegistryClient {
}
let mut results = Vec::new();
- for index in it {
- match self.simple_single_index(package_name, index).await {
- Ok(metadata) => {
- results.push((index, metadata));
-
- // If we're only using the first match, we can stop here.
- if self.index_strategy == IndexStrategy::FirstIndex {
- break;
- }
- }
- Err(err) => match err.into_kind() {
- // The package could not be found in the remote index.
- ErrorKind::WrappedReqwestError(url, err) => match err.status() {
- Some(StatusCode::NOT_FOUND) => {}
- Some(StatusCode::UNAUTHORIZED) => {
- capabilities.set_unauthorized(index.clone());
- }
- Some(StatusCode::FORBIDDEN) => {
- capabilities.set_forbidden(index.clone());
+
+ match self.index_strategy {
+ // If we're searching for the first index that contains the package, fetch serially.
+ IndexStrategy::FirstIndex => {
+ for index in it {
+ match self.simple_single_index(package_name, index).await {
+ Ok(metadata) => {
+ results.push((index, metadata));
+ break;
}
- _ => return Err(ErrorKind::WrappedReqwestError(url, err).into()),
- },
+ Err(err) => match err.into_kind() {
+ // The package could not be found in the remote index.
+ ErrorKind::WrappedReqwestError(url, err) => match err.status() {
+ Some(StatusCode::NOT_FOUND) => {}
+ Some(StatusCode::UNAUTHORIZED) => {
+ capabilities.set_unauthorized(index.clone());
+ }
+ Some(StatusCode::FORBIDDEN) => {
+ capabilities.set_forbidden(index.clone());
+ }
+ _ => return Err(ErrorKind::WrappedReqwestError(url, err).into()),
+ },
+
+ // The package is unavailable due to a lack of connectivity.
+ ErrorKind::Offline(_) => {}
+
+ // The package could not be found in the local index.
+ ErrorKind::FileNotFound(_) => {}
+
+ err => return Err(err.into()),
+ },
+ };
+ }
+ }
- // The package is unavailable due to a lack of connectivity.
- ErrorKind::Offline(_) => {}
+ // Otherwise, fetch concurrently.
+ IndexStrategy::UnsafeBestMatch | IndexStrategy::UnsafeFirstMatch => {
+ let fetches = futures::stream::iter(it)
+ .map(|index| async move {
+ match self.simple_single_index(package_name, index).await {
+ Ok(metadata) => Ok(Some((index, metadata))),
+ Err(err) => match err.into_kind() {
+ // The package could not be found in the remote index.
+ ErrorKind::WrappedReqwestError(url, err) => match err.status() {
+ Some(StatusCode::NOT_FOUND) => Ok(None),
+ Some(StatusCode::UNAUTHORIZED) => {
+ capabilities.set_unauthorized(index.clone());
+ Ok(None)
+ }
+ Some(StatusCode::FORBIDDEN) => {
+ capabilities.set_forbidden(index.clone());
+ Ok(None)
+ }
+ _ => Err(ErrorKind::WrappedReqwestError(url, err).into()),
+ },
+
+ // The package is unavailable due to a lack of connectivity.
+ ErrorKind::Offline(_) => Ok(None),
+
+ // The package could not be found in the local index.
+ ErrorKind::FileNotFound(_) => Ok(None),
+
+ err => Err(err.into()),
+ },
+ }
+ })
+ .buffered(8);
- // The package could not be found in the local index.
- ErrorKind::FileNotFound(_) => {}
+ futures::pin_mut!(fetches);
- other => return Err(other.into()),
- },
- };
+ while let Some(result) = fetches.next().await {
+ match result {
+ Ok(Some((index, metadata))) => {
+ results.push((index, metadata));
+ }
+ Ok(None) => continue,
+ Err(err) => return Err(err),
+ }
+ }
+ }
}
if results.is_empty() {
From 14b685d9fb8e059744225b3b9ca7e49e34e8373a Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 13:19:51 -0500
Subject: [PATCH 087/135] Warn-and-ignore for unsupported `requirements.txt`
options (#10420)
## Summary
Closes https://github.com/astral-sh/uv/issues/10366.
---
Cargo.lock | 1 +
crates/uv-requirements-txt/Cargo.toml | 9 ++-
crates/uv-requirements-txt/src/lib.rs | 107 +++++++++++++++++++++++---
crates/uv/tests/it/pip_install.rs | 33 ++++++++
4 files changed, 135 insertions(+), 15 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 3a8e8184e2df..198ac0923b59 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5485,6 +5485,7 @@ dependencies = [
"uv-normalize",
"uv-pep508",
"uv-pypi-types",
+ "uv-warnings",
]
[[package]]
diff --git a/crates/uv-requirements-txt/Cargo.toml b/crates/uv-requirements-txt/Cargo.toml
index 11e7d1666866..7dcc2964ecd2 100644
--- a/crates/uv-requirements-txt/Cargo.toml
+++ b/crates/uv-requirements-txt/Cargo.toml
@@ -16,13 +16,14 @@ doctest = false
workspace = true
[dependencies]
-uv-distribution-types = { workspace = true }
-uv-pep508 = { workspace = true }
-uv-pypi-types = { workspace = true }
uv-client = { workspace = true }
+uv-configuration = { workspace = true }
+uv-distribution-types = { workspace = true }
uv-fs = { workspace = true }
uv-normalize = { workspace = true }
-uv-configuration = { workspace = true }
+uv-pep508 = { workspace = true }
+uv-pypi-types = { workspace = true }
+uv-warnings = { workspace = true }
fs-err = { workspace = true }
regex = { workspace = true }
diff --git a/crates/uv-requirements-txt/src/lib.rs b/crates/uv-requirements-txt/src/lib.rs
index 3c8264d53e9d..c4893f22bd6e 100644
--- a/crates/uv-requirements-txt/src/lib.rs
+++ b/crates/uv-requirements-txt/src/lib.rs
@@ -88,6 +88,8 @@ enum RequirementsTxtStatement {
NoBinary(NoBinary),
/// `--only-binary`
OnlyBinary(NoBuild),
+ /// An unsupported option (e.g., `--trusted-host`).
+ UnsupportedOption(UnsupportedOption),
}
/// A [Requirement] with additional metadata from the `requirements.txt`, currently only hashes but in
@@ -384,6 +386,28 @@ impl RequirementsTxt {
RequirementsTxtStatement::OnlyBinary(only_binary) => {
data.only_binary.extend(only_binary);
}
+ RequirementsTxtStatement::UnsupportedOption(flag) => {
+ if requirements_txt == Path::new("-") {
+ if flag.cli() {
+ uv_warnings::warn_user!("Ignoring unsupported option from stdin: `{flag}` (hint: pass `{flag}` on the command line instead)", flag = flag.green());
+ } else {
+ uv_warnings::warn_user!(
+ "Ignoring unsupported option from stdin: `{flag}`",
+ flag = flag.green()
+ );
+ }
+ } else {
+ if flag.cli() {
+ uv_warnings::warn_user!("Ignoring unsupported option in `{path}`: `{flag}` (hint: pass `{flag}` on the command line instead)", path = requirements_txt.user_display().cyan(), flag = flag.green());
+ } else {
+ uv_warnings::warn_user!(
+ "Ignoring unsupported option in `{path}`: `{flag}`",
+ path = requirements_txt.user_display().cyan(),
+ flag = flag.green()
+ );
+ }
+ }
+ }
}
}
Ok(data)
@@ -416,15 +440,70 @@ impl RequirementsTxt {
}
}
+/// An unsupported option (e.g., `--trusted-host`).
+///
+/// See:
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum UnsupportedOption {
+ PreferBinary,
+ RequireHashes,
+ Pre,
+ TrustedHost,
+ UseFeature,
+}
+
+impl UnsupportedOption {
+ /// The name of the unsupported option.
+ fn name(self) -> &'static str {
+ match self {
+ UnsupportedOption::PreferBinary => "--prefer-binary",
+ UnsupportedOption::RequireHashes => "--require-hashes",
+ UnsupportedOption::Pre => "--pre",
+ UnsupportedOption::TrustedHost => "--trusted-host",
+ UnsupportedOption::UseFeature => "--use-feature",
+ }
+ }
+
+ /// Returns `true` if the option is supported on the CLI.
+ fn cli(self) -> bool {
+ match self {
+ UnsupportedOption::PreferBinary => false,
+ UnsupportedOption::RequireHashes => true,
+ UnsupportedOption::Pre => true,
+ UnsupportedOption::TrustedHost => true,
+ UnsupportedOption::UseFeature => false,
+ }
+ }
+
+ /// Returns an iterator over all unsupported options.
+ fn iter() -> impl Iterator- {
+ [
+ UnsupportedOption::PreferBinary,
+ UnsupportedOption::RequireHashes,
+ UnsupportedOption::Pre,
+ UnsupportedOption::TrustedHost,
+ UnsupportedOption::UseFeature,
+ ]
+ .iter()
+ .copied()
+ }
+}
+
+impl Display for UnsupportedOption {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.name())
+ }
+}
+
/// Returns `true` if the character is a newline or a comment character.
const fn is_terminal(c: char) -> bool {
matches!(c, '\n' | '\r' | '#')
}
-/// Parse a single entry, that is a requirement, an inclusion or a comment line
+/// Parse a single entry, that is a requirement, an inclusion or a comment line.
///
-/// Consumes all preceding trivia (whitespace and comments). If it returns None, we've reached
-/// the end of file
+/// Consumes all preceding trivia (whitespace and comments). If it returns `None`, we've reached
+/// the end of file.
fn parse_entry(
s: &mut Scanner,
content: &str,
@@ -595,14 +674,20 @@ fn parse_entry(
hashes,
})
} else if let Some(char) = s.peek() {
- let (line, column) = calculate_row_column(content, s.cursor());
- return Err(RequirementsTxtParserError::Parser {
- message: format!(
- "Unexpected '{char}', expected '-c', '-e', '-r' or the start of a requirement"
- ),
- line,
- column,
- });
+ // Identify an unsupported option, like `--trusted-host`.
+ if let Some(option) = UnsupportedOption::iter().find(|option| s.eat_if(option.name())) {
+ s.eat_while(|c: char| !is_terminal(c));
+ RequirementsTxtStatement::UnsupportedOption(option)
+ } else {
+ let (line, column) = calculate_row_column(content, s.cursor());
+ return Err(RequirementsTxtParserError::Parser {
+ message: format!(
+ "Unexpected '{char}', expected '-c', '-e', '-r' or the start of a requirement"
+ ),
+ line,
+ column,
+ });
+ }
} else {
// EOF
return Ok(None);
diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs
index cdd067ee5c85..789ce3835e4d 100644
--- a/crates/uv/tests/it/pip_install.rs
+++ b/crates/uv/tests/it/pip_install.rs
@@ -486,6 +486,39 @@ fn install_requirements_txt() -> Result<()> {
Ok(())
}
+/// Warn (but don't fail) when unsupported flags are set in the `requirements.txt`.
+#[test]
+fn install_unsupported_flag() -> Result<()> {
+ let context = TestContext::new("3.12");
+
+ let requirements_txt = context.temp_dir.child("requirements.txt");
+ requirements_txt.write_str(indoc! {r"
+ --pre
+ --prefer-binary :all:
+ iniconfig
+ "})?;
+
+ uv_snapshot!(context.pip_install()
+ .arg("-r")
+ .arg("requirements.txt")
+ .arg("--strict"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ warning: Ignoring unsupported option in `requirements.txt`: `--pre` (hint: pass `--pre` on the command line instead)
+ warning: Ignoring unsupported option in `requirements.txt`: `--prefer-binary`
+ Resolved 1 package in [TIME]
+ Prepared 1 package in [TIME]
+ Installed 1 package in [TIME]
+ + iniconfig==2.0.0
+ "###
+ );
+
+ Ok(())
+}
+
/// Install a requirements file with pins that conflict
///
/// This is likely to occur in the real world when compiled on one platform then installed on another.
From 57367ed327566b2eb1141ca29cf1ad44dcf88d26 Mon Sep 17 00:00:00 2001
From: konsti
Date: Thu, 9 Jan 2025 19:31:51 +0100
Subject: [PATCH 088/135] Use faster disjointness check for markers (#10439)
---
crates/uv-pep508/src/marker/algebra.rs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/crates/uv-pep508/src/marker/algebra.rs b/crates/uv-pep508/src/marker/algebra.rs
index 089e22432176..892c38f8ddc1 100644
--- a/crates/uv-pep508/src/marker/algebra.rs
+++ b/crates/uv-pep508/src/marker/algebra.rs
@@ -1412,8 +1412,7 @@ impl Edges {
// not the resulting edges.
for (left_range, left_child) in left_edges {
for (right_range, right_child) in right_edges {
- let intersection = right_range.intersection(left_range);
- if intersection.is_empty() {
+ if right_range.is_disjoint(left_range) {
continue;
}
From 56d39d21c23100ad51f9c3f10cb49412045754e1 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 15:37:28 -0500
Subject: [PATCH 089/135] Visit proxy packages eagerly (#10441)
## Summary
The issue here is that we add `urllib3{python_full_version >= '3.8'}` as
a dependency, then `requests{python_full_version >= '3.8'}`, which adds
`urllib3`, but at that point, we haven't expanded
`urllib3{python_full_version >= '3.8'}`, so we "lose" the singleton
constraint. The solution is to ensure that we visit proxies eagerly, so
that we accumulate constraints as early as possible.
Closes
https://github.com/astral-sh/uv/issues/10425#issuecomment-2580324578.
---
crates/uv-resolver/src/pubgrub/priority.rs | 24 ++-
crates/uv-resolver/src/resolver/mod.rs | 8 +-
crates/uv-resolver/src/version_map.rs | 2 +
crates/uv/tests/it/lock_conflict.rs | 24 +--
crates/uv/tests/it/lock_scenarios.rs | 10 +-
crates/uv/tests/it/pip_compile.rs | 4 +-
crates/uv/tests/it/pip_install.rs | 142 +++++++++++++++++
crates/uv/tests/it/pip_install_scenarios.rs | 4 +-
...it__ecosystem__transformers-lock-file.snap | 145 ++++--------------
...cosystem__transformers-uv-lock-output.snap | 2 +-
10 files changed, 219 insertions(+), 146 deletions(-)
diff --git a/crates/uv-resolver/src/pubgrub/priority.rs b/crates/uv-resolver/src/pubgrub/priority.rs
index 588b8f6bcc67..80b1acc38c5b 100644
--- a/crates/uv-resolver/src/pubgrub/priority.rs
+++ b/crates/uv-resolver/src/pubgrub/priority.rs
@@ -1,12 +1,13 @@
-use pubgrub::Range;
-use rustc_hash::FxHashMap;
use std::cmp::Reverse;
use std::collections::hash_map::OccupiedEntry;
-use crate::fork_urls::ForkUrls;
+use pubgrub::Range;
+use rustc_hash::FxHashMap;
+
use uv_normalize::PackageName;
use uv_pep440::Version;
+use crate::fork_urls::ForkUrls;
use crate::pubgrub::package::PubGrubPackage;
use crate::pubgrub::PubGrubPackageInner;
@@ -57,6 +58,7 @@ impl PubGrubPriorities {
let priority = if urls.get(name).is_some() {
PubGrubPriority::DirectUrl(Reverse(index))
} else if version.as_singleton().is_some() {
+ // TODO(charlie): Take local version ranges into account (e.g., `[2.0, 2.0+[max])`).
PubGrubPriority::Singleton(Reverse(index))
} else {
// Keep the conflict-causing packages to avoid loops where we seesaw between
@@ -80,6 +82,7 @@ impl PubGrubPriorities {
let priority = if urls.get(name).is_some() {
PubGrubPriority::DirectUrl(Reverse(next))
} else if version.as_singleton().is_some() {
+ // TODO(charlie): Take local version ranges into account (e.g., `[2.0, 2.0+[max])`).
PubGrubPriority::Singleton(Reverse(next))
} else {
PubGrubPriority::Unspecified(Reverse(next))
@@ -98,7 +101,7 @@ impl PubGrubPriorities {
| PubGrubPriority::ConflictEarly(Reverse(index))
| PubGrubPriority::Singleton(Reverse(index))
| PubGrubPriority::DirectUrl(Reverse(index)) => Some(*index),
- PubGrubPriority::Root => None,
+ PubGrubPriority::Proxy | PubGrubPriority::Root => None,
}
}
@@ -107,9 +110,9 @@ impl PubGrubPriorities {
let package_priority = match &**package {
PubGrubPackageInner::Root(_) => Some(PubGrubPriority::Root),
PubGrubPackageInner::Python(_) => Some(PubGrubPriority::Root),
- PubGrubPackageInner::Marker { name, .. } => self.package_priority.get(name).copied(),
- PubGrubPackageInner::Extra { name, .. } => self.package_priority.get(name).copied(),
- PubGrubPackageInner::Dev { name, .. } => self.package_priority.get(name).copied(),
+ PubGrubPackageInner::Marker { .. } => Some(PubGrubPriority::Proxy),
+ PubGrubPackageInner::Extra { .. } => Some(PubGrubPriority::Proxy),
+ PubGrubPackageInner::Dev { .. } => Some(PubGrubPriority::Proxy),
PubGrubPackageInner::Package { name, .. } => self.package_priority.get(name).copied(),
};
let virtual_package_tiebreaker = self
@@ -224,6 +227,13 @@ pub(crate) enum PubGrubPriority {
/// [`ForkUrls`].
DirectUrl(Reverse),
+ /// The package is a proxy package.
+ ///
+ /// We process proxy packages eagerly since each proxy package expands into two "regular"
+ /// [`PubGrubPackage`] packages, which gives us additional constraints while not affecting the
+ /// priorities (since the expanded dependencies are all linked to the same package name).
+ Proxy,
+
/// The package is the root package.
Root,
}
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index 3bc26c62d36d..60d309bb8194 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -2591,7 +2591,9 @@ impl ForkState {
}
}
- if let Some(name) = self.pubgrub.package_store[for_package]
+ let for_package = &self.pubgrub.package_store[for_package];
+
+ if let Some(name) = for_package
.name_no_root()
.filter(|name| !workspace_members.contains(name))
{
@@ -2622,7 +2624,9 @@ impl ForkState {
}
// Update the package priorities.
- self.priorities.insert(package, version, &self.fork_urls);
+ if !for_package.is_proxy() {
+ self.priorities.insert(package, version, &self.fork_urls);
+ }
}
let conflict = self.pubgrub.add_package_version_dependencies(
diff --git a/crates/uv-resolver/src/version_map.rs b/crates/uv-resolver/src/version_map.rs
index bf9a87983242..36ece551f3aa 100644
--- a/crates/uv-resolver/src/version_map.rs
+++ b/crates/uv-resolver/src/version_map.rs
@@ -162,6 +162,8 @@ impl VersionMap {
range: &Ranges,
) -> impl DoubleEndedIterator
- {
// Performance optimization: If we only have a single version, return that version directly.
+ //
+ // TODO(charlie): Now that we use local version sentinels, does this ever trigger?
if let Some(version) = range.as_singleton() {
either::Either::Left(match self.inner {
VersionMapInner::Eager(ref eager) => {
diff --git a/crates/uv/tests/it/lock_conflict.rs b/crates/uv/tests/it/lock_conflict.rs
index a056f7b366bb..334150536728 100644
--- a/crates/uv/tests/it/lock_conflict.rs
+++ b/crates/uv/tests/it/lock_conflict.rs
@@ -50,7 +50,7 @@ fn extra_basic() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project[extra2] depends on sortedcontainers==2.4.0 and project[extra1] depends on sortedcontainers==2.3.0, we can conclude that project[extra1] and project[extra2] are incompatible.
+ ╰─▶ Because project[extra1] depends on sortedcontainers==2.3.0 and project[extra2] depends on sortedcontainers==2.4.0, we can conclude that project[extra1] and project[extra2] are incompatible.
And because your project requires project[extra1] and project[extra2], we can conclude that your project's requirements are unsatisfiable.
"###);
@@ -250,7 +250,7 @@ fn extra_basic_three_extras() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project[project3] depends on sortedcontainers==2.4.0 and project[extra2] depends on sortedcontainers==2.3.0, we can conclude that project[extra2] and project[project3] are incompatible.
+ ╰─▶ Because project[extra2] depends on sortedcontainers==2.3.0 and project[project3] depends on sortedcontainers==2.4.0, we can conclude that project[extra2] and project[project3] are incompatible.
And because your project requires project[extra2] and project[project3], we can conclude that your project's requirements are unsatisfiable.
"###);
@@ -538,7 +538,7 @@ fn extra_multiple_not_conflicting2() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project[project4] depends on sortedcontainers==2.4.0 and project[project3] depends on sortedcontainers==2.3.0, we can conclude that project[project3] and project[project4] are incompatible.
+ ╰─▶ Because project[project3] depends on sortedcontainers==2.3.0 and project[project4] depends on sortedcontainers==2.4.0, we can conclude that project[project3] and project[project4] are incompatible.
And because your project requires project[project3] and project[project4], we can conclude that your project's requirements are unsatisfiable.
"###);
@@ -582,7 +582,7 @@ fn extra_multiple_not_conflicting2() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project[project3] depends on sortedcontainers==2.3.0 and project[extra2] depends on sortedcontainers==2.4.0, we can conclude that project[extra2] and project[project3] are incompatible.
+ ╰─▶ Because project[extra2] depends on sortedcontainers==2.4.0 and project[project3] depends on sortedcontainers==2.3.0, we can conclude that project[extra2] and project[project3] are incompatible.
And because your project requires project[extra2] and project[project3], we can conclude that your project's requirements are unsatisfiable.
"###);
@@ -715,7 +715,7 @@ fn extra_multiple_independent() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project[project4] depends on anyio==4.2.0 and project[project3] depends on anyio==4.1.0, we can conclude that project[project3] and project[project4] are incompatible.
+ ╰─▶ Because project[project3] depends on anyio==4.1.0 and project[project4] depends on anyio==4.2.0, we can conclude that project[project3] and project[project4] are incompatible.
And because your project requires project[project3] and project[project4], we can conclude that your project's requirements are unsatisfiable.
"###);
@@ -755,7 +755,7 @@ fn extra_multiple_independent() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project[extra2] depends on sortedcontainers==2.4.0 and project[extra1] depends on sortedcontainers==2.3.0, we can conclude that project[extra1] and project[extra2] are incompatible.
+ ╰─▶ Because project[extra1] depends on sortedcontainers==2.3.0 and project[extra2] depends on sortedcontainers==2.4.0, we can conclude that project[extra1] and project[extra2] are incompatible.
And because your project requires project[extra1] and project[extra2], we can conclude that your project's requirements are unsatisfiable.
"###);
@@ -1047,7 +1047,7 @@ fn extra_config_change_ignore_lockfile() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project[extra2] depends on sortedcontainers==2.4.0 and project[extra1] depends on sortedcontainers==2.3.0, we can conclude that project[extra1] and project[extra2] are incompatible.
+ ╰─▶ Because project[extra1] depends on sortedcontainers==2.3.0 and project[extra2] depends on sortedcontainers==2.4.0, we can conclude that project[extra1] and project[extra2] are incompatible.
And because your project requires project[extra1] and project[extra2], we can conclude that your project's requirements are unsatisfiable.
"###);
@@ -1289,9 +1289,9 @@ fn extra_nested_across_workspace() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because dummy[extra2] depends on proxy1[extra2] and only proxy1[extra2]==0.1.0 is available, we can conclude that dummy[extra2] depends on proxy1[extra2]==0.1.0.
- And because proxy1[extra2]==0.1.0 depends on anyio==4.2.0 and proxy1[extra1]==0.1.0 depends on anyio==4.1.0, we can conclude that proxy1[extra1]==0.1.0 and dummy[extra2] are incompatible.
- And because only proxy1[extra1]==0.1.0 is available and dummysub[extra1] depends on proxy1[extra1], we can conclude that dummysub[extra1] and dummy[extra2] are incompatible.
+ ╰─▶ Because proxy1[extra1]==0.1.0 depends on anyio==4.1.0 and proxy1[extra2]==0.1.0 depends on anyio==4.2.0, we can conclude that proxy1[extra1]==0.1.0 and proxy1[extra2]==0.1.0 are incompatible.
+ And because only proxy1[extra1]==0.1.0 is available and dummysub[extra1] depends on proxy1[extra1], we can conclude that dummysub[extra1] and proxy1[extra2]==0.1.0 are incompatible.
+ And because only proxy1[extra2]==0.1.0 is available and dummy[extra2] depends on proxy1[extra2], we can conclude that dummy[extra2] and dummysub[extra1] are incompatible.
And because your workspace requires dummy[extra2] and dummysub[extra1], we can conclude that your workspace's requirements are unsatisfiable.
"###);
@@ -1415,7 +1415,7 @@ fn group_basic() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project:group2 depends on sortedcontainers==2.4.0 and project:group1 depends on sortedcontainers==2.3.0, we can conclude that project:group1 and project:group2 are incompatible.
+ ╰─▶ Because project:group1 depends on sortedcontainers==2.3.0 and project:group2 depends on sortedcontainers==2.4.0, we can conclude that project:group1 and project:group2 are incompatible.
And because your project requires project:group1 and project:group2, we can conclude that your project's requirements are unsatisfiable.
"###);
@@ -1793,7 +1793,7 @@ fn mixed() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because project:group1 depends on sortedcontainers==2.3.0 and project[extra1] depends on sortedcontainers==2.4.0, we can conclude that project[extra1] and project:group1 are incompatible.
+ ╰─▶ Because project[extra1] depends on sortedcontainers==2.4.0 and project:group1 depends on sortedcontainers==2.3.0, we can conclude that project:group1 and project[extra1] are incompatible.
And because your project requires project[extra1] and project:group1, we can conclude that your project's requirements are unsatisfiable.
"###);
diff --git a/crates/uv/tests/it/lock_scenarios.rs b/crates/uv/tests/it/lock_scenarios.rs
index febac6b07a09..4ac688b7f457 100644
--- a/crates/uv/tests/it/lock_scenarios.rs
+++ b/crates/uv/tests/it/lock_scenarios.rs
@@ -588,7 +588,7 @@ fn conflict_in_fork() -> Result<()> {
× No solution found when resolving dependencies for split (sys_platform == 'darwin'):
╰─▶ Because only package-b==1.0.0 is available and package-b==1.0.0 depends on package-d==1, we can conclude that all versions of package-b depend on package-d==1.
And because package-c==1.0.0 depends on package-d==2 and only package-c==1.0.0 is available, we can conclude that all versions of package-b and all versions of package-c are incompatible.
- And because package-a{sys_platform == 'darwin'}==1.0.0 depends on package-b and package-c, we can conclude that package-a{sys_platform == 'darwin'}==1.0.0 cannot be used.
+ And because package-a==1.0.0 depends on package-b and package-c, we can conclude that package-a==1.0.0 cannot be used.
And because only the following versions of package-a{sys_platform == 'darwin'} are available:
package-a{sys_platform == 'darwin'}==1.0.0
package-a{sys_platform == 'darwin'}>2
@@ -2940,7 +2940,7 @@ fn fork_non_local_fork_marker_direct() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because package-a{sys_platform == 'linux'}==1.0.0 depends on package-c<2.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0, we can conclude that package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible.
+ ╰─▶ Because package-b==1.0.0 depends on package-c>=2.0.0 and package-a==1.0.0 depends on package-c<2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible.
And because your project depends on package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0, we can conclude that your project's requirements are unsatisfiable.
"###
);
@@ -3015,8 +3015,10 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> {
╰─▶ Because package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0 and only the following versions of package-c{sys_platform == 'linux'} are available:
package-c{sys_platform == 'linux'}==1.0.0
package-c{sys_platform == 'linux'}>2.0.0
- we can conclude that package-a==1.0.0 depends on package-c{sys_platform == 'linux'}==1.0.0.
- And because only package-c{sys_platform == 'darwin'}<=2.0.0 is available and package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible.
+ we can conclude that package-a==1.0.0 depends on package-c{sys_platform == 'linux'}==1.0.0. (1)
+
+ Because only package-c{sys_platform == 'darwin'}<=2.0.0 is available and package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0, we can conclude that package-b==1.0.0 depends on package-c==2.0.0.
+ And because we know from (1) that package-a==1.0.0 depends on package-c{sys_platform == 'linux'}==1.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible.
And because your project depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that your project's requirements are unsatisfiable.
"###
);
diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs
index 87a3264068f3..0d997d65e98e 100644
--- a/crates/uv/tests/it/pip_compile.rs
+++ b/crates/uv/tests/it/pip_compile.rs
@@ -13622,9 +13622,9 @@ fn unsupported_requires_python_dynamic_metadata() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies for split (python_full_version >= '3.10'):
- ╰─▶ Because source-distribution{python_full_version >= '3.10'}==0.0.3 requires Python >=3.10 and you require source-distribution{python_full_version >= '3.10'}==0.0.3, we can conclude that your requirements are unsatisfiable.
+ ╰─▶ Because source-distribution==0.0.3 requires Python >=3.10 and you require source-distribution{python_full_version >= '3.10'}==0.0.3, we can conclude that your requirements are unsatisfiable.
- hint: The source distribution for `source-distribution{python_full_version >= '3.10'}` (v0.0.3) does not include static metadata. Generating metadata for this package requires Python >=3.10, but Python 3.8.[X] is installed.
+ hint: The source distribution for `source-distribution` (v0.0.3) does not include static metadata. Generating metadata for this package requires Python >=3.10, but Python 3.8.[X] is installed.
"###);
Ok(())
diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs
index 789ce3835e4d..93d410560a9c 100644
--- a/crates/uv/tests/it/pip_install.rs
+++ b/crates/uv/tests/it/pip_install.rs
@@ -6340,6 +6340,148 @@ fn require_hashes_override() -> Result<()> {
Ok(())
}
+/// Provide valid hashes for all dependencies with `--require-hashes` with accompanying markers.
+/// Critically, one package (`requests`) depends on another (`urllib3`).
+#[test]
+fn require_hashes_marker() -> Result<()> {
+ static EXCLUDE_NEWER: &str = "2025-01-01T00:00:00Z";
+
+ let context = TestContext::new("3.12");
+
+ // Write to a requirements file.
+ let requirements_txt = context.temp_dir.child("requirements.txt");
+ requirements_txt.write_str(indoc::indoc! {r"
+ certifi==2024.12.14 ; python_version >= '3.8' \
+ --hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56 \
+ --hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db
+ charset-normalizer==3.4.1 ; python_version >= '3.8' \
+ --hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \
+ --hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \
+ --hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \
+ --hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \
+ --hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \
+ --hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \
+ --hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \
+ --hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \
+ --hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \
+ --hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \
+ --hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \
+ --hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \
+ --hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \
+ --hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \
+ --hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \
+ --hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \
+ --hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \
+ --hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \
+ --hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \
+ --hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \
+ --hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \
+ --hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \
+ --hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \
+ --hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \
+ --hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \
+ --hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \
+ --hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \
+ --hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 \
+ --hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \
+ --hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \
+ --hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \
+ --hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \
+ --hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \
+ --hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \
+ --hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \
+ --hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \
+ --hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \
+ --hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \
+ --hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \
+ --hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \
+ --hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \
+ --hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \
+ --hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \
+ --hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \
+ --hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \
+ --hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \
+ --hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \
+ --hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \
+ --hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \
+ --hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \
+ --hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \
+ --hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \
+ --hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \
+ --hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \
+ --hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \
+ --hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \
+ --hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \
+ --hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \
+ --hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \
+ --hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \
+ --hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \
+ --hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \
+ --hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \
+ --hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \
+ --hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \
+ --hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \
+ --hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \
+ --hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \
+ --hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \
+ --hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \
+ --hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \
+ --hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \
+ --hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \
+ --hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \
+ --hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \
+ --hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \
+ --hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \
+ --hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \
+ --hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \
+ --hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \
+ --hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \
+ --hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \
+ --hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \
+ --hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \
+ --hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \
+ --hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \
+ --hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \
+ --hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \
+ --hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \
+ --hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \
+ --hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \
+ --hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616
+ idna==3.10 ; python_version >= '3.8' \
+ --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
+ --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
+ requests==2.32.3 ; python_version >= '3.8' \
+ --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
+ --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
+ urllib3==2.2.3 ; python_version >= '3.8' \
+ --hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
+ --hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9
+ "})?;
+
+ uv_snapshot!(context.pip_install()
+ .env(EnvVars::UV_EXCLUDE_NEWER, EXCLUDE_NEWER)
+ .arg("-r")
+ .arg("requirements.txt")
+ .arg("--require-hashes"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 5 packages in [TIME]
+ Prepared 5 packages in [TIME]
+ Installed 5 packages in [TIME]
+ + certifi==2024.12.14
+ + charset-normalizer==3.4.1
+ + idna==3.10
+ + requests==2.32.3
+ + urllib3==2.2.3
+ "###
+ );
+
+ Ok(())
+}
+
/// Provide valid hashes for all dependencies with `--require-hashes`.
#[test]
fn verify_hashes() -> Result<()> {
diff --git a/crates/uv/tests/it/pip_install_scenarios.rs b/crates/uv/tests/it/pip_install_scenarios.rs
index cd8882c870e7..4dce0c7337e2 100644
--- a/crates/uv/tests/it/pip_install_scenarios.rs
+++ b/crates/uv/tests/it/pip_install_scenarios.rs
@@ -836,8 +836,8 @@ fn extra_incompatible_with_extra() {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because only package-a[extra-c]==1.0.0 is available and package-a[extra-c]==1.0.0 depends on package-b==2.0.0, we can conclude that all versions of package-a[extra-c] depend on package-b==2.0.0.
- And because package-a[extra-b]==1.0.0 depends on package-b==1.0.0 and only package-a[extra-b]==1.0.0 is available, we can conclude that all versions of package-a[extra-b] and all versions of package-a[extra-c] are incompatible.
+ ╰─▶ Because package-a[extra-b]==1.0.0 depends on package-b==1.0.0 and package-a[extra-c]==1.0.0 depends on package-b==2.0.0, we can conclude that package-a[extra-b]==1.0.0 and package-a[extra-c]==1.0.0 are incompatible.
+ And because only package-a[extra-c]==1.0.0 is available and only package-a[extra-b]==1.0.0 is available, we can conclude that all versions of package-a[extra-b] and all versions of package-a[extra-c] are incompatible.
And because you require package-a[extra-b] and package-a[extra-c], we can conclude that your requirements are unsatisfiable.
"###);
diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap
index 67d4e4fe2bf2..c0b0a9ba8d5b 100644
--- a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap
+++ b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap
@@ -770,69 +770,26 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/da/ce/43f77dc8e7bbad02a9f88d07bf794eaf68359df756a28bb9f2f78e255bb1/dash_table-5.0.0-py3-none-any.whl", hash = "sha256:19036fa352bb1c11baf38068ec62d172f0515f73ca3276c79dee49b95ddc16c9", size = 3912 },
]
-[[package]]
-name = "datasets"
-version = "2.14.4"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13' and sys_platform == 'darwin'",
- "python_full_version == '3.12.*' and sys_platform == 'darwin'",
- "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
- "python_full_version == '3.11.*' and sys_platform == 'darwin'",
- "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
- "python_full_version == '3.10.*' and sys_platform == 'darwin'",
- "python_full_version == '3.10.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.10.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
- "python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin'",
- "python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version < '3.10' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and sys_platform != 'darwin' and sys_platform != 'linux')",
-]
-dependencies = [
- { name = "aiohttp", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "dill", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, extra = ["http"], marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "huggingface-hub", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "multiprocess", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "numpy", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "packaging", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "pandas", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "pyarrow", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "pyyaml", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "requests", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "tqdm", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "xxhash", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/1d/69/8cc725b5d38968fd118e4ce56a483b16e75b7793854c1a392ec4a34eeb31/datasets-2.14.4.tar.gz", hash = "sha256:ef29c2b5841de488cd343cfc26ab979bff77efa4d2285af51f1ad7db5c46a83b", size = 2178719 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/66/f8/38298237d18d4b6a8ee5dfe390e97bed5adb8e01ec6f9680c0ddf3066728/datasets-2.14.4-py3-none-any.whl", hash = "sha256:29336bd316a7d827ccd4da2236596279b20ca2ac78f64c04c9483da7cbc2459b", size = 519335 },
-]
-
[[package]]
name = "datasets"
version = "2.20.0"
source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
-]
dependencies = [
- { name = "aiohttp", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "dill", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "filelock", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, extra = ["http"], marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "huggingface-hub", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "multiprocess", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "numpy", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "packaging", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "pandas", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "pyarrow", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "pyarrow-hotfix", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "pyyaml", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "requests", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "tqdm", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "xxhash", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
+ { name = "aiohttp" },
+ { name = "dill" },
+ { name = "filelock" },
+ { name = "fsspec", extra = ["http"] },
+ { name = "huggingface-hub" },
+ { name = "multiprocess" },
+ { name = "numpy" },
+ { name = "packaging" },
+ { name = "pandas" },
+ { name = "pyarrow" },
+ { name = "pyarrow-hotfix" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "tqdm" },
+ { name = "xxhash" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d5/59/b94bfb5f6225c4c931cd516390b3f006e232a036a48337f72889c6c9ab27/datasets-2.20.0.tar.gz", hash = "sha256:3c4dbcd27e0f642b9d41d20ff2efa721a5e04b32b2ca4009e0fc9139e324553f", size = 2225757 }
wheels = [
@@ -945,7 +902,7 @@ wheels = [
[package.optional-dependencies]
epath = [
- { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
+ { name = "fsspec", marker = "python_full_version < '3.10'" },
{ name = "importlib-resources", marker = "python_full_version < '3.10'" },
{ name = "typing-extensions", marker = "python_full_version < '3.10'" },
{ name = "zipp", marker = "python_full_version < '3.10'" },
@@ -970,7 +927,7 @@ wheels = [
[package.optional-dependencies]
epath = [
- { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
+ { name = "fsspec", marker = "python_full_version == '3.10.*'" },
{ name = "importlib-resources", marker = "python_full_version == '3.10.*'" },
{ name = "typing-extensions", marker = "python_full_version == '3.10.*'" },
{ name = "zipp", marker = "python_full_version == '3.10.*'" },
@@ -1001,8 +958,7 @@ wheels = [
[package.optional-dependencies]
epath = [
- { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and python_full_version < '3.13') or (python_full_version >= '3.11' and sys_platform == 'darwin')" },
+ { name = "fsspec", marker = "python_full_version >= '3.11'" },
{ name = "importlib-resources", marker = "python_full_version >= '3.11'" },
{ name = "typing-extensions", marker = "python_full_version >= '3.11'" },
{ name = "zipp", marker = "python_full_version >= '3.11'" },
@@ -1016,11 +972,9 @@ name = "evaluate"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
+ { name = "datasets" },
{ name = "dill" },
- { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, extra = ["http"], marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, extra = ["http"], marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
+ { name = "fsspec", extra = ["http"] },
{ name = "huggingface-hub" },
{ name = "multiprocess" },
{ name = "numpy" },
@@ -1246,10 +1200,6 @@ wheels = [
name = "fsspec"
version = "2024.5.0"
source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
-]
sdist = { url = "https://files.pythonhosted.org/packages/71/28/cbf337fddd6f22686b7c2639b80e006accd904db152fe333fd98f4cd8d1e/fsspec-2024.5.0.tar.gz", hash = "sha256:1d021b0b0f933e3b3029ed808eb400c08ba101ca2de4b3483fbc9ca23fcee94a", size = 400066 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/a3/16e9fe32187e9c8bc7f9b7bcd9728529faa725231a0c96f2f98714ff2fc5/fsspec-2024.5.0-py3-none-any.whl", hash = "sha256:e0fdbc446d67e182f49a70b82cf7889028a63588fde6b222521f10937b2b670c", size = 316106 },
@@ -1257,36 +1207,7 @@ wheels = [
[package.optional-dependencies]
http = [
- { name = "aiohttp", marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
-]
-
-[[package]]
-name = "fsspec"
-version = "2024.6.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.13' and sys_platform == 'darwin'",
- "python_full_version == '3.12.*' and sys_platform == 'darwin'",
- "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
- "python_full_version == '3.11.*' and sys_platform == 'darwin'",
- "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
- "python_full_version == '3.10.*' and sys_platform == 'darwin'",
- "python_full_version == '3.10.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.10.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
- "python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin'",
- "python_full_version < '3.10' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "(python_full_version < '3.10' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.10' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10' and sys_platform != 'darwin' and sys_platform != 'linux')",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/90/b6/eba5024a9889fcfff396db543a34bef0ab9d002278f163129f9f01005960/fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49", size = 284584 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5e/44/73bea497ac69bafde2ee4269292fa3b41f1198f4bb7bbaaabde30ad29d4a/fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e", size = 177561 },
-]
-
-[package.optional-dependencies]
-http = [
- { name = "aiohttp", marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
+ { name = "aiohttp" },
]
[[package]]
@@ -1565,8 +1486,7 @@ version = "0.24.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "filelock" },
- { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
+ { name = "fsspec" },
{ name = "packaging" },
{ name = "pyyaml" },
{ name = "requests" },
@@ -3572,8 +3492,7 @@ wheels = [
[package.optional-dependencies]
tune = [
- { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
+ { name = "fsspec" },
{ name = "pandas" },
{ name = "pyarrow" },
{ name = "requests" },
@@ -4933,8 +4852,7 @@ version = "2.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "filelock" },
- { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
- { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
+ { name = "fsspec" },
{ name = "jinja2" },
{ name = "networkx", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "networkx", version = "3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
@@ -5074,8 +4992,7 @@ accelerate = [
]
agents = [
{ name = "accelerate" },
- { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
+ { name = "datasets" },
{ name = "diffusers" },
{ name = "opencv-python" },
{ name = "pillow" },
@@ -5129,8 +5046,7 @@ deepspeed-testing = [
{ name = "accelerate" },
{ name = "beautifulsoup4" },
{ name = "cookiecutter" },
- { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
+ { name = "datasets" },
{ name = "deepspeed" },
{ name = "dill" },
{ name = "evaluate" },
@@ -5161,8 +5077,7 @@ dev-dependencies = [
{ name = "beautifulsoup4" },
{ name = "codecarbon" },
{ name = "cookiecutter" },
- { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
+ { name = "datasets" },
{ name = "decord" },
{ name = "dill" },
{ name = "evaluate" },
@@ -5300,8 +5215,7 @@ optuna = [
{ name = "optuna" },
]
quality = [
- { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
+ { name = "datasets" },
{ name = "gitpython" },
{ name = "hf-doc-builder" },
{ name = "isort" },
@@ -5312,8 +5226,7 @@ ray = [
{ name = "ray", extra = ["tune"] },
]
retrieval = [
- { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or sys_platform == 'darwin'" },
- { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" },
+ { name = "datasets" },
{ name = "faiss-cpu" },
]
sagemaker = [
diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-uv-lock-output.snap b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-uv-lock-output.snap
index f1a57fbd8613..a3408eda5ba2 100644
--- a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-uv-lock-output.snap
+++ b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-uv-lock-output.snap
@@ -7,4 +7,4 @@ exit_code: 0
----- stdout -----
----- stderr -----
-Resolved 288 packages in [TIME]
+Resolved 286 packages in [TIME]
From e2c5526fbb5bf4192c6cee70ad9676fa5a3a44c6 Mon Sep 17 00:00:00 2001
From: Aria Desires
Date: Thu, 9 Jan 2025 16:01:23 -0500
Subject: [PATCH 090/135] replace backoff with backon (#10442)
This should be essentially the exact same behaviour, but backon is a
total API redesign, so things had to be expressed slightly differently.
Overall I think the code is more readable, which is nice.
Fixes #10001
---
Cargo.lock | 27 +++--
Cargo.toml | 2 +-
crates/uv-fs/Cargo.toml | 4 +-
crates/uv-fs/src/lib.rs | 220 ++++++++++++++++++++++------------------
4 files changed, 143 insertions(+), 110 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 198ac0923b59..5bdaed13303f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -295,16 +295,13 @@ dependencies = [
]
[[package]]
-name = "backoff"
-version = "0.4.0"
+name = "backon"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
+checksum = "ba5289ec98f68f28dd809fd601059e6aa908bb8f6108620930828283d4ee23d7"
dependencies = [
- "futures-core",
- "getrandom",
- "instant",
- "pin-project-lite",
- "rand",
+ "fastrand",
+ "gloo-timers",
"tokio",
]
@@ -1372,6 +1369,18 @@ dependencies = [
"walkdir",
]
+[[package]]
+name = "gloo-timers"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "goblin"
version = "0.9.2"
@@ -5055,7 +5064,7 @@ dependencies = [
name = "uv-fs"
version = "0.0.1"
dependencies = [
- "backoff",
+ "backon",
"cachedir",
"dunce",
"either",
diff --git a/Cargo.toml b/Cargo.toml
index bae813d11bda..6bba2c93e93b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -77,7 +77,7 @@ async-trait = { version = "0.1.82" }
async_http_range_reader = { version = "0.9.1" }
async_zip = { git = "https://github.com/charliermarsh/rs-async-zip", rev = "c909fda63fcafe4af496a07bfda28a5aae97e58d", features = ["deflate", "tokio"] }
axoupdater = { version = "0.9.0", default-features = false }
-backoff = { version = "0.4.0" }
+backon = { version = "1.3.0" }
base64 = { version = "0.22.1" }
bitflags = { version = "2.6.0" }
boxcar = { version = "0.2.5" }
diff --git a/crates/uv-fs/Cargo.toml b/crates/uv-fs/Cargo.toml
index a77c71e10907..8c58022ac12c 100644
--- a/crates/uv-fs/Cargo.toml
+++ b/crates/uv-fs/Cargo.toml
@@ -38,9 +38,9 @@ winsafe = { workspace = true }
rustix = { workspace = true }
[target.'cfg(windows)'.dependencies]
-backoff = { workspace = true }
+backon = { workspace = true }
junction = { workspace = true }
[features]
default = []
-tokio = ["dep:tokio", "fs-err/tokio", "backoff/tokio"]
+tokio = ["dep:tokio", "fs-err/tokio"]
diff --git a/crates/uv-fs/src/lib.rs b/crates/uv-fs/src/lib.rs
index 31abae0e3ce1..f7dadb6aeddb 100644
--- a/crates/uv-fs/src/lib.rs
+++ b/crates/uv-fs/src/lib.rs
@@ -187,10 +187,15 @@ pub fn copy_atomic_sync(from: impl AsRef, to: impl AsRef) -> std::io
}
#[cfg(windows)]
-fn backoff_file_move() -> backoff::ExponentialBackoff {
- backoff::ExponentialBackoffBuilder::default()
- .with_initial_interval(std::time::Duration::from_millis(10))
- .with_max_elapsed_time(Some(std::time::Duration::from_secs(10)))
+fn backoff_file_move() -> backon::ExponentialBackoff {
+ use backon::BackoffBuilder;
+ // This amounts to 10 total seconds of trying the operation.
+ // We start at 10 milliseconds and try 9 times, doubling each time, so the last try will take
+ // about 10*(2^9) milliseconds ~= 5 seconds. All other attempts combined should equal
+ // the length of the last attempt (because it's a sum of powers of 2), so 10 seconds overall.
+ backon::ExponentialBuilder::default()
+ .with_min_delay(std::time::Duration::from_millis(10))
+ .with_max_times(9)
.build()
}
@@ -202,6 +207,7 @@ pub async fn rename_with_retry(
) -> Result<(), std::io::Error> {
#[cfg(windows)]
{
+ use backon::Retryable;
// On Windows, antivirus software can lock files temporarily, making them inaccessible.
// This is most common for DLLs, and the common suggestion is to retry the operation with
// some backoff.
@@ -210,23 +216,21 @@ pub async fn rename_with_retry(
let from = from.as_ref();
let to = to.as_ref();
- let backoff = backoff_file_move();
- backoff::future::retry(backoff, || async move {
- match fs_err::rename(from, to) {
- Ok(()) => Ok(()),
- Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => {
- warn!(
- "Retrying rename from {} to {} due to transient error: {}",
- from.display(),
- to.display(),
- err
- );
- Err(backoff::Error::transient(err))
- }
- Err(err) => Err(backoff::Error::permanent(err)),
- }
- })
- .await
+ let rename = || async { fs_err::rename(from, to) };
+
+ rename
+ .retry(backoff_file_move())
+ .sleep(tokio::time::sleep)
+ .when(|e| e.kind() == std::io::ErrorKind::PermissionDenied)
+ .notify(|err, _dur| {
+ warn!(
+ "Retrying rename from {} to {} due to transient error: {}",
+ from.display(),
+ to.display(),
+ err
+ );
+ })
+ .await
}
#[cfg(not(windows))]
{
@@ -241,6 +245,7 @@ pub fn rename_with_retry_sync(
) -> Result<(), std::io::Error> {
#[cfg(windows)]
{
+ use backon::BlockingRetryable;
// On Windows, antivirus software can lock files temporarily, making them inaccessible.
// This is most common for DLLs, and the common suggestion is to retry the operation with
// some backoff.
@@ -248,32 +253,32 @@ pub fn rename_with_retry_sync(
// See: &
let from = from.as_ref();
let to = to.as_ref();
+ let rename = || fs_err::rename(from, to);
- let backoff = backoff_file_move();
- backoff::retry(backoff, || match fs_err::rename(from, to) {
- Ok(()) => Ok(()),
- Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => {
+ rename
+ .retry(backoff_file_move())
+ .sleep(std::thread::sleep)
+ .when(|err| err.kind() == std::io::ErrorKind::PermissionDenied)
+ .notify(|err, _dur| {
warn!(
"Retrying rename from {} to {} due to transient error: {}",
from.display(),
to.display(),
err
);
- Err(backoff::Error::transient(err))
- }
- Err(err) => Err(backoff::Error::permanent(err)),
- })
- .map_err(|err| {
- std::io::Error::new(
- std::io::ErrorKind::Other,
- format!(
- "Failed to rename {} to {}: {}",
- from.display(),
- to.display(),
- err
- ),
- )
- })
+ })
+ .call()
+ .map_err(|err| {
+ std::io::Error::new(
+ std::io::ErrorKind::Other,
+ format!(
+ "Failed to rename {} to {}: {}",
+ from.display(),
+ to.display(),
+ err
+ ),
+ )
+ })
}
#[cfg(not(windows))]
{
@@ -281,6 +286,15 @@ pub fn rename_with_retry_sync(
}
}
+/// Why a file persist failed
+#[cfg(windows)]
+enum PersistRetryError {
+ /// Something went wrong while persisting, maybe retry (contains error message)
+ Persist(String),
+ /// Something went wrong trying to retrieve the file to persist, we must bail
+ LostState,
+}
+
/// Persist a `NamedTempFile`, retrying (on Windows) if it fails due to transient operating system errors, in a synchronous context.
pub async fn persist_with_retry(
from: NamedTempFile,
@@ -288,6 +302,7 @@ pub async fn persist_with_retry(
) -> Result<(), std::io::Error> {
#[cfg(windows)]
{
+ use backon::Retryable;
// On Windows, antivirus software can lock files temporarily, making them inaccessible.
// This is most common for DLLs, and the common suggestion is to retry the operation with
// some backoff.
@@ -300,52 +315,55 @@ pub async fn persist_with_retry(
// So we will update the `from` optional value in safe and borrow-checker friendly way every retry
// Allows us to use the NamedTempFile inside a FnMut closure used for backoff::retry
let mut from = Some(from);
-
- let backoff = backoff_file_move();
- let persisted = backoff::future::retry(backoff, move || {
+ let persist = move || {
// Needed because we cannot move out of `from`, a captured variable in an `FnMut` closure, and then pass it to the async move block
- let mut from = from.take();
+ let mut from: Option = from.take();
async move {
if let Some(file) = from.take() {
file.persist(to).map_err(|err| {
let error_message = err.to_string();
- warn!(
- "Retrying to persist temporary file to {}: {}",
- to.display(),
- error_message
- );
-
// Set back the NamedTempFile returned back by the Error
from = Some(err.file);
-
- backoff::Error::transient(std::io::Error::new(
- std::io::ErrorKind::Other,
- format!(
- "Failed to persist temporary file to {}: {}",
- to.display(),
- error_message
- ),
- ))
+ PersistRetryError::Persist(error_message)
})
} else {
- Err(backoff::Error::permanent(std::io::Error::new(
- std::io::ErrorKind::Other,
- format!(
- "Failed to retrieve temporary file while trying to persist to {}",
- to.display()
- ),
- )))
+ Err(PersistRetryError::LostState)
}
}
- })
- .await;
+ };
+
+ let persisted = persist
+ .retry(backoff_file_move())
+ .sleep(tokio::time::sleep)
+ .when(|err| matches!(err, PersistRetryError::Persist(_)))
+ .notify(|err, _dur| {
+ if let PersistRetryError::Persist(error_message) = err {
+ warn!(
+ "Retrying to persist temporary file to {}: {}",
+ to.display(),
+ error_message,
+ );
+ };
+ })
+ .await;
match persisted {
Ok(_) => Ok(()),
- Err(err) => Err(std::io::Error::new(
+ Err(PersistRetryError::Persist(error_message)) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
- err.to_string(),
+ format!(
+ "Failed to persist temporary file to {}: {}",
+ to.display(),
+ error_message,
+ ),
+ )),
+ Err(PersistRetryError::LostState) => Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ format!(
+ "Failed to retrieve temporary file while trying to persist to {}",
+ to.display()
+ ),
)),
}
}
@@ -362,6 +380,7 @@ pub fn persist_with_retry_sync(
) -> Result<(), std::io::Error> {
#[cfg(windows)]
{
+ use backon::BlockingRetryable;
// On Windows, antivirus software can lock files temporarily, making them inaccessible.
// This is most common for DLLs, and the common suggestion is to retry the operation with
// some backoff.
@@ -374,46 +393,51 @@ pub fn persist_with_retry_sync(
// So we will update the `from` optional value in safe and borrow-checker friendly way every retry
// Allows us to use the NamedTempFile inside a FnMut closure used for backoff::retry
let mut from = Some(from);
-
- let backoff = backoff_file_move();
- let persisted = backoff::retry(backoff, move || {
+ let persist = || {
+ // Needed because we cannot move out of `from`, a captured variable in an `FnMut` closure, and then pass it to the async move block
if let Some(file) = from.take() {
file.persist(to).map_err(|err| {
let error_message = err.to_string();
- warn!(
- "Retrying to persist temporary file to {}: {}",
- to.display(),
- error_message
- );
-
// Set back the NamedTempFile returned back by the Error
from = Some(err.file);
-
- backoff::Error::transient(std::io::Error::new(
- std::io::ErrorKind::Other,
- format!(
- "Failed to persist temporary file to {}: {}",
- to.display(),
- error_message
- ),
- ))
+ PersistRetryError::Persist(error_message)
})
} else {
- Err(backoff::Error::permanent(std::io::Error::new(
- std::io::ErrorKind::Other,
- format!(
- "Failed to retrieve temporary file while trying to persist to {}",
- to.display()
- ),
- )))
+ Err(PersistRetryError::LostState)
}
- });
+ };
+
+ let persisted = persist
+ .retry(backoff_file_move())
+ .sleep(std::thread::sleep)
+ .when(|err| matches!(err, PersistRetryError::Persist(_)))
+ .notify(|err, _dur| {
+ if let PersistRetryError::Persist(error_message) = err {
+ warn!(
+ "Retrying to persist temporary file to {}: {}",
+ to.display(),
+ error_message,
+ );
+ };
+ })
+ .call();
match persisted {
Ok(_) => Ok(()),
- Err(err) => Err(std::io::Error::new(
+ Err(PersistRetryError::Persist(error_message)) => Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ format!(
+ "Failed to persist temporary file to {}: {}",
+ to.display(),
+ error_message,
+ ),
+ )),
+ Err(PersistRetryError::LostState) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
- err.to_string(),
+ format!(
+ "Failed to retrieve temporary file while trying to persist to {}",
+ to.display()
+ ),
)),
}
}
From 7096e83812fc5103008a60f21fbcf323eeddac28 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 16:19:49 -0500
Subject: [PATCH 091/135] Respect sentinels in prioritization (#10443)
## Summary
If a user provides a constraint like `flask==3.0.0`, that gets expanded
to `[3.0.0, 3.0.0+[max])`. So it's not a _singleton_, but it should be
treated as such for the purposes of prioritization, since in practice it
will almost always map to a single version.
---
crates/uv-resolver/src/error.rs | 23 +++++++++++++---
crates/uv-resolver/src/pubgrub/priority.rs | 20 +++++++-------
crates/uv-resolver/src/version_map.rs | 2 --
crates/uv/tests/it/pip_install_scenarios.rs | 8 +++---
...it__ecosystem__transformers-lock-file.snap | 27 +++++++++----------
5 files changed, 45 insertions(+), 35 deletions(-)
diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs
index de9c15646a9a..be35a4fc9ef5 100644
--- a/crates/uv-resolver/src/error.rs
+++ b/crates/uv-resolver/src/error.rs
@@ -235,7 +235,7 @@ impl NoSolutionError {
match derivation_tree {
DerivationTree::External(External::NotRoot(_, _)) => Some(derivation_tree),
DerivationTree::External(External::NoVersions(package, versions)) => {
- if SentinelRange::from(&versions).is_sentinel() {
+ if SentinelRange::from(&versions).is_complement() {
return None;
}
@@ -977,13 +977,30 @@ impl<'range> From<&'range Range> for SentinelRange<'range> {
}
impl SentinelRange<'_> {
- /// Returns `true` if the range appears to be, e.g., `>1.0.0, <1.0.0+[max]`.
+ /// Returns `true` if the range appears to be, e.g., `>=1.0.0, <1.0.0+[max]`.
pub fn is_sentinel(&self) -> bool {
+ self.0.iter().all(|(lower, upper)| {
+ let (Bound::Included(lower), Bound::Excluded(upper)) = (lower, upper) else {
+ return false;
+ };
+ if !lower.local().is_empty() {
+ return false;
+ }
+ if upper.local() != LocalVersionSlice::Max {
+ return false;
+ }
+ *lower == upper.clone().without_local()
+ })
+ }
+
+ /// Returns `true` if the range appears to be, e.g., `>1.0.0, <1.0.0+[max]` (i.e., a sentinel
+ /// range with the non-local version removed).
+ pub fn is_complement(&self) -> bool {
self.0.iter().all(|(lower, upper)| {
let (Bound::Excluded(lower), Bound::Excluded(upper)) = (lower, upper) else {
return false;
};
- if lower.local() == LocalVersionSlice::Max {
+ if !lower.local().is_empty() {
return false;
}
if upper.local() != LocalVersionSlice::Max {
diff --git a/crates/uv-resolver/src/pubgrub/priority.rs b/crates/uv-resolver/src/pubgrub/priority.rs
index 80b1acc38c5b..5fc5a97575b6 100644
--- a/crates/uv-resolver/src/pubgrub/priority.rs
+++ b/crates/uv-resolver/src/pubgrub/priority.rs
@@ -10,6 +10,7 @@ use uv_pep440::Version;
use crate::fork_urls::ForkUrls;
use crate::pubgrub::package::PubGrubPackage;
use crate::pubgrub::PubGrubPackageInner;
+use crate::SentinelRange;
/// A prioritization map to guide the PubGrub resolution process.
///
@@ -57,8 +58,9 @@ impl PubGrubPriorities {
// Compute the priority.
let priority = if urls.get(name).is_some() {
PubGrubPriority::DirectUrl(Reverse(index))
- } else if version.as_singleton().is_some() {
- // TODO(charlie): Take local version ranges into account (e.g., `[2.0, 2.0+[max])`).
+ } else if version.as_singleton().is_some()
+ || SentinelRange::from(version).is_sentinel()
+ {
PubGrubPriority::Singleton(Reverse(index))
} else {
// Keep the conflict-causing packages to avoid loops where we seesaw between
@@ -81,8 +83,9 @@ impl PubGrubPriorities {
// Compute the priority.
let priority = if urls.get(name).is_some() {
PubGrubPriority::DirectUrl(Reverse(next))
- } else if version.as_singleton().is_some() {
- // TODO(charlie): Take local version ranges into account (e.g., `[2.0, 2.0+[max])`).
+ } else if version.as_singleton().is_some()
+ || SentinelRange::from(version).is_sentinel()
+ {
PubGrubPriority::Singleton(Reverse(next))
} else {
PubGrubPriority::Unspecified(Reverse(next))
@@ -140,10 +143,7 @@ impl PubGrubPriorities {
};
match self.package_priority.entry(name.clone()) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
- if matches!(
- entry.get(),
- PubGrubPriority::ConflictEarly(_) | PubGrubPriority::Singleton(_)
- ) {
+ if matches!(entry.get(), PubGrubPriority::ConflictEarly(_)) {
// Already in the right category
return false;
};
@@ -178,9 +178,7 @@ impl PubGrubPriorities {
// The ConflictEarly` match avoids infinite loops.
if matches!(
entry.get(),
- PubGrubPriority::ConflictLate(_)
- | PubGrubPriority::ConflictEarly(_)
- | PubGrubPriority::Singleton(_)
+ PubGrubPriority::ConflictLate(_) | PubGrubPriority::ConflictEarly(_)
) {
// Already in the right category
return false;
diff --git a/crates/uv-resolver/src/version_map.rs b/crates/uv-resolver/src/version_map.rs
index 36ece551f3aa..bf9a87983242 100644
--- a/crates/uv-resolver/src/version_map.rs
+++ b/crates/uv-resolver/src/version_map.rs
@@ -162,8 +162,6 @@ impl VersionMap {
range: &Ranges,
) -> impl DoubleEndedIterator
- {
// Performance optimization: If we only have a single version, return that version directly.
- //
- // TODO(charlie): Now that we use local version sentinels, does this ever trigger?
if let Some(version) = range.as_singleton() {
either::Either::Left(match self.inner {
VersionMapInner::Eager(ref eager) => {
diff --git a/crates/uv/tests/it/pip_install_scenarios.rs b/crates/uv/tests/it/pip_install_scenarios.rs
index 4dce0c7337e2..efd65ae30cca 100644
--- a/crates/uv/tests/it/pip_install_scenarios.rs
+++ b/crates/uv/tests/it/pip_install_scenarios.rs
@@ -531,17 +531,17 @@ fn excluded_only_compatible_version() {
----- stderr -----
× No solution found when resolving dependencies:
- ╰─▶ Because only the following versions of package-a are available:
+ ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available:
package-a==1.0.0
package-a==2.0.0
package-a==3.0.0
- and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0.
+ we can conclude that package-a<2.0.0 depends on package-b==1.0.0.
And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that all of:
package-a<2.0.0
package-a>2.0.0
depend on one of:
- package-b==1.0.0
- package-b==3.0.0
+ package-b<=1.0.0
+ package-b>=3.0.0
And because you require one of:
package-a<2.0.0
diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap
index c0b0a9ba8d5b..2ed54f7ce7e7 100644
--- a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap
+++ b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap
@@ -3001,20 +3001,18 @@ wheels = [
[[package]]
name = "protobuf"
-version = "3.20.3"
+version = "4.25.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/55/5b/e3d951e34f8356e5feecacd12a8e3b258a1da6d9a03ad1770f28925f29bc/protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2", size = 216768 }
+sdist = { url = "https://files.pythonhosted.org/packages/e8/ab/cb61a4b87b2e7e6c312dce33602bd5884797fd054e0e53205f1c27cf0f66/protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d", size = 380283 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/28/55/b80e8567ec327c060fa39b242392e25690c8899c489ecd7bb65b46b7bb55/protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99", size = 918427 },
- { url = "https://files.pythonhosted.org/packages/31/be/80a9c6f16dfa4d41be3edbe655349778ae30882407fa8275eb46b4d34854/protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e", size = 1051042 },
- { url = "https://files.pythonhosted.org/packages/db/96/948d3fcc1fa816e7ae1d27af59b9d8c5c5e582f3994fd14394f31da95b99/protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c", size = 780167 },
- { url = "https://files.pythonhosted.org/packages/6f/5e/fc6feb366b0a9f28e0a2de3b062667c521cd9517d4ff55077b8f351ba2f3/protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7", size = 904029 },
- { url = "https://files.pythonhosted.org/packages/00/e7/d23c439c55c90ae2e52184363162f7079ca3e7d86205b411d4e9dc266f81/protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b", size = 982826 },
- { url = "https://files.pythonhosted.org/packages/99/25/5825472ecd911f4ac2ac4e9ab039a48b6d03874e2add92fb633e080bf3eb/protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b", size = 918423 },
- { url = "https://files.pythonhosted.org/packages/c7/df/ec3ecb8c940b36121c7b77c10acebf3d1c736498aa2f1fe3b6231ee44e76/protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402", size = 1019250 },
- { url = "https://files.pythonhosted.org/packages/36/8b/433071fed0058322090a55021bdc8da76d16c7bc9823f5795797803dd6d0/protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480", size = 780270 },
- { url = "https://files.pythonhosted.org/packages/11/a5/e52b731415ad6ef3d841e9e6e337a690249e800cc7c06f0749afab26348c/protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7", size = 904215 },
- { url = "https://files.pythonhosted.org/packages/8d/14/619e24a4c70df2901e1f4dbc50a6291eb63a759172558df326347dce1f0d/protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", size = 162128 },
+ { url = "https://files.pythonhosted.org/packages/c8/43/27b48d9040763b78177d3083e16c70dba6e3c3ee2af64b659f6332c2b06e/protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4", size = 392409 },
+ { url = "https://files.pythonhosted.org/packages/0c/d4/589d673ada9c4c62d5f155218d7ff7ac796efb9c6af95b0bd29d438ae16e/protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d", size = 413398 },
+ { url = "https://files.pythonhosted.org/packages/34/ca/bf85ffe3dd16f1f2aaa6c006da8118800209af3da160ae4d4f47500eabd9/protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b", size = 394160 },
+ { url = "https://files.pythonhosted.org/packages/68/1d/e8961af9a8e534d66672318d6b70ea8e3391a6b13e16a29b039e4a99c214/protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835", size = 293700 },
+ { url = "https://files.pythonhosted.org/packages/ca/6c/cc7ab2fb3a4a7f07f211d8a7bbb76bba633eb09b148296dbd4281e217f95/protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040", size = 294612 },
+ { url = "https://files.pythonhosted.org/packages/a4/b5/f7e2460dec8347d67e6108bef6ad3291c76e38c898a1087e2c836c02951e/protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca", size = 392490 },
+ { url = "https://files.pythonhosted.org/packages/c7/0b/15bd1a224e5e5744a0dcccf11bcd5dc1405877be38e477b1359d7c2c3737/protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f", size = 413357 },
+ { url = "https://files.pythonhosted.org/packages/b5/95/0ba7f66934a0a798006f06fc3d74816da2b7a2bcfd9b98c53d26f684c89e/protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978", size = 156464 },
]
[[package]]
@@ -4700,18 +4698,17 @@ wheels = [
[[package]]
name = "tf2onnx"
-version = "1.16.1"
+version = "1.8.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "flatbuffers" },
{ name = "numpy" },
{ name = "onnx" },
- { name = "protobuf" },
{ name = "requests" },
{ name = "six" },
]
wheels = [
- { url = "https://files.pythonhosted.org/packages/3f/48/826db3d02645d84e7ee5d5ce8407f771057d40fe224d9c3e89536674ccef/tf2onnx-1.16.1-py3-none-any.whl", hash = "sha256:90fb5f62575896d47884d27dc313cfebff36b8783e1094335ad00824ce923a8a", size = 455820 },
+ { url = "https://files.pythonhosted.org/packages/db/32/33ce509a79c207a39cf04bfa3ec3353da15d1e6553a6ad912f117cc29130/tf2onnx-1.8.4-py3-none-any.whl", hash = "sha256:1ebabb96c914da76e23222b6107a8b248a024bf259d77f027e6690099512d457", size = 345298 },
]
[[package]]
From d77598a08ca230350d1023c8e0e3f0cfcd85e704 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 16:43:09 -0500
Subject: [PATCH 092/135] Use Windows-specific instructions in Jupyter guide
(#10446)
## Summary
Closes https://github.com/astral-sh/uv/issues/10407.
---
docs/guides/integration/jupyter.md | 28 +++++++++++++++++++++-------
docs/guides/projects.md | 23 +++++++++++++++++------
2 files changed, 38 insertions(+), 13 deletions(-)
diff --git a/docs/guides/integration/jupyter.md b/docs/guides/integration/jupyter.md
index f1292a58123c..a81890287811 100644
--- a/docs/guides/integration/jupyter.md
+++ b/docs/guides/integration/jupyter.md
@@ -106,12 +106,23 @@ If you need to run Jupyter in a virtual environment that isn't associated with a
[project](../../concepts/projects/index.md) (e.g., has no `pyproject.toml` or `uv.lock`), you can do
so by adding Jupyter to the environment directly. For example:
-```console
-$ uv venv --seed
-$ uv pip install pydantic
-$ uv pip install jupyterlab
-$ .venv/bin/jupyter lab
-```
+=== "macOS and Linux"
+
+ ```console
+ $ uv venv --seed
+ $ uv pip install pydantic
+ $ uv pip install jupyterlab
+ $ .venv/bin/jupyter lab
+ ```
+
+=== "Windows"
+
+ ```powershell
+ uv venv --seed
+ uv pip install pydantic
+ uv pip install jupyterlab
+ .venv\Scripts\jupyter lab
+ ```
From here, `import pydantic` will work within the notebook, and you can install additional packages
via `!uv pip install`, or even `!pip install`.
@@ -125,10 +136,13 @@ project, as in the following:
```console
# Create a project.
$ uv init project
+
# Move into the project directory.
$ cd project
+
# Add ipykernel as a dev dependency.
$ uv add --dev ipykernel
+
# Open the project in VS Code.
$ code .
```
@@ -136,7 +150,7 @@ $ code .
Once the project directory is open in VS Code, you can create a new Jupyter notebook by selecting
"Create: New Jupyter Notebook" from the command palette. When prompted to select a kernel, choose
"Python Environments" and select the virtual environment you created earlier (e.g.,
-`.venv/bin/python`).
+`.venv/bin/python` on macOS and Linux, or `.venv\Scripts\python` on Windows).
!!! note
diff --git a/docs/guides/projects.md b/docs/guides/projects.md
index 867a25238b44..a9d219162b55 100644
--- a/docs/guides/projects.md
+++ b/docs/guides/projects.md
@@ -177,12 +177,23 @@ $ uv run example.py
Alternatively, you can use `uv sync` to manually update the environment then activate it before
executing a command:
-```console
-$ uv sync
-$ source .venv/bin/activate
-$ flask run -p 3000
-$ python example.py
-```
+=== "macOS and Linux"
+
+ ```console
+ $ uv sync
+ $ source .venv/bin/activate
+ $ flask run -p 3000
+ $ python example.py
+ ```
+
+=== "Windows"
+
+ ```powershell
+ uv sync
+ source .venv\Scripts\activate
+ flask run -p 3000
+ python example.py
+ ```
!!! note
From 452cafc63988e797f4fc897112a911196ba47e45 Mon Sep 17 00:00:00 2001
From: Ahmed Ilyas
Date: Thu, 9 Jan 2025 22:48:06 +0100
Subject: [PATCH 093/135] Improve tool list output when tool environment is
broken (#10409)
## Summary
Closes #9579
## Test Plan
`cargo test`
---
crates/uv/src/commands/tool/list.rs | 9 ++++++++-
crates/uv/tests/it/tool_list.rs | 2 +-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/crates/uv/src/commands/tool/list.rs b/crates/uv/src/commands/tool/list.rs
index 35e5860af239..721ed9de25be 100644
--- a/crates/uv/src/commands/tool/list.rs
+++ b/crates/uv/src/commands/tool/list.rs
@@ -51,7 +51,14 @@ pub(crate) async fn list(
let version = match installed_tools.version(&name, cache) {
Ok(version) => version,
Err(e) => {
- writeln!(printer.stderr(), "{e}")?;
+ if let uv_tool::Error::EnvironmentError(e) = e {
+ warn_user!(
+ "{e} (run `{}` to reinstall)",
+ format!("uv tool install {name} --reinstall").green()
+ );
+ } else {
+ writeln!(printer.stderr(), "{e}")?;
+ }
continue;
}
};
diff --git a/crates/uv/tests/it/tool_list.rs b/crates/uv/tests/it/tool_list.rs
index 71e17f87c78a..d0b0d6b9389f 100644
--- a/crates/uv/tests/it/tool_list.rs
+++ b/crates/uv/tests/it/tool_list.rs
@@ -156,7 +156,7 @@ fn tool_list_bad_environment() -> Result<()> {
- ruff
----- stderr -----
- Invalid environment at `tools/black`: missing Python executable at `tools/black/[BIN]/python`
+ warning: Invalid environment at `tools/black`: missing Python executable at `tools/black/[BIN]/python` (run `uv tool install black --reinstall` to reinstall)
"###
);
From 129a75e2d08df0b992a21d98d660bc1212dea468 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 17:39:00 -0500
Subject: [PATCH 094/135] Add meta descriptions for remaining guides (#10450)
## Summary
Closes https://github.com/astral-sh/uv/issues/10418.
---
docs/guides/integration/alternative-indexes.md | 7 +++++++
docs/guides/integration/dependency-bots.md | 5 +++++
docs/guides/integration/github.md | 7 +++++++
docs/guides/integration/gitlab.md | 6 ++++++
docs/guides/integration/pytorch.md | 7 +++++++
5 files changed, 32 insertions(+)
diff --git a/docs/guides/integration/alternative-indexes.md b/docs/guides/integration/alternative-indexes.md
index 9e8142891056..b686ed5a9b67 100644
--- a/docs/guides/integration/alternative-indexes.md
+++ b/docs/guides/integration/alternative-indexes.md
@@ -1,3 +1,10 @@
+---
+title: Using alternative package indexes
+description:
+ A guide to using alternative package indexes with uv, including Azure Artifacts, Google Artifact
+ Registry, AWS CodeArtifact, and more.
+---
+
# Using alternative package indexes
While uv uses the official Python Package Index (PyPI) by default, it also supports alternative
diff --git a/docs/guides/integration/dependency-bots.md b/docs/guides/integration/dependency-bots.md
index 63cff791052d..90a45f61e1eb 100644
--- a/docs/guides/integration/dependency-bots.md
+++ b/docs/guides/integration/dependency-bots.md
@@ -1,3 +1,8 @@
+---
+title: Using uv with dependency bots
+description: A guide to using uv with dependency bots like Renovate and Dependabot.
+---
+
# Dependency bots
It is considered best practice to regularly update dependencies, to avoid being exposed to
diff --git a/docs/guides/integration/github.md b/docs/guides/integration/github.md
index 26204abd1f0c..f8543c7750cc 100644
--- a/docs/guides/integration/github.md
+++ b/docs/guides/integration/github.md
@@ -1,3 +1,10 @@
+---
+title: Using uv in GitHub Actions
+description:
+ A guide to using uv in GitHub Actions, including installation, setting up Python, installing
+ dependencies, and more.
+---
+
# Using uv in GitHub Actions
## Installation
diff --git a/docs/guides/integration/gitlab.md b/docs/guides/integration/gitlab.md
index 60bff070e9f5..ef84efbcf7c0 100644
--- a/docs/guides/integration/gitlab.md
+++ b/docs/guides/integration/gitlab.md
@@ -1,3 +1,9 @@
+---
+title: Using uv in GitLab CI/CD
+description: A guide to using uv in GitLab CI/CD, including installation, setting up Python,
+ installing dependencies, and more.
+---
+
# Using uv in GitLab CI/CD
## Using the uv image
diff --git a/docs/guides/integration/pytorch.md b/docs/guides/integration/pytorch.md
index 8b71d8567e41..ea1104706261 100644
--- a/docs/guides/integration/pytorch.md
+++ b/docs/guides/integration/pytorch.md
@@ -1,3 +1,10 @@
+---
+title: Using uv with PyTorch
+description:
+ A guide to using uv with PyTorch, including installing PyTorch, configuring per-platform and
+ per-accelerator builds, and more.
+---
+
# Using uv with PyTorch
The [PyTorch](https://pytorch.org/) ecosystem is a popular choice for deep learning research and
From 22222e945f28f4e0d2e7ba70605620caa7b744cc Mon Sep 17 00:00:00 2001
From: Zanie Blue
Date: Thu, 9 Jan 2025 16:39:37 -0600
Subject: [PATCH 095/135] Allow reading `--with-requirements` from stdin in `uv
add` and `uv run` (#10447)
For some reason this was banned when originally added (I did not see
discussion about it). I think it's fine to allow. With `uv run`, there's
a bit of nuance because we also allow the script to be read from stdin.
---
crates/uv/src/commands/project/add.rs | 5 ----
crates/uv/src/commands/project/run.rs | 15 ++++++++++-
crates/uv/tests/it/edit.rs | 13 +++++++++
crates/uv/tests/it/run.rs | 38 ++++++++++++++++++++++++---
4 files changed, 62 insertions(+), 9 deletions(-)
diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs
index 227bf676faed..0191c3a0745c 100644
--- a/crates/uv/src/commands/project/add.rs
+++ b/crates/uv/src/commands/project/add.rs
@@ -98,11 +98,6 @@ pub(crate) async fn add(
RequirementsSource::SetupCfg(_) => {
bail!("Adding requirements from a `setup.cfg` is not supported in `uv add`");
}
- RequirementsSource::RequirementsTxt(path) => {
- if path == Path::new("-") {
- bail!("Reading requirements from stdin is not supported in `uv add`");
- }
- }
_ => {}
}
}
diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs
index 27a2e22b1bcb..dd327ede6c91 100644
--- a/crates/uv/src/commands/project/run.rs
+++ b/crates/uv/src/commands/project/run.rs
@@ -93,6 +93,7 @@ pub(crate) async fn run(
) -> anyhow::Result {
// These cases seem quite complex because (in theory) they should change the "current package".
// Let's ban them entirely for now.
+ let mut requirements_from_stdin: bool = false;
for source in &requirements {
match source {
RequirementsSource::PyprojectToml(_) => {
@@ -106,13 +107,22 @@ pub(crate) async fn run(
}
RequirementsSource::RequirementsTxt(path) => {
if path == Path::new("-") {
- bail!("Reading requirements from stdin is not supported in `uv run`");
+ requirements_from_stdin = true;
}
}
_ => {}
}
}
+ // Fail early if stdin is used for multiple purposes.
+ if matches!(
+ command,
+ Some(RunCommand::PythonStdin(..) | RunCommand::PythonGuiStdin(..))
+ ) && requirements_from_stdin
+ {
+ bail!("Cannot read both requirements file and script from stdin");
+ }
+
// Initialize any shared state.
let state = SharedState::default();
@@ -169,6 +179,9 @@ pub(crate) async fn run(
)?;
}
Pep723Item::Stdin(_) => {
+ if requirements_from_stdin {
+ bail!("Cannot read both requirements file and script from stdin");
+ }
writeln!(
printer.stderr(),
"Reading inline script metadata from `{}`",
diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs
index e3e1d8b39012..537f2f83d454 100644
--- a/crates/uv/tests/it/edit.rs
+++ b/crates/uv/tests/it/edit.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::disallowed_types)]
+
use anyhow::Result;
use assert_cmd::assert::OutputAssertExt;
use assert_fs::prelude::*;
@@ -4611,6 +4613,17 @@ fn add_requirements_file() -> Result<()> {
);
});
+ // Passing stdin should succeed
+ uv_snapshot!(context.filters(), context.add().arg("-r").arg("-").stdin(std::fs::File::open(requirements_txt)?), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved [N] packages in [TIME]
+ Audited [N] packages in [TIME]
+ "###);
+
// Passing a `setup.py` should fail.
uv_snapshot!(context.filters(), context.add().arg("-r").arg("setup.py"), @r###"
success: false
diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs
index fd506a7a2853..d915d58522b7 100644
--- a/crates/uv/tests/it/run.rs
+++ b/crates/uv/tests/it/run.rs
@@ -2124,19 +2124,51 @@ fn run_requirements_txt() -> Result<()> {
+ sniffio==1.3.1
"###);
- // But reject `-` as a requirements file.
+ // Allow `-` for stdin.
uv_snapshot!(context.filters(), context.run()
.arg("--with-requirements")
.arg("-")
.arg("--with")
.arg("iniconfig")
- .arg("main.py"), @r###"
+ .arg("main.py")
+ .stdin(std::fs::File::open(&requirements_txt)?), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 6 packages in [TIME]
+ Audited 4 packages in [TIME]
+ Resolved 2 packages in [TIME]
+ "###);
+
+ // But not in combination with reading the script from stdin
+ uv_snapshot!(context.filters(), context.run()
+ .arg("--with-requirements")
+ .arg("-")
+ // The script to run
+ .arg("-")
+ .stdin(std::fs::File::open(&requirements_txt)?), @r###"
+ success: false
+ exit_code: 2
+ ----- stdout -----
+
+ ----- stderr -----
+ error: Cannot read both requirements file and script from stdin
+ "###);
+
+ uv_snapshot!(context.filters(), context.run()
+ .arg("--with-requirements")
+ .arg("-")
+ .arg("--script")
+ .arg("-")
+ .stdin(std::fs::File::open(&requirements_txt)?), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
- error: Reading requirements from stdin is not supported in `uv run`
+ error: Cannot read both requirements file and script from stdin
"###);
Ok(())
From c5e536f0ec66515e20dacf237817914f3b56fc79 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 17:45:13 -0500
Subject: [PATCH 096/135] De-duplicate result handling in Simple API responses
(#10449)
## Summary
See: https://github.com/astral-sh/uv/pull/10432#issuecomment-2581084234
---
crates/uv-client/src/registry_client.rs | 122 +++++++++++-------------
1 file changed, 54 insertions(+), 68 deletions(-)
diff --git a/crates/uv-client/src/registry_client.rs b/crates/uv-client/src/registry_client.rs
index d9812253a2d4..6901123abe07 100644
--- a/crates/uv-client/src/registry_client.rs
+++ b/crates/uv-client/src/registry_client.rs
@@ -253,80 +253,36 @@ impl RegistryClient {
// If we're searching for the first index that contains the package, fetch serially.
IndexStrategy::FirstIndex => {
for index in it {
- match self.simple_single_index(package_name, index).await {
- Ok(metadata) => {
- results.push((index, metadata));
- break;
- }
- Err(err) => match err.into_kind() {
- // The package could not be found in the remote index.
- ErrorKind::WrappedReqwestError(url, err) => match err.status() {
- Some(StatusCode::NOT_FOUND) => {}
- Some(StatusCode::UNAUTHORIZED) => {
- capabilities.set_unauthorized(index.clone());
- }
- Some(StatusCode::FORBIDDEN) => {
- capabilities.set_forbidden(index.clone());
- }
- _ => return Err(ErrorKind::WrappedReqwestError(url, err).into()),
- },
-
- // The package is unavailable due to a lack of connectivity.
- ErrorKind::Offline(_) => {}
-
- // The package could not be found in the local index.
- ErrorKind::FileNotFound(_) => {}
-
- err => return Err(err.into()),
- },
- };
+ if let Some(metadata) = self
+ .simple_single_index(package_name, index, capabilities)
+ .await?
+ {
+ results.push((index, metadata));
+ break;
+ }
}
}
// Otherwise, fetch concurrently.
IndexStrategy::UnsafeBestMatch | IndexStrategy::UnsafeFirstMatch => {
- let fetches = futures::stream::iter(it)
+ // TODO(charlie): Respect concurrency limits.
+ results = futures::stream::iter(it)
.map(|index| async move {
- match self.simple_single_index(package_name, index).await {
- Ok(metadata) => Ok(Some((index, metadata))),
- Err(err) => match err.into_kind() {
- // The package could not be found in the remote index.
- ErrorKind::WrappedReqwestError(url, err) => match err.status() {
- Some(StatusCode::NOT_FOUND) => Ok(None),
- Some(StatusCode::UNAUTHORIZED) => {
- capabilities.set_unauthorized(index.clone());
- Ok(None)
- }
- Some(StatusCode::FORBIDDEN) => {
- capabilities.set_forbidden(index.clone());
- Ok(None)
- }
- _ => Err(ErrorKind::WrappedReqwestError(url, err).into()),
- },
-
- // The package is unavailable due to a lack of connectivity.
- ErrorKind::Offline(_) => Ok(None),
-
- // The package could not be found in the local index.
- ErrorKind::FileNotFound(_) => Ok(None),
-
- err => Err(err.into()),
- },
- }
+ let metadata = self
+ .simple_single_index(package_name, index, capabilities)
+ .await?;
+ Ok((index, metadata))
})
- .buffered(8);
-
- futures::pin_mut!(fetches);
-
- while let Some(result) = fetches.next().await {
- match result {
- Ok(Some((index, metadata))) => {
- results.push((index, metadata));
+ .buffered(8)
+ .filter_map(|result: Result<_, Error>| async move {
+ match result {
+ Ok((index, Some(metadata))) => Some(Ok((index, metadata))),
+ Ok((_, None)) => None,
+ Err(err) => Some(Err(err)),
}
- Ok(None) => continue,
- Err(err) => return Err(err),
- }
- }
+ })
+ .try_collect::>()
+ .await?;
}
}
@@ -346,11 +302,14 @@ impl RegistryClient {
///
/// The index can either be a PEP 503-compatible remote repository, or a local directory laid
/// out in the same format.
+ ///
+ /// Returns `Ok(None)` if the package is not found in the index.
async fn simple_single_index(
&self,
package_name: &PackageName,
index: &IndexUrl,
- ) -> Result, Error> {
+ capabilities: &IndexCapabilities,
+ ) -> Result>, Error> {
// Format the URL for PyPI.
let mut url: Url = index.clone().into();
url.path_segments_mut()
@@ -377,11 +336,38 @@ impl RegistryClient {
Connectivity::Offline => CacheControl::AllowStale,
};
- if matches!(index, IndexUrl::Path(_)) {
+ let result = if matches!(index, IndexUrl::Path(_)) {
self.fetch_local_index(package_name, &url).await
} else {
self.fetch_remote_index(package_name, &url, &cache_entry, cache_control)
.await
+ };
+
+ match result {
+ Ok(metadata) => Ok(Some(metadata)),
+ Err(err) => match err.into_kind() {
+ // The package could not be found in the remote index.
+ ErrorKind::WrappedReqwestError(url, err) => match err.status() {
+ Some(StatusCode::NOT_FOUND) => Ok(None),
+ Some(StatusCode::UNAUTHORIZED) => {
+ capabilities.set_unauthorized(index.clone());
+ Ok(None)
+ }
+ Some(StatusCode::FORBIDDEN) => {
+ capabilities.set_forbidden(index.clone());
+ Ok(None)
+ }
+ _ => Err(ErrorKind::WrappedReqwestError(url, err).into()),
+ },
+
+ // The package is unavailable due to a lack of connectivity.
+ ErrorKind::Offline(_) => Ok(None),
+
+ // The package could not be found in the local index.
+ ErrorKind::FileNotFound(_) => Ok(None),
+
+ err => Err(err.into()),
+ },
}
}
From 7bf514d8862c5166f0b16fd327938118c8b1b8aa Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Thu, 9 Jan 2025 22:32:30 -0500
Subject: [PATCH 097/135] Remove `get_with_version` methods (#10456)
## Summary
I think these are vestigial.
---
crates/uv-resolver/src/version_map.rs | 29 ++++-----------------------
1 file changed, 4 insertions(+), 25 deletions(-)
diff --git a/crates/uv-resolver/src/version_map.rs b/crates/uv-resolver/src/version_map.rs
index bf9a87983242..715d9ab5d27d 100644
--- a/crates/uv-resolver/src/version_map.rs
+++ b/crates/uv-resolver/src/version_map.rs
@@ -115,23 +115,9 @@ impl VersionMap {
/// Return the [`DistFile`] for the given version, if any.
pub(crate) fn get(&self, version: &Version) -> Option<&PrioritizedDist> {
- self.get_with_version(version).map(|(_version, dist)| dist)
- }
-
- /// Return the [`DistFile`] and the `Version` from the map for the given
- /// version, if any.
- ///
- /// This is useful when you depend on access to the specific `Version`
- /// stored in this map. For example, the versions `1.2.0` and `1.2` are
- /// semantically equivalent, but when converted to strings, they are
- /// distinct.
- pub(crate) fn get_with_version(
- &self,
- version: &Version,
- ) -> Option<(&Version, &PrioritizedDist)> {
match self.inner {
- VersionMapInner::Eager(ref eager) => eager.map.get_key_value(version),
- VersionMapInner::Lazy(ref lazy) => lazy.get_with_version(version),
+ VersionMapInner::Eager(ref eager) => eager.map.get(version),
+ VersionMapInner::Lazy(ref lazy) => lazy.get(version),
}
}
@@ -349,16 +335,9 @@ struct VersionMapLazy {
impl VersionMapLazy {
/// Returns the distribution for the given version, if it exists.
fn get(&self, version: &Version) -> Option<&PrioritizedDist> {
- self.get_with_version(version)
- .map(|(_, prioritized_dist)| prioritized_dist)
- }
-
- /// Returns the distribution for the given version along with the version
- /// in this map, if it exists.
- fn get_with_version(&self, version: &Version) -> Option<(&Version, &PrioritizedDist)> {
- let (version, lazy_dist) = self.map.get_key_value(version)?;
+ let lazy_dist = self.map.get(version)?;
let priority_dist = self.get_lazy(lazy_dist)?;
- Some((version, priority_dist))
+ Some(priority_dist)
}
/// Given a reference to a possibly-initialized distribution that is in
From f4f1587549c55aac946e71b8974c6af399946bc4 Mon Sep 17 00:00:00 2001
From: Charles Tapley Hoyt
Date: Fri, 10 Jan 2025 07:27:15 -0500
Subject: [PATCH 098/135] Add remaining Python type annotations to build
backend (#10434)
---
python/uv/_build_backend.py | 40 ++++++++++++++++++++++++-------------
1 file changed, 26 insertions(+), 14 deletions(-)
diff --git a/python/uv/_build_backend.py b/python/uv/_build_backend.py
index 88bd54c91286..127342d8eea8 100644
--- a/python/uv/_build_backend.py
+++ b/python/uv/_build_backend.py
@@ -16,15 +16,19 @@
them while IDEs and type checker can see through the quotes.
"""
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from typing import Any # noqa:I001
-def warn_config_settings(config_settings: "dict | None" = None):
+
+def warn_config_settings(config_settings: "dict[Any, Any] | None" = None) -> None:
import sys
if config_settings:
print("Warning: Config settings are not supported", file=sys.stderr)
-def call(args: "list[str]", config_settings: "dict | None" = None) -> str:
+def call(args: "list[str]", config_settings: "dict[Any, Any] | None" = None) -> str:
"""Invoke a uv subprocess and return the filename from stdout."""
import shutil
import subprocess
@@ -49,7 +53,9 @@ def call(args: "list[str]", config_settings: "dict | None" = None) -> str:
return stdout[-1].strip()
-def build_sdist(sdist_directory: str, config_settings: "dict | None" = None):
+def build_sdist(
+ sdist_directory: str, config_settings: "dict[Any, Any] | None" = None
+) -> str:
"""PEP 517 hook `build_sdist`."""
args = ["build-backend", "build-sdist", sdist_directory]
return call(args, config_settings)
@@ -57,9 +63,9 @@ def build_sdist(sdist_directory: str, config_settings: "dict | None" = None):
def build_wheel(
wheel_directory: str,
- config_settings: "dict | None" = None,
+ config_settings: "dict[Any, Any] | None" = None,
metadata_directory: "str | None" = None,
-):
+) -> str:
"""PEP 517 hook `build_wheel`."""
args = ["build-backend", "build-wheel", wheel_directory]
if metadata_directory:
@@ -67,21 +73,25 @@ def build_wheel(
return call(args, config_settings)
-def get_requires_for_build_sdist(config_settings: "dict | None" = None):
+def get_requires_for_build_sdist(
+ config_settings: "dict[Any, Any] | None" = None,
+) -> "list[str]":
"""PEP 517 hook `get_requires_for_build_sdist`."""
warn_config_settings(config_settings)
return []
-def get_requires_for_build_wheel(config_settings: "dict | None" = None):
+def get_requires_for_build_wheel(
+ config_settings: "dict[Any, Any] | None" = None,
+) -> "list[str]":
"""PEP 517 hook `get_requires_for_build_wheel`."""
warn_config_settings(config_settings)
return []
def prepare_metadata_for_build_wheel(
- metadata_directory: str, config_settings: "dict | None" = None
-):
+ metadata_directory: str, config_settings: "dict[Any, Any] | None" = None
+) -> str:
"""PEP 517 hook `prepare_metadata_for_build_wheel`."""
args = ["build-backend", "prepare-metadata-for-build-wheel", metadata_directory]
return call(args, config_settings)
@@ -89,9 +99,9 @@ def prepare_metadata_for_build_wheel(
def build_editable(
wheel_directory: str,
- config_settings: "dict | None" = None,
+ config_settings: "dict[Any, Any] | None" = None,
metadata_directory: "str | None" = None,
-):
+) -> str:
"""PEP 660 hook `build_editable`."""
args = ["build-backend", "build-editable", wheel_directory]
if metadata_directory:
@@ -99,15 +109,17 @@ def build_editable(
return call(args, config_settings)
-def get_requires_for_build_editable(config_settings: "dict | None" = None):
+def get_requires_for_build_editable(
+ config_settings: "dict[Any, Any] | None" = None,
+) -> "list[str]":
"""PEP 660 hook `get_requires_for_build_editable`."""
warn_config_settings(config_settings)
return []
def prepare_metadata_for_build_editable(
- metadata_directory: str, config_settings: "dict | None" = None
-):
+ metadata_directory: str, config_settings: "dict[Any, Any] | None" = None
+) -> str:
"""PEP 660 hook `prepare_metadata_for_build_editable`."""
args = ["build-backend", "prepare-metadata-for-build-editable", metadata_directory]
return call(args, config_settings)
From 8d25f295af20f8c822dfbfb5ab24b6134ded4bbb Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Fri, 10 Jan 2025 08:10:27 -0500
Subject: [PATCH 099/135] Only track markers for compatible versions (#10457)
## Summary
We shouldn't consider incompatible distributions (e.g., those that don't
match the required Python version) when determining the implied markers.
---
.../src/prioritized_distribution.rs | 24 +++++++++++++------
crates/uv-pep440/src/version.rs | 2 +-
crates/uv/tests/it/pip_compile.rs | 16 ++++++-------
3 files changed, 26 insertions(+), 16 deletions(-)
diff --git a/crates/uv-distribution-types/src/prioritized_distribution.rs b/crates/uv-distribution-types/src/prioritized_distribution.rs
index def371bc247c..c6261076af21 100644
--- a/crates/uv-distribution-types/src/prioritized_distribution.rs
+++ b/crates/uv-distribution-types/src/prioritized_distribution.rs
@@ -45,7 +45,7 @@ impl Default for PrioritizedDistInner {
}
/// A distribution that can be used for both resolution and installation.
-#[derive(Debug, Clone)]
+#[derive(Debug, Copy, Clone)]
pub enum CompatibleDist<'a> {
/// The distribution is already installed and can be used.
InstalledDist(&'a InstalledDist),
@@ -314,6 +314,12 @@ impl PrioritizedDist {
hashes: impl IntoIterator
- ,
compatibility: WheelCompatibility,
) {
+ // Track the implied markers.
+ if compatibility.is_compatible() {
+ if !self.0.markers.is_true() {
+ self.0.markers.or(implied_markers(&dist.filename));
+ }
+ }
// Track the highest-priority wheel.
if let Some((.., existing_compatibility)) = self.best_wheel() {
if compatibility.is_more_compatible(existing_compatibility) {
@@ -323,9 +329,6 @@ impl PrioritizedDist {
self.0.best_wheel_index = Some(self.0.wheels.len());
}
self.0.hashes.extend(hashes);
- if !self.0.markers.is_true() {
- self.0.markers.or(implied_markers(&dist.filename));
- }
self.0.wheels.push((dist, compatibility));
}
@@ -336,6 +339,10 @@ impl PrioritizedDist {
hashes: impl IntoIterator
- ,
compatibility: SourceDistCompatibility,
) {
+ // Track the implied markers.
+ if compatibility.is_compatible() {
+ self.0.markers = MarkerTree::TRUE;
+ }
// Track the highest-priority source.
if let Some((.., existing_compatibility)) = &self.0.source {
if compatibility.is_more_compatible(existing_compatibility) {
@@ -344,9 +351,6 @@ impl PrioritizedDist {
} else {
self.0.source = Some((dist, compatibility));
}
- if !self.0.markers.is_true() {
- self.0.markers.or(MarkerTree::TRUE);
- }
self.0.hashes.extend(hashes);
}
@@ -526,6 +530,7 @@ impl<'a> CompatibleDist<'a> {
}
impl WheelCompatibility {
+ /// Return `true` if the distribution is compatible.
pub fn is_compatible(&self) -> bool {
matches!(self, Self::Compatible(_, _, _))
}
@@ -552,6 +557,11 @@ impl WheelCompatibility {
}
impl SourceDistCompatibility {
+ /// Return `true` if the distribution is compatible.
+ pub fn is_compatible(&self) -> bool {
+ matches!(self, Self::Compatible(_))
+ }
+
/// Return the higher priority compatibility.
///
/// Compatible source distributions are always higher priority than incompatible source distributions.
diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs
index 7eeb2ad198e1..aaf8dfc1eb18 100644
--- a/crates/uv-pep440/src/version.rs
+++ b/crates/uv-pep440/src/version.rs
@@ -1616,7 +1616,7 @@ impl LocalVersionSlice<'_> {
/// > should be considered an integer for comparison purposes and if a segment contains any ASCII
/// > letters then that segment is compared lexicographically with case insensitivity. When
/// > comparing a numeric and lexicographic segment, the numeric section always compares as greater
-/// > than the lexicographic segment. Additionally a local version with a great number of segments
+/// > than the lexicographic segment. Additionally, a local version with a great number of segments
/// > will always compare as greater than a local version with fewer segments, as long as the
/// > shorter local version’s segments match the beginning of the longer local version’s segments
/// > exactly.
diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs
index 0d997d65e98e..9fc609c5675e 100644
--- a/crates/uv/tests/it/pip_compile.rs
+++ b/crates/uv/tests/it/pip_compile.rs
@@ -7629,9 +7629,9 @@ fn universal_transitive_disjoint_locals() -> Result<()> {
# -r requirements.in
# torchvision
# triton
- torchvision==0.15.1 ; platform_machine != 'x86_64' or sys_platform == 'darwin' or sys_platform == 'win32'
+ torchvision==0.15.1 ; platform_machine != 'x86_64' or sys_platform == 'darwin'
# via -r requirements.in
- torchvision==0.15.1+rocm5.4.2 ; platform_machine == 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32'
+ torchvision==0.15.1+rocm5.4.2 ; platform_machine == 'x86_64' and sys_platform != 'darwin'
# via -r requirements.in
triton==2.0.0 ; platform_machine == 'x86_64' and sys_platform == 'linux'
# via torch
@@ -8010,7 +8010,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
# via sympy
networkx==3.2.1
# via torch
- pytorch-triton-rocm==2.3.0 ; (implementation_name != 'cpython' and platform_machine != 'aarch64' and sys_platform == 'linux') or (implementation_name != 'cpython' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')
+ pytorch-triton-rocm==2.3.0 ; (implementation_name != 'cpython' and platform_machine != 'aarch64' and sys_platform == 'linux') or (implementation_name != 'cpython' and sys_platform != 'darwin' and sys_platform != 'linux')
# via torch
sympy==1.12
# via torch
@@ -8021,9 +8021,9 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> {
# -r requirements.in
# example
# triton
- torch==2.3.0 ; (implementation_name != 'cpython' and platform_machine == 'aarch64' and sys_platform == 'linux') or (implementation_name != 'cpython' and sys_platform == 'darwin') or (implementation_name != 'cpython' and sys_platform == 'win32')
+ torch==2.3.0 ; (implementation_name != 'cpython' and platform_machine == 'aarch64' and sys_platform == 'linux') or (implementation_name != 'cpython' and sys_platform == 'darwin')
# via -r requirements.in
- torch==2.3.0+rocm6.0 ; (implementation_name != 'cpython' and platform_machine != 'aarch64' and sys_platform == 'linux') or (implementation_name != 'cpython' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')
+ torch==2.3.0+rocm6.0 ; (implementation_name != 'cpython' and platform_machine != 'aarch64' and sys_platform == 'linux') or (implementation_name != 'cpython' and sys_platform != 'darwin' and sys_platform != 'linux')
# via -r requirements.in
triton==2.0.0 ; implementation_name == 'cpython' and platform_machine == 'x86_64' and sys_platform == 'linux'
# via torch
@@ -8174,7 +8174,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> {
# via sympy
networkx==3.2.1
# via torch
- pytorch-triton-rocm==2.3.0 ; (os_name != 'Linux' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')
+ pytorch-triton-rocm==2.3.0 ; (os_name != 'Linux' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')
# via torch
sympy==1.12
# via torch
@@ -8189,9 +8189,9 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> {
# -r requirements.in
# example
# triton
- torch==2.3.0 ; (os_name != 'Linux' and platform_machine == 'aarch64' and sys_platform == 'linux') or (os_name != 'Linux' and sys_platform == 'darwin') or (os_name != 'Linux' and sys_platform == 'win32')
+ torch==2.3.0 ; (os_name != 'Linux' and platform_machine == 'aarch64' and sys_platform == 'linux') or (os_name != 'Linux' and sys_platform == 'darwin')
# via -r requirements.in
- torch==2.3.0+rocm6.0 ; (os_name != 'Linux' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')
+ torch==2.3.0+rocm6.0 ; (os_name != 'Linux' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name != 'Linux' and sys_platform != 'darwin' and sys_platform != 'linux')
# via -r requirements.in
triton==2.0.0 ; implementation_name == 'cpython' and os_name == 'Linux' and platform_machine == 'x86_64' and sys_platform == 'linux'
# via torch
From bee2baa64e37401367ffd32f81a33e2d26e68f48 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Fri, 10 Jan 2025 08:10:39 -0500
Subject: [PATCH 100/135] Misc. changes based on ABI explorations (#10458)
---
.../src/prioritized_distribution.rs | 4 +--
crates/uv-platform-tags/src/platform.rs | 22 ++++++------
crates/uv-platform-tags/src/tags.rs | 34 ++++++-------------
crates/uv-resolver/src/version_map.rs | 24 ++++++-------
4 files changed, 36 insertions(+), 48 deletions(-)
diff --git a/crates/uv-distribution-types/src/prioritized_distribution.rs b/crates/uv-distribution-types/src/prioritized_distribution.rs
index c6261076af21..61b010df26b5 100644
--- a/crates/uv-distribution-types/src/prioritized_distribution.rs
+++ b/crates/uv-distribution-types/src/prioritized_distribution.rs
@@ -222,7 +222,7 @@ impl Display for IncompatibleDist {
}
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum PythonRequirementKind {
/// The installed version of Python.
Installed,
@@ -266,7 +266,7 @@ pub enum IncompatibleSource {
NoBuild,
}
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum HashComparison {
/// The hash is present, but does not match the expected value.
Mismatched,
diff --git a/crates/uv-platform-tags/src/platform.rs b/crates/uv-platform-tags/src/platform.rs
index 697891b19d31..b82335036286 100644
--- a/crates/uv-platform-tags/src/platform.rs
+++ b/crates/uv-platform-tags/src/platform.rs
@@ -56,17 +56,17 @@ pub enum Os {
impl fmt::Display for Os {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
- Self::Manylinux { .. } => write!(f, "Manylinux"),
- Self::Musllinux { .. } => write!(f, "Musllinux"),
- Self::Windows => write!(f, "Windows"),
- Self::Macos { .. } => write!(f, "MacOS"),
- Self::FreeBsd { .. } => write!(f, "FreeBSD"),
- Self::NetBsd { .. } => write!(f, "NetBSD"),
- Self::OpenBsd { .. } => write!(f, "OpenBSD"),
- Self::Dragonfly { .. } => write!(f, "DragonFly"),
- Self::Illumos { .. } => write!(f, "Illumos"),
- Self::Haiku { .. } => write!(f, "Haiku"),
- Self::Android { .. } => write!(f, "Android"),
+ Self::Manylinux { .. } => write!(f, "manylinux"),
+ Self::Musllinux { .. } => write!(f, "musllinux"),
+ Self::Windows => write!(f, "windows"),
+ Self::Macos { .. } => write!(f, "macos"),
+ Self::FreeBsd { .. } => write!(f, "freebsd"),
+ Self::NetBsd { .. } => write!(f, "netbsd"),
+ Self::OpenBsd { .. } => write!(f, "openbsd"),
+ Self::Dragonfly { .. } => write!(f, "dragonfly"),
+ Self::Illumos { .. } => write!(f, "illumos"),
+ Self::Haiku { .. } => write!(f, "haiku"),
+ Self::Android { .. } => write!(f, "android"),
}
}
}
diff --git a/crates/uv-platform-tags/src/tags.rs b/crates/uv-platform-tags/src/tags.rs
index 9dc99c618af1..8a15f6f13f09 100644
--- a/crates/uv-platform-tags/src/tags.rs
+++ b/crates/uv-platform-tags/src/tags.rs
@@ -21,7 +21,7 @@ pub enum TagsError {
GilIsACPythonProblem(String),
}
-#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Clone)]
+#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Copy, Clone)]
pub enum IncompatibleTag {
/// The tag is invalid and cannot be used.
Invalid,
@@ -35,7 +35,7 @@ pub enum IncompatibleTag {
Platform,
}
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum TagCompatibility {
Incompatible(IncompatibleTag),
Compatible(TagPriority),
@@ -59,6 +59,7 @@ impl PartialOrd for TagCompatibility {
}
impl TagCompatibility {
+ /// Returns `true` if the tag is compatible.
pub fn is_compatible(&self) -> bool {
matches!(self, Self::Compatible(_))
}
@@ -128,7 +129,7 @@ impl Tags {
if let Implementation::CPython { gil_disabled } = implementation {
// For some reason 3.2 is the minimum python for the cp abi
for minor in (2..=python_version.1).rev() {
- // No abi3 for freethreading python
+ // No abi3 for free-threading python
if !gil_disabled {
for platform_tag in &platform_tags {
tags.push((
@@ -406,8 +407,6 @@ impl Implementation {
///
/// We have two cases: Actual platform specific tags (including "merged" tags such as universal2)
/// and "any".
-///
-/// Bit of a mess, needs to be cleaned up.
fn compatible_tags(platform: &Platform) -> Result, PlatformError> {
let os = platform.os();
let arch = platform.arch();
@@ -431,13 +430,13 @@ fn compatible_tags(platform: &Platform) -> Result, PlatformError> {
}
}
}
- // Non-manylinux is lowest priority
+ // Non-manylinux is given lowest priority.
//
platform_tags.push(format!("linux_{arch}"));
platform_tags
}
(Os::Musllinux { major, minor }, _) => {
- let mut platform_tags = vec![format!("linux_{}", arch)];
+ let mut platform_tags = vec![format!("linux_{arch}")];
// musl 1.1 is the lowest supported version in musllinux
platform_tags
.extend((1..=*minor).map(|minor| format!("musllinux_{major}_{minor}_{arch}")));
@@ -516,12 +515,7 @@ fn compatible_tags(platform: &Platform) -> Result, PlatformError> {
_,
) => {
let release = release.replace(['.', '-'], "_");
- vec![format!(
- "{}_{}_{}",
- os.to_string().to_lowercase(),
- release,
- arch
- )]
+ vec![format!("{os}_{release}_{arch}")]
}
(Os::Illumos { release, arch }, _) => {
// See https://github.com/python/cpython/blob/46c8d915715aa2bd4d697482aa051fe974d440e1/Lib/sysconfig.py#L722-L730
@@ -533,23 +527,17 @@ fn compatible_tags(platform: &Platform) -> Result, PlatformError> {
})?;
if major_ver >= 5 {
// SunOS 5 == Solaris 2
- let os = "solaris".to_string();
+ let os = "solaris";
let release = format!("{}_{}", major_ver - 3, other);
let arch = format!("{arch}_64bit");
- return Ok(vec![format!("{}_{}_{}", os, release, arch)]);
+ return Ok(vec![format!("{os}_{release}_{arch}")]);
}
}
- let os = os.to_string().to_lowercase();
- vec![format!("{}_{}_{}", os, release, arch)]
+ vec![format!("{os}_{release}_{arch}")]
}
(Os::Android { api_level }, _) => {
- vec![format!(
- "{}_{}_{}",
- os.to_string().to_lowercase(),
- api_level,
- arch
- )]
+ vec![format!("{os}_{api_level}_{arch}")]
}
_ => {
return Err(PlatformError::OsVersionDetectionError(format!(
diff --git a/crates/uv-resolver/src/version_map.rs b/crates/uv-resolver/src/version_map.rs
index 715d9ab5d27d..5056916738fa 100644
--- a/crates/uv-resolver/src/version_map.rs
+++ b/crates/uv-resolver/src/version_map.rs
@@ -521,14 +521,22 @@ impl VersionMapLazy {
}
// Determine a compatibility for the wheel based on tags.
- let priority = match &self.tags {
- Some(tags) => match filename.compatibility(tags) {
+ let priority = if let Some(tags) = &self.tags {
+ match filename.compatibility(tags) {
TagCompatibility::Incompatible(tag) => {
return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(tag))
}
TagCompatibility::Compatible(priority) => Some(priority),
- },
- None => None,
+ }
+ } else {
+ // Check if the wheel is compatible with the `requires-python` (i.e., the Python
+ // ABI tag is not less than the `requires-python` minimum version).
+ if !self.requires_python.matches_wheel_tag(filename) {
+ return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(
+ IncompatibleTag::AbiPythonVersion,
+ ));
+ }
+ None
};
// Check if hashes line up. If hashes aren't required, they're considered matching.
@@ -546,14 +554,6 @@ impl VersionMapLazy {
}
};
- // Check if the wheel is compatible with the `requires-python` (i.e., the Python ABI tag
- // is not less than the `requires-python` minimum version).
- if !self.requires_python.matches_wheel_tag(filename) {
- return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(
- IncompatibleTag::AbiPythonVersion,
- ));
- }
-
// Break ties with the build tag.
let build_tag = filename.build_tag.clone();
From 685a53d9656746832a435d68bbb4f6415fd88422 Mon Sep 17 00:00:00 2001
From: konsti
Date: Fri, 10 Jan 2025 15:42:03 +0100
Subject: [PATCH 101/135] Add second apache airflow test case (#10463)
---
scripts/requirements/airflow2-constraints.txt | 299 ++++++++++++++++++
scripts/requirements/airflow2-req.in | 7 +
2 files changed, 306 insertions(+)
create mode 100644 scripts/requirements/airflow2-constraints.txt
create mode 100644 scripts/requirements/airflow2-req.in
diff --git a/scripts/requirements/airflow2-constraints.txt b/scripts/requirements/airflow2-constraints.txt
new file mode 100644
index 000000000000..8fdda5d3d7c6
--- /dev/null
+++ b/scripts/requirements/airflow2-constraints.txt
@@ -0,0 +1,299 @@
+adal==1.2.7
+alembic==1.8.1
+amqp==5.1.1
+anyio==3.6.1
+apache-airflow==2.3.4
+apache-airflow-providers-amazon==5.0.0
+apache-airflow-providers-celery==3.0.0
+apache-airflow-providers-cncf-kubernetes==4.3.0
+apache-airflow-providers-common-sql==1.1.0
+apache-airflow-providers-docker==3.1.0
+apache-airflow-providers-elasticsearch==4.2.0
+apache-airflow-providers-ftp==3.1.0
+apache-airflow-providers-google==8.3.0
+apache-airflow-providers-grpc==3.0.0
+apache-airflow-providers-hashicorp==3.1.0
+apache-airflow-providers-http==4.0.0
+apache-airflow-providers-imap==3.0.0
+apache-airflow-providers-microsoft-azure==4.2.0
+apache-airflow-providers-mysql==3.2.0
+apache-airflow-providers-odbc==3.1.1
+apache-airflow-providers-postgres==5.2.0
+apache-airflow-providers-redis==3.0.0
+apache-airflow-providers-sendgrid==3.0.0
+apache-airflow-providers-sftp==4.0.0
+apache-airflow-providers-slack==5.1.0
+apache-airflow-providers-sqlite==3.2.0
+apache-airflow-providers-ssh==3.1.0
+apispec==3.3.2
+argcomplete==2.0.0
+asn1crypto==1.5.1
+attrs==22.1.0
+Authlib==0.15.5
+azure-batch==12.0.0
+azure-common==1.1.28
+azure-core==1.25.0
+azure-cosmos==4.3.0
+azure-datalake-store==0.0.52
+azure-identity==1.10.0
+azure-keyvault-secrets==4.5.1
+azure-kusto-data==0.0.45
+azure-mgmt-containerinstance==1.5.0
+azure-mgmt-core==1.3.2
+azure-mgmt-datafactory==1.1.0
+azure-mgmt-datalake-nspkg==3.0.1
+azure-mgmt-datalake-store==0.5.0
+azure-mgmt-nspkg==3.0.2
+azure-mgmt-resource==21.1.0
+azure-nspkg==3.0.2
+azure-servicebus==7.8.0
+azure-storage-blob==12.8.1
+azure-storage-common==2.1.0
+azure-storage-file==2.1.0
+Babel==2.10.3
+bcrypt==3.2.2
+beautifulsoup4==4.11.1
+billiard==3.6.4.0
+blinker==1.5
+boto3==1.24.56
+botocore==1.27.56
+cachelib==0.9.0
+cachetools==4.2.2
+cattrs==22.1.0
+celery==5.2.7
+certifi==2022.6.15
+cffi==1.15.1
+charset-normalizer==2.0.12
+click==8.1.3
+click-didyoumean==0.3.0
+click-plugins==1.1.1
+click-repl==0.2.0
+clickclick==20.10.2
+cloudpickle==2.1.0
+colorama==0.4.5
+colorlog==4.8.0
+commonmark==0.9.1
+connexion==2.14.0
+cron_descriptor==1.2.31
+croniter==1.3.5
+cryptography==36.0.2
+dask==2022.8.0
+db-dtypes==1.0.3
+decorator==5.1.1
+Deprecated==1.2.13
+dill==0.3.1.1
+distlib==0.3.5
+distributed==2022.8.0
+dnspython==2.2.1
+docker==6.1.3
+docutils==0.19
+elasticsearch==7.13.4
+elasticsearch-dbapi==0.2.9
+elasticsearch-dsl==7.4.0
+email-validator==1.2.1
+eventlet==0.33.1
+exceptiongroup==1.0.0rc8
+filelock==3.8.0
+Flask==2.2.2
+Flask-AppBuilder==4.1.3
+Flask-Babel==2.0.0
+Flask-Caching==2.0.1
+Flask-JWT-Extended==4.4.4
+Flask-Login==0.6.2
+Flask-Session==0.4.0
+Flask-SQLAlchemy==2.5.1
+Flask-WTF==0.15.1
+flower==1.2.0
+fsspec==2022.7.1
+future==0.18.2
+gevent==21.12.0
+google-ads==18.0.0
+google-api-core==2.8.2
+google-api-python-client==1.12.11
+google-auth==2.10.0
+google-auth-httplib2==0.1.0
+google-auth-oauthlib==0.5.2
+google-cloud-aiplatform==1.16.1
+google-cloud-appengine-logging==1.1.3
+google-cloud-audit-log==0.2.4
+google-cloud-automl==2.8.0
+google-cloud-bigquery==2.34.4
+google-cloud-bigquery-datatransfer==3.7.0
+google-cloud-bigquery-storage==2.14.1
+google-cloud-bigtable==1.7.2
+google-cloud-build==3.9.0
+google-cloud-container==2.11.1
+google-cloud-core==2.3.2
+google-cloud-datacatalog==3.9.0
+google-cloud-dataform==0.2.0
+google-cloud-dataplex==1.1.0
+google-cloud-dataproc==5.0.0
+google-cloud-dataproc-metastore==1.6.0
+google-cloud-dlp==1.0.2
+google-cloud-kms==2.12.0
+google-cloud-language==1.3.2
+google-cloud-logging==3.2.1
+google-cloud-memcache==1.4.1
+google-cloud-monitoring==2.11.0
+google-cloud-orchestration-airflow==1.4.1
+google-cloud-os-login==2.7.1
+google-cloud-pubsub==2.13.5
+google-cloud-redis==2.9.0
+google-cloud-resource-manager==1.6.0
+google-cloud-secret-manager==1.0.2
+google-cloud-spanner==1.19.3
+google-cloud-speech==1.3.4
+google-cloud-storage==1.44.0
+google-cloud-tasks==2.10.1
+google-cloud-texttospeech==1.0.3
+google-cloud-translate==1.7.2
+google-cloud-videointelligence==1.16.3
+google-cloud-vision==1.0.2
+google-cloud-workflows==1.7.1
+google-crc32c==1.3.0
+google-resumable-media==2.3.3
+googleapis-common-protos==1.56.4
+graphviz==0.20.1
+greenlet==1.1.2
+grpc-google-iam-v1==0.12.4
+grpcio==1.47.0
+grpcio-gcp==0.2.2
+grpcio-status==1.47.0
+gunicorn==20.1.0
+h11==0.12.0
+HeapDict==1.0.1
+httpcore==0.15.0
+httplib2==0.20.4
+httpx==0.23.0
+humanize==4.3.0
+hvac==1.1.1
+idna==3.3
+inflection==0.5.1
+isodate==0.6.1
+itsdangerous==2.1.2
+Jinja2==3.1.2
+jmespath==0.10.0
+json-merge-patch==0.2
+jsonpath-ng==1.5.3
+jsonschema==4.13.0
+kombu==5.2.4
+kubernetes==23.6.0
+lazy-object-proxy==1.7.1
+ldap3==2.9.1
+linkify-it-py==2.0.0
+locket==1.0.0
+lockfile==0.12.2
+looker-sdk==22.10.0
+lxml==4.9.1
+Mako==1.2.1
+Markdown==3.4.1
+markdown-it-py==2.1.0
+MarkupSafe==2.1.1
+marshmallow==3.17.0
+marshmallow-enum==1.5.1
+marshmallow-oneofschema==3.0.1
+marshmallow-sqlalchemy==0.26.1
+mdit-py-plugins==0.3.0
+mdurl==0.1.2
+msal==1.18.0
+msal-extensions==1.0.0
+msgpack==1.0.4
+msrest==0.7.1
+msrestazure==0.6.4
+mypy-boto3-appflow==1.24.36.post1
+mypy-boto3-rds==1.24.54
+mypy-boto3-redshift-data==1.24.36.post1
+mysql-connector-python==8.0.30
+mysqlclient==2.1.1
+numpy==1.22.4
+oauthlib==3.2.0
+packaging==21.3
+pandas==1.4.3
+pandas-gbq==0.17.8
+paramiko==2.11.0
+partd==1.3.0
+pathspec==0.9.0
+pendulum==2.1.2
+platformdirs==2.5.2
+pluggy==1.0.0
+ply==3.11
+portalocker==2.5.1
+prison==0.2.1
+prometheus-client==0.14.1
+prompt-toolkit==3.0.30
+proto-plus==1.19.6
+protobuf==3.20.0
+psutil==5.9.1
+psycopg2-binary==2.9.3
+pyarrow==6.0.1
+pyasn1==0.4.8
+pyasn1-modules==0.2.8
+pycparser==2.21
+pydata-google-auth==1.4.0
+Pygments==2.13.0
+PyJWT==2.4.0
+PyNaCl==1.5.0
+pyodbc==4.0.34
+pyOpenSSL==22.0.0
+pyparsing==3.0.9
+pyrsistent==0.18.1
+python-daemon==2.3.1
+python-dateutil==2.8.2
+python-http-client==3.3.7
+python-ldap==3.4.2
+python-nvd3==0.15.0
+python-slugify==6.1.2
+pytz==2022.1
+pytzdata==2020.1
+PyYAML==6.0
+redis==3.5.3
+redshift-connector==2.0.908
+requests==2.28.0
+requests-oauthlib==1.3.1
+requests-toolbelt==0.9.1
+rfc3986==1.5.0
+rich==12.5.1
+rsa==4.9
+s3transfer==0.6.0
+scramp==1.4.1
+sendgrid==6.9.7
+setproctitle==1.3.2
+six==1.16.0
+slack-sdk==3.18.1
+sniffio==1.2.0
+sortedcontainers==2.4.0
+soupsieve==2.3.2.post1
+SQLAlchemy==1.4.27
+sqlalchemy-bigquery==1.4.4
+SQLAlchemy-JSONField==1.0.0
+sqlalchemy-redshift==0.8.11
+SQLAlchemy-Utils==0.38.3
+sqlparse==0.4.2
+sshtunnel==0.4.0
+starkbank-ecdsa==2.0.3
+statsd==3.3.0
+swagger-ui-bundle==0.0.9
+tabulate==0.8.10
+tblib==1.7.0
+tenacity==8.0.1
+termcolor==1.1.0
+text-unidecode==1.3
+toolz==0.12.0
+tornado==6.1
+typing_extensions==4.3.0
+uamqp==1.6.0
+uc-micro-py==1.0.1
+unicodecsv==0.14.1
+uritemplate==3.0.1
+urllib3==1.26.11
+vine==5.0.0
+virtualenv==20.16.3
+watchtower==2.0.1
+wcwidth==0.2.5
+websocket-client==1.3.3
+Werkzeug==2.2.2
+wrapt==1.14.1
+WTForms==2.3.3
+zict==2.2.0
+zope.event==4.5.0
+zope.interface==5.4.0
diff --git a/scripts/requirements/airflow2-req.in b/scripts/requirements/airflow2-req.in
new file mode 100644
index 000000000000..70ed2851a9d8
--- /dev/null
+++ b/scripts/requirements/airflow2-req.in
@@ -0,0 +1,7 @@
+# Run with:
+# ```
+# uv pip compile scripts/requirements/airflow2-req.in -c scripts/requirements/airflow2-constraints.txt --universal --python-version 3.8
+# ```
+apache-airflow[amazon,async,google,google_auth,grpc,hashicorp,http,ldap,mysql,odbc,pandas,postgres,redis,sftp,slack,ssh,statsd,virtualenv]==2.3.4
+alembic>=1.8.1
+ipython>=8.4.0
From 503f9a97af39abbe05ce47ac8d57397ed8bbf274 Mon Sep 17 00:00:00 2001
From: Andrew Gallant
Date: Fri, 10 Jan 2025 11:23:19 -0500
Subject: [PATCH 102/135] uv-resolver: pre-compute PEP 508 markers from
universal markers (#10472)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
It turns out that we use `UniversalMarker::pep508` quite a bit. To the
point that it makes sense to pre-compute it when constructing a
`UniversalMarker`.
This still isn't necessarily the fastest thing we can do, but this
results in a major speed-up and `without_extras` no longer shows up for
me in a profile.
Motivating benchmarks. First, from #10430:
```
$ hyperfine 'rm -f uv.lock && uv lock' 'rm -f uv.lock && uv-ag-optimize-without-extras lock'
Benchmark 1: rm -f uv.lock && uv lock
Time (mean ± σ): 408.3 ms ± 276.6 ms [User: 333.6 ms, System: 111.1 ms]
Range (min … max): 316.9 ms … 1195.3 ms 10 runs
Warning: The first benchmarking run for this command was significantly slower than the rest (1.195 s). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.
Benchmark 2: rm -f uv.lock && uv-ag-optimize-without-extras lock
Time (mean ± σ): 209.4 ms ± 2.2 ms [User: 209.8 ms, System: 103.8 ms]
Range (min … max): 206.1 ms … 213.4 ms 14 runs
Summary
rm -f uv.lock && uv-ag-optimize-without-extras lock ran
1.95 ± 1.32 times faster than rm -f uv.lock && uv lock
```
And now from #10438:
```
$ hyperfine 'uv pip compile requirements.in -c constraints.txt --universal --no-progress --python-version 3.8 --offline > /dev/null' 'uv-ag-optimize-without-extras pip compile requirements.in -c constraints.txt --universal --no-progress --python-version 3.8 --offline > /dev/null'
Benchmark 1: uv pip compile requirements.in -c constraints.txt --universal --no-progress --python-version 3.8 --offline > /dev/null
Time (mean ± σ): 12.718 s ± 0.052 s [User: 12.818 s, System: 0.140 s]
Range (min … max): 12.650 s … 12.815 s 10 runs
Benchmark 2: uv-ag-optimize-without-extras pip compile requirements.in -c constraints.txt --universal --no-progress --python-version 3.8 --offline > /dev/null
Time (mean ± σ): 419.5 ms ± 6.7 ms [User: 434.7 ms, System: 100.6 ms]
Range (min … max): 412.7 ms … 434.3 ms 10 runs
Summary
uv-ag-optimize-without-extras pip compile requirements.in -c constraints.txt --universal --no-progress --python-version 3.8 --offline > /dev/null ran
30.32 ± 0.50 times faster than uv pip compile requirements.in -c constraints.txt --universal --no-progress --python-version 3.8 --offline > /dev/null
```
Fixes #10430, Fixes #10438
---
crates/uv-resolver/src/universal_marker.rs | 23 ++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/crates/uv-resolver/src/universal_marker.rs b/crates/uv-resolver/src/universal_marker.rs
index 00673e95ae2b..5724f19ab5b1 100644
--- a/crates/uv-resolver/src/universal_marker.rs
+++ b/crates/uv-resolver/src/universal_marker.rs
@@ -69,17 +69,24 @@ pub struct UniversalMarker {
/// stand-point, evaluating it requires uv's custom encoding of extras (and
/// groups).
marker: MarkerTree,
+ /// The strictly PEP 508 version of `marker`. Basically, `marker`, but
+ /// without any extras in it. This could be computed on demand (and
+ /// that's what we used to do), but we do it enough that it was causing a
+ /// regression in some cases.
+ pep508: MarkerTree,
}
impl UniversalMarker {
/// A constant universal marker that always evaluates to `true`.
pub(crate) const TRUE: UniversalMarker = UniversalMarker {
marker: MarkerTree::TRUE,
+ pep508: MarkerTree::TRUE,
};
/// A constant universal marker that always evaluates to `false`.
pub(crate) const FALSE: UniversalMarker = UniversalMarker {
marker: MarkerTree::FALSE,
+ pep508: MarkerTree::FALSE,
};
/// Creates a new universal marker from its constituent pieces.
@@ -94,7 +101,10 @@ impl UniversalMarker {
/// Creates a new universal marker from a marker that has already been
/// combined from a PEP 508 and conflict marker.
pub(crate) fn from_combined(marker: MarkerTree) -> UniversalMarker {
- UniversalMarker { marker }
+ UniversalMarker {
+ marker,
+ pep508: marker.without_extras(),
+ }
}
/// Combine this universal marker with the one given in a way that unions
@@ -102,6 +112,7 @@ impl UniversalMarker {
/// `other` evaluate to `true`.
pub(crate) fn or(&mut self, other: UniversalMarker) {
self.marker.or(other.marker);
+ self.pep508.or(other.pep508);
}
/// Combine this universal marker with the one given in a way that
@@ -109,6 +120,7 @@ impl UniversalMarker {
/// `self` and `other` evaluate to `true`.
pub(crate) fn and(&mut self, other: UniversalMarker) {
self.marker.and(other.marker);
+ self.pep508.and(other.pep508);
}
/// Imbibes the world knowledge expressed by `conflicts` into this marker.
@@ -121,6 +133,7 @@ impl UniversalMarker {
let self_marker = self.marker;
self.marker = conflicts.marker;
self.marker.implies(self_marker);
+ self.pep508 = self.marker.without_extras();
}
/// Assumes that a given extra/group for the given package is activated.
@@ -132,6 +145,7 @@ impl UniversalMarker {
ConflictPackage::Extra(ref extra) => self.assume_extra(item.package(), extra),
ConflictPackage::Group(ref group) => self.assume_group(item.package(), group),
}
+ self.pep508 = self.marker.without_extras();
}
/// Assumes that a given extra/group for the given package is not
@@ -144,6 +158,7 @@ impl UniversalMarker {
ConflictPackage::Extra(ref extra) => self.assume_not_extra(item.package(), extra),
ConflictPackage::Group(ref group) => self.assume_not_group(item.package(), group),
}
+ self.pep508 = self.marker.without_extras();
}
/// Assumes that a given extra for the given package is activated.
@@ -155,6 +170,7 @@ impl UniversalMarker {
self.marker = self
.marker
.simplify_extras_with(|candidate| *candidate == extra);
+ self.pep508 = self.marker.without_extras();
}
/// Assumes that a given extra for the given package is not activated.
@@ -166,6 +182,7 @@ impl UniversalMarker {
self.marker = self
.marker
.simplify_not_extras_with(|candidate| *candidate == extra);
+ self.pep508 = self.marker.without_extras();
}
/// Assumes that a given group for the given package is activated.
@@ -177,6 +194,7 @@ impl UniversalMarker {
self.marker = self
.marker
.simplify_extras_with(|candidate| *candidate == extra);
+ self.pep508 = self.marker.without_extras();
}
/// Assumes that a given group for the given package is not activated.
@@ -188,6 +206,7 @@ impl UniversalMarker {
self.marker = self
.marker
.simplify_not_extras_with(|candidate| *candidate == extra);
+ self.pep508 = self.marker.without_extras();
}
/// Returns true if this universal marker will always evaluate to `true`.
@@ -259,7 +278,7 @@ impl UniversalMarker {
/// always use a universal marker since it accounts for all possible ways
/// for a package to be installed.
pub fn pep508(self) -> MarkerTree {
- self.marker.without_extras()
+ self.pep508
}
/// Returns the non-PEP 508 marker expression that represents conflicting
From b3d7beb1a00637639b3dff4d9f9e897a4dd90128 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Fri, 10 Jan 2025 14:46:36 -0500
Subject: [PATCH 103/135] Use `arcstr` for package, extra, and group names
(#10475)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
This appears to be a consistent 1% performance improvement and should
also reduce memory quite a bit. We've also decided to use these for
markers, so it's nice to use the same optimization here.
```
❯ hyperfine "./uv pip compile --universal scripts/requirements/airflow.in" "./arcstr pip compile --universal scripts/requirements/airflow.in" --min-runs 50 --warmup 20
Benchmark 1: ./uv pip compile --universal scripts/requirements/airflow.in
Time (mean ± σ): 136.3 ms ± 4.0 ms [User: 139.1 ms, System: 241.9 ms]
Range (min … max): 131.5 ms … 149.5 ms 50 runs
Benchmark 2: ./arcstr pip compile --universal scripts/requirements/airflow.in
Time (mean ± σ): 134.9 ms ± 3.2 ms [User: 137.6 ms, System: 239.0 ms]
Range (min … max): 130.1 ms … 151.8 ms 50 runs
Summary
./arcstr pip compile --universal scripts/requirements/airflow.in ran
1.01 ± 0.04 times faster than ./uv pip compile --universal scripts/requirements/airflow.in
```
---
Cargo.lock | 7 ++
Cargo.toml | 1 +
crates/uv-normalize/Cargo.toml | 1 +
crates/uv-normalize/src/extra_name.rs | 5 +-
crates/uv-normalize/src/group_name.rs | 5 +-
crates/uv-normalize/src/lib.rs | 34 ++++--
crates/uv-normalize/src/package_name.rs | 7 +-
crates/uv-normalize/src/small_string.rs | 119 +++++++++++++++++++++
crates/uv-resolver/src/universal_marker.rs | 3 +-
9 files changed, 166 insertions(+), 16 deletions(-)
create mode 100644 crates/uv-normalize/src/small_string.rs
diff --git a/Cargo.lock b/Cargo.lock
index 5bdaed13303f..4e07002b0479 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -93,6 +93,12 @@ version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+[[package]]
+name = "arcstr"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d"
+
[[package]]
name = "arrayref"
version = "0.3.9"
@@ -5227,6 +5233,7 @@ dependencies = [
name = "uv-normalize"
version = "0.0.1"
dependencies = [
+ "arcstr",
"rkyv",
"schemars",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
index 6bba2c93e93b..ae7315f428e5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -71,6 +71,7 @@ uv-workspace = { path = "crates/uv-workspace" }
anstream = { version = "0.6.15" }
anyhow = { version = "1.0.89" }
+arcstr = { version = "1.2.0" }
async-channel = { version = "2.3.1" }
async-compression = { version = "0.4.12", features = ["bzip2", "gzip", "xz", "zstd"] }
async-trait = { version = "0.1.82" }
diff --git a/crates/uv-normalize/Cargo.toml b/crates/uv-normalize/Cargo.toml
index 8f4db6a15d82..8d9c1c23e512 100644
--- a/crates/uv-normalize/Cargo.toml
+++ b/crates/uv-normalize/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
workspace = true
[dependencies]
+arcstr = { workspace = true }
rkyv = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
diff --git a/crates/uv-normalize/src/extra_name.rs b/crates/uv-normalize/src/extra_name.rs
index f23431178d2c..bc3c5f737bb8 100644
--- a/crates/uv-normalize/src/extra_name.rs
+++ b/crates/uv-normalize/src/extra_name.rs
@@ -4,6 +4,7 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize};
+use crate::small_string::SmallString;
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
/// The normalized name of an extra dependency.
@@ -14,9 +15,9 @@ use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNam
/// See:
/// -
/// -
-#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
-pub struct ExtraName(String);
+pub struct ExtraName(SmallString);
impl ExtraName {
/// Create a validated, normalized extra name.
diff --git a/crates/uv-normalize/src/group_name.rs b/crates/uv-normalize/src/group_name.rs
index 72aa898ec729..a3ecb74c8c24 100644
--- a/crates/uv-normalize/src/group_name.rs
+++ b/crates/uv-normalize/src/group_name.rs
@@ -5,6 +5,7 @@ use std::sync::LazyLock;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
+use crate::small_string::SmallString;
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
/// The normalized name of a dependency group.
@@ -12,9 +13,9 @@ use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNam
/// See:
/// -
/// -
-#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
-pub struct GroupName(String);
+pub struct GroupName(SmallString);
impl GroupName {
/// Create a validated, normalized group name.
diff --git a/crates/uv-normalize/src/lib.rs b/crates/uv-normalize/src/lib.rs
index 583b17bcc6af..17beee6b9995 100644
--- a/crates/uv-normalize/src/lib.rs
+++ b/crates/uv-normalize/src/lib.rs
@@ -5,26 +5,37 @@ pub use dist_info_name::DistInfoName;
pub use extra_name::ExtraName;
pub use group_name::{GroupName, DEV_DEPENDENCIES};
pub use package_name::PackageName;
+use small_string::SmallString;
mod dist_info_name;
mod extra_name;
mod group_name;
mod package_name;
+mod small_string;
/// Validate and normalize an owned package or extra name.
-pub(crate) fn validate_and_normalize_owned(name: String) -> Result {
+pub(crate) fn validate_and_normalize_owned(name: String) -> Result {
if is_normalized(&name)? {
- Ok(name)
+ Ok(SmallString::from(name))
} else {
- validate_and_normalize_ref(name)
+ Ok(SmallString::from(normalize(&name)?))
}
}
/// Validate and normalize an unowned package or extra name.
pub(crate) fn validate_and_normalize_ref(
name: impl AsRef,
-) -> Result {
+) -> Result {
let name = name.as_ref();
+ if is_normalized(name)? {
+ Ok(SmallString::from(name))
+ } else {
+ Ok(SmallString::from(normalize(name)?))
+ }
+}
+
+/// Normalize an unowned package or extra name.
+fn normalize(name: &str) -> Result {
let mut normalized = String::with_capacity(name.len());
let mut last = None;
@@ -136,9 +147,14 @@ mod tests {
"FrIeNdLy-._.-bArD",
];
for input in inputs {
- assert_eq!(validate_and_normalize_ref(input).unwrap(), "friendly-bard");
assert_eq!(
- validate_and_normalize_owned(input.to_string()).unwrap(),
+ validate_and_normalize_ref(input).unwrap().as_ref(),
+ "friendly-bard"
+ );
+ assert_eq!(
+ validate_and_normalize_owned(input.to_string())
+ .unwrap()
+ .as_ref(),
"friendly-bard"
);
}
@@ -169,9 +185,11 @@ mod tests {
// Unchanged
let unchanged = ["friendly-bard", "1okay", "okay2"];
for input in unchanged {
- assert_eq!(validate_and_normalize_ref(input).unwrap(), input);
+ assert_eq!(validate_and_normalize_ref(input).unwrap().as_ref(), input);
assert_eq!(
- validate_and_normalize_owned(input.to_string()).unwrap(),
+ validate_and_normalize_owned(input.to_string())
+ .unwrap()
+ .as_ref(),
input
);
assert!(is_normalized(input).unwrap());
diff --git a/crates/uv-normalize/src/package_name.rs b/crates/uv-normalize/src/package_name.rs
index d3067f17aec7..742867e7f51b 100644
--- a/crates/uv-normalize/src/package_name.rs
+++ b/crates/uv-normalize/src/package_name.rs
@@ -1,8 +1,10 @@
use std::borrow::Cow;
+use std::cmp::PartialEq;
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize};
+use crate::small_string::SmallString;
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
/// The normalized name of a package.
@@ -13,7 +15,6 @@ use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNam
/// See:
#[derive(
Debug,
- Default,
Clone,
PartialEq,
Eq,
@@ -27,7 +28,7 @@ use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNam
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[rkyv(derive(Debug))]
-pub struct PackageName(String);
+pub struct PackageName(SmallString);
impl PackageName {
/// Create a validated, normalized package name.
@@ -56,7 +57,7 @@ impl PackageName {
Cow::Owned(owned_string)
} else {
- Cow::Borrowed(self.0.as_str())
+ Cow::Borrowed(self.0.as_ref())
}
}
diff --git a/crates/uv-normalize/src/small_string.rs b/crates/uv-normalize/src/small_string.rs
new file mode 100644
index 000000000000..ca6d3ea6dadb
--- /dev/null
+++ b/crates/uv-normalize/src/small_string.rs
@@ -0,0 +1,119 @@
+use std::cmp::PartialEq;
+use std::ops::Deref;
+
+/// An optimized small string type for short identifiers, like package names.
+///
+/// Represented as an [`arcstr::ArcStr`] internally.
+#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub(crate) struct SmallString(arcstr::ArcStr);
+
+impl From<&str> for SmallString {
+ #[inline]
+ fn from(s: &str) -> Self {
+ Self(s.into())
+ }
+}
+
+impl From for SmallString {
+ #[inline]
+ fn from(s: String) -> Self {
+ Self(s.into())
+ }
+}
+
+impl AsRef for SmallString {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ &self.0
+ }
+}
+
+impl Deref for SmallString {
+ type Target = str;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl core::fmt::Debug for SmallString {
+ #[inline]
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ core::fmt::Debug::fmt(&self.0, f)
+ }
+}
+
+impl core::fmt::Display for SmallString {
+ #[inline]
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ core::fmt::Display::fmt(&self.0, f)
+ }
+}
+
+/// A [`serde::Serialize`] implementation for [`SmallString`].
+impl serde::Serialize for SmallString {
+ fn serialize
(&self, serializer: S) -> Result
+ where
+ S: serde::Serializer,
+ {
+ self.0.serialize(serializer)
+ }
+}
+
+/// An [`rkyv`] implementation for [`SmallString`].
+impl rkyv::Archive for SmallString {
+ type Archived = rkyv::string::ArchivedString;
+ type Resolver = rkyv::string::StringResolver;
+
+ #[inline]
+ fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place) {
+ rkyv::string::ArchivedString::resolve_from_str(&self.0, resolver, out);
+ }
+}
+
+impl rkyv::Serialize for SmallString
+where
+ S: rkyv::rancor::Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized,
+ S::Error: rkyv::rancor::Source,
+{
+ fn serialize(&self, serializer: &mut S) -> Result {
+ rkyv::string::ArchivedString::serialize_from_str(&self.0, serializer)
+ }
+}
+
+impl rkyv::Deserialize
+ for rkyv::string::ArchivedString
+{
+ fn deserialize(&self, _deserializer: &mut D) -> Result {
+ Ok(SmallString::from(self.as_str()))
+ }
+}
+
+impl PartialEq for rkyv::string::ArchivedString {
+ fn eq(&self, other: &SmallString) -> bool {
+ **other == **self
+ }
+}
+
+impl PartialOrd for rkyv::string::ArchivedString {
+ fn partial_cmp(&self, other: &SmallString) -> Option<::core::cmp::Ordering> {
+ Some(self.as_str().cmp(other))
+ }
+}
+
+/// An [`schemars::JsonSchema`] implementation for [`SmallString`].
+#[cfg(feature = "schemars")]
+impl schemars::JsonSchema for SmallString {
+ fn is_referenceable() -> bool {
+ String::is_referenceable()
+ }
+
+ fn schema_name() -> String {
+ String::schema_name()
+ }
+
+ fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+ String::json_schema(_gen)
+ }
+}
diff --git a/crates/uv-resolver/src/universal_marker.rs b/crates/uv-resolver/src/universal_marker.rs
index 5724f19ab5b1..0789ad264a61 100644
--- a/crates/uv-resolver/src/universal_marker.rs
+++ b/crates/uv-resolver/src/universal_marker.rs
@@ -488,6 +488,7 @@ fn encode_package_group(package: &PackageName, group: &GroupName) -> ExtraName {
#[cfg(test)]
mod tests {
use super::*;
+ use std::str::FromStr;
use uv_pypi_types::ConflictSet;
@@ -516,7 +517,7 @@ mod tests {
/// Shortcut for creating a package name.
fn create_package(name: &str) -> PackageName {
- PackageName::new(name.to_string()).unwrap()
+ PackageName::from_str(name).unwrap()
}
/// Shortcut for creating an extra name.
From 2982c2074cba9af442d509a917cef742804a40cc Mon Sep 17 00:00:00 2001
From: Jeremy Foxcroft
Date: Fri, 10 Jan 2025 15:04:35 -0500
Subject: [PATCH 104/135] Fix `UV_FIND_LINKS` delimiter to split on commas
(#10477)
#8061 incorrectly claims to change the delimiter for `UV_FIND_LINKS`
from spaces to commas. In reality, it prevents `UV_FIND_LINKS` from
being split. This commit fixes that.
---
crates/uv-cli/src/lib.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs
index 8c0dc6f8cee9..ec38129b1d1b 100644
--- a/crates/uv-cli/src/lib.rs
+++ b/crates/uv-cli/src/lib.rs
@@ -4593,6 +4593,7 @@ pub struct IndexArgs {
long,
short,
env = EnvVars::UV_FIND_LINKS,
+ value_delimiter = ',',
value_parser = parse_find_links,
help_heading = "Index options"
)]
From d44affaac0a16af52775b63dba76935f155f31e4 Mon Sep 17 00:00:00 2001
From: konsti
Date: Fri, 10 Jan 2025 21:10:54 +0100
Subject: [PATCH 105/135] Read publish username from URL (#10469)
---
crates/uv/src/commands/publish.rs | 245 ++++++++++++++++++++++++------
1 file changed, 201 insertions(+), 44 deletions(-)
diff --git a/crates/uv/src/commands/publish.rs b/crates/uv/src/commands/publish.rs
index 88dcdf684245..b036dc1444e3 100644
--- a/crates/uv/src/commands/publish.rs
+++ b/crates/uv/src/commands/publish.rs
@@ -11,7 +11,9 @@ use std::time::Duration;
use tracing::{debug, info};
use url::Url;
use uv_cache::Cache;
-use uv_client::{AuthIntegration, BaseClientBuilder, Connectivity, RegistryClientBuilder};
+use uv_client::{
+ AuthIntegration, BaseClient, BaseClientBuilder, Connectivity, RegistryClientBuilder,
+};
use uv_configuration::{KeyringProviderType, TrustedHost, TrustedPublishing};
use uv_distribution_types::{Index, IndexCapabilities, IndexLocations, IndexUrl};
use uv_publish::{
@@ -68,6 +70,18 @@ pub(crate) async fn publish(
.auth_integration(AuthIntegration::NoAuthMiddleware)
.wrap_existing(&upload_client);
+ let (publish_url, username, password) = gather_credentials(
+ publish_url,
+ username,
+ password,
+ trusted_publishing,
+ keyring_provider,
+ &oidc_client,
+ check_url.as_ref(),
+ printer,
+ )
+ .await?;
+
// Initialize the registry client.
let check_url_client = if let Some(index_url) = &check_url {
let index_urls = IndexLocations::new(
@@ -93,6 +107,83 @@ pub(crate) async fn publish(
None
};
+ for (file, raw_filename, filename) in files {
+ if let Some(check_url_client) = &check_url_client {
+ if uv_publish::check_url(check_url_client, &file, &filename).await? {
+ writeln!(printer.stderr(), "File {filename} already exists, skipping")?;
+ continue;
+ }
+ }
+
+ let size = fs_err::metadata(&file)?.len();
+ let (bytes, unit) = human_readable_bytes(size);
+ writeln!(
+ printer.stderr(),
+ "{} {filename} {}",
+ "Uploading".bold().green(),
+ format!("({bytes:.1}{unit})").dimmed()
+ )?;
+ let reporter = PublishReporter::single(printer);
+ let uploaded = upload(
+ &file,
+ &raw_filename,
+ &filename,
+ &publish_url,
+ &upload_client,
+ username.as_deref(),
+ password.as_deref(),
+ check_url_client.as_ref(),
+ // Needs to be an `Arc` because the reqwest `Body` static lifetime requirement
+ Arc::new(reporter),
+ )
+ .await?; // Filename and/or URL are already attached, if applicable.
+ info!("Upload succeeded");
+ if !uploaded {
+ writeln!(
+ printer.stderr(),
+ "{}",
+ "File already exists, skipping".dimmed()
+ )?;
+ }
+ }
+
+ Ok(ExitStatus::Success)
+}
+
+/// Unify the different possible source for username and password information.
+///
+/// Returns the publish URL, the username and the password.
+async fn gather_credentials(
+ mut publish_url: Url,
+ mut username: Option,
+ mut password: Option,
+ trusted_publishing: TrustedPublishing,
+ keyring_provider: KeyringProviderType,
+ oidc_client: &BaseClient,
+ check_url: Option<&IndexUrl>,
+ printer: Printer,
+) -> Result<(Url, Option, Option)> {
+ // Support reading username and password from the URL, for symmetry with the index API.
+ if let Some(url_password) = publish_url.password() {
+ if password.is_some_and(|password| password != url_password) {
+ bail!("The password can't be set both in the publish URL and in the CLI");
+ }
+ password = Some(url_password.to_string());
+ publish_url
+ .set_password(None)
+ .expect("Failed to clear publish URL password");
+ }
+
+ if !publish_url.username().is_empty() {
+ if username.is_some_and(|username| username != publish_url.username()) {
+ bail!("The username can't be set both in the publish URL and in the CLI");
+ }
+ username = Some(publish_url.username().to_string());
+ publish_url
+ .set_username("")
+ .expect("Failed to clear publish URL username");
+ }
+
// If applicable, attempt obtaining a token for trusted publishing.
let trusted_publishing_token = check_trusted_publishing(
username.as_deref(),
@@ -100,7 +191,7 @@ pub(crate) async fn publish(
keyring_provider,
trusted_publishing,
&publish_url,
- &oidc_client,
+ oidc_client,
)
.await?;
@@ -179,48 +270,7 @@ pub(crate) async fn publish(
// We may be using the keyring for the simple index.
}
}
-
- for (file, raw_filename, filename) in files {
- if let Some(check_url_client) = &check_url_client {
- if uv_publish::check_url(check_url_client, &file, &filename).await? {
- writeln!(printer.stderr(), "File {filename} already exists, skipping")?;
- continue;
- }
- }
-
- let size = fs_err::metadata(&file)?.len();
- let (bytes, unit) = human_readable_bytes(size);
- writeln!(
- printer.stderr(),
- "{} {filename} {}",
- "Uploading".bold().green(),
- format!("({bytes:.1}{unit})").dimmed()
- )?;
- let reporter = PublishReporter::single(printer);
- let uploaded = upload(
- &file,
- &raw_filename,
- &filename,
- &publish_url,
- &upload_client,
- username.as_deref(),
- password.as_deref(),
- check_url_client.as_ref(),
- // Needs to be an `Arc` because the reqwest `Body` static lifetime requirement
- Arc::new(reporter),
- )
- .await?; // Filename and/or URL are already attached, if applicable.
- info!("Upload succeeded");
- if !uploaded {
- writeln!(
- printer.stderr(),
- "{}",
- "File already exists, skipping".dimmed()
- )?;
- }
- }
-
- Ok(ExitStatus::Success)
+ Ok((publish_url, username, password))
}
fn prompt_username_and_password() -> Result<(Option, Option)> {
@@ -235,3 +285,110 @@ fn prompt_username_and_password() -> Result<(Option, Option)> {
uv_console::password(password_prompt, &term).context("Failed to read password")?;
Ok((Some(username), Some(password)))
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use insta::assert_snapshot;
+ use std::str::FromStr;
+ use url::Url;
+
+ async fn credentials(
+ url: Url,
+ username: Option,
+ password: Option,
+ ) -> Result<(Url, Option, Option)> {
+ let client = BaseClientBuilder::new().build();
+ gather_credentials(
+ url,
+ username,
+ password,
+ TrustedPublishing::Never,
+ KeyringProviderType::Disabled,
+ &client,
+ None,
+ Printer::Quiet,
+ )
+ .await
+ }
+
+ #[tokio::test]
+ async fn username_password_sources() {
+ let example_url = Url::from_str("https://example.com").unwrap();
+ let example_url_username = Url::from_str("https://ferris@example.com").unwrap();
+ let example_url_username_password =
+ Url::from_str("https://ferris:f3rr1s@example.com").unwrap();
+
+ let (publish_url, username, password) =
+ credentials(example_url.clone(), None, None).await.unwrap();
+ assert_eq!(publish_url, example_url);
+ assert_eq!(username, None);
+ assert_eq!(password, None);
+
+ let (publish_url, username, password) =
+ credentials(example_url_username.clone(), None, None)
+ .await
+ .unwrap();
+ assert_eq!(publish_url, example_url);
+ assert_eq!(username.as_deref(), Some("ferris"));
+ assert_eq!(password, None);
+
+ let (publish_url, username, password) =
+ credentials(example_url_username_password.clone(), None, None)
+ .await
+ .unwrap();
+ assert_eq!(publish_url, example_url);
+ assert_eq!(username.as_deref(), Some("ferris"));
+ assert_eq!(password.as_deref(), Some("f3rr1s"));
+
+ // Ok: The username is the same between CLI/env vars and URL
+ let (publish_url, username, password) = credentials(
+ example_url_username_password.clone(),
+ Some("ferris".to_string()),
+ None,
+ )
+ .await
+ .unwrap();
+ assert_eq!(publish_url, example_url);
+ assert_eq!(username.as_deref(), Some("ferris"));
+ assert_eq!(password.as_deref(), Some("f3rr1s"));
+
+ // Err: There are two different usernames between CLI/env vars and URL
+ let err = credentials(
+ example_url_username_password.clone(),
+ Some("packaging-platypus".to_string()),
+ None,
+ )
+ .await
+ .unwrap_err();
+ assert_snapshot!(
+ err.to_string(),
+ @"The username can't be set both in the publish URL and in the CLI"
+ );
+
+ // Ok: The username and password are the same between CLI/env vars and URL
+ let (publish_url, username, password) = credentials(
+ example_url_username_password.clone(),
+ Some("ferris".to_string()),
+ Some("f3rr1s".to_string()),
+ )
+ .await
+ .unwrap();
+ assert_eq!(publish_url, example_url);
+ assert_eq!(username.as_deref(), Some("ferris"));
+ assert_eq!(password.as_deref(), Some("f3rr1s"));
+
+ // Err: There are two different passwords between CLI/env vars and URL
+ let err = credentials(
+ example_url_username_password.clone(),
+ Some("ferris".to_string()),
+ Some("secret".to_string()),
+ )
+ .await
+ .unwrap_err();
+ assert_snapshot!(
+ err.to_string(),
+ @"The password can't be set both in the publish URL and in the CLI"
+ );
+ }
+}
From 7a21b713b4701048b7c60378bb1f4668a8461129 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Fri, 10 Jan 2025 15:12:23 -0500
Subject: [PATCH 106/135] Avoid allocating for names in the PEP 508 parser
(#10476)
## Summary
We can read from the slice directly. I don't think this will affect
performance today, because `from_str` will then allocate, but it
_should_ be a speedup once #10475 merges, since we can then avoid
allocating a `String` and go straight from `str` to `ArcStr`.
---
crates/uv-pep508/src/lib.rs | 43 ++++++++++++++++---------------------
1 file changed, 19 insertions(+), 24 deletions(-)
diff --git a/crates/uv-pep508/src/lib.rs b/crates/uv-pep508/src/lib.rs
index 95f4a4774c4e..c4b1193a1845 100644
--- a/crates/uv-pep508/src/lib.rs
+++ b/crates/uv-pep508/src/lib.rs
@@ -394,12 +394,9 @@ fn parse_name(cursor: &mut Cursor) -> Result(cursor: &mut Cursor) -> Result {
- name.push(char);
- cursor.next();
- // [.-_] can't be the final character
- if cursor.peek().is_none() && matches!(char, '.' | '-' | '_') {
- return Err(Pep508Error {
- message: Pep508ErrorSource::String(format!(
- "Package name must end with an alphanumeric character, not '{char}'"
- )),
- start: index,
- len: char.len_utf8(),
- input: cursor.to_string(),
- });
- }
- }
- Some(_) | None => {
- return Ok(PackageName::new(name)
- .expect("`PackageName` validation should match PEP 508 parsing"));
+ if let Some((index, char @ ('A'..='Z' | 'a'..='z' | '0'..='9' | '.' | '-' | '_'))) =
+ cursor.peek()
+ {
+ cursor.next();
+ // [.-_] can't be the final character
+ if cursor.peek().is_none() && matches!(char, '.' | '-' | '_') {
+ return Err(Pep508Error {
+ message: Pep508ErrorSource::String(format!(
+ "Package name must end with an alphanumeric character, not `{char}`"
+ )),
+ start: index,
+ len: char.len_utf8(),
+ input: cursor.to_string(),
+ });
}
+ } else {
+ let len = cursor.pos() - start;
+ return Ok(PackageName::from_str(cursor.slice(start, len)).unwrap());
}
}
}
@@ -1033,7 +1028,7 @@ mod tests {
assert_snapshot!(
parse_pep508_err("name_"),
@"
- Package name must end with an alphanumeric character, not '_'
+ Package name must end with an alphanumeric character, not `_`
name_
^"
);
From 8420195aa75821f33d7024c500ec2624c68ab669 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Fri, 10 Jan 2025 15:15:12 -0500
Subject: [PATCH 107/135] Use `ArcStr` for marker values (#10453)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
N.B. After fixing #10430, `ArcStr` became the fastest implementation
(and the gains were significantly reduced, down to 1-2%). See:
https://github.com/astral-sh/uv/pull/10453#issuecomment-2583344414.
## Summary
I tried out a variety of small string crates, but `Arc`
outperformed them, giving a ~10% speed-up:
```console
❯ hyperfine "../arcstr lock" "../flexstr lock" "uv lock" "../arc lock" "../compact_str lock" --prepare "rm -f uv.lock" --min-runs 50 --warmup 20
Benchmark 1: ../arcstr lock
Time (mean ± σ): 304.6 ms ± 2.3 ms [User: 302.9 ms, System: 117.8 ms]
Range (min … max): 299.0 ms … 311.3 ms 50 runs
Benchmark 2: ../flexstr lock
Time (mean ± σ): 319.2 ms ± 1.7 ms [User: 317.7 ms, System: 118.2 ms]
Range (min … max): 316.8 ms … 323.3 ms 50 runs
Benchmark 3: uv lock
Time (mean ± σ): 330.6 ms ± 1.5 ms [User: 328.1 ms, System: 139.3 ms]
Range (min … max): 326.6 ms … 334.2 ms 50 runs
Benchmark 4: ../arc lock
Time (mean ± σ): 303.0 ms ± 1.2 ms [User: 301.6 ms, System: 118.4 ms]
Range (min … max): 300.3 ms … 305.3 ms 50 runs
Benchmark 5: ../compact_str lock
Time (mean ± σ): 320.4 ms ± 2.0 ms [User: 318.7 ms, System: 120.8 ms]
Range (min … max): 317.3 ms … 326.7 ms 50 runs
Summary
../arc lock ran
1.01 ± 0.01 times faster than ../arcstr lock
1.05 ± 0.01 times faster than ../flexstr lock
1.06 ± 0.01 times faster than ../compact_str lock
1.09 ± 0.01 times faster than uv lock
```
---
Cargo.lock | 3 +
crates/uv-distribution-types/Cargo.toml | 1 +
.../src/prioritized_distribution.rs | 21 +++----
crates/uv-pep508/Cargo.toml | 1 +
crates/uv-pep508/src/lib.rs | 6 +-
crates/uv-pep508/src/marker/algebra.rs | 60 ++++++++++---------
crates/uv-pep508/src/marker/parse.rs | 4 +-
crates/uv-pep508/src/marker/simplify.rs | 8 ++-
crates/uv-pep508/src/marker/tree.rs | 24 ++++----
crates/uv-resolver/Cargo.toml | 1 +
crates/uv-resolver/src/resolution/output.rs | 3 +-
crates/uv-resolver/src/resolver/mod.rs | 8 ++-
12 files changed, 80 insertions(+), 60 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 4e07002b0479..7d894b5e005a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5013,6 +5013,7 @@ name = "uv-distribution-types"
version = "0.0.1"
dependencies = [
"anyhow",
+ "arcstr",
"bitflags 2.6.0",
"fs-err 3.0.0",
"itertools 0.14.0",
@@ -5272,6 +5273,7 @@ dependencies = [
name = "uv-pep508"
version = "0.6.0"
dependencies = [
+ "arcstr",
"boxcar",
"indexmap",
"insta",
@@ -5508,6 +5510,7 @@ dependencies = [
name = "uv-resolver"
version = "0.0.1"
dependencies = [
+ "arcstr",
"clap",
"dashmap",
"either",
diff --git a/crates/uv-distribution-types/Cargo.toml b/crates/uv-distribution-types/Cargo.toml
index 4a501926b7ec..d4c5309f5e05 100644
--- a/crates/uv-distribution-types/Cargo.toml
+++ b/crates/uv-distribution-types/Cargo.toml
@@ -29,6 +29,7 @@ uv-platform-tags = { workspace = true }
uv-pypi-types = { workspace = true }
anyhow = { workspace = true }
+arcstr = { workspace = true }
bitflags = { workspace = true }
fs-err = { workspace = true }
itertools = { workspace = true }
diff --git a/crates/uv-distribution-types/src/prioritized_distribution.rs b/crates/uv-distribution-types/src/prioritized_distribution.rs
index 61b010df26b5..d5bfc4d5ecd2 100644
--- a/crates/uv-distribution-types/src/prioritized_distribution.rs
+++ b/crates/uv-distribution-types/src/prioritized_distribution.rs
@@ -1,5 +1,6 @@
use std::fmt::{Display, Formatter};
+use arcstr::ArcStr;
use tracing::debug;
use uv_distribution_filename::{BuildTag, WheelFilename};
@@ -662,12 +663,12 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "win32".to_string(),
+ value: arcstr::literal!("win32"),
});
tag_marker.and(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal,
- value: "x86".to_string(),
+ value: arcstr::literal!("x86"),
}));
marker.or(tag_marker);
}
@@ -675,12 +676,12 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "win32".to_string(),
+ value: arcstr::literal!("win32"),
});
tag_marker.and(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal,
- value: "x86_64".to_string(),
+ value: arcstr::literal!("x86_64"),
}));
marker.or(tag_marker);
}
@@ -688,12 +689,12 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "win32".to_string(),
+ value: arcstr::literal!("win32"),
});
tag_marker.and(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal,
- value: "arm64".to_string(),
+ value: arcstr::literal!("arm64"),
}));
marker.or(tag_marker);
}
@@ -703,7 +704,7 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "darwin".to_string(),
+ value: arcstr::literal!("darwin"),
});
// Parse the macOS version from the tag.
@@ -787,7 +788,7 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
arch_marker.or(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal,
- value: (*arch).to_string(),
+ value: ArcStr::from(*arch),
}));
}
tag_marker.and(arch_marker);
@@ -800,7 +801,7 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
let mut tag_marker = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "linux".to_string(),
+ value: arcstr::literal!("linux"),
});
// Parse the architecture from the tag.
@@ -866,7 +867,7 @@ pub fn implied_markers(filename: &WheelFilename) -> MarkerTree {
tag_marker.and(MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::PlatformMachine,
operator: MarkerOperator::Equal,
- value: arch.to_string(),
+ value: ArcStr::from(arch),
}));
marker.or(tag_marker);
diff --git a/crates/uv-pep508/Cargo.toml b/crates/uv-pep508/Cargo.toml
index 912a226f7c0d..1fff96287684 100644
--- a/crates/uv-pep508/Cargo.toml
+++ b/crates/uv-pep508/Cargo.toml
@@ -23,6 +23,7 @@ uv-fs = { workspace = true }
uv-normalize = { workspace = true }
uv-pep440 = { workspace = true }
+arcstr = { workspace = true}
boxcar = { workspace = true }
indexmap = { workspace = true }
itertools = { workspace = true }
diff --git a/crates/uv-pep508/src/lib.rs b/crates/uv-pep508/src/lib.rs
index c4b1193a1845..087f69d316f4 100644
--- a/crates/uv-pep508/src/lib.rs
+++ b/crates/uv-pep508/src/lib.rs
@@ -1334,17 +1334,17 @@ mod tests {
let mut b = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "win32".to_string(),
+ value: arcstr::literal!("win32"),
});
let mut c = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
- value: "linux".to_string(),
+ value: arcstr::literal!("linux"),
});
let d = MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::ImplementationName,
operator: MarkerOperator::Equal,
- value: "cpython".to_string(),
+ value: arcstr::literal!("cpython"),
});
c.and(d);
diff --git a/crates/uv-pep508/src/marker/algebra.rs b/crates/uv-pep508/src/marker/algebra.rs
index 892c38f8ddc1..303d2696f494 100644
--- a/crates/uv-pep508/src/marker/algebra.rs
+++ b/crates/uv-pep508/src/marker/algebra.rs
@@ -51,6 +51,7 @@ use std::ops::Bound;
use std::sync::Mutex;
use std::sync::MutexGuard;
+use arcstr::ArcStr;
use itertools::{Either, Itertools};
use rustc_hash::FxHashMap;
use std::sync::LazyLock;
@@ -287,28 +288,31 @@ impl InternerGuard<'_> {
// values in `exclusions`.
//
// See: https://discuss.python.org/t/clarify-usage-of-platform-system/70900
- let (key, value) = match (key, value.as_str()) {
- (MarkerValueString::PlatformSystem, "Windows") => {
- (CanonicalMarkerValueString::SysPlatform, "win32".to_string())
- }
+ let (key, value) = match (key, value.as_ref()) {
+ (MarkerValueString::PlatformSystem, "Windows") => (
+ CanonicalMarkerValueString::SysPlatform,
+ arcstr::literal!("win32"),
+ ),
(MarkerValueString::PlatformSystem, "Darwin") => (
CanonicalMarkerValueString::SysPlatform,
- "darwin".to_string(),
+ arcstr::literal!("darwin"),
+ ),
+ (MarkerValueString::PlatformSystem, "Linux") => (
+ CanonicalMarkerValueString::SysPlatform,
+ arcstr::literal!("linux"),
+ ),
+ (MarkerValueString::PlatformSystem, "AIX") => (
+ CanonicalMarkerValueString::SysPlatform,
+ arcstr::literal!("aix"),
),
- (MarkerValueString::PlatformSystem, "Linux") => {
- (CanonicalMarkerValueString::SysPlatform, "linux".to_string())
- }
- (MarkerValueString::PlatformSystem, "AIX") => {
- (CanonicalMarkerValueString::SysPlatform, "aix".to_string())
- }
(MarkerValueString::PlatformSystem, "Emscripten") => (
CanonicalMarkerValueString::SysPlatform,
- "emscripten".to_string(),
+ arcstr::literal!("emscripten"),
),
// See: https://peps.python.org/pep-0738/#sys
(MarkerValueString::PlatformSystem, "Android") => (
CanonicalMarkerValueString::SysPlatform,
- "android".to_string(),
+ arcstr::literal!("android"),
),
_ => (key.into(), value),
};
@@ -869,48 +873,48 @@ impl InternerGuard<'_> {
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
- value: "nt".to_string(),
+ value: arcstr::literal!("nt"),
},
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "linux".to_string(),
+ value: arcstr::literal!("linux"),
},
),
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
- value: "nt".to_string(),
+ value: arcstr::literal!("nt"),
},
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "darwin".to_string(),
+ value: arcstr::literal!("darwin"),
},
),
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
- value: "nt".to_string(),
+ value: arcstr::literal!("nt"),
},
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "ios".to_string(),
+ value: arcstr::literal!("ios"),
},
),
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
- value: "posix".to_string(),
+ value: arcstr::literal!("posix"),
},
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: "win32".to_string(),
+ value: arcstr::literal!("win32"),
},
),
];
@@ -950,12 +954,12 @@ impl InternerGuard<'_> {
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
- value: platform_system.to_string(),
+ value: ArcStr::from(platform_system),
},
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
- value: sys_platform.to_string(),
+ value: ArcStr::from(sys_platform),
},
));
}
@@ -996,13 +1000,13 @@ pub(crate) enum Variable {
/// string marker and value.
In {
key: CanonicalMarkerValueString,
- value: String,
+ value: ArcStr,
},
/// A variable representing a ` in ` expression for a particular
/// string marker and value.
Contains {
key: CanonicalMarkerValueString,
- value: String,
+ value: ArcStr,
},
/// A variable representing the existence or absence of a given extra.
///
@@ -1128,7 +1132,7 @@ pub(crate) enum Edges {
// Invariant: All ranges are simple, meaning they can be represented by a bounded
// interval without gaps. Additionally, there are at least two edges in the set.
String {
- edges: SmallVec<(Ranges, NodeId)>,
+ edges: SmallVec<(Ranges, NodeId)>,
},
// The edges of a boolean variable, representing the values `true` (the `high` child)
// and `false` (the `low` child).
@@ -1158,8 +1162,8 @@ impl Edges {
///
/// This function will panic for the `In` and `Contains` marker operators, which
/// should be represented as separate boolean variables.
- fn from_string(operator: MarkerOperator, value: String) -> Edges {
- let range: Ranges = match operator {
+ fn from_string(operator: MarkerOperator, value: ArcStr) -> Edges {
+ let range: Ranges = match operator {
MarkerOperator::Equal => Ranges::singleton(value),
MarkerOperator::NotEqual => Ranges::singleton(value).complement(),
MarkerOperator::GreaterThan => Ranges::strictly_higher_than(value),
diff --git a/crates/uv-pep508/src/marker/parse.rs b/crates/uv-pep508/src/marker/parse.rs
index d85af429f8b2..bae032d9b270 100644
--- a/crates/uv-pep508/src/marker/parse.rs
+++ b/crates/uv-pep508/src/marker/parse.rs
@@ -1,5 +1,5 @@
+use arcstr::ArcStr;
use std::str::FromStr;
-
use uv_normalize::ExtraName;
use uv_pep440::{Version, VersionPattern, VersionSpecifier};
@@ -92,7 +92,7 @@ pub(crate) fn parse_marker_value(
Some((start_pos, quotation_mark @ ('"' | '\''))) => {
cursor.next();
let (start, len) = cursor.take_while(|c| c != quotation_mark);
- let value = cursor.slice(start, len).to_string();
+ let value = ArcStr::from(cursor.slice(start, len));
cursor.next_expect_char(quotation_mark, start_pos)?;
Ok(MarkerValue::QuotedString(value))
}
diff --git a/crates/uv-pep508/src/marker/simplify.rs b/crates/uv-pep508/src/marker/simplify.rs
index ea868c3cfe4f..34c095b09efc 100644
--- a/crates/uv-pep508/src/marker/simplify.rs
+++ b/crates/uv-pep508/src/marker/simplify.rs
@@ -1,12 +1,14 @@
use std::fmt;
use std::ops::Bound;
+use arcstr::ArcStr;
use indexmap::IndexMap;
use itertools::Itertools;
use rustc_hash::FxBuildHasher;
-use uv_pep440::{Version, VersionSpecifier};
use version_ranges::Ranges;
+use uv_pep440::{Version, VersionSpecifier};
+
use crate::{ExtraOperator, MarkerExpression, MarkerOperator, MarkerTree, MarkerTreeKind};
/// Returns a simplified DNF expression for a given marker tree.
@@ -131,7 +133,7 @@ fn collect_dnf(
let expr = MarkerExpression::String {
key: marker.key().into(),
- value: marker.value().to_owned(),
+ value: ArcStr::from(marker.value()),
operator,
};
@@ -150,7 +152,7 @@ fn collect_dnf(
let expr = MarkerExpression::String {
key: marker.key().into(),
- value: marker.value().to_owned(),
+ value: ArcStr::from(marker.value()),
operator,
};
diff --git a/crates/uv-pep508/src/marker/tree.rs b/crates/uv-pep508/src/marker/tree.rs
index 55e8b5d7d06c..099fdb7e66d9 100644
--- a/crates/uv-pep508/src/marker/tree.rs
+++ b/crates/uv-pep508/src/marker/tree.rs
@@ -3,6 +3,7 @@ use std::fmt::{self, Display, Formatter};
use std::ops::{Bound, Deref};
use std::str::FromStr;
+use arcstr::ArcStr;
use itertools::Itertools;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use version_ranges::Ranges;
@@ -129,7 +130,7 @@ pub enum MarkerValue {
/// `extra`. This one is special because it's a list and not env but user given
Extra,
/// Not a constant, but a user given quoted string with a value inside such as '3.8' or "windows"
- QuotedString(String),
+ QuotedString(ArcStr),
}
impl FromStr for MarkerValue {
@@ -272,8 +273,8 @@ impl MarkerOperator {
/// Returns the marker operator and value whose union represents the given range.
pub fn from_bounds(
- bounds: (&Bound, &Bound),
- ) -> impl Iterator- {
+ bounds: (&Bound, &Bound),
+ ) -> impl Iterator
- {
let (b1, b2) = match bounds {
(Bound::Included(v1), Bound::Included(v2)) if v1 == v2 => {
(Some((MarkerOperator::Equal, v1.clone())), None)
@@ -291,7 +292,7 @@ impl MarkerOperator {
}
/// Returns a value specifier representing the given lower bound.
- pub fn from_lower_bound(bound: &Bound) -> Option<(MarkerOperator, String)> {
+ pub fn from_lower_bound(bound: &Bound) -> Option<(MarkerOperator, ArcStr)> {
match bound {
Bound::Included(value) => Some((MarkerOperator::GreaterEqual, value.clone())),
Bound::Excluded(value) => Some((MarkerOperator::GreaterThan, value.clone())),
@@ -300,7 +301,7 @@ impl MarkerOperator {
}
/// Returns a value specifier representing the given upper bound.
- pub fn from_upper_bound(bound: &Bound) -> Option<(MarkerOperator, String)> {
+ pub fn from_upper_bound(bound: &Bound) -> Option<(MarkerOperator, ArcStr)> {
match bound {
Bound::Included(value) => Some((MarkerOperator::LessEqual, value.clone())),
Bound::Excluded(value) => Some((MarkerOperator::LessThan, value.clone())),
@@ -485,7 +486,7 @@ pub enum MarkerExpression {
String {
key: MarkerValueString,
operator: MarkerOperator,
- value: String,
+ value: ArcStr,
},
/// `extra '...'` or `'...' extra`.
Extra {
@@ -1383,7 +1384,7 @@ impl Ord for VersionMarkerTree<'_> {
pub struct StringMarkerTree<'a> {
id: NodeId,
key: CanonicalMarkerValueString,
- map: &'a [(Ranges, NodeId)],
+ map: &'a [(Ranges, NodeId)],
}
impl StringMarkerTree<'_> {
@@ -1393,7 +1394,7 @@ impl StringMarkerTree<'_> {
}
/// The edges of this node, corresponding to possible output ranges of the given variable.
- pub fn children(&self) -> impl ExactSizeIterator
- , MarkerTree)> {
+ pub fn children(&self) -> impl ExactSizeIterator
- , MarkerTree)> {
self.map
.iter()
.map(|(range, node)| (range, MarkerTree(node.negate(self.id))))
@@ -1418,7 +1419,7 @@ impl Ord for StringMarkerTree<'_> {
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct InMarkerTree<'a> {
key: CanonicalMarkerValueString,
- value: &'a str,
+ value: &'a ArcStr,
high: NodeId,
low: NodeId,
}
@@ -1430,7 +1431,7 @@ impl InMarkerTree<'_> {
}
/// The value (RHS) for this expression.
- pub fn value(&self) -> &str {
+ pub fn value(&self) -> &ArcStr {
self.value
}
@@ -1654,6 +1655,7 @@ mod test {
use std::str::FromStr;
use insta::assert_snapshot;
+
use uv_normalize::ExtraName;
use uv_pep440::Version;
@@ -2041,7 +2043,7 @@ mod test {
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
- value: "nt".to_string(),
+ value: arcstr::literal!("nt")
}
);
}
diff --git a/crates/uv-resolver/Cargo.toml b/crates/uv-resolver/Cargo.toml
index 62706a4e6c89..4f0a4687a7dd 100644
--- a/crates/uv-resolver/Cargo.toml
+++ b/crates/uv-resolver/Cargo.toml
@@ -38,6 +38,7 @@ uv-types = { workspace = true }
uv-warnings = { workspace = true }
uv-workspace = { workspace = true }
+arcstr = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
dashmap = { workspace = true }
either = { workspace = true }
diff --git a/crates/uv-resolver/src/resolution/output.rs b/crates/uv-resolver/src/resolution/output.rs
index aa9385d71ed2..deec8ace1730 100644
--- a/crates/uv-resolver/src/resolution/output.rs
+++ b/crates/uv-resolver/src/resolution/output.rs
@@ -7,6 +7,7 @@ use petgraph::{
Directed, Direction,
};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
+
use uv_configuration::{Constraints, Overrides};
use uv_distribution::Metadata;
use uv_distribution_types::{
@@ -726,7 +727,7 @@ impl ResolverOutput {
MarkerExpression::String {
key: value_string.into(),
operator: MarkerOperator::Equal,
- value: from_env.to_string(),
+ value: from_env.into(),
}
}
};
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index 60d309bb8194..c1343a0f44e7 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -1424,11 +1424,15 @@ impl ResolverState
Date: Fri, 10 Jan 2025 15:52:56 -0500
Subject: [PATCH 108/135] Bump version to v0.5.17 (#10480)
---
CHANGELOG.md | 56 +++++++++++++++++++++++++++
Cargo.lock | 4 +-
crates/uv-version/Cargo.toml | 2 +-
crates/uv/Cargo.toml | 2 +-
docs/getting-started/installation.md | 4 +-
docs/guides/integration/aws-lambda.md | 4 +-
docs/guides/integration/docker.md | 8 ++--
docs/guides/integration/github.md | 2 +-
docs/guides/integration/pre-commit.md | 6 +--
pyproject.toml | 2 +-
10 files changed, 73 insertions(+), 17 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e4af1bff5e8..4c308ca9e796 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,61 @@
# Changelog
+## 0.5.17
+
+This release includes support for generating lockfiles from scripts based on inline metadata, as defined in PEP 723.
+
+By default, scripts remain unlocked, and must be locked explicitly with `uv lock --script /path/to/script.py`, which
+will generate a lockfile adjacent to the script (e.g., `script.py.lock`). Once generated, the lockfile will be
+respected (and updated, if necessary) across `uv run --script`, `uv add --script`, and `uv remove --script` invocations.
+
+This release also includes support for `uv export --script` and `uv tree --script`. Both commands support PEP 723
+scripts with and without accompanying lockfiles.
+
+### Enhancements
+
+- Add support for locking PEP 723 scripts ([#10135](https://github.com/astral-sh/uv/pull/10135))
+- Respect PEP 723 script lockfiles in `uv run` ([#10136](https://github.com/astral-sh/uv/pull/10136))
+- Update PEP 723 lockfile in `uv add --script` ([#10145](https://github.com/astral-sh/uv/pull/10145))
+- Update PEP 723 lockfile in `uv remove --script` ([#10162](https://github.com/astral-sh/uv/pull/10162))
+- Add `--script` support to `uv export` for PEP 723 scripts ([#10160](https://github.com/astral-sh/uv/pull/10160))
+- Add `--script` support to `uv tree` for PEP 723 scripts ([#10159](https://github.com/astral-sh/uv/pull/10159))
+- Add `ls` alias to `uv {tool, python, pip} list` ([#10240](https://github.com/astral-sh/uv/pull/10240))
+- Allow reading `--with-requirements` from stdin in `uv add` and `uv run` ([#10447](https://github.com/astral-sh/uv/pull/10447))
+- Warn-and-ignore for unsupported `requirements.txt` options ([#10420](https://github.com/astral-sh/uv/pull/10420))
+
+### Preview features
+
+- Add remaining Python type annotations to build backend ([#10434](https://github.com/astral-sh/uv/pull/10434))
+
+### Performance
+
+- Avoid allocating for names in the PEP 508 parser ([#10476](https://github.com/astral-sh/uv/pull/10476))
+- Fetch concurrently for non-first-match index strategies ([#10432](https://github.com/astral-sh/uv/pull/10432))
+- Remove unnecessary `.to_string()` call ([#10419](https://github.com/astral-sh/uv/pull/10419))
+- Respect sentinels in package prioritization ([#10443](https://github.com/astral-sh/uv/pull/10443))
+- Use `ArcStr` for marker values ([#10453](https://github.com/astral-sh/uv/pull/10453))
+- Use `ArcStr` for package, extra, and group names ([#10475](https://github.com/astral-sh/uv/pull/10475))
+- Use `matches!` rather than `contains` in `requirements.txt` parsing ([#10423](https://github.com/astral-sh/uv/pull/10423))
+- Use faster disjointness check for markers ([#10439](https://github.com/astral-sh/uv/pull/10439))
+- Pre-compute PEP 508 markers from universal markers ([#10472](https://github.com/astral-sh/uv/pull/10472))
+
+### Bug fixes
+
+- Fix `UV_FIND_LINKS` delimiter to split on commas ([#10477](https://github.com/astral-sh/uv/pull/10477))
+- Improve `uv tool list` output when tool environment is broken ([#10409](https://github.com/astral-sh/uv/pull/10409))
+- Only track markers for compatible versions ([#10457](https://github.com/astral-sh/uv/pull/10457))
+- Respect `requires-python` when installing tools ([#10401](https://github.com/astral-sh/uv/pull/10401))
+- Visit proxy packages eagerly ([#10441](https://github.com/astral-sh/uv/pull/10441))
+- Improve shell compatibility of `venv` activate scripts ([#10397](https://github.com/astral-sh/uv/pull/10397))
+- Read publish username from URL ([#10469](https://github.com/astral-sh/uv/pull/10469))
+
+### Documentation
+
+- Add Lambda layer instructions to AWS Lambda guide ([#10411](https://github.com/astral-sh/uv/pull/10411))
+- Add `uv lock --script` to the docs ([#10414](https://github.com/astral-sh/uv/pull/10414))
+- Use Windows-specific instructions in Jupyter guide ([#10446](https://github.com/astral-sh/uv/pull/10446))
+
+
## 0.5.16
### Enhancements
diff --git a/Cargo.lock b/Cargo.lock
index 7d894b5e005a..794fe515d071 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4489,7 +4489,7 @@ checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
[[package]]
name = "uv"
-version = "0.5.16"
+version = "0.5.17"
dependencies = [
"anstream",
"anyhow",
@@ -5701,7 +5701,7 @@ dependencies = [
[[package]]
name = "uv-version"
-version = "0.5.16"
+version = "0.5.17"
[[package]]
name = "uv-virtualenv"
diff --git a/crates/uv-version/Cargo.toml b/crates/uv-version/Cargo.toml
index bfc8226167fd..36f44dc72c2c 100644
--- a/crates/uv-version/Cargo.toml
+++ b/crates/uv-version/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uv-version"
-version = "0.5.16"
+version = "0.5.17"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml
index 5516afcdb635..2149039f8323 100644
--- a/crates/uv/Cargo.toml
+++ b/crates/uv/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uv"
-version = "0.5.16"
+version = "0.5.17"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
index d631f330479d..66e4b2dd68ba 100644
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -25,7 +25,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:
```console
- $ curl -LsSf https://astral.sh/uv/0.5.16/install.sh | sh
+ $ curl -LsSf https://astral.sh/uv/0.5.17/install.sh | sh
```
=== "Windows"
@@ -41,7 +41,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:
```console
- $ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.5.16/install.ps1 | iex"
+ $ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.5.17/install.ps1 | iex"
```
!!! tip
diff --git a/docs/guides/integration/aws-lambda.md b/docs/guides/integration/aws-lambda.md
index 11ed1d7ea50a..7121c02f10ae 100644
--- a/docs/guides/integration/aws-lambda.md
+++ b/docs/guides/integration/aws-lambda.md
@@ -92,7 +92,7 @@ the second stage, we'll copy this directory over to the final image, omitting th
other unnecessary files.
```dockerfile title="Dockerfile"
-FROM ghcr.io/astral-sh/uv:0.5.16 AS uv
+FROM ghcr.io/astral-sh/uv:0.5.17 AS uv
# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
@@ -294,7 +294,7 @@ And confirm that opening http://127.0.0.1:8000/ in a web browser displays, "Hell
Finally, we'll update the Dockerfile to include the local library in the deployment package:
```dockerfile title="Dockerfile"
-FROM ghcr.io/astral-sh/uv:0.5.16 AS uv
+FROM ghcr.io/astral-sh/uv:0.5.17 AS uv
# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
diff --git a/docs/guides/integration/docker.md b/docs/guides/integration/docker.md
index fee04193f3d1..dcdb73e9144f 100644
--- a/docs/guides/integration/docker.md
+++ b/docs/guides/integration/docker.md
@@ -28,7 +28,7 @@ $ docker run ghcr.io/astral-sh/uv --help
uv provides a distroless Docker image including the `uv` binary. The following tags are published:
- `ghcr.io/astral-sh/uv:latest`
-- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.5.16`
+- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.5.17`
- `ghcr.io/astral-sh/uv:{major}.{minor}`, e.g., `ghcr.io/astral-sh/uv:0.5` (the latest patch
version)
@@ -69,7 +69,7 @@ In addition, uv publishes the following images:
As with the distroless image, each image is published with uv version tags as
`ghcr.io/astral-sh/uv:{major}.{minor}.{patch}-{base}` and
-`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.5.16-alpine`.
+`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.5.17-alpine`.
For more details, see the [GitHub Container](https://github.com/astral-sh/uv/pkgs/container/uv)
page.
@@ -107,13 +107,13 @@ Note this requires `curl` to be available.
In either case, it is best practice to pin to a specific uv version, e.g., with:
```dockerfile
-COPY --from=ghcr.io/astral-sh/uv:0.5.16 /uv /uvx /bin/
+COPY --from=ghcr.io/astral-sh/uv:0.5.17 /uv /uvx /bin/
```
Or, with the installer:
```dockerfile
-ADD https://astral.sh/uv/0.5.16/install.sh /uv-installer.sh
+ADD https://astral.sh/uv/0.5.17/install.sh /uv-installer.sh
```
### Installing a project
diff --git a/docs/guides/integration/github.md b/docs/guides/integration/github.md
index f8543c7750cc..b46635c5d0ec 100644
--- a/docs/guides/integration/github.md
+++ b/docs/guides/integration/github.md
@@ -47,7 +47,7 @@ jobs:
uses: astral-sh/setup-uv@v5
with:
# Install a specific version of uv.
- version: "0.5.16"
+ version: "0.5.17"
```
## Setting up Python
diff --git a/docs/guides/integration/pre-commit.md b/docs/guides/integration/pre-commit.md
index e60c1785465b..5168c21f48be 100644
--- a/docs/guides/integration/pre-commit.md
+++ b/docs/guides/integration/pre-commit.md
@@ -36,7 +36,7 @@ To compile requirements via pre-commit, add the following to the `.pre-commit-co
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.16
+ rev: 0.5.17
hooks:
# Compile requirements
- id: pip-compile
@@ -48,7 +48,7 @@ To compile alternative files, modify `args` and `files`:
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.16
+ rev: 0.5.17
hooks:
# Compile requirements
- id: pip-compile
@@ -61,7 +61,7 @@ To run the hook over multiple files at the same time:
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.16
+ rev: 0.5.17
hooks:
# Compile requirements
- id: pip-compile
diff --git a/pyproject.toml b/pyproject.toml
index 843193109c26..7387c616ae3f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "maturin"
[project]
name = "uv"
-version = "0.5.16"
+version = "0.5.17"
description = "An extremely fast Python package and project manager, written in Rust."
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
requires-python = ">=3.8"
From 918ddef090b36da7bd3a3117e59ea1a657d29795 Mon Sep 17 00:00:00 2001
From: bnorick
Date: Fri, 10 Jan 2025 17:07:03 -0800
Subject: [PATCH 109/135] Fixes bug in `uv remove` when only comments exist
(#10484)
## Summary
Fixes a bug when there are only comments in the dependencies section.
Basically, after one removes all dependencies, if there are remaining
comments then the value unwrapped here
https://github.com/astral-sh/uv/blob/c198e2233efaa037a9154fd7fe2625e4c78d976b/crates/uv-workspace/src/pyproject_mut.rs#L1309
is never properly initialized.
It's initialized to `None`, here
https://github.com/astral-sh/uv/blob/c198e2233efaa037a9154fd7fe2625e4c78d976b/crates/uv-workspace/src/pyproject_mut.rs#L1256,
but doesn't get set to `Some(...)` until the first dependency here
https://github.com/astral-sh/uv/blob/c198e2233efaa037a9154fd7fe2625e4c78d976b/crates/uv-workspace/src/pyproject_mut.rs#L1276
and since we remove them all... there are none.
## Test Plan
Manually induced bug with
```
[project]
name = "t1"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"duct>=0.6.4",
"minilog>=2.3.1",
# comment
]
```
Then running
```
$ RUST_LOG=trace RUST_BACKTRACE=full uv remove duct minilog
DEBUG uv 0.5.8
DEBUG Found project root: `/home/bnorick/dev/workspace/t1`
DEBUG No workspace root found, using project root
thread 'main' panicked at crates/uv-workspace/src/pyproject_mut.rs:1294:73:
called `Option::unwrap()` on a `None` value
stack backtrace:
0: 0x5638d7bed6ba -
1: 0x5638d783760b -
2: 0x5638d7bae232 -
3: 0x5638d7bf0f07 -
4: 0x5638d7bf215c -
5: 0x5638d7bf1972 -
6: 0x5638d7bf1909 -
7: 0x5638d7bf18f4 -
8: 0x5638d75087d2 -
9: 0x5638d750896b -
10: 0x5638d7508d68 -
11: 0x5638d8dcf1bb -
12: 0x5638d76be271 -
13: 0x5638d75ef1f9 -
14: 0x5638d75fc3cd -
15: 0x5638d772d9de -
16: 0x5638d8476812 -
17: 0x5638d83e1894 -
18: 0x5638d84722d3 -
19: 0x5638d83e1372 -
20: 0x7f851cfc7d90 -
21: 0x7f851cfc7e40 - __libc_start_main
22: 0x5638d758e992 -
23: 0x0 -
```
---
crates/uv-workspace/src/pyproject_mut.rs | 12 ++---
crates/uv/tests/it/edit.rs | 61 ++++++++++++++++++++++++
2 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/crates/uv-workspace/src/pyproject_mut.rs b/crates/uv-workspace/src/pyproject_mut.rs
index 248bcc1035b5..10d7591ed19b 100644
--- a/crates/uv-workspace/src/pyproject_mut.rs
+++ b/crates/uv-workspace/src/pyproject_mut.rs
@@ -1272,15 +1272,11 @@ fn reformat_array_multiline(deps: &mut Array) {
.map(|(s, _)| s)
.unwrap_or(decor_prefix);
- // If there is no indentation, use four-space.
- indentation_prefix = Some(if decor_prefix.is_empty() {
- " ".to_string()
- } else {
- decor_prefix.to_string()
- });
+ indentation_prefix = (!decor_prefix.is_empty()).then_some(decor_prefix.to_string());
}
- let indentation_prefix_str = format!("\n{}", indentation_prefix.as_ref().unwrap());
+ let indentation_prefix_str =
+ format!("\n{}", indentation_prefix.as_deref().unwrap_or(" "));
for comment in find_comments(decor.prefix()).chain(find_comments(decor.suffix())) {
match comment.comment_type {
@@ -1306,7 +1302,7 @@ fn reformat_array_multiline(deps: &mut Array) {
match comment.comment_type {
CommentType::OwnLine => {
let indentation_prefix_str =
- format!("\n{}", indentation_prefix.as_ref().unwrap());
+ format!("\n{}", indentation_prefix.as_deref().unwrap_or(" "));
rv.push_str(&indentation_prefix_str);
}
CommentType::EndOfLine => {
diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs
index 537f2f83d454..84f8cd2cef51 100644
--- a/crates/uv/tests/it/edit.rs
+++ b/crates/uv/tests/it/edit.rs
@@ -9024,3 +9024,64 @@ fn remove_requirement() -> Result<()> {
Ok(())
}
+
+/// Remove all dependencies with remaining comments
+#[test]
+fn remove_all_with_comments() -> Result<()> {
+ let context = TestContext::new("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.12"
+ dependencies = [
+ "duct",
+ "minilog",
+ # foo
+ # bar
+ ]
+
+ [build-system]
+ requires = ["setuptools>=42"]
+ build-backend = "setuptools.build_meta"
+ "#})?;
+
+ uv_snapshot!(context.filters(), context.remove().arg("duct").arg("minilog"), @r###"
+ success: true
+ exit_code: 0
+ ----- stdout -----
+
+ ----- stderr -----
+ Resolved 1 package in [TIME]
+ Prepared 1 package in [TIME]
+ Installed 1 package in [TIME]
+ + project==0.1.0 (from file://[TEMP_DIR]/)
+ "###);
+
+ let pyproject_toml = context.read("pyproject.toml");
+
+ insta::with_settings!({
+ filters => context.filters(),
+ }, {
+ assert_snapshot!(
+ pyproject_toml, @r###"
+ [project]
+ name = "project"
+ version = "0.1.0"
+ requires-python = ">=3.12"
+ dependencies = [
+ # foo
+ # bar
+ ]
+
+ [build-system]
+ requires = ["setuptools>=42"]
+ build-backend = "setuptools.build_meta"
+ "###
+ );
+ });
+
+ Ok(())
+}
From 54b3a438d03483c6e76f05168bd417657b693e4b Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Fri, 10 Jan 2025 22:30:04 -0500
Subject: [PATCH 110/135] Avoid forking for identical markers (#10490)
## Summary
If you have a dependency with a marker, and you add a constraint, it
causes us to _always_ fork, because we represent the constraint as a
second dependency with the marker repeated (and, therefore, we have two
requirements of the same name, both with markers). I don't think we
should fork here -- and in the end it's leading to this undesirable
resolution: #10481.
I tried to change constraints such that we just _reuse_ and augment the
initial requirement, but that has a fairly negative effect on error
messages: #10489. So this fix seems a bit better to me.
Closes https://github.com/astral-sh/uv/issues/10481.
---
crates/uv-resolver/src/resolver/mod.rs | 22 ++++++++++++++++++++++
crates/uv/tests/it/lock.rs | 26 ++++++++++----------------
crates/uv/tests/it/lock_scenarios.rs | 2 +-
crates/uv/tests/it/pip_compile.rs | 14 +++++++++-----
4 files changed, 42 insertions(+), 22 deletions(-)
diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs
index c1343a0f44e7..ced06cf26c9e 100644
--- a/crates/uv-resolver/src/resolver/mod.rs
+++ b/crates/uv-resolver/src/resolver/mod.rs
@@ -3302,6 +3302,28 @@ impl Forks {
}
continue;
}
+ } else {
+ // If all dependencies have the same markers, we should also avoid forking.
+ if let Some(dep) = deps.first() {
+ let marker = dep.package.marker();
+ if deps.iter().all(|dep| marker == dep.package.marker()) {
+ // Unless that "same marker" is a Python requirement that is stricter than
+ // the current Python requirement. In that case, we need to fork to respect
+ // the stricter requirement.
+ if marker::requires_python(marker)
+ .is_none_or(|bound| !python_requirement.raises(&bound))
+ {
+ for dep in deps {
+ for fork in &mut forks {
+ if fork.env.included_by_marker(marker) {
+ fork.add_dependency(dep.clone());
+ }
+ }
+ }
+ continue;
+ }
+ }
+ }
}
for dep in deps {
let mut forker = match ForkingPossibility::new(env, &dep) {
diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs
index d4039ab4bf5c..6a67f8273de2 100644
--- a/crates/uv/tests/it/lock.rs
+++ b/crates/uv/tests/it/lock.rs
@@ -21706,9 +21706,7 @@ fn lock_pytorch_cpu() -> Result<()> {
version = 1
requires-python = ">=3.12.[X]"
resolution-markers = [
- "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
- "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
- "(platform_machine != 'aarch64' and platform_machine != 'x86_64') or sys_platform != 'linux'",
+ "platform_machine != 'aarch64' or sys_platform != 'linux'",
"platform_machine == 'aarch64' and sys_platform == 'linux'",
"(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')",
"(platform_machine == 'aarch64' and sys_platform == 'linux') or sys_platform == 'darwin'",
@@ -21915,7 +21913,7 @@ fn lock_pytorch_cpu() -> Result<()> {
version = "9.1.0.70"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
+ { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 },
@@ -21927,7 +21925,7 @@ fn lock_pytorch_cpu() -> Result<()> {
version = "11.2.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
+ { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/8a/0e728f749baca3fbeffad762738276e5df60851958be7783af121a7221e7/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399", size = 211422548 },
@@ -21950,9 +21948,9 @@ fn lock_pytorch_cpu() -> Result<()> {
version = "11.6.1.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
+ { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "nvidia-cusparse-cu12", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/46/6b/a5c33cf16af09166845345275c34ad2190944bcc6026797a39f8e0a282e0/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e", size = 127634111 },
@@ -21965,7 +21963,7 @@ fn lock_pytorch_cpu() -> Result<()> {
version = "12.3.1.170"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
+ { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/96/a9/c0d2f83a53d40a4a41be14cea6a0bf9e668ffcf8b004bd65633f433050c0/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3", size = 207381987 },
@@ -22161,9 +22159,7 @@ fn lock_pytorch_cpu() -> Result<()> {
version = "2.5.1+cu124"
source = { registry = "https://download.pytorch.org/whl/cu124" }
resolution-markers = [
- "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
- "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
- "(platform_machine != 'aarch64' and platform_machine != 'x86_64') or sys_platform != 'linux'",
+ "platform_machine != 'aarch64' or sys_platform != 'linux'",
]
dependencies = [
{ name = "filelock", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
@@ -22248,9 +22244,7 @@ fn lock_pytorch_cpu() -> Result<()> {
version = "0.20.1+cu124"
source = { registry = "https://download.pytorch.org/whl/cu124" }
resolution-markers = [
- "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
- "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
- "(platform_machine != 'aarch64' and platform_machine != 'x86_64') or sys_platform != 'linux'",
+ "platform_machine != 'aarch64' or sys_platform != 'linux'",
]
dependencies = [
{ name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
@@ -22267,7 +22261,7 @@ fn lock_pytorch_cpu() -> Result<()> {
version = "3.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "filelock", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'" },
+ { name = "filelock", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/eb/65f5ba83c2a123f6498a3097746607e5b2f16add29e36765305e4ac7fdd8/triton-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8182f42fd8080a7d39d666814fa36c5e30cc00ea7eeeb1a2983dbb4c99a0fdc", size = 209551444 },
diff --git a/crates/uv/tests/it/lock_scenarios.rs b/crates/uv/tests/it/lock_scenarios.rs
index 4ac688b7f457..377ab064139c 100644
--- a/crates/uv/tests/it/lock_scenarios.rs
+++ b/crates/uv/tests/it/lock_scenarios.rs
@@ -1320,7 +1320,7 @@ fn fork_marker_disjoint() -> Result<()> {
----- stdout -----
----- stderr -----
- × No solution found when resolving dependencies for split (sys_platform == 'linux'):
+ × No solution found when resolving dependencies:
╰─▶ Because your project depends on package-a{sys_platform == 'linux'}>=2 and package-a{sys_platform == 'linux'}<2, we can conclude that your project's requirements are unsatisfiable.
"###
);
diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs
index 9fc609c5675e..c222d619f436 100644
--- a/crates/uv/tests/it/pip_compile.rs
+++ b/crates/uv/tests/it/pip_compile.rs
@@ -13270,7 +13270,9 @@ exceptiongroup==1.0.0rc8
# uv pip compile --cache-dir [CACHE_DIR] requirements.in -c constraints.txt --universal -p 3.10
alembic==1.8.1
# via -r requirements.in
- astroid==2.13.5
+ astroid==2.13.5 ; python_full_version >= '3.11'
+ # via pylint
+ astroid==3.1.0 ; python_full_version < '3.11'
# via pylint
asttokens==2.4.1
# via stack-data
@@ -13298,7 +13300,7 @@ exceptiongroup==1.0.0rc8
# via pylint
jedi==0.19.1
# via ipython
- lazy-object-proxy==1.10.0
+ lazy-object-proxy==1.10.0 ; python_full_version >= '3.11'
# via astroid
mako==1.3.2
# via alembic
@@ -13322,7 +13324,9 @@ exceptiongroup==1.0.0rc8
# via stack-data
pygments==2.17.2
# via ipython
- pylint==2.15.8
+ pylint==2.15.8 ; python_full_version >= '3.11'
+ # via -r requirements.in
+ pylint==3.1.0 ; python_full_version < '3.11'
# via -r requirements.in
six==1.16.0
# via asttokens
@@ -13344,11 +13348,11 @@ exceptiongroup==1.0.0rc8
# sqlalchemy
wcwidth==0.2.13
# via prompt-toolkit
- wrapt==1.16.0
+ wrapt==1.16.0 ; python_full_version >= '3.11'
# via astroid
----- stderr -----
- Resolved 34 packages in [TIME]
+ Resolved 36 packages in [TIME]
"###);
Ok(())
From e57acc55518b19c177c48d8d9d449e8976fb1555 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Sat, 11 Jan 2025 09:06:26 -0500
Subject: [PATCH 111/135] Avoid prompting on terminals during publish tests
(#10496)
## Summary
Closes https://github.com/astral-sh/uv/issues/10493.
## Test Plan
Run `cargo test --profile fast-build --no-fail-fast -p uv
username_password_sources` from a terminal.
---
crates/uv/src/commands/publish.rs | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/crates/uv/src/commands/publish.rs b/crates/uv/src/commands/publish.rs
index b036dc1444e3..b3405ccd9d07 100644
--- a/crates/uv/src/commands/publish.rs
+++ b/crates/uv/src/commands/publish.rs
@@ -78,6 +78,7 @@ pub(crate) async fn publish(
keyring_provider,
&oidc_client,
check_url.as_ref(),
+ Prompt::Enabled,
printer,
)
.await?;
@@ -150,6 +151,14 @@ pub(crate) async fn publish(
Ok(ExitStatus::Success)
}
+/// Whether to allow prompting for username and password.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum Prompt {
+ Enabled,
+ #[allow(dead_code)]
+ Disabled,
+}
+
/// Unify the different possible source for username and password information.
///
/// Returns the publish URL, the username and the password.
@@ -161,6 +170,7 @@ async fn gather_credentials(
keyring_provider: KeyringProviderType,
oidc_client: &BaseClient,
check_url: Option<&IndexUrl>,
+ prompt: Prompt,
printer: Printer,
) -> Result<(Url, Option, Option)> {
// Support reading username and password from the URL, for symmetry with the index API.
@@ -200,7 +210,10 @@ async fn gather_credentials(
(Some("__token__".to_string()), Some(password.to_string()))
} else {
if username.is_none() && password.is_none() {
- prompt_username_and_password()?
+ match prompt {
+ Prompt::Enabled => prompt_username_and_password()?,
+ Prompt::Disabled => (None, None),
+ }
} else {
(username, password)
}
@@ -289,8 +302,10 @@ fn prompt_username_and_password() -> Result<(Option, Option)> {
#[cfg(test)]
mod tests {
use super::*;
- use insta::assert_snapshot;
+
use std::str::FromStr;
+
+ use insta::assert_snapshot;
use url::Url;
async fn credentials(
@@ -307,6 +322,7 @@ mod tests {
KeyringProviderType::Disabled,
&client,
None,
+ Prompt::Disabled,
Printer::Quiet,
)
.await
From 5bc09a1e9e9cc1b2ac8ad2c968401e4e36f47085 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Sat, 11 Jan 2025 09:23:07 -0500
Subject: [PATCH 112/135] Revert "improve shell compatibility of venv activate
scripts (#10397)" (#10497)
## Summary
This reverts commit 2f7f9ea571f4fa8d2eefbfa4a95f6bc8fd18175c
(https://github.com/astral-sh/uv/pull/10397). We're seeing some
user-reported failures, so we need to investigate further before
re-shipping.
Re-opens https://github.com/astral-sh/uv/issues/7480.
Closes https://github.com/astral-sh/uv/issues/10487.
---
.github/workflows/ci.yml | 1 -
crates/uv-virtualenv/src/activator/activate | 21 ++++++++-------------
2 files changed, 8 insertions(+), 14 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 736aaffd2833..fce3bceaa495 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -95,7 +95,6 @@ jobs:
version: ${{ env.SHELLCHECK_VERSION }}
severity: style
check_together: "yes"
- additional_files: activate
cargo-clippy:
timeout-minutes: 10
diff --git a/crates/uv-virtualenv/src/activator/activate b/crates/uv-virtualenv/src/activator/activate
index 011eac5abb82..5a49d9e48596 100644
--- a/crates/uv-virtualenv/src/activator/activate
+++ b/crates/uv-virtualenv/src/activator/activate
@@ -31,12 +31,9 @@ if [ -n "${BASH_VERSION:+x}" ] ; then
exit 33
fi
elif [ -n "${ZSH_VERSION:+x}" ] ; then
- # we use eval in these paths to "hide" the fact that these branches
- # genuinely don't work when run in the wrong shell. Any shell or
- # linter that eagerly checks them would be right to complain!
- eval 'SCRIPT_PATH="${(%):-%x}"'
+ SCRIPT_PATH="${(%):-%x}"
elif [ -n "${KSH_VERSION:+x}" ] ; then
- eval 'SCRIPT_PATH="${.sh.file}"'
+ SCRIPT_PATH="${.sh.file}"
fi
deactivate () {
@@ -44,12 +41,12 @@ deactivate () {
# reset old environment variables
# ! [ -z ${VAR+_} ] returns true if VAR is declared at all
- if [ -n "${_OLD_VIRTUAL_PATH:+_}" ] ; then
+ if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then
PATH="$_OLD_VIRTUAL_PATH"
export PATH
unset _OLD_VIRTUAL_PATH
fi
- if [ -n "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then
+ if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then
PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
@@ -60,7 +57,7 @@ deactivate () {
# we made may not be respected
hash -r 2>/dev/null
- if [ -n "${_OLD_VIRTUAL_PS1+_}" ] ; then
+ if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then
PS1="$_OLD_VIRTUAL_PS1"
export PS1
unset _OLD_VIRTUAL_PS1
@@ -78,7 +75,7 @@ deactivate () {
deactivate nondestructive
VIRTUAL_ENV='{{ VIRTUAL_ENV_DIR }}'
-if { [ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]; } && command -v cygpath &> /dev/null ; then
+if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then
VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV")
fi
export VIRTUAL_ENV
@@ -87,8 +84,6 @@ _OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/{{ BIN_NAME }}:$PATH"
export PATH
-# this file is templated, so the constant comparison here actually varies
-# shellcheck disable=SC2050
if [ "x{{ VIRTUAL_PROMPT }}" != x ] ; then
VIRTUAL_ENV_PROMPT="({{ VIRTUAL_PROMPT }}) "
else
@@ -97,7 +92,7 @@ fi
export VIRTUAL_ENV_PROMPT
# unset PYTHONHOME if set
-if [ -n "${PYTHONHOME+_}" ] ; then
+if ! [ -z "${PYTHONHOME+_}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME"
unset PYTHONHOME
fi
@@ -109,7 +104,7 @@ if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then
fi
# Make sure to unalias pydoc if it's already there
-{ alias pydoc 2>/dev/null >/dev/null && unalias pydoc; } || true
+alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true
pydoc () {
python -m pydoc "$@"
From 27d1bad55076ebcad1fb536f0f1949de03cb7c5a Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Sat, 11 Jan 2025 09:38:26 -0500
Subject: [PATCH 113/135] Bump version to v0.5.18 (#10499)
---
CHANGELOG.md | 9 ++++++++-
Cargo.lock | 4 ++--
crates/uv-version/Cargo.toml | 2 +-
crates/uv/Cargo.toml | 2 +-
docs/getting-started/installation.md | 4 ++--
docs/guides/integration/aws-lambda.md | 4 ++--
docs/guides/integration/docker.md | 8 ++++----
docs/guides/integration/github.md | 2 +-
docs/guides/integration/pre-commit.md | 6 +++---
pyproject.toml | 2 +-
10 files changed, 25 insertions(+), 18 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c308ca9e796..f1ca6fca8fb6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.5.18
+
+### Bug fixes
+
+- Avoid forking for identical markers ([#10490](https://github.com/astral-sh/uv/pull/10490))
+- Avoid panic in `uv remove` when only comments exist ([#10484](https://github.com/astral-sh/uv/pull/10484))
+- Revert "improve shell compatibility of venv activate scripts (#10397)" ([#10497](https://github.com/astral-sh/uv/pull/10497))
+
## 0.5.17
This release includes support for generating lockfiles from scripts based on inline metadata, as defined in PEP 723.
@@ -55,7 +63,6 @@ scripts with and without accompanying lockfiles.
- Add `uv lock --script` to the docs ([#10414](https://github.com/astral-sh/uv/pull/10414))
- Use Windows-specific instructions in Jupyter guide ([#10446](https://github.com/astral-sh/uv/pull/10446))
-
## 0.5.16
### Enhancements
diff --git a/Cargo.lock b/Cargo.lock
index 794fe515d071..b4b7daefc07f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4489,7 +4489,7 @@ checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
[[package]]
name = "uv"
-version = "0.5.17"
+version = "0.5.18"
dependencies = [
"anstream",
"anyhow",
@@ -5701,7 +5701,7 @@ dependencies = [
[[package]]
name = "uv-version"
-version = "0.5.17"
+version = "0.5.18"
[[package]]
name = "uv-virtualenv"
diff --git a/crates/uv-version/Cargo.toml b/crates/uv-version/Cargo.toml
index 36f44dc72c2c..876e2d578ab0 100644
--- a/crates/uv-version/Cargo.toml
+++ b/crates/uv-version/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uv-version"
-version = "0.5.17"
+version = "0.5.18"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml
index 2149039f8323..de27506f98d4 100644
--- a/crates/uv/Cargo.toml
+++ b/crates/uv/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uv"
-version = "0.5.17"
+version = "0.5.18"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
index 66e4b2dd68ba..1eec38086336 100644
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -25,7 +25,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:
```console
- $ curl -LsSf https://astral.sh/uv/0.5.17/install.sh | sh
+ $ curl -LsSf https://astral.sh/uv/0.5.18/install.sh | sh
```
=== "Windows"
@@ -41,7 +41,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:
```console
- $ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.5.17/install.ps1 | iex"
+ $ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.5.18/install.ps1 | iex"
```
!!! tip
diff --git a/docs/guides/integration/aws-lambda.md b/docs/guides/integration/aws-lambda.md
index 7121c02f10ae..16cdab8e9691 100644
--- a/docs/guides/integration/aws-lambda.md
+++ b/docs/guides/integration/aws-lambda.md
@@ -92,7 +92,7 @@ the second stage, we'll copy this directory over to the final image, omitting th
other unnecessary files.
```dockerfile title="Dockerfile"
-FROM ghcr.io/astral-sh/uv:0.5.17 AS uv
+FROM ghcr.io/astral-sh/uv:0.5.18 AS uv
# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
@@ -294,7 +294,7 @@ And confirm that opening http://127.0.0.1:8000/ in a web browser displays, "Hell
Finally, we'll update the Dockerfile to include the local library in the deployment package:
```dockerfile title="Dockerfile"
-FROM ghcr.io/astral-sh/uv:0.5.17 AS uv
+FROM ghcr.io/astral-sh/uv:0.5.18 AS uv
# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
diff --git a/docs/guides/integration/docker.md b/docs/guides/integration/docker.md
index dcdb73e9144f..6d0ca0fb651d 100644
--- a/docs/guides/integration/docker.md
+++ b/docs/guides/integration/docker.md
@@ -28,7 +28,7 @@ $ docker run ghcr.io/astral-sh/uv --help
uv provides a distroless Docker image including the `uv` binary. The following tags are published:
- `ghcr.io/astral-sh/uv:latest`
-- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.5.17`
+- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.5.18`
- `ghcr.io/astral-sh/uv:{major}.{minor}`, e.g., `ghcr.io/astral-sh/uv:0.5` (the latest patch
version)
@@ -69,7 +69,7 @@ In addition, uv publishes the following images:
As with the distroless image, each image is published with uv version tags as
`ghcr.io/astral-sh/uv:{major}.{minor}.{patch}-{base}` and
-`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.5.17-alpine`.
+`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.5.18-alpine`.
For more details, see the [GitHub Container](https://github.com/astral-sh/uv/pkgs/container/uv)
page.
@@ -107,13 +107,13 @@ Note this requires `curl` to be available.
In either case, it is best practice to pin to a specific uv version, e.g., with:
```dockerfile
-COPY --from=ghcr.io/astral-sh/uv:0.5.17 /uv /uvx /bin/
+COPY --from=ghcr.io/astral-sh/uv:0.5.18 /uv /uvx /bin/
```
Or, with the installer:
```dockerfile
-ADD https://astral.sh/uv/0.5.17/install.sh /uv-installer.sh
+ADD https://astral.sh/uv/0.5.18/install.sh /uv-installer.sh
```
### Installing a project
diff --git a/docs/guides/integration/github.md b/docs/guides/integration/github.md
index b46635c5d0ec..1f3c4aa510d9 100644
--- a/docs/guides/integration/github.md
+++ b/docs/guides/integration/github.md
@@ -47,7 +47,7 @@ jobs:
uses: astral-sh/setup-uv@v5
with:
# Install a specific version of uv.
- version: "0.5.17"
+ version: "0.5.18"
```
## Setting up Python
diff --git a/docs/guides/integration/pre-commit.md b/docs/guides/integration/pre-commit.md
index 5168c21f48be..8e7f623e7c39 100644
--- a/docs/guides/integration/pre-commit.md
+++ b/docs/guides/integration/pre-commit.md
@@ -36,7 +36,7 @@ To compile requirements via pre-commit, add the following to the `.pre-commit-co
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.17
+ rev: 0.5.18
hooks:
# Compile requirements
- id: pip-compile
@@ -48,7 +48,7 @@ To compile alternative files, modify `args` and `files`:
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.17
+ rev: 0.5.18
hooks:
# Compile requirements
- id: pip-compile
@@ -61,7 +61,7 @@ To run the hook over multiple files at the same time:
```yaml title=".pre-commit-config.yaml"
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
- rev: 0.5.17
+ rev: 0.5.18
hooks:
# Compile requirements
- id: pip-compile
diff --git a/pyproject.toml b/pyproject.toml
index 7387c616ae3f..88b0e3802edf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "maturin"
[project]
name = "uv"
-version = "0.5.17"
+version = "0.5.18"
description = "An extremely fast Python package and project manager, written in Rust."
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
requires-python = ">=3.8"
From aa1fd76a15f5e776753480b059dce8352e6451f1 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:26:36 -0500
Subject: [PATCH 114/135] Update Rust crate async-trait to v0.1.85 (#10501)
---
Cargo.lock | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index b4b7daefc07f..99933e0dae48 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -184,9 +184,9 @@ dependencies = [
[[package]]
name = "async-trait"
-version = "0.1.84"
+version = "0.1.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1244b10dcd56c92219da4e14caa97e312079e185f04ba3eea25061561dc0a0"
+checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
dependencies = [
"proc-macro2",
"quote",
From 59eb60b81992235999143172fd17d7e559aa929c Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:26:44 -0500
Subject: [PATCH 115/135] Update Rust crate cargo-util to v0.2.17 (#10502)
---
Cargo.lock | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 99933e0dae48..41e7b4c7af78 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -476,9 +476,9 @@ dependencies = [
[[package]]
name = "cargo-util"
-version = "0.2.16"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b15bbe49616ee353fadadf6de5a24136f3fe8fdbd5eb0894be9f8a42c905674"
+checksum = "7cccd15f96a29696e13e1d5fa10dd1dbed2e172f58b6e6124a9a4fa695363fdd"
dependencies = [
"anyhow",
"core-foundation",
@@ -684,7 +684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1038,7 +1038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1912,7 +1912,7 @@ dependencies = [
"portable-atomic",
"portable-atomic-util",
"serde",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -2800,7 +2800,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -3237,7 +3237,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -3806,7 +3806,7 @@ dependencies = [
"getrandom",
"once_cell",
"rustix",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -5992,7 +5992,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
From 716f3ec9ebc2b84710b8d683e107a2fd77181863 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:27:01 -0500
Subject: [PATCH 116/135] Update Rust crate goblin to v0.9.3 (#10504)
---
Cargo.lock | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 41e7b4c7af78..fe9b8445c916 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1389,9 +1389,9 @@ dependencies = [
[[package]]
name = "goblin"
-version = "0.9.2"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53ab3f32d1d77146981dea5d6b1e8fe31eedcb7013e5e00d6ccd1259a4b4d923"
+checksum = "daa0a64d21a7eb230583b4c5f4e23b7e4e57974f96620f42a7e75e08ae66d745"
dependencies = [
"log",
"plain",
From 94d4babe1a3792d1f354cdfc740c7cdf2b66e849 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:27:53 -0500
Subject: [PATCH 117/135] Update Rust crate proc-macro2 to v1.0.93 (#10506)
---
Cargo.lock | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index fe9b8445c916..94f2c61fcb6b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2688,9 +2688,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.92"
+version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
From bee6eff23546b6c2be0cac2bf8a9525b14102afb Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:28:23 -0500
Subject: [PATCH 118/135] Update Rust crate serde_json to v1.0.135 (#10509)
---
Cargo.lock | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 94f2c61fcb6b..0d3f6751bb58 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3476,9 +3476,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.134"
+version = "1.0.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
+checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
dependencies = [
"itoa",
"memchr",
From 3e2a8cbd04c96c399d900ba6c98c2ad72026633a Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:28:56 -0500
Subject: [PATCH 119/135] Update Rust crate syn to v2.0.96 (#10510)
---
Cargo.lock | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 0d3f6751bb58..a57923b38513 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3724,9 +3724,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.95"
+version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [
"proc-macro2",
"quote",
From ed5a53f01b571328ab28d1612cf7f69a3f262fcf Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:29:03 -0500
Subject: [PATCH 120/135] Update Rust crate thiserror to v2.0.11 (#10511)
---
Cargo.lock | 84 ++++++++++++++++-----------------
crates/uv-trampoline/Cargo.lock | 8 ++--
2 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index a57923b38513..900b598a7dcc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -253,7 +253,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"url",
"walkdir",
]
@@ -295,7 +295,7 @@ dependencies = [
"self-replace",
"serde",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"url",
]
@@ -2475,7 +2475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
dependencies = [
"memchr",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"ucd-trie",
]
@@ -2747,7 +2747,7 @@ dependencies = [
"log",
"priority-queue",
"rustc-hash",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"version-ranges",
]
@@ -2764,7 +2764,7 @@ dependencies = [
"rustc-hash",
"rustls",
"socket2",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tracing",
]
@@ -2783,7 +2783,7 @@ dependencies = [
"rustls",
"rustls-pki-types",
"slab",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tinyvec",
"tracing",
"web-time",
@@ -3911,11 +3911,11 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.9"
+version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
+checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
dependencies = [
- "thiserror-impl 2.0.9",
+ "thiserror-impl 2.0.11",
]
[[package]]
@@ -3931,9 +3931,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
-version = "2.0.9"
+version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
+checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [
"proc-macro2",
"quote",
@@ -4530,7 +4530,7 @@ dependencies = [
"tar",
"tempfile",
"textwrap",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"toml",
"toml_edit",
@@ -4655,7 +4655,7 @@ dependencies = [
"spdx",
"tar",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"toml",
"tracing",
"uv-distribution-filename",
@@ -4687,7 +4687,7 @@ dependencies = [
"serde",
"serde_json",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"toml_edit",
"tracing",
@@ -4736,7 +4736,7 @@ dependencies = [
"globwalk",
"schemars",
"serde",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"toml",
"tracing",
]
@@ -4804,7 +4804,7 @@ dependencies = [
"serde",
"serde_json",
"sys-info",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tl",
"tokio",
"tokio-util",
@@ -4843,7 +4843,7 @@ dependencies = [
"serde",
"serde-untagged",
"serde_json",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tracing",
"url",
"uv-auth",
@@ -4926,7 +4926,7 @@ dependencies = [
"futures",
"itertools 0.14.0",
"rustc-hash",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tracing",
"uv-build-backend",
@@ -4966,7 +4966,7 @@ dependencies = [
"rustc-hash",
"serde",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tokio-util",
"tracing",
@@ -5001,7 +5001,7 @@ dependencies = [
"insta",
"rkyv",
"serde",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"url",
"uv-normalize",
"uv-pep440",
@@ -5024,7 +5024,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tracing",
"url",
"urlencoding",
@@ -5056,7 +5056,7 @@ dependencies = [
"reqwest",
"rustc-hash",
"sha2",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tokio-util",
"tracing",
@@ -5101,7 +5101,7 @@ dependencies = [
"reqwest",
"reqwest-middleware",
"serde",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tracing",
"url",
@@ -5122,7 +5122,7 @@ dependencies = [
"regex",
"regex-automata 0.4.9",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tracing",
"walkdir",
]
@@ -5152,7 +5152,7 @@ dependencies = [
"serde_json",
"sha2",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tracing",
"uv-cache-info",
"uv-distribution-filename",
@@ -5180,7 +5180,7 @@ dependencies = [
"rustc-hash",
"same-file",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tracing",
"url",
@@ -5221,7 +5221,7 @@ dependencies = [
"async_zip",
"fs-err 3.0.0",
"futures",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tokio-util",
"uv-distribution-filename",
@@ -5284,7 +5284,7 @@ dependencies = [
"serde",
"serde_json",
"smallvec",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tracing",
"tracing-test",
"unicode-width 0.1.14",
@@ -5318,7 +5318,7 @@ dependencies = [
"insta",
"rustc-hash",
"serde",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
]
[[package]]
@@ -5339,7 +5339,7 @@ dependencies = [
"rustc-hash",
"serde",
"serde_json",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tokio-util",
"tracing",
@@ -5372,7 +5372,7 @@ dependencies = [
"schemars",
"serde",
"serde-untagged",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"toml",
"toml_edit",
"tracing",
@@ -5414,7 +5414,7 @@ dependencies = [
"temp-env",
"tempfile",
"test-log",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tokio-util",
"tracing",
@@ -5453,7 +5453,7 @@ dependencies = [
"futures",
"rustc-hash",
"serde",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"toml",
"tracing",
"url",
@@ -5491,7 +5491,7 @@ dependencies = [
"reqwest-middleware",
"tempfile",
"test-case",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tracing",
"unscanny",
@@ -5529,7 +5529,7 @@ dependencies = [
"schemars",
"serde",
"textwrap",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"tokio-stream",
"toml",
@@ -5567,7 +5567,7 @@ dependencies = [
"indoc",
"memchr",
"serde",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"toml",
"uv-pep440",
"uv-pep508",
@@ -5588,7 +5588,7 @@ dependencies = [
"schemars",
"serde",
"textwrap",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"toml",
"tracing",
"url",
@@ -5645,7 +5645,7 @@ dependencies = [
"pathdiff",
"self-replace",
"serde",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"toml",
"toml_edit",
"tracing",
@@ -5672,7 +5672,7 @@ dependencies = [
"assert_cmd",
"assert_fs",
"fs-err 3.0.0",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"uv-fs",
"which",
"zip",
@@ -5684,7 +5684,7 @@ version = "0.0.1"
dependencies = [
"anyhow",
"rustc-hash",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"url",
"uv-cache",
"uv-configuration",
@@ -5711,7 +5711,7 @@ dependencies = [
"itertools 0.14.0",
"pathdiff",
"self-replace",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tracing",
"uv-fs",
"uv-platform-tags",
@@ -5747,7 +5747,7 @@ dependencies = [
"schemars",
"serde",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.11",
"tokio",
"toml",
"toml_edit",
diff --git a/crates/uv-trampoline/Cargo.lock b/crates/uv-trampoline/Cargo.lock
index eaf671b0e217..ca87ceb981e0 100644
--- a/crates/uv-trampoline/Cargo.lock
+++ b/crates/uv-trampoline/Cargo.lock
@@ -426,18 +426,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "thiserror"
-version = "2.0.9"
+version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
+checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "2.0.9"
+version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
+checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [
"proc-macro2",
"quote",
From 08569667cb2080312ff0cc6dcf73d6b352534725 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:29:13 -0500
Subject: [PATCH 121/135] Update Rust crate bitflags to v2.7.0 (#10512)
---
Cargo.lock | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 900b598a7dcc..c5c604fbc44e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -352,9 +352,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.6.0"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
[[package]]
name = "block-buffer"
@@ -1370,7 +1370,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"ignore",
"walkdir",
]
@@ -2027,7 +2027,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"libc",
"redox_syscall 0.5.8",
]
@@ -2280,7 +2280,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -2701,7 +2701,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"flate2",
"hex",
"procfs-core",
@@ -2714,7 +2714,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"hex",
]
@@ -2907,7 +2907,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
]
[[package]]
@@ -3233,7 +3233,7 @@ version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"errno",
"libc",
"linux-raw-sys",
@@ -3398,7 +3398,7 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -5014,7 +5014,7 @@ version = "0.0.1"
dependencies = [
"anyhow",
"arcstr",
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"fs-err 3.0.0",
"itertools 0.14.0",
"jiff",
From 69102e056399d4e6c9f93ee21a0427fc472f4fbc Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 17:55:31 +0000
Subject: [PATCH 122/135] Update Rust crate tokio to v1.43.0 (#10514)
---
Cargo.lock | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index c5c604fbc44e..fde624b563c1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4037,9 +4037,9 @@ source = "git+https://github.com/astral-sh/tl.git?rev=6e25b2ee2513d75385101a8ff9
[[package]]
name = "tokio"
-version = "1.42.0"
+version = "1.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
+checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
dependencies = [
"backtrace",
"bytes",
@@ -4055,9 +4055,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
From 2c021e2f7d5ed8fab047969495737487a69c1508 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 18:08:13 +0000
Subject: [PATCH 123/135] Update Rust crate winreg to 0.53.0 (#10518)
---
Cargo.lock | 4 ++--
Cargo.toml | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index fde624b563c1..d5e5fec81f8e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6308,9 +6308,9 @@ dependencies = [
[[package]]
name = "winreg"
-version = "0.52.0"
+version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
+checksum = "89a47b489f8fc5b949477e89dca4d1617f162c6c53fbcbefde553ab17b342ff9"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
diff --git a/Cargo.toml b/Cargo.toml
index ae7315f428e5..f26f2de4c7b2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -182,7 +182,7 @@ which = { version = "7.0.0", features = ["regex"] }
windows-registry = { version = "0.3.0" }
windows-result = { version = "0.2.0" }
windows-sys = { version = "0.59.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Ioctl", "Win32_System_IO"] }
-winreg = { version = "0.52.0" }
+winreg = { version = "0.53.0" }
winsafe = { version = "0.0.22", features = ["kernel"] }
wiremock = { version = "0.6.2" }
xz2 = { version = "0.1.7" }
From 0650e178bfbd70f3b5e343a96833525b6e517ee4 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 18:16:20 +0000
Subject: [PATCH 124/135] Update pre-commit hook astral-sh/ruff-pre-commit to
v0.9.1 (#10519)
---
.pre-commit-config.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0c4cff397a42..ebd051fec2f1 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -42,7 +42,7 @@ repos:
types_or: [yaml, json5]
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.8.6
+ rev: v0.9.1
hooks:
- id: ruff-format
- id: ruff
From 8e0c7cfd76a744a2367e85078a128a9bb894c076 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 19:24:45 +0000
Subject: [PATCH 125/135] Update Rust crate clap to v4.5.26 (#10503)
---
Cargo.lock | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index d5e5fec81f8e..69e7ec7e889a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -565,9 +565,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.23"
+version = "4.5.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
+checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
dependencies = [
"clap_builder",
"clap_derive",
@@ -575,9 +575,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.23"
+version = "4.5.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
+checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
dependencies = [
"anstream",
"anstyle",
@@ -618,9 +618,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.18"
+version = "4.5.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
+checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
dependencies = [
"heck",
"proc-macro2",
@@ -684,7 +684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
- "windows-sys 0.52.0",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -1038,7 +1038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -1912,7 +1912,7 @@ dependencies = [
"portable-atomic",
"portable-atomic-util",
"serde",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2800,7 +2800,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -3237,7 +3237,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -3806,7 +3806,7 @@ dependencies = [
"getrandom",
"once_cell",
"rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -5992,7 +5992,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.48.0",
]
[[package]]
From 5aa53863a77a008b7dcabaef0f57b47839ba464d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 11 Jan 2025 19:26:21 +0000
Subject: [PATCH 126/135] Update Rust crate rustix to v0.38.43 (#10508)
---
Cargo.lock | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 69e7ec7e889a..d2ba39b1a9fc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3229,9 +3229,9 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
[[package]]
name = "rustix"
-version = "0.38.42"
+version = "0.38.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
+checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
dependencies = [
"bitflags 2.7.0",
"errno",
From 9e948b73638c3c9a164a222fdc23dafe21bff2df Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Sat, 11 Jan 2025 18:12:11 -0500
Subject: [PATCH 127/135] Remove resolved build tag TODO (#10526)
---
crates/uv-distribution-filename/src/wheel.rs | 6 ------
1 file changed, 6 deletions(-)
diff --git a/crates/uv-distribution-filename/src/wheel.rs b/crates/uv-distribution-filename/src/wheel.rs
index 0d476213578b..fda2de853d4b 100644
--- a/crates/uv-distribution-filename/src/wheel.rs
+++ b/crates/uv-distribution-filename/src/wheel.rs
@@ -102,12 +102,6 @@ impl WheelFilename {
// The wheel filename should contain either five or six entries. If six, then the third
// entry is the build tag. If five, then the third entry is the Python tag.
// https://www.python.org/dev/peps/pep-0427/#file-name-convention
- //
- // 2023-11-08(burntsushi): It looks like the code below actually drops
- // the build tag if one is found. According to PEP 0427, the build tag
- // is used to break ties. This might mean that we generate identical
- // `WheelName` values for multiple distinct wheels, but it's not clear
- // if this is a problem in practice.
let mut parts = stem.split('-');
let name = parts
From 5788cd2b18605d02402a7bb5b568736b435ff535 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Sat, 11 Jan 2025 18:39:46 -0500
Subject: [PATCH 128/135] Fix typo in `version_map.rs` (#10528)
---
crates/uv-resolver/src/version_map.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/crates/uv-resolver/src/version_map.rs b/crates/uv-resolver/src/version_map.rs
index 5056916738fa..60d822f20ad6 100644
--- a/crates/uv-resolver/src/version_map.rs
+++ b/crates/uv-resolver/src/version_map.rs
@@ -524,7 +524,7 @@ impl VersionMapLazy {
let priority = if let Some(tags) = &self.tags {
match filename.compatibility(tags) {
TagCompatibility::Incompatible(tag) => {
- return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(tag))
+ return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(tag));
}
TagCompatibility::Compatible(priority) => Some(priority),
}
@@ -565,7 +565,7 @@ impl VersionMapLazy {
/// a single version of a package.
#[derive(Debug)]
enum LazyPrioritizedDist {
- /// Represents a eagerly constructed distribution from a
+ /// Represents an eagerly constructed distribution from a
/// `FlatDistributions`.
OnlyFlat(PrioritizedDist),
/// Represents a lazily constructed distribution from an index into a
From 72692734583a648c93ddc855f40f192db3c0dfbd Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Sat, 11 Jan 2025 20:37:57 -0500
Subject: [PATCH 129/135] Update packse to include `--python-platform` (#10531)
## Summary
Relevant for: https://github.com/astral-sh/uv/pull/10527.
---
crates/uv/tests/it/common/mod.rs | 2 +-
crates/uv/tests/it/lock_scenarios.rs | 2 +-
crates/uv/tests/it/pip_compile_scenarios.rs | 2 +-
crates/uv/tests/it/pip_install_scenarios.rs | 5 ++++-
scripts/scenarios/requirements.txt | 2 +-
scripts/scenarios/templates/install.mustache | 3 +++
6 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/crates/uv/tests/it/common/mod.rs b/crates/uv/tests/it/common/mod.rs
index b34a6faaf585..7a25115f1440 100644
--- a/crates/uv/tests/it/common/mod.rs
+++ b/crates/uv/tests/it/common/mod.rs
@@ -32,7 +32,7 @@ use uv_static::EnvVars;
// Exclude any packages uploaded after this date.
static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z";
-pub const PACKSE_VERSION: &str = "0.3.42";
+pub const PACKSE_VERSION: &str = "0.3.43";
/// Using a find links url allows using `--index-url` instead of `--extra-index-url` in tests
/// to prevent dependency confusion attacks against our test suite.
diff --git a/crates/uv/tests/it/lock_scenarios.rs b/crates/uv/tests/it/lock_scenarios.rs
index 377ab064139c..831d9bf2a9b2 100644
--- a/crates/uv/tests/it/lock_scenarios.rs
+++ b/crates/uv/tests/it/lock_scenarios.rs
@@ -1,7 +1,7 @@
//! DO NOT EDIT
//!
//! Generated with `./scripts/sync_scenarios.sh`
-//! Scenarios from
+//! Scenarios from
//!
#![cfg(all(feature = "python", feature = "pypi"))]
#![allow(clippy::needless_raw_string_hashes)]
diff --git a/crates/uv/tests/it/pip_compile_scenarios.rs b/crates/uv/tests/it/pip_compile_scenarios.rs
index 722d3f0e001c..93157dc32c76 100644
--- a/crates/uv/tests/it/pip_compile_scenarios.rs
+++ b/crates/uv/tests/it/pip_compile_scenarios.rs
@@ -1,7 +1,7 @@
//! DO NOT EDIT
//!
//! Generated with `./scripts/sync_scenarios.sh`
-//! Scenarios from
+//! Scenarios from
//!
#![cfg(all(feature = "python", feature = "pypi", unix))]
diff --git a/crates/uv/tests/it/pip_install_scenarios.rs b/crates/uv/tests/it/pip_install_scenarios.rs
index efd65ae30cca..1965a4f596f7 100644
--- a/crates/uv/tests/it/pip_install_scenarios.rs
+++ b/crates/uv/tests/it/pip_install_scenarios.rs
@@ -1,7 +1,7 @@
//! DO NOT EDIT
//!
//! Generated with `./scripts/sync_scenarios.sh`
-//! Scenarios from
+//! Scenarios from
//!
#![cfg(all(feature = "python", feature = "pypi", unix))]
@@ -4079,6 +4079,7 @@ fn no_sdist_no_wheels_with_matching_abi() {
filters.push((r"no-sdist-no-wheels-with-matching-abi-", "package-"));
uv_snapshot!(filters, command(&context)
+ .arg("--python-platform=x86_64-manylinux2014")
.arg("no-sdist-no-wheels-with-matching-abi-a")
, @r###"
success: false
@@ -4119,6 +4120,7 @@ fn no_sdist_no_wheels_with_matching_platform() {
filters.push((r"no-sdist-no-wheels-with-matching-platform-", "package-"));
uv_snapshot!(filters, command(&context)
+ .arg("--python-platform=x86_64-manylinux2014")
.arg("no-sdist-no-wheels-with-matching-platform-a")
, @r###"
success: false
@@ -4159,6 +4161,7 @@ fn no_sdist_no_wheels_with_matching_python() {
filters.push((r"no-sdist-no-wheels-with-matching-python-", "package-"));
uv_snapshot!(filters, command(&context)
+ .arg("--python-platform=x86_64-manylinux2014")
.arg("no-sdist-no-wheels-with-matching-python-a")
, @r###"
success: false
diff --git a/scripts/scenarios/requirements.txt b/scripts/scenarios/requirements.txt
index d6c9203d0956..0a6f0cbcd5fd 100644
--- a/scripts/scenarios/requirements.txt
+++ b/scripts/scenarios/requirements.txt
@@ -10,7 +10,7 @@ msgspec==0.18.6
# via packse
packaging==24.2
# via hatchling
-packse==0.3.42
+packse==0.3.43
# via -r scripts/scenarios/requirements.in
pathspec==0.12.1
# via hatchling
diff --git a/scripts/scenarios/templates/install.mustache b/scripts/scenarios/templates/install.mustache
index 48f86e7ffe1a..be425f92948c 100644
--- a/scripts/scenarios/templates/install.mustache
+++ b/scripts/scenarios/templates/install.mustache
@@ -88,6 +88,9 @@ fn {{module_name}}() {
.arg("--no-binary")
.arg("{{.}}")
{{/resolver_options.no_binary}}
+ {{#resolver_options.python_platform}}
+ .arg("--python-platform={{.}}")
+ {{/resolver_options.python_platform}}
{{#root.requires}}
.arg("{{requirement}}")
{{/root.requires}}, @r###"
From 051aaa5fe57de3a5e461693c4c75d5631382d1df Mon Sep 17 00:00:00 2001
From: Sergei Nizovtsev
Date: Sun, 12 Jan 2025 05:30:46 +0300
Subject: [PATCH 130/135] Fix git-tag cache-key reader in case of slashes
(#10467) (#10500)
## Summary
The assumption that all tags are listed under a flat `.git/ref/tags`
structure was wrong. Git creates a hierarchy of directories for tags
containing slashes. To fix the cache key calculation, we need to
recursively traverse all files under that folder instead.
## Test Plan
1. Create an `uv` project with git-tag cache-keys;
2. Add any tag with slash;
3. Run `uv sync` and see uv_cache_info error in verbose log;
4. `uv sync` doesn't trigger reinstall on next tag addition or removal;
5. With fix applied, reinstall triggers on every tag update and there
are no errors in the log.
Fixes #10467
---------
Co-authored-by: Sergei Nizovtsev
Co-authored-by: Charlie Marsh
---
Cargo.lock | 1 +
crates/uv-cache-info/Cargo.toml | 1 +
crates/uv-cache-info/src/git_info.rs | 32 +++++++++++++++++-----------
3 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index d2ba39b1a9fc..c6696c60679b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4739,6 +4739,7 @@ dependencies = [
"thiserror 2.0.11",
"toml",
"tracing",
+ "walkdir",
]
[[package]]
diff --git a/crates/uv-cache-info/Cargo.toml b/crates/uv-cache-info/Cargo.toml
index ec17a4416a0d..6b10bbebe853 100644
--- a/crates/uv-cache-info/Cargo.toml
+++ b/crates/uv-cache-info/Cargo.toml
@@ -23,3 +23,4 @@ serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true }
+walkdir = { workspace = true }
diff --git a/crates/uv-cache-info/src/git_info.rs b/crates/uv-cache-info/src/git_info.rs
index 9df098e6959e..ad566d13bdf9 100644
--- a/crates/uv-cache-info/src/git_info.rs
+++ b/crates/uv-cache-info/src/git_info.rs
@@ -1,6 +1,9 @@
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
+use tracing::warn;
+use walkdir::WalkDir;
+
#[derive(Debug, thiserror::Error)]
pub(crate) enum GitInfoError {
#[error("The repository at {0} is missing a `.git` directory")]
@@ -80,24 +83,27 @@ impl Tags {
.find(|git_dir| git_dir.exists())
.ok_or_else(|| GitInfoError::MissingGitDir(path.to_path_buf()))?;
- let git_refs_path =
- git_refs(&git_dir).ok_or_else(|| GitInfoError::MissingRefs(git_dir.clone()))?;
+ let git_tags_path = git_refs(&git_dir)
+ .ok_or_else(|| GitInfoError::MissingRefs(git_dir.clone()))?
+ .join("tags");
let mut tags = BTreeMap::new();
// Map each tag to its commit.
- let read_dir = match fs_err::read_dir(git_refs_path.join("tags")) {
- Ok(read_dir) => read_dir,
- Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
- return Ok(Self(tags));
- }
- Err(err) => return Err(err.into()),
- };
- for entry in read_dir {
- let entry = entry?;
+ for entry in WalkDir::new(&git_tags_path).contents_first(true) {
+ let entry = match entry {
+ Ok(entry) => entry,
+ Err(err) => {
+ warn!("Failed to read Git tags: {err}");
+ continue;
+ }
+ };
let path = entry.path();
- if let Some(tag) = path.file_name().and_then(|name| name.to_str()) {
- let commit = fs_err::read_to_string(&path)?.trim().to_string();
+ if !entry.file_type().is_file() {
+ continue;
+ }
+ if let Ok(Some(tag)) = path.strip_prefix(&git_tags_path).map(|name| name.to_str()) {
+ let commit = fs_err::read_to_string(path)?.trim().to_string();
// The commit should be 40 hexadecimal characters.
if commit.len() != 40 {
From 4d3809cc6b28d71f26a782929b994abfebd7973c Mon Sep 17 00:00:00 2001
From: samypr100 <3933065+samypr100@users.noreply.github.com>
Date: Sat, 11 Jan 2025 22:19:33 -0500
Subject: [PATCH 131/135] Upgrade Rust toolchain to 1.84.0 (#10533)
## Summary
Upgrade the rust toolchain to 1.84.0. This PR does not bump the MSRV.
---
Cargo.toml | 3 +++
crates/uv-auth/src/middleware.rs | 6 +++---
crates/uv-configuration/src/dev.rs | 2 +-
crates/uv-dispatch/src/lib.rs | 2 +-
crates/uv-extract/src/stream.rs | 4 ++--
crates/uv-fs/src/lib.rs | 4 ++--
crates/uv-install-wheel/src/wheel.rs | 4 ++--
crates/uv-pep508/src/lib.rs | 6 +++---
crates/uv-pep508/src/unnamed.rs | 6 +++---
crates/uv-pep508/src/verbatim_url.rs | 2 +-
crates/uv-python/src/discovery.rs | 2 +-
crates/uv-python/src/installation.rs | 8 ++++----
crates/uv-python/src/interpreter.rs | 8 ++++----
crates/uv-python/src/lib.rs | 12 ++++++------
crates/uv-resolver/src/lock/mod.rs | 4 ++--
crates/uv-resolver/src/resolver/provider.rs | 2 +-
crates/uv-resolver/src/yanks.rs | 2 +-
crates/uv-scripts/src/lib.rs | 4 ++--
crates/uv-trampoline-builder/src/lib.rs | 12 ++++++------
crates/uv/src/commands/pip/latest.rs | 2 +-
crates/uv/src/commands/project/init.rs | 12 ++++++------
crates/uv/src/commands/project/mod.rs | 2 +-
crates/uv/src/commands/project/run.rs | 8 +++-----
crates/uv/tests/it/common/mod.rs | 2 +-
crates/uv/tests/it/pip_compile.rs | 4 ++--
crates/uv/tests/it/pip_install.rs | 4 ++--
rust-toolchain.toml | 2 +-
27 files changed, 65 insertions(+), 64 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index f26f2de4c7b2..e97d1ae6657c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -225,6 +225,9 @@ rc_mutex = "warn"
rest_pat_in_fully_bound_structs = "warn"
if_not_else = "allow"
+# Diagnostics are not actionable: Enable once https://github.com/rust-lang/rust-clippy/issues/13774 is resolved.
+large_stack_arrays = "allow"
+
[profile.release]
strip = true
lto = "fat"
diff --git a/crates/uv-auth/src/middleware.rs b/crates/uv-auth/src/middleware.rs
index 3c78600d865b..16c4e542b72b 100644
--- a/crates/uv-auth/src/middleware.rs
+++ b/crates/uv-auth/src/middleware.rs
@@ -741,7 +741,7 @@ mod tests {
let mut netrc_file = NamedTempFile::new()?;
writeln!(
netrc_file,
- r#"machine {} login {username} password {password}"#,
+ r"machine {} login {username} password {password}",
base_url.host_str().unwrap()
)?;
@@ -788,7 +788,7 @@ mod tests {
let mut netrc_file = NamedTempFile::new()?;
writeln!(
netrc_file,
- r#"machine example.com login {username} password {password}"#,
+ r"machine example.com login {username} password {password}",
)?;
let client = test_client_builder()
@@ -829,7 +829,7 @@ mod tests {
let mut netrc_file = NamedTempFile::new()?;
writeln!(
netrc_file,
- r#"machine {} login {username} password {password}"#,
+ r"machine {} login {username} password {password}",
base_url.host_str().unwrap()
)?;
diff --git a/crates/uv-configuration/src/dev.rs b/crates/uv-configuration/src/dev.rs
index cce967ba1f89..3a94c6f4be16 100644
--- a/crates/uv-configuration/src/dev.rs
+++ b/crates/uv-configuration/src/dev.rs
@@ -292,7 +292,7 @@ impl DevGroupsSpecification {
self.groups
.as_ref()
- .map_or(false, |groups| groups.contains(group))
+ .is_some_and(|groups| groups.contains(group))
}
}
diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs
index c84e81271c0a..28a0a5ba0564 100644
--- a/crates/uv-dispatch/src/lib.rs
+++ b/crates/uv-dispatch/src/lib.rs
@@ -163,7 +163,7 @@ impl<'a> BuildDispatch<'a> {
}
#[allow(refining_impl_trait)]
-impl<'a> BuildContext for BuildDispatch<'a> {
+impl BuildContext for BuildDispatch<'_> {
type SourceDistBuilder = SourceBuild;
fn interpreter(&self) -> &Interpreter {
diff --git a/crates/uv-extract/src/stream.rs b/crates/uv-extract/src/stream.rs
index 483be2f50588..49ae138c451d 100644
--- a/crates/uv-extract/src/stream.rs
+++ b/crates/uv-extract/src/stream.rs
@@ -139,8 +139,8 @@ pub async fn unzip(
/// Unpack the given tar archive into the destination directory.
///
/// This is equivalent to `archive.unpack_in(dst)`, but it also preserves the executable bit.
-async fn untar_in<'a>(
- mut archive: tokio_tar::Archive<&'a mut (dyn tokio::io::AsyncRead + Unpin)>,
+async fn untar_in(
+ mut archive: tokio_tar::Archive<&'_ mut (dyn tokio::io::AsyncRead + Unpin)>,
dst: &Path,
) -> std::io::Result<()> {
let mut entries = archive.entries()?;
diff --git a/crates/uv-fs/src/lib.rs b/crates/uv-fs/src/lib.rs
index f7dadb6aeddb..8f6cbc9377e4 100644
--- a/crates/uv-fs/src/lib.rs
+++ b/crates/uv-fs/src/lib.rs
@@ -516,7 +516,7 @@ pub fn is_temporary(path: impl AsRef) -> bool {
path.as_ref()
.file_name()
.and_then(|name| name.to_str())
- .map_or(false, |name| name.starts_with(".tmp"))
+ .is_some_and(|name| name.starts_with(".tmp"))
}
/// A file lock that is automatically released when dropped.
@@ -588,7 +588,7 @@ impl LockedFile {
impl Drop for LockedFile {
fn drop(&mut self) {
- if let Err(err) = self.0.file().unlock() {
+ if let Err(err) = fs2::FileExt::unlock(self.0.file()) {
error!(
"Failed to unlock {}; program may be stuck: {}",
self.0.path().display(),
diff --git a/crates/uv-install-wheel/src/wheel.rs b/crates/uv-install-wheel/src/wheel.rs
index 8218f75ae758..9e5698e0db8a 100644
--- a/crates/uv-install-wheel/src/wheel.rs
+++ b/crates/uv-install-wheel/src/wheel.rs
@@ -34,7 +34,7 @@ fn get_script_launcher(entry_point: &Script, shebang: &str) -> String {
let import_name = entry_point.import_name();
format!(
- r##"{shebang}
+ r#"{shebang}
# -*- coding: utf-8 -*-
import re
import sys
@@ -42,7 +42,7 @@ from {module} import {import_name}
if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit({function}())
-"##
+"#
)
}
diff --git a/crates/uv-pep508/src/lib.rs b/crates/uv-pep508/src/lib.rs
index 087f69d316f4..b30bbc670a97 100644
--- a/crates/uv-pep508/src/lib.rs
+++ b/crates/uv-pep508/src/lib.rs
@@ -930,12 +930,12 @@ fn parse_pep508_requirement(
if let Some((pos, char)) = cursor.next().filter(|(_, c)| *c != '#') {
let message = if char == '#' {
format!(
- r#"Expected end of input or `;`, found `{char}`; comments must be preceded by a leading space"#
+ r"Expected end of input or `;`, found `{char}`; comments must be preceded by a leading space"
)
} else if marker.is_none() {
- format!(r#"Expected end of input or `;`, found `{char}`"#)
+ format!(r"Expected end of input or `;`, found `{char}`")
} else {
- format!(r#"Expected end of input, found `{char}`"#)
+ format!(r"Expected end of input, found `{char}`")
};
return Err(Pep508Error {
message: Pep508ErrorSource::String(message),
diff --git a/crates/uv-pep508/src/unnamed.rs b/crates/uv-pep508/src/unnamed.rs
index 2e4361b940cc..0893cc989b04 100644
--- a/crates/uv-pep508/src/unnamed.rs
+++ b/crates/uv-pep508/src/unnamed.rs
@@ -176,12 +176,12 @@ fn parse_unnamed_requirement(
if let Some((pos, char)) = cursor.next() {
let message = if char == '#' {
format!(
- r#"Expected end of input or `;`, found `{char}`; comments must be preceded by a leading space"#
+ r"Expected end of input or `;`, found `{char}`; comments must be preceded by a leading space"
)
} else if marker.is_none() {
- format!(r#"Expected end of input or `;`, found `{char}`"#)
+ format!(r"Expected end of input or `;`, found `{char}`")
} else {
- format!(r#"Expected end of input, found `{char}`"#)
+ format!(r"Expected end of input, found `{char}`")
};
return Err(Pep508Error {
message: Pep508ErrorSource::String(message),
diff --git a/crates/uv-pep508/src/verbatim_url.rs b/crates/uv-pep508/src/verbatim_url.rs
index 73b06158a916..4d715b389989 100644
--- a/crates/uv-pep508/src/verbatim_url.rs
+++ b/crates/uv-pep508/src/verbatim_url.rs
@@ -406,7 +406,7 @@ pub fn looks_like_git_repository(url: &Url) -> bool {
.map_or(true, |ext| ext.eq_ignore_ascii_case("git"))
&& url
.path_segments()
- .map_or(false, |segments| segments.count() == 2)
+ .is_some_and(|segments| segments.count() == 2)
}
/// Split the fragment from a URL.
diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs
index 879bb8b240ef..77fb6acf64be 100644
--- a/crates/uv-python/src/discovery.rs
+++ b/crates/uv-python/src/discovery.rs
@@ -1154,7 +1154,7 @@ pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
component.starts_with("python")
&& std::path::Path::new(component)
.extension()
- .map_or(false, |ext| ext.eq_ignore_ascii_case("exe"))
+ .is_some_and(|ext| ext.eq_ignore_ascii_case("exe"))
})
{
return false;
diff --git a/crates/uv-python/src/installation.rs b/crates/uv-python/src/installation.rs
index 2733a14a0885..e7fc3994056c 100644
--- a/crates/uv-python/src/installation.rs
+++ b/crates/uv-python/src/installation.rs
@@ -78,12 +78,12 @@ impl PythonInstallation {
/// Find or fetch a [`PythonInstallation`].
///
/// Unlike [`PythonInstallation::find`], if the required Python is not installed it will be installed automatically.
- pub async fn find_or_download<'a>(
+ pub async fn find_or_download(
request: Option<&PythonRequest>,
environments: EnvironmentPreference,
preference: PythonPreference,
python_downloads: PythonDownloads,
- client_builder: &BaseClientBuilder<'a>,
+ client_builder: &BaseClientBuilder<'_>,
cache: &Cache,
reporter: Option<&dyn Reporter>,
python_install_mirror: Option<&str>,
@@ -127,9 +127,9 @@ impl PythonInstallation {
}
/// Download and install the requested installation.
- pub async fn fetch<'a>(
+ pub async fn fetch(
request: PythonDownloadRequest,
- client_builder: &BaseClientBuilder<'a>,
+ client_builder: &BaseClientBuilder<'_>,
cache: &Cache,
reporter: Option<&dyn Reporter>,
python_install_mirror: Option<&str>,
diff --git a/crates/uv-python/src/interpreter.rs b/crates/uv-python/src/interpreter.rs
index e725fbccd0f0..13a12be575f0 100644
--- a/crates/uv-python/src/interpreter.rs
+++ b/crates/uv-python/src/interpreter.rs
@@ -894,10 +894,10 @@ mod tests {
fs::write(
&mocked_interpreter,
- formatdoc! {r##"
+ formatdoc! {r"
#!/bin/bash
echo '{json}'
- "##},
+ "},
)
.unwrap();
@@ -913,10 +913,10 @@ mod tests {
);
fs::write(
&mocked_interpreter,
- formatdoc! {r##"
+ formatdoc! {r"
#!/bin/bash
echo '{}'
- "##, json.replace("3.12", "3.13")},
+ ", json.replace("3.12", "3.13")},
)
.unwrap();
let interpreter = Interpreter::query(&mocked_interpreter, &cache).unwrap();
diff --git a/crates/uv-python/src/lib.rs b/crates/uv-python/src/lib.rs
index 982878ff3d56..ea5b3e57275a 100644
--- a/crates/uv-python/src/lib.rs
+++ b/crates/uv-python/src/lib.rs
@@ -282,10 +282,10 @@ mod tests {
fs_err::create_dir_all(path.parent().unwrap())?;
fs_err::write(
path,
- formatdoc! {r##"
+ formatdoc! {r"
#!/bin/bash
echo '{json}'
- "##},
+ "},
)?;
fs_err::set_permissions(path, std::os::unix::fs::PermissionsExt::from_mode(0o770))?;
@@ -304,10 +304,10 @@ mod tests {
fs_err::write(
path,
- formatdoc! {r##"
+ formatdoc! {r"
#!/bin/bash
echo '{output}' 1>&2
- "##},
+ "},
)?;
fs_err::set_permissions(path, std::os::unix::fs::PermissionsExt::from_mode(0o770))?;
@@ -525,10 +525,10 @@ mod tests {
#[cfg(unix)]
fs_err::write(
children[0].join(format!("python{}", env::consts::EXE_SUFFIX)),
- formatdoc! {r##"
+ formatdoc! {r"
#!/bin/bash
echo 'foo'
- "##},
+ "},
)?;
fs_err::set_permissions(
children[0].join(format!("python{}", env::consts::EXE_SUFFIX)),
diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs
index c0255804641c..283889b91eca 100644
--- a/crates/uv-resolver/src/lock/mod.rs
+++ b/crates/uv-resolver/src/lock/mod.rs
@@ -953,7 +953,7 @@ impl Lock {
.ok()
.flatten()
.map(|package| matches!(package.id.source, Source::Virtual(_)));
- if actual.map_or(true, |actual| actual != expected) {
+ if actual != Some(expected) {
return Ok(SatisfiesResult::MismatchedSources(name.clone(), expected));
}
}
@@ -973,7 +973,7 @@ impl Lock {
.ok()
.flatten()
.map(|package| &package.id.version);
- if actual.map_or(true, |actual| actual != expected) {
+ if actual != Some(expected) {
return Ok(SatisfiesResult::MismatchedVersion(
name.clone(),
expected.clone(),
diff --git a/crates/uv-resolver/src/resolver/provider.rs b/crates/uv-resolver/src/resolver/provider.rs
index 2d6173a8c6ba..dc9b38e08ec9 100644
--- a/crates/uv-resolver/src/resolver/provider.rs
+++ b/crates/uv-resolver/src/resolver/provider.rs
@@ -145,7 +145,7 @@ impl<'a, Context: BuildContext> DefaultResolverProvider<'a, Context> {
}
}
-impl<'a, Context: BuildContext> ResolverProvider for DefaultResolverProvider<'a, Context> {
+impl ResolverProvider for DefaultResolverProvider<'_, Context> {
/// Make a "Simple API" request for the package and convert the result to a [`VersionMap`].
async fn get_package_versions<'io>(
&'io self,
diff --git a/crates/uv-resolver/src/yanks.rs b/crates/uv-resolver/src/yanks.rs
index 6ed70a57e93f..6c43f2ed9802 100644
--- a/crates/uv-resolver/src/yanks.rs
+++ b/crates/uv-resolver/src/yanks.rs
@@ -55,6 +55,6 @@ impl AllowedYanks {
pub fn contains(&self, package_name: &PackageName, version: &Version) -> bool {
self.0
.get(package_name)
- .map_or(false, |versions| versions.contains(version))
+ .is_some_and(|versions| versions.contains(version))
}
}
diff --git a/crates/uv-scripts/src/lib.rs b/crates/uv-scripts/src/lib.rs
index a17398e278a0..ad25ac131c9d 100644
--- a/crates/uv-scripts/src/lib.rs
+++ b/crates/uv-scripts/src/lib.rs
@@ -205,10 +205,10 @@ impl Pep723Script {
let metadata = serialize_metadata(&default_metadata);
let script = if let Some(existing_contents) = existing_contents {
- indoc::formatdoc! {r#"
+ indoc::formatdoc! {r"
{metadata}
{content}
- "#,
+ ",
content = String::from_utf8(existing_contents).map_err(|err| Pep723Error::Utf8(err.utf8_error()))?}
} else {
indoc::formatdoc! {r#"
diff --git a/crates/uv-trampoline-builder/src/lib.rs b/crates/uv-trampoline-builder/src/lib.rs
index a5920fd2d054..2fb461fc8e5b 100644
--- a/crates/uv-trampoline-builder/src/lib.rs
+++ b/crates/uv-trampoline-builder/src/lib.rs
@@ -268,7 +268,7 @@ pub fn windows_script_launcher(
launcher.extend_from_slice(&payload);
launcher.extend_from_slice(python_path.as_bytes());
launcher.extend_from_slice(
- &u32::try_from(python_path.as_bytes().len())
+ &u32::try_from(python_path.len())
.expect("file path should be smaller than 4GB")
.to_le_bytes(),
);
@@ -300,7 +300,7 @@ pub fn windows_python_launcher(
launcher.extend_from_slice(launcher_bin);
launcher.extend_from_slice(python_path.as_bytes());
launcher.extend_from_slice(
- &u32::try_from(python_path.as_bytes().len())
+ &u32::try_from(python_path.len())
.expect("file path should be smaller than 4GB")
.to_le_bytes(),
);
@@ -377,7 +377,7 @@ mod test {
fn get_script_launcher(shebang: &str, is_gui: bool) -> String {
if is_gui {
format!(
- r##"{shebang}
+ r#"{shebang}
# -*- coding: utf-8 -*-
import re
import sys
@@ -394,11 +394,11 @@ def make_gui() -> None:
if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(make_gui())
-"##
+"#
)
} else {
format!(
- r##"{shebang}
+ r#"{shebang}
# -*- coding: utf-8 -*-
import re
import sys
@@ -412,7 +412,7 @@ def main_console() -> None:
if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(main_console())
-"##
+"#
)
}
}
diff --git a/crates/uv/src/commands/pip/latest.rs b/crates/uv/src/commands/pip/latest.rs
index 1331c29f5bc1..abc028a0076b 100644
--- a/crates/uv/src/commands/pip/latest.rs
+++ b/crates/uv/src/commands/pip/latest.rs
@@ -21,7 +21,7 @@ pub(crate) struct LatestClient<'env> {
pub(crate) requires_python: &'env RequiresPython,
}
-impl<'env> LatestClient<'env> {
+impl LatestClient<'_> {
/// Find the latest version of a package from an index.
pub(crate) async fn find_latest(
&self,
diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs
index faa3302493d3..75f8008a6a88 100644
--- a/crates/uv/src/commands/project/init.rs
+++ b/crates/uv/src/commands/project/init.rs
@@ -1006,7 +1006,7 @@ fn pyproject_build_backend_prerequisites(
if !build_file.try_exists()? {
fs_err::write(
build_file,
- indoc::formatdoc! {r#"
+ indoc::formatdoc! {r"
cmake_minimum_required(VERSION 3.15)
project(${{SKBUILD_PROJECT_NAME}} LANGUAGES CXX)
@@ -1015,7 +1015,7 @@ fn pyproject_build_backend_prerequisites(
pybind11_add_module(_core MODULE src/main.cpp)
install(TARGETS _core DESTINATION ${{SKBUILD_PROJECT_NAME}})
- "#},
+ "},
)?;
}
}
@@ -1052,21 +1052,21 @@ fn generate_package_scripts(
// Python script for binary-based packaged apps or libs
let binary_call_script = if is_lib {
- indoc::formatdoc! {r#"
+ indoc::formatdoc! {r"
from {module_name}._core import hello_from_bin
def hello() -> str:
return hello_from_bin()
- "#}
+ "}
} else {
- indoc::formatdoc! {r#"
+ indoc::formatdoc! {r"
from {module_name}._core import hello_from_bin
def main() -> None:
print(hello_from_bin())
- "#}
+ "}
};
// .pyi file for binary script
diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs
index 4f3c1416efcb..74cfbe5ffc09 100644
--- a/crates/uv/src/commands/project/mod.rs
+++ b/crates/uv/src/commands/project/mod.rs
@@ -1117,7 +1117,7 @@ impl<'lock> EnvironmentSpecification<'lock> {
}
/// Run dependency resolution for an interpreter, returning the [`ResolverOutput`].
-pub(crate) async fn resolve_environment<'a>(
+pub(crate) async fn resolve_environment(
spec: EnvironmentSpecification<'_>,
interpreter: &Interpreter,
settings: ResolverSettingsRef<'_>,
diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs
index dd327ede6c91..16cd4b8f01ed 100644
--- a/crates/uv/src/commands/project/run.rs
+++ b/crates/uv/src/commands/project/run.rs
@@ -1535,8 +1535,8 @@ impl RunCommand {
}
let metadata = target_path.metadata();
- let is_file = metadata.as_ref().map_or(false, std::fs::Metadata::is_file);
- let is_dir = metadata.as_ref().map_or(false, std::fs::Metadata::is_dir);
+ let is_file = metadata.as_ref().is_ok_and(std::fs::Metadata::is_file);
+ let is_dir = metadata.as_ref().is_ok_and(std::fs::Metadata::is_dir);
if target.eq_ignore_ascii_case("python") {
Ok(Self::Python(args.to_vec()))
@@ -1569,9 +1569,7 @@ impl RunCommand {
fn is_python_zipapp(target: &Path) -> bool {
if let Ok(file) = fs_err::File::open(target) {
if let Ok(mut archive) = zip::ZipArchive::new(file) {
- return archive
- .by_name("__main__.py")
- .map_or(false, |f| f.is_file());
+ return archive.by_name("__main__.py").is_ok_and(|f| f.is_file());
}
}
false
diff --git a/crates/uv/tests/it/common/mod.rs b/crates/uv/tests/it/common/mod.rs
index 7a25115f1440..7dd8ac935dfd 100644
--- a/crates/uv/tests/it/common/mod.rs
+++ b/crates/uv/tests/it/common/mod.rs
@@ -922,7 +922,7 @@ impl TestContext {
/// For when we add pypy to the test suite.
#[allow(clippy::unused_self)]
- pub fn python_kind(&self) -> &str {
+ pub fn python_kind(&self) -> &'static str {
"python"
}
diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs
index c222d619f436..7adf4a635f0e 100644
--- a/crates/uv/tests/it/pip_compile.rs
+++ b/crates/uv/tests/it/pip_compile.rs
@@ -6859,10 +6859,10 @@ dependencies = [
// Write to a requirements file.
let requirements_in = context.temp_dir.child("requirements.in");
- requirements_in.write_str(&indoc::formatdoc! {r#"
+ requirements_in.write_str(&indoc::formatdoc! {r"
-e {}
-e {}
- "#,
+ ",
editable_dir1.path().display(),
editable_dir2.path().display()
})?;
diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs
index 93d410560a9c..14e7dee5008b 100644
--- a/crates/uv/tests/it/pip_install.rs
+++ b/crates/uv/tests/it/pip_install.rs
@@ -7077,10 +7077,10 @@ fn local_index_requirements_txt_absolute() -> Result<()> {
"#, Url::from_directory_path(context.workspace_root.join("scripts/links/")).unwrap().as_str()})?;
let requirements_txt = context.temp_dir.child("requirements.txt");
- requirements_txt.write_str(&indoc::formatdoc! {r#"
+ requirements_txt.write_str(&indoc::formatdoc! {r"
--index-url {}
tqdm
- "#, Url::from_directory_path(root).unwrap().as_str()})?;
+ ", Url::from_directory_path(root).unwrap().as_str()})?;
uv_snapshot!(context.filters(), context.pip_install()
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index a718dc2fc84c..9db33c0e40d1 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,2 +1,2 @@
[toolchain]
-channel = "1.83"
+channel = "1.84"
From 15e20501d1e12431341336821012872fa11a36b8 Mon Sep 17 00:00:00 2001
From: sergue1
Date: Sun, 12 Jan 2025 18:07:04 +0300
Subject: [PATCH 132/135] typo (#10538)
fix typo in docs
---
docs/guides/tools.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/guides/tools.md b/docs/guides/tools.md
index 8300d1d5db33..e1419f0e60f2 100644
--- a/docs/guides/tools.md
+++ b/docs/guides/tools.md
@@ -235,4 +235,4 @@ $ uv tool upgrade --all
To learn more about managing tools with uv, see the [Tools concept](../concepts/tools.md) page and
the [command reference](../reference/cli.md#uv-tool).
-Or, read on to learn how to to [work on projects](./projects.md).
+Or, read on to learn how to [work on projects](./projects.md).
From 4ca5e048ccfa35477353429f9b68b1770510be44 Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Sun, 12 Jan 2025 13:45:16 -0500
Subject: [PATCH 133/135] Upgrade packse to v0.3.44 (#10544)
---
crates/uv/tests/it/common/mod.rs | 2 +-
crates/uv/tests/it/lock_scenarios.rs | 2 +-
crates/uv/tests/it/pip_compile_scenarios.rs | 2 +-
crates/uv/tests/it/pip_install_scenarios.rs | 2 +-
scripts/scenarios/requirements.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/crates/uv/tests/it/common/mod.rs b/crates/uv/tests/it/common/mod.rs
index 7dd8ac935dfd..6a20c930e1a0 100644
--- a/crates/uv/tests/it/common/mod.rs
+++ b/crates/uv/tests/it/common/mod.rs
@@ -32,7 +32,7 @@ use uv_static::EnvVars;
// Exclude any packages uploaded after this date.
static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z";
-pub const PACKSE_VERSION: &str = "0.3.43";
+pub const PACKSE_VERSION: &str = "0.3.44";
/// Using a find links url allows using `--index-url` instead of `--extra-index-url` in tests
/// to prevent dependency confusion attacks against our test suite.
diff --git a/crates/uv/tests/it/lock_scenarios.rs b/crates/uv/tests/it/lock_scenarios.rs
index 831d9bf2a9b2..a645f4f0f9fd 100644
--- a/crates/uv/tests/it/lock_scenarios.rs
+++ b/crates/uv/tests/it/lock_scenarios.rs
@@ -1,7 +1,7 @@
//! DO NOT EDIT
//!
//! Generated with `./scripts/sync_scenarios.sh`
-//! Scenarios from
+//! Scenarios from
//!
#![cfg(all(feature = "python", feature = "pypi"))]
#![allow(clippy::needless_raw_string_hashes)]
diff --git a/crates/uv/tests/it/pip_compile_scenarios.rs b/crates/uv/tests/it/pip_compile_scenarios.rs
index 93157dc32c76..a49792c8782d 100644
--- a/crates/uv/tests/it/pip_compile_scenarios.rs
+++ b/crates/uv/tests/it/pip_compile_scenarios.rs
@@ -1,7 +1,7 @@
//! DO NOT EDIT
//!
//! Generated with `./scripts/sync_scenarios.sh`
-//! Scenarios from
+//! Scenarios from
//!
#![cfg(all(feature = "python", feature = "pypi", unix))]
diff --git a/crates/uv/tests/it/pip_install_scenarios.rs b/crates/uv/tests/it/pip_install_scenarios.rs
index 1965a4f596f7..aa16c7941d85 100644
--- a/crates/uv/tests/it/pip_install_scenarios.rs
+++ b/crates/uv/tests/it/pip_install_scenarios.rs
@@ -1,7 +1,7 @@
//! DO NOT EDIT
//!
//! Generated with `./scripts/sync_scenarios.sh`
-//! Scenarios from
+//! Scenarios from
//!
#![cfg(all(feature = "python", feature = "pypi", unix))]
diff --git a/scripts/scenarios/requirements.txt b/scripts/scenarios/requirements.txt
index 0a6f0cbcd5fd..733e9ea67a49 100644
--- a/scripts/scenarios/requirements.txt
+++ b/scripts/scenarios/requirements.txt
@@ -10,7 +10,7 @@ msgspec==0.18.6
# via packse
packaging==24.2
# via hatchling
-packse==0.3.43
+packse==0.3.44
# via -r scripts/scenarios/requirements.in
pathspec==0.12.1
# via hatchling
From 1e48c1283709919af10174bb8a5094142b0da07f Mon Sep 17 00:00:00 2001
From: Charlie Marsh
Date: Sun, 12 Jan 2025 15:23:18 -0500
Subject: [PATCH 134/135] Add a shared `uv-small-str` crate (#10545)
## Summary
I want to use `SmallString` elsewhere.
---
Cargo.lock | 12 +++++++++-
Cargo.toml | 3 ++-
crates/uv-normalize/Cargo.toml | 6 ++++-
crates/uv-normalize/src/extra_name.rs | 3 ++-
crates/uv-normalize/src/group_name.rs | 3 ++-
crates/uv-normalize/src/lib.rs | 4 ++--
crates/uv-normalize/src/package_name.rs | 3 ++-
crates/uv-small-str/Cargo.toml | 22 +++++++++++++++++++
.../src/lib.rs} | 22 ++++++++++++++-----
9 files changed, 65 insertions(+), 13 deletions(-)
create mode 100644 crates/uv-small-str/Cargo.toml
rename crates/{uv-normalize/src/small_string.rs => uv-small-str/src/lib.rs} (86%)
diff --git a/Cargo.lock b/Cargo.lock
index c6696c60679b..2e3f383bd6bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5235,10 +5235,10 @@ dependencies = [
name = "uv-normalize"
version = "0.0.1"
dependencies = [
- "arcstr",
"rkyv",
"schemars",
"serde",
+ "uv-small-str",
]
[[package]]
@@ -5622,6 +5622,16 @@ dependencies = [
"winreg",
]
+[[package]]
+name = "uv-small-str"
+version = "0.0.1"
+dependencies = [
+ "arcstr",
+ "rkyv",
+ "schemars",
+ "serde",
+]
+
[[package]]
name = "uv-state"
version = "0.0.1"
diff --git a/Cargo.toml b/Cargo.toml
index e97d1ae6657c..ad3717a20177 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -59,10 +59,11 @@ uv-resolver = { path = "crates/uv-resolver" }
uv-scripts = { path = "crates/uv-scripts" }
uv-settings = { path = "crates/uv-settings" }
uv-shell = { path = "crates/uv-shell" }
+uv-small-str = { path = "crates/uv-small-str" }
uv-state = { path = "crates/uv-state" }
uv-static = { path = "crates/uv-static" }
-uv-trampoline-builder = { path = "crates/uv-trampoline-builder" }
uv-tool = { path = "crates/uv-tool" }
+uv-trampoline-builder = { path = "crates/uv-trampoline-builder" }
uv-types = { path = "crates/uv-types" }
uv-version = { path = "crates/uv-version" }
uv-virtualenv = { path = "crates/uv-virtualenv" }
diff --git a/crates/uv-normalize/Cargo.toml b/crates/uv-normalize/Cargo.toml
index 8d9c1c23e512..d40faf3d72b9 100644
--- a/crates/uv-normalize/Cargo.toml
+++ b/crates/uv-normalize/Cargo.toml
@@ -11,7 +11,11 @@ doctest = false
workspace = true
[dependencies]
-arcstr = { workspace = true }
+uv-small-str = { workspace = true }
+
rkyv = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
+
+[features]
+schemars = ["dep:schemars", "uv-small-str/schemars"]
diff --git a/crates/uv-normalize/src/extra_name.rs b/crates/uv-normalize/src/extra_name.rs
index bc3c5f737bb8..11a6707ef127 100644
--- a/crates/uv-normalize/src/extra_name.rs
+++ b/crates/uv-normalize/src/extra_name.rs
@@ -4,7 +4,8 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize};
-use crate::small_string::SmallString;
+use uv_small_str::SmallString;
+
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
/// The normalized name of an extra dependency.
diff --git a/crates/uv-normalize/src/group_name.rs b/crates/uv-normalize/src/group_name.rs
index a3ecb74c8c24..a11ed01bb73a 100644
--- a/crates/uv-normalize/src/group_name.rs
+++ b/crates/uv-normalize/src/group_name.rs
@@ -5,7 +5,8 @@ use std::sync::LazyLock;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
-use crate::small_string::SmallString;
+use uv_small_str::SmallString;
+
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
/// The normalized name of a dependency group.
diff --git a/crates/uv-normalize/src/lib.rs b/crates/uv-normalize/src/lib.rs
index 17beee6b9995..06234db96a71 100644
--- a/crates/uv-normalize/src/lib.rs
+++ b/crates/uv-normalize/src/lib.rs
@@ -5,13 +5,13 @@ pub use dist_info_name::DistInfoName;
pub use extra_name::ExtraName;
pub use group_name::{GroupName, DEV_DEPENDENCIES};
pub use package_name::PackageName;
-use small_string::SmallString;
+
+use uv_small_str::SmallString;
mod dist_info_name;
mod extra_name;
mod group_name;
mod package_name;
-mod small_string;
/// Validate and normalize an owned package or extra name.
pub(crate) fn validate_and_normalize_owned(name: String) -> Result {
diff --git a/crates/uv-normalize/src/package_name.rs b/crates/uv-normalize/src/package_name.rs
index 742867e7f51b..59d7ea912e5f 100644
--- a/crates/uv-normalize/src/package_name.rs
+++ b/crates/uv-normalize/src/package_name.rs
@@ -4,7 +4,8 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize};
-use crate::small_string::SmallString;
+use uv_small_str::SmallString;
+
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
/// The normalized name of a package.
diff --git a/crates/uv-small-str/Cargo.toml b/crates/uv-small-str/Cargo.toml
new file mode 100644
index 000000000000..c8e6cf18baf6
--- /dev/null
+++ b/crates/uv-small-str/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "uv-small-str"
+version = "0.0.1"
+edition = { workspace = true }
+rust-version = { workspace = true }
+homepage = { workspace = true }
+documentation = { workspace = true }
+repository = { workspace = true }
+authors = { workspace = true }
+license = { workspace = true }
+
+[lib]
+doctest = false
+
+[lints]
+workspace = true
+
+[dependencies]
+arcstr = { workspace = true }
+rkyv = { workspace = true }
+schemars = { workspace = true, optional = true }
+serde = { workspace = true }
diff --git a/crates/uv-normalize/src/small_string.rs b/crates/uv-small-str/src/lib.rs
similarity index 86%
rename from crates/uv-normalize/src/small_string.rs
rename to crates/uv-small-str/src/lib.rs
index ca6d3ea6dadb..20d66190ef7d 100644
--- a/crates/uv-normalize/src/small_string.rs
+++ b/crates/uv-small-str/src/lib.rs
@@ -1,11 +1,16 @@
use std::cmp::PartialEq;
use std::ops::Deref;
-/// An optimized small string type for short identifiers, like package names.
-///
-/// Represented as an [`arcstr::ArcStr`] internally.
-#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub(crate) struct SmallString(arcstr::ArcStr);
+/// An optimized type for immutable identifiers. Represented as an [`arcstr::ArcStr`] internally.
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SmallString(arcstr::ArcStr);
+
+impl From for SmallString {
+ #[inline]
+ fn from(s: arcstr::ArcStr) -> Self {
+ Self(s)
+ }
+}
impl From<&str> for SmallString {
#[inline]
@@ -28,6 +33,13 @@ impl AsRef for SmallString {
}
}
+impl core::borrow::Borrow for SmallString {
+ #[inline]
+ fn borrow(&self) -> &str {
+ self
+ }
+}
+
impl Deref for SmallString {
type Target = str;
From b38d3fec64c03f299cf203e4a7dd9ababdb3c377 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 12 Jan 2025 20:33:01 -0500
Subject: [PATCH 135/135] Update Rust crate jiff to v0.1.22 (#10550)
---
Cargo.lock | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 2e3f383bd6bf..c3fe0acc4b3e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1038,7 +1038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1903,16 +1903,16 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "jiff"
-version = "0.1.21"
+version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed0ce60560149333a8e41ca7dc78799c47c5fd435e2bc18faf6a054382eec037"
+checksum = "5c258647f65892e500c2478ef2c71ba008e7dc1774a8289345adbbb502a4def1"
dependencies = [
"jiff-tzdb-platform",
"log",
"portable-atomic",
"portable-atomic-util",
"serde",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -2800,7 +2800,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -3237,7 +3237,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -3806,7 +3806,7 @@ dependencies = [
"getrandom",
"once_cell",
"rustix",
- "windows-sys 0.59.0",
+ "windows-sys 0.52.0",
]
[[package]]