mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-03 13:14:41 +00:00
Add UV_SKIP_WHEEL_FILENAME_CHECK to allow installing invalid wheels (#16046)
## Summary This PR adds a user setting to allow (in rare cases) accepting wheels with mismatched filenames and internal metadata. Closes https://github.com/astral-sh/uv/issues/8082. Closes https://github.com/astral-sh/uv/issues/15647.
This commit is contained in:
parent
170ab1cd7f
commit
7d9ea797b0
12 changed files with 159 additions and 101 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -6070,6 +6070,7 @@ dependencies = [
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"indoc",
|
"indoc",
|
||||||
"mailparse",
|
"mailparse",
|
||||||
|
"owo-colors",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"reflink-copy",
|
"reflink-copy",
|
||||||
"regex",
|
"regex",
|
||||||
|
|
@ -6090,6 +6091,7 @@ dependencies = [
|
||||||
"uv-preview",
|
"uv-preview",
|
||||||
"uv-pypi-types",
|
"uv-pypi-types",
|
||||||
"uv-shell",
|
"uv-shell",
|
||||||
|
"uv-static",
|
||||||
"uv-trampoline-builder",
|
"uv-trampoline-builder",
|
||||||
"uv-warnings",
|
"uv-warnings",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ uv-pep440 = { workspace = true }
|
||||||
uv-preview = { workspace = true }
|
uv-preview = { workspace = true }
|
||||||
uv-pypi-types = { workspace = true }
|
uv-pypi-types = { workspace = true }
|
||||||
uv-shell = { workspace = true }
|
uv-shell = { workspace = true }
|
||||||
|
uv-static = { workspace = true }
|
||||||
uv-trampoline-builder = { workspace = true }
|
uv-trampoline-builder = { workspace = true }
|
||||||
uv-warnings = { workspace = true }
|
uv-warnings = { workspace = true }
|
||||||
|
|
||||||
|
|
@ -37,6 +38,7 @@ csv = { workspace = true }
|
||||||
data-encoding = { workspace = true }
|
data-encoding = { workspace = true }
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
mailparse = { workspace = true }
|
mailparse = { workspace = true }
|
||||||
|
owo-colors = { workspace = true }
|
||||||
pathdiff = { workspace = true }
|
pathdiff = { workspace = true }
|
||||||
reflink-copy = { workspace = true }
|
reflink-copy = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use tracing::{instrument, trace};
|
||||||
use uv_distribution_filename::WheelFilename;
|
use uv_distribution_filename::WheelFilename;
|
||||||
use uv_pep440::Version;
|
use uv_pep440::Version;
|
||||||
use uv_pypi_types::{DirectUrl, Metadata10};
|
use uv_pypi_types::{DirectUrl, Metadata10};
|
||||||
|
use uv_static::{EnvVars, parse_boolish_environment_variable};
|
||||||
|
|
||||||
use crate::linker::{LinkMode, Locks};
|
use crate::linker::{LinkMode, Locks};
|
||||||
use crate::wheel::{
|
use crate::wheel::{
|
||||||
|
|
@ -50,11 +51,21 @@ pub fn install_wheel<Cache: serde::Serialize, Build: serde::Serialize>(
|
||||||
// Validate the wheel name and version.
|
// Validate the wheel name and version.
|
||||||
{
|
{
|
||||||
if name != filename.name {
|
if name != filename.name {
|
||||||
return Err(Error::MismatchedName(name, filename.name.clone()));
|
if !matches!(
|
||||||
|
parse_boolish_environment_variable(EnvVars::UV_SKIP_WHEEL_FILENAME_CHECK),
|
||||||
|
Ok(Some(true))
|
||||||
|
) {
|
||||||
|
return Err(Error::MismatchedName(name, filename.name.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if version != filename.version && version != filename.version.clone().without_local() {
|
if version != filename.version && version != filename.version.clone().without_local() {
|
||||||
return Err(Error::MismatchedVersion(version, filename.version.clone()));
|
if !matches!(
|
||||||
|
parse_boolish_environment_variable(EnvVars::UV_SKIP_WHEEL_FILENAME_CHECK),
|
||||||
|
Ok(Some(true))
|
||||||
|
) {
|
||||||
|
return Err(Error::MismatchedVersion(version, filename.version.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use owo_colors::OwoColorize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
|
|
@ -74,9 +75,9 @@ pub enum Error {
|
||||||
MissingTopLevel(PathBuf),
|
MissingTopLevel(PathBuf),
|
||||||
#[error("Invalid package version")]
|
#[error("Invalid package version")]
|
||||||
InvalidVersion(#[from] uv_pep440::VersionParseError),
|
InvalidVersion(#[from] uv_pep440::VersionParseError),
|
||||||
#[error("Wheel package name does not match filename: {0} != {1}")]
|
#[error("Wheel package name does not match filename ({0} != {1}), which indicates a malformed wheel. If this is intentional, set `{env_var}`.", env_var = "UV_SKIP_WHEEL_FILENAME_CHECK=1".green())]
|
||||||
MismatchedName(PackageName, PackageName),
|
MismatchedName(PackageName, PackageName),
|
||||||
#[error("Wheel version does not match filename: {0} != {1}")]
|
#[error("Wheel version does not match filename ({0} != {1}), which indicates a malformed wheel. If this is intentional, set `{env_var}`.", env_var = "UV_SKIP_WHEEL_FILENAME_CHECK=1".green())]
|
||||||
MismatchedVersion(Version, Version),
|
MismatchedVersion(Version, Version),
|
||||||
#[error("Invalid egg-link")]
|
#[error("Invalid egg-link")]
|
||||||
InvalidEggLink(PathBuf),
|
InvalidEggLink(PathBuf),
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ use uv_pypi_types::{
|
||||||
};
|
};
|
||||||
use uv_redacted::DisplaySafeUrl;
|
use uv_redacted::DisplaySafeUrl;
|
||||||
use uv_small_str::SmallString;
|
use uv_small_str::SmallString;
|
||||||
|
use uv_static::{EnvVars, parse_boolish_environment_variable};
|
||||||
use uv_types::{BuildContext, HashStrategy};
|
use uv_types::{BuildContext, HashStrategy};
|
||||||
use uv_workspace::{Editability, WorkspaceMember};
|
use uv_workspace::{Editability, WorkspaceMember};
|
||||||
|
|
||||||
|
|
@ -3245,11 +3246,16 @@ impl PackageWire {
|
||||||
if *version != wheel.filename.version
|
if *version != wheel.filename.version
|
||||||
&& *version != wheel.filename.version.clone().without_local()
|
&& *version != wheel.filename.version.clone().without_local()
|
||||||
{
|
{
|
||||||
return Err(LockError::from(LockErrorKind::InconsistentVersions {
|
if !matches!(
|
||||||
name: self.id.name,
|
parse_boolish_environment_variable(EnvVars::UV_SKIP_WHEEL_FILENAME_CHECK),
|
||||||
version: version.clone(),
|
Ok(Some(true))
|
||||||
wheel: wheel.clone(),
|
) {
|
||||||
}));
|
return Err(LockError::from(LockErrorKind::InconsistentVersions {
|
||||||
|
name: self.id.name,
|
||||||
|
version: version.clone(),
|
||||||
|
wheel: wheel.clone(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We can't check the source dist version since it does not need to contain the version
|
// We can't check the source dist version since it does not need to contain the version
|
||||||
|
|
@ -5866,7 +5872,7 @@ enum LockErrorKind {
|
||||||
},
|
},
|
||||||
/// A package has inconsistent versions in a single entry
|
/// A package has inconsistent versions in a single entry
|
||||||
// Using name instead of id since the version in the id is part of the conflict.
|
// Using name instead of id since the version in the id is part of the conflict.
|
||||||
#[error("The entry for package `{name}` v{version} has wheel `{wheel_filename}` with inconsistent version: v{wheel_version} ", name = name.cyan(), wheel_filename = wheel.filename, wheel_version = wheel.filename.version)]
|
#[error("The entry for package `{name}` ({version}) has wheel `{wheel_filename}` with inconsistent version ({wheel_version}), which indicates a malformed wheel. If this is intentional, set `{env_var}`.", name = name.cyan(), wheel_filename = wheel.filename, wheel_version = wheel.filename.version, env_var = "UV_SKIP_WHEEL_FILENAME_CHECK=1".green())]
|
||||||
InconsistentVersions {
|
InconsistentVersions {
|
||||||
/// The name of the package with the inconsistent entry.
|
/// The name of the package with the inconsistent entry.
|
||||||
name: PackageName,
|
name: PackageName,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use uv_dirs::{system_config_file, user_config_dir};
|
use uv_dirs::{system_config_file, user_config_dir};
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_static::EnvVars;
|
use uv_static::{EnvVars, parse_boolish_environment_variable, parse_string_environment_variable};
|
||||||
use uv_warnings::warn_user;
|
use uv_warnings::warn_user;
|
||||||
|
|
||||||
pub use crate::combine::*;
|
pub use crate::combine::*;
|
||||||
|
|
@ -554,12 +554,8 @@ pub enum Error {
|
||||||
#[error("Failed to parse: `{}`. The `{}` field is not allowed in a `uv.toml` file. `{}` is only applicable in the context of a project, and should be placed in a `pyproject.toml` file instead.", _0.user_display(), _1, _1)]
|
#[error("Failed to parse: `{}`. The `{}` field is not allowed in a `uv.toml` file. `{}` is only applicable in the context of a project, and should be placed in a `pyproject.toml` file instead.", _0.user_display(), _1, _1)]
|
||||||
PyprojectOnlyField(PathBuf, &'static str),
|
PyprojectOnlyField(PathBuf, &'static str),
|
||||||
|
|
||||||
#[error("Failed to parse environment variable `{name}` with invalid value `{value}`: {err}")]
|
#[error("{0}")]
|
||||||
InvalidEnvironmentVariable {
|
InvalidEnvironmentVariable(String),
|
||||||
name: String,
|
|
||||||
value: String,
|
|
||||||
err: String,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options loaded from environment variables.
|
/// Options loaded from environment variables.
|
||||||
|
|
@ -578,95 +574,28 @@ impl EnvironmentOptions {
|
||||||
/// Create a new [`EnvironmentOptions`] from environment variables.
|
/// Create a new [`EnvironmentOptions`] from environment variables.
|
||||||
pub fn new() -> Result<Self, Error> {
|
pub fn new() -> Result<Self, Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
python_install_bin: parse_boolish_environment_variable(EnvVars::UV_PYTHON_INSTALL_BIN)?,
|
python_install_bin: parse_boolish_environment_variable(EnvVars::UV_PYTHON_INSTALL_BIN)
|
||||||
|
.map_err(Error::InvalidEnvironmentVariable)?,
|
||||||
python_install_registry: parse_boolish_environment_variable(
|
python_install_registry: parse_boolish_environment_variable(
|
||||||
EnvVars::UV_PYTHON_INSTALL_REGISTRY,
|
EnvVars::UV_PYTHON_INSTALL_REGISTRY,
|
||||||
)?,
|
)
|
||||||
|
.map_err(Error::InvalidEnvironmentVariable)?,
|
||||||
install_mirrors: PythonInstallMirrors {
|
install_mirrors: PythonInstallMirrors {
|
||||||
python_install_mirror: parse_string_environment_variable(
|
python_install_mirror: parse_string_environment_variable(
|
||||||
EnvVars::UV_PYTHON_INSTALL_MIRROR,
|
EnvVars::UV_PYTHON_INSTALL_MIRROR,
|
||||||
)?,
|
)
|
||||||
|
.map_err(Error::InvalidEnvironmentVariable)?,
|
||||||
pypy_install_mirror: parse_string_environment_variable(
|
pypy_install_mirror: parse_string_environment_variable(
|
||||||
EnvVars::UV_PYPY_INSTALL_MIRROR,
|
EnvVars::UV_PYPY_INSTALL_MIRROR,
|
||||||
)?,
|
)
|
||||||
|
.map_err(Error::InvalidEnvironmentVariable)?,
|
||||||
python_downloads_json_url: parse_string_environment_variable(
|
python_downloads_json_url: parse_string_environment_variable(
|
||||||
EnvVars::UV_PYTHON_DOWNLOADS_JSON_URL,
|
EnvVars::UV_PYTHON_DOWNLOADS_JSON_URL,
|
||||||
)?,
|
)
|
||||||
|
.map_err(Error::InvalidEnvironmentVariable)?,
|
||||||
},
|
},
|
||||||
log_context: parse_boolish_environment_variable(EnvVars::UV_LOG_CONTEXT)?,
|
log_context: parse_boolish_environment_variable(EnvVars::UV_LOG_CONTEXT)
|
||||||
|
.map_err(Error::InvalidEnvironmentVariable)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a boolean environment variable.
|
|
||||||
///
|
|
||||||
/// Adapted from Clap's `BoolishValueParser` which is dual licensed under the MIT and Apache-2.0.
|
|
||||||
fn parse_boolish_environment_variable(name: &'static str) -> Result<Option<bool>, Error> {
|
|
||||||
// See `clap_builder/src/util/str_to_bool.rs`
|
|
||||||
// We want to match Clap's accepted values
|
|
||||||
|
|
||||||
// True values are `y`, `yes`, `t`, `true`, `on`, and `1`.
|
|
||||||
const TRUE_LITERALS: [&str; 6] = ["y", "yes", "t", "true", "on", "1"];
|
|
||||||
|
|
||||||
// False values are `n`, `no`, `f`, `false`, `off`, and `0`.
|
|
||||||
const FALSE_LITERALS: [&str; 6] = ["n", "no", "f", "false", "off", "0"];
|
|
||||||
|
|
||||||
// Converts a string literal representation of truth to true or false.
|
|
||||||
//
|
|
||||||
// `false` values are `n`, `no`, `f`, `false`, `off`, and `0` (case insensitive).
|
|
||||||
//
|
|
||||||
// Any other value will be considered as `true`.
|
|
||||||
fn str_to_bool(val: impl AsRef<str>) -> Option<bool> {
|
|
||||||
let pat: &str = &val.as_ref().to_lowercase();
|
|
||||||
if TRUE_LITERALS.contains(&pat) {
|
|
||||||
Some(true)
|
|
||||||
} else if FALSE_LITERALS.contains(&pat) {
|
|
||||||
Some(false)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(value) = std::env::var_os(name) else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(value) = value.to_str() else {
|
|
||||||
return Err(Error::InvalidEnvironmentVariable {
|
|
||||||
name: name.to_string(),
|
|
||||||
value: value.to_string_lossy().to_string(),
|
|
||||||
err: "expected a valid UTF-8 string".to_string(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(value) = str_to_bool(value) else {
|
|
||||||
return Err(Error::InvalidEnvironmentVariable {
|
|
||||||
name: name.to_string(),
|
|
||||||
value: value.to_string(),
|
|
||||||
err: "expected a boolish value".to_string(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a string environment variable.
|
|
||||||
fn parse_string_environment_variable(name: &'static str) -> Result<Option<String>, Error> {
|
|
||||||
match std::env::var(name) {
|
|
||||||
Ok(v) => {
|
|
||||||
if v.is_empty() {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
std::env::VarError::NotPresent => Ok(None),
|
|
||||||
std::env::VarError::NotUnicode(err) => Err(Error::InvalidEnvironmentVariable {
|
|
||||||
name: name.to_string(),
|
|
||||||
value: err.to_string_lossy().to_string(),
|
|
||||||
err: "expected a valid UTF-8 string".to_string(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -937,4 +937,10 @@ impl EnvVars {
|
||||||
|
|
||||||
/// The AWS shared credentials file to use when signing S3 requests.
|
/// The AWS shared credentials file to use when signing S3 requests.
|
||||||
pub const AWS_SHARED_CREDENTIALS_FILE: &'static str = "AWS_SHARED_CREDENTIALS_FILE";
|
pub const AWS_SHARED_CREDENTIALS_FILE: &'static str = "AWS_SHARED_CREDENTIALS_FILE";
|
||||||
|
|
||||||
|
/// Avoid verifying that wheel filenames match their contents when installing wheels. This
|
||||||
|
/// is not recommended, as wheels with inconsistent filenames should be considered invalid and
|
||||||
|
/// corrected by the relevant package maintainers; however, this option can be used to work
|
||||||
|
/// around invalid artifacts in rare cases.
|
||||||
|
pub const UV_SKIP_WHEEL_FILENAME_CHECK: &'static str = "UV_SKIP_WHEEL_FILENAME_CHECK";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,74 @@
|
||||||
pub use env_vars::*;
|
pub use env_vars::*;
|
||||||
|
|
||||||
mod env_vars;
|
mod env_vars;
|
||||||
|
|
||||||
|
/// Parse a boolean environment variable.
|
||||||
|
///
|
||||||
|
/// Adapted from Clap's `BoolishValueParser` which is dual licensed under the MIT and Apache-2.0.
|
||||||
|
pub fn parse_boolish_environment_variable(name: &'static str) -> Result<Option<bool>, String> {
|
||||||
|
// See `clap_builder/src/util/str_to_bool.rs`
|
||||||
|
// We want to match Clap's accepted values
|
||||||
|
|
||||||
|
// True values are `y`, `yes`, `t`, `true`, `on`, and `1`.
|
||||||
|
const TRUE_LITERALS: [&str; 6] = ["y", "yes", "t", "true", "on", "1"];
|
||||||
|
|
||||||
|
// False values are `n`, `no`, `f`, `false`, `off`, and `0`.
|
||||||
|
const FALSE_LITERALS: [&str; 6] = ["n", "no", "f", "false", "off", "0"];
|
||||||
|
|
||||||
|
// Converts a string literal representation of truth to true or false.
|
||||||
|
//
|
||||||
|
// `false` values are `n`, `no`, `f`, `false`, `off`, and `0` (case insensitive).
|
||||||
|
//
|
||||||
|
// Any other value will be considered as `true`.
|
||||||
|
fn str_to_bool(val: impl AsRef<str>) -> Option<bool> {
|
||||||
|
let pat: &str = &val.as_ref().to_lowercase();
|
||||||
|
if TRUE_LITERALS.contains(&pat) {
|
||||||
|
Some(true)
|
||||||
|
} else if FALSE_LITERALS.contains(&pat) {
|
||||||
|
Some(false)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(value) = std::env::var_os(name) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(value) = value.to_str() else {
|
||||||
|
return Err(format!(
|
||||||
|
"Failed to parse environment variable `{}` with invalid value `{}`: expected a valid UTF-8 string",
|
||||||
|
name,
|
||||||
|
value.to_string_lossy()
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(value) = str_to_bool(value) else {
|
||||||
|
return Err(format!(
|
||||||
|
"Failed to parse environment variable `{name}` with invalid value `{value}`: expected a boolish value"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a string environment variable.
|
||||||
|
pub fn parse_string_environment_variable(name: &'static str) -> Result<Option<String>, String> {
|
||||||
|
match std::env::var(name) {
|
||||||
|
Ok(v) => {
|
||||||
|
if v.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => match e {
|
||||||
|
std::env::VarError::NotPresent => Ok(None),
|
||||||
|
std::env::VarError::NotUnicode(err) => Err(format!(
|
||||||
|
"Failed to parse environment variable `{}` with invalid value `{}`: expected a valid UTF-8 string",
|
||||||
|
name,
|
||||||
|
err.to_string_lossy()
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1233,7 +1233,7 @@ fn mismatched_version() -> Result<()> {
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.pip_sync()
|
uv_snapshot!(context.filters(), context.pip_sync()
|
||||||
.arg("requirements.txt")
|
.arg("requirements.txt")
|
||||||
.arg("--strict"), @r###"
|
.arg("--strict"), @r"
|
||||||
success: false
|
success: false
|
||||||
exit_code: 2
|
exit_code: 2
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
@ -1242,8 +1242,23 @@ fn mismatched_version() -> Result<()> {
|
||||||
Resolved 1 package in [TIME]
|
Resolved 1 package in [TIME]
|
||||||
Prepared 1 package in [TIME]
|
Prepared 1 package in [TIME]
|
||||||
error: Failed to install: tomli-3.7.2-py3-none-any.whl (tomli==3.7.2 (from file://[TEMP_DIR]/tomli-3.7.2-py3-none-any.whl))
|
error: Failed to install: tomli-3.7.2-py3-none-any.whl (tomli==3.7.2 (from file://[TEMP_DIR]/tomli-3.7.2-py3-none-any.whl))
|
||||||
Caused by: Wheel version does not match filename: 2.0.1 != 3.7.2
|
Caused by: Wheel version does not match filename (2.0.1 != 3.7.2), which indicates a malformed wheel. If this is intentional, set `UV_SKIP_WHEEL_FILENAME_CHECK=1`.
|
||||||
"###
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.pip_sync()
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--strict")
|
||||||
|
.env(EnvVars::UV_SKIP_WHEEL_FILENAME_CHECK, "1"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ tomli==3.7.2 (from file://[TEMP_DIR]/tomli-3.7.2-py3-none-any.whl)
|
||||||
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -11538,7 +11538,7 @@ fn locked_version_coherence() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
error: Failed to parse `uv.lock`
|
error: Failed to parse `uv.lock`
|
||||||
Caused by: The entry for package `iniconfig` v1.0.0 has wheel `iniconfig-2.0.0-py3-none-any.whl` with inconsistent version: v2.0.0
|
Caused by: The entry for package `iniconfig` (1.0.0) has wheel `iniconfig-2.0.0-py3-none-any.whl` with inconsistent version (2.0.0), which indicates a malformed wheel. If this is intentional, set `UV_SKIP_WHEEL_FILENAME_CHECK=1`.
|
||||||
");
|
");
|
||||||
|
|
||||||
// Without `--locked`, we could fail or recreate the lockfile, currently, we fail.
|
// Without `--locked`, we could fail or recreate the lockfile, currently, we fail.
|
||||||
|
|
@ -11549,7 +11549,7 @@ fn locked_version_coherence() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
error: Failed to parse `uv.lock`
|
error: Failed to parse `uv.lock`
|
||||||
Caused by: The entry for package `iniconfig` v1.0.0 has wheel `iniconfig-2.0.0-py3-none-any.whl` with inconsistent version: v2.0.0
|
Caused by: The entry for package `iniconfig` (1.0.0) has wheel `iniconfig-2.0.0-py3-none-any.whl` with inconsistent version (2.0.0), which indicates a malformed wheel. If this is intentional, set `UV_SKIP_WHEEL_FILENAME_CHECK=1`.
|
||||||
");
|
");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -481,3 +481,11 @@ is.
|
||||||
For example, `uv pip install foo bar` prioritizes newer versions of `foo` over `bar` and could
|
For example, `uv pip install foo bar` prioritizes newer versions of `foo` over `bar` and could
|
||||||
result in a different resolution than `uv pip install bar foo`. Similarly, this behavior applies to
|
result in a different resolution than `uv pip install bar foo`. Similarly, this behavior applies to
|
||||||
the ordering of requirements in input files for `uv pip compile`.
|
the ordering of requirements in input files for `uv pip compile`.
|
||||||
|
|
||||||
|
## Wheel filename and metadata validation
|
||||||
|
|
||||||
|
By default, uv will reject wheels whose filenames are inconsistent with the wheel metadata inside
|
||||||
|
the file. For example, a wheel named `foo-1.0.0-py3-none-any.whl` that contains metadata indicating
|
||||||
|
the version is `1.0.1` will be rejected by uv, but accepted by pip.
|
||||||
|
|
||||||
|
To force uv to accept such wheels, set `UV_SKIP_WHEEL_FILENAME_CHECK=1` in the environment.
|
||||||
|
|
|
||||||
|
|
@ -499,6 +499,13 @@ The URL to treat as an S3-compatible storage endpoint. Requests to this endpoint
|
||||||
will be signed using AWS Signature Version 4 based on the `AWS_ACCESS_KEY_ID`,
|
will be signed using AWS Signature Version 4 based on the `AWS_ACCESS_KEY_ID`,
|
||||||
`AWS_SECRET_ACCESS_KEY`, `AWS_PROFILE`, and `AWS_CONFIG_FILE` environment variables.
|
`AWS_SECRET_ACCESS_KEY`, `AWS_PROFILE`, and `AWS_CONFIG_FILE` environment variables.
|
||||||
|
|
||||||
|
### `UV_SKIP_WHEEL_FILENAME_CHECK`
|
||||||
|
|
||||||
|
Avoid verifying that wheel filenames match their contents when installing wheels. This
|
||||||
|
is not recommended, as wheels with inconsistent filenames should be considered invalid and
|
||||||
|
corrected by the relevant package maintainers; however, this option can be used to work
|
||||||
|
around invalid artifacts in rare cases.
|
||||||
|
|
||||||
### `UV_STACK_SIZE`
|
### `UV_STACK_SIZE`
|
||||||
|
|
||||||
Use to set the stack size used by uv.
|
Use to set the stack size used by uv.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue