mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
uv-resolver: support unambiguous omission of 'source' and 'version'
When there is only one distribution for a particular package name, any dependencies (the edges in the resolution graph) that reference that package name are completely unambiguous. Therefore, we can actually omit their version and source information and instead derive it from the distribution entry. We add some tests to check the success and error cases. That is, when `source` or `version` are omitted and there are more than one corresponding distribution for the package name (i.e., it's ambiguous), then lock deserialization should fail.
This commit is contained in:
parent
4cb1595136
commit
4accbfd915
7 changed files with 944 additions and 18 deletions
|
@ -453,6 +453,15 @@ impl Lock {
|
||||||
doc.insert("requires-python", value(requires_python.to_string()));
|
doc.insert("requires-python", value(requires_python.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count the number of distributions for each package name. When
|
||||||
|
// there's only one distribution for a particular package name (the
|
||||||
|
// overwhelmingly common case), we can omit some data (like source and
|
||||||
|
// version) on dependency edges since it is strictly redundant.
|
||||||
|
let mut dist_count_by_name: FxHashMap<PackageName, u64> = FxHashMap::default();
|
||||||
|
for dist in &self.distributions {
|
||||||
|
*dist_count_by_name.entry(dist.id.name.clone()).or_default() += 1;
|
||||||
|
}
|
||||||
|
|
||||||
let mut distributions = ArrayOfTables::new();
|
let mut distributions = ArrayOfTables::new();
|
||||||
for dist in &self.distributions {
|
for dist in &self.distributions {
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
|
@ -469,7 +478,7 @@ impl Lock {
|
||||||
let deps = dist
|
let deps = dist
|
||||||
.dependencies
|
.dependencies
|
||||||
.iter()
|
.iter()
|
||||||
.map(Dependency::to_toml)
|
.map(|dep| dep.to_toml(&dist_count_by_name))
|
||||||
.collect::<ArrayOfTables>();
|
.collect::<ArrayOfTables>();
|
||||||
table.insert("dependencies", Item::ArrayOfTables(deps));
|
table.insert("dependencies", Item::ArrayOfTables(deps));
|
||||||
}
|
}
|
||||||
|
@ -479,7 +488,7 @@ impl Lock {
|
||||||
for (extra, deps) in &dist.optional_dependencies {
|
for (extra, deps) in &dist.optional_dependencies {
|
||||||
let deps = deps
|
let deps = deps
|
||||||
.iter()
|
.iter()
|
||||||
.map(Dependency::to_toml)
|
.map(|dep| dep.to_toml(&dist_count_by_name))
|
||||||
.collect::<ArrayOfTables>();
|
.collect::<ArrayOfTables>();
|
||||||
optional_deps.insert(extra.as_ref(), Item::ArrayOfTables(deps));
|
optional_deps.insert(extra.as_ref(), Item::ArrayOfTables(deps));
|
||||||
}
|
}
|
||||||
|
@ -491,7 +500,7 @@ impl Lock {
|
||||||
for (extra, deps) in &dist.dev_dependencies {
|
for (extra, deps) in &dist.dev_dependencies {
|
||||||
let deps = deps
|
let deps = deps
|
||||||
.iter()
|
.iter()
|
||||||
.map(Dependency::to_toml)
|
.map(|dep| dep.to_toml(&dist_count_by_name))
|
||||||
.collect::<ArrayOfTables>();
|
.collect::<ArrayOfTables>();
|
||||||
dev_dependencies.insert(extra.as_ref(), Item::ArrayOfTables(deps));
|
dev_dependencies.insert(extra.as_ref(), Item::ArrayOfTables(deps));
|
||||||
}
|
}
|
||||||
|
@ -532,10 +541,27 @@ impl TryFrom<LockWire> for Lock {
|
||||||
type Error = LockError;
|
type Error = LockError;
|
||||||
|
|
||||||
fn try_from(wire: LockWire) -> Result<Lock, LockError> {
|
fn try_from(wire: LockWire) -> Result<Lock, LockError> {
|
||||||
|
// Count the number of distributions for each package name. When
|
||||||
|
// there's only one distribution for a particular package name (the
|
||||||
|
// overwhelmingly common case), we can omit some data (like source and
|
||||||
|
// version) on dependency edges since it is strictly redundant.
|
||||||
|
let mut unambiguous_dist_ids: FxHashMap<PackageName, DistributionId> = FxHashMap::default();
|
||||||
|
let mut ambiguous = FxHashSet::default();
|
||||||
|
for dist in &wire.distributions {
|
||||||
|
if ambiguous.contains(&dist.id.name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if unambiguous_dist_ids.remove(&dist.id.name).is_some() {
|
||||||
|
ambiguous.insert(dist.id.name.clone());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unambiguous_dist_ids.insert(dist.id.name.clone(), dist.id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
let distributions = wire
|
let distributions = wire
|
||||||
.distributions
|
.distributions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(DistributionWire::unwire)
|
.map(|dist| dist.unwire(&unambiguous_dist_ids))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
Lock::new(wire.version, distributions, wire.requires_python)
|
Lock::new(wire.version, distributions, wire.requires_python)
|
||||||
}
|
}
|
||||||
|
@ -844,9 +870,14 @@ struct DistributionWire {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DistributionWire {
|
impl DistributionWire {
|
||||||
fn unwire(self) -> Result<Distribution, LockError> {
|
fn unwire(
|
||||||
|
self,
|
||||||
|
unambiguous_dist_ids: &FxHashMap<PackageName, DistributionId>,
|
||||||
|
) -> Result<Distribution, LockError> {
|
||||||
let unwire_deps = |deps: Vec<DependencyWire>| -> Result<Vec<Dependency>, LockError> {
|
let unwire_deps = |deps: Vec<DependencyWire>| -> Result<Vec<Dependency>, LockError> {
|
||||||
deps.into_iter().map(DependencyWire::unwire).collect()
|
deps.into_iter()
|
||||||
|
.map(|dep| dep.unwire(unambiguous_dist_ids))
|
||||||
|
.collect()
|
||||||
};
|
};
|
||||||
Ok(Distribution {
|
Ok(Distribution {
|
||||||
id: self.id,
|
id: self.id,
|
||||||
|
@ -922,16 +953,38 @@ impl std::fmt::Display for DistributionId {
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, serde::Deserialize)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, serde::Deserialize)]
|
||||||
struct DistributionIdForDependency {
|
struct DistributionIdForDependency {
|
||||||
name: PackageName,
|
name: PackageName,
|
||||||
version: Version,
|
version: Option<Version>,
|
||||||
source: Source,
|
source: Option<Source>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DistributionIdForDependency {
|
impl DistributionIdForDependency {
|
||||||
fn unwire(self) -> Result<DistributionId, LockError> {
|
fn unwire(
|
||||||
|
self,
|
||||||
|
unambiguous_dist_ids: &FxHashMap<PackageName, DistributionId>,
|
||||||
|
) -> Result<DistributionId, LockError> {
|
||||||
|
let unambiguous_dist_id = unambiguous_dist_ids.get(&self.name);
|
||||||
|
let version = self.version.map(Ok::<_, LockError>).unwrap_or_else(|| {
|
||||||
|
let Some(dist_id) = unambiguous_dist_id else {
|
||||||
|
return Err(LockErrorKind::MissingDependencyVersion {
|
||||||
|
name: self.name.clone(),
|
||||||
|
}
|
||||||
|
.into());
|
||||||
|
};
|
||||||
|
Ok(dist_id.version.clone())
|
||||||
|
})?;
|
||||||
|
let source = self.source.map(Ok::<_, LockError>).unwrap_or_else(|| {
|
||||||
|
let Some(dist_id) = unambiguous_dist_id else {
|
||||||
|
return Err(LockErrorKind::MissingDependencySource {
|
||||||
|
name: self.name.clone(),
|
||||||
|
}
|
||||||
|
.into());
|
||||||
|
};
|
||||||
|
Ok(dist_id.source.clone())
|
||||||
|
})?;
|
||||||
Ok(DistributionId {
|
Ok(DistributionId {
|
||||||
name: self.name,
|
name: self.name,
|
||||||
version: self.version,
|
version,
|
||||||
source: self.source,
|
source,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -940,8 +993,8 @@ impl From<DistributionId> for DistributionIdForDependency {
|
||||||
fn from(id: DistributionId) -> DistributionIdForDependency {
|
fn from(id: DistributionId) -> DistributionIdForDependency {
|
||||||
DistributionIdForDependency {
|
DistributionIdForDependency {
|
||||||
name: id.name,
|
name: id.name,
|
||||||
version: id.version,
|
version: Some(id.version),
|
||||||
source: id.source,
|
source: Some(id.source),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1762,11 +1815,17 @@ impl Dependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the TOML representation of this dependency.
|
/// Returns the TOML representation of this dependency.
|
||||||
fn to_toml(&self) -> Table {
|
fn to_toml(&self, dist_count_by_name: &FxHashMap<PackageName, u64>) -> Table {
|
||||||
|
let count = dist_count_by_name
|
||||||
|
.get(&self.distribution_id.name)
|
||||||
|
.copied()
|
||||||
|
.expect("all dependencies have a corresponding distribution");
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
table.insert("name", value(self.distribution_id.name.to_string()));
|
table.insert("name", value(self.distribution_id.name.to_string()));
|
||||||
table.insert("version", value(self.distribution_id.version.to_string()));
|
if count > 1 {
|
||||||
table.insert("source", value(self.distribution_id.source.to_string()));
|
table.insert("version", value(self.distribution_id.version.to_string()));
|
||||||
|
table.insert("source", value(self.distribution_id.source.to_string()));
|
||||||
|
}
|
||||||
if let Some(ref extra) = self.extra {
|
if let Some(ref extra) = self.extra {
|
||||||
table.insert("extra", value(extra.to_string()));
|
table.insert("extra", value(extra.to_string()));
|
||||||
}
|
}
|
||||||
|
@ -1813,9 +1872,12 @@ struct DependencyWire {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DependencyWire {
|
impl DependencyWire {
|
||||||
fn unwire(self) -> Result<Dependency, LockError> {
|
fn unwire(
|
||||||
|
self,
|
||||||
|
unambiguous_dist_ids: &FxHashMap<PackageName, DistributionId>,
|
||||||
|
) -> Result<Dependency, LockError> {
|
||||||
Ok(Dependency {
|
Ok(Dependency {
|
||||||
distribution_id: self.distribution_id.unwire()?,
|
distribution_id: self.distribution_id.unwire(unambiguous_dist_ids)?,
|
||||||
extra: self.extra,
|
extra: self.extra,
|
||||||
marker: self.marker,
|
marker: self.marker,
|
||||||
})
|
})
|
||||||
|
@ -2034,6 +2096,26 @@ enum LockErrorKind {
|
||||||
#[source]
|
#[source]
|
||||||
err: VerbatimUrlError,
|
err: VerbatimUrlError,
|
||||||
},
|
},
|
||||||
|
/// An error that occurs when an ambiguous `distribution.dependency` is
|
||||||
|
/// missing a `version` field.
|
||||||
|
#[error(
|
||||||
|
"dependency {name} has missing `version` \
|
||||||
|
field but has more than one matching distribution"
|
||||||
|
)]
|
||||||
|
MissingDependencyVersion {
|
||||||
|
/// The name of the dependency that is missing a `version` field.
|
||||||
|
name: PackageName,
|
||||||
|
},
|
||||||
|
/// An error that occurs when an ambiguous `distribution.dependency` is
|
||||||
|
/// missing a `source` field.
|
||||||
|
#[error(
|
||||||
|
"dependency {name} has missing `source` \
|
||||||
|
field but has more than one matching distribution"
|
||||||
|
)]
|
||||||
|
MissingDependencySource {
|
||||||
|
/// The name of the dependency that is missing a `source` field.
|
||||||
|
name: PackageName,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error that occurs when a source string could not be parsed.
|
/// An error that occurs when a source string could not be parsed.
|
||||||
|
@ -2092,6 +2174,172 @@ impl std::fmt::Display for HashParseError {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_dependency_source_unambiguous() {
|
||||||
|
let data = r#"
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "b"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution.dependencies]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
"#;
|
||||||
|
let result: Result<Lock, _> = toml::from_str(data);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_dependency_version_unambiguous() {
|
||||||
|
let data = r#"
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "b"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution.dependencies]]
|
||||||
|
name = "a"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
"#;
|
||||||
|
let result: Result<Lock, _> = toml::from_str(data);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_dependency_source_version_unambiguous() {
|
||||||
|
let data = r#"
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "b"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution.dependencies]]
|
||||||
|
name = "a"
|
||||||
|
"#;
|
||||||
|
let result: Result<Lock, _> = toml::from_str(data);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_dependency_source_ambiguous() {
|
||||||
|
let data = r#"
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "b"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution.dependencies]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
"#;
|
||||||
|
let result: Result<Lock, _> = toml::from_str(data);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_dependency_version_ambiguous() {
|
||||||
|
let data = r#"
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "b"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution.dependencies]]
|
||||||
|
name = "a"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
"#;
|
||||||
|
let result: Result<Lock, _> = toml::from_str(data);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_dependency_source_version_ambiguous() {
|
||||||
|
let data = r#"
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "a"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution]]
|
||||||
|
name = "b"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://pypi.org/simple"
|
||||||
|
sdist = { url = "https://example.com", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 0 }
|
||||||
|
|
||||||
|
[[distribution.dependencies]]
|
||||||
|
name = "a"
|
||||||
|
"#;
|
||||||
|
let result: Result<Lock, _> = toml::from_str(data);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hash_required_present() {
|
fn hash_required_present() {
|
||||||
let data = r#"
|
let data = r#"
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
source: crates/uv-resolver/src/lock.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Err(
|
||||||
|
Error {
|
||||||
|
inner: Error {
|
||||||
|
inner: TomlError {
|
||||||
|
message: "dependency a has missing `source` field but has more than one matching distribution",
|
||||||
|
raw: None,
|
||||||
|
keys: [],
|
||||||
|
span: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,210 @@
|
||||||
|
---
|
||||||
|
source: crates/uv-resolver/src/lock.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
Lock {
|
||||||
|
version: 1,
|
||||||
|
distributions: [
|
||||||
|
Distribution {
|
||||||
|
id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
sdist: Some(
|
||||||
|
Url {
|
||||||
|
url: Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"example.com",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
metadata: SourceDistMetadata {
|
||||||
|
hash: Some(
|
||||||
|
Hash(
|
||||||
|
HashDigest {
|
||||||
|
algorithm: Sha256,
|
||||||
|
digest: "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
size: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
wheels: [],
|
||||||
|
dependencies: [],
|
||||||
|
optional_dependencies: {},
|
||||||
|
dev_dependencies: {},
|
||||||
|
},
|
||||||
|
Distribution {
|
||||||
|
id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
sdist: Some(
|
||||||
|
Url {
|
||||||
|
url: Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"example.com",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
metadata: SourceDistMetadata {
|
||||||
|
hash: Some(
|
||||||
|
Hash(
|
||||||
|
HashDigest {
|
||||||
|
algorithm: Sha256,
|
||||||
|
digest: "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
size: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
wheels: [],
|
||||||
|
dependencies: [
|
||||||
|
Dependency {
|
||||||
|
distribution_id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
extra: None,
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
optional_dependencies: {},
|
||||||
|
dev_dependencies: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
requires_python: None,
|
||||||
|
by_id: {
|
||||||
|
DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}: 0,
|
||||||
|
DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
source: crates/uv-resolver/src/lock.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Err(
|
||||||
|
Error {
|
||||||
|
inner: Error {
|
||||||
|
inner: TomlError {
|
||||||
|
message: "dependency a has missing `version` field but has more than one matching distribution",
|
||||||
|
raw: None,
|
||||||
|
keys: [],
|
||||||
|
span: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,210 @@
|
||||||
|
---
|
||||||
|
source: crates/uv-resolver/src/lock.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
Lock {
|
||||||
|
version: 1,
|
||||||
|
distributions: [
|
||||||
|
Distribution {
|
||||||
|
id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
sdist: Some(
|
||||||
|
Url {
|
||||||
|
url: Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"example.com",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
metadata: SourceDistMetadata {
|
||||||
|
hash: Some(
|
||||||
|
Hash(
|
||||||
|
HashDigest {
|
||||||
|
algorithm: Sha256,
|
||||||
|
digest: "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
size: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
wheels: [],
|
||||||
|
dependencies: [],
|
||||||
|
optional_dependencies: {},
|
||||||
|
dev_dependencies: {},
|
||||||
|
},
|
||||||
|
Distribution {
|
||||||
|
id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
sdist: Some(
|
||||||
|
Url {
|
||||||
|
url: Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"example.com",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
metadata: SourceDistMetadata {
|
||||||
|
hash: Some(
|
||||||
|
Hash(
|
||||||
|
HashDigest {
|
||||||
|
algorithm: Sha256,
|
||||||
|
digest: "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
size: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
wheels: [],
|
||||||
|
dependencies: [
|
||||||
|
Dependency {
|
||||||
|
distribution_id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
extra: None,
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
optional_dependencies: {},
|
||||||
|
dev_dependencies: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
requires_python: None,
|
||||||
|
by_id: {
|
||||||
|
DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}: 0,
|
||||||
|
DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
source: crates/uv-resolver/src/lock.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Err(
|
||||||
|
Error {
|
||||||
|
inner: Error {
|
||||||
|
inner: TomlError {
|
||||||
|
message: "dependency a has missing `version` field but has more than one matching distribution",
|
||||||
|
raw: None,
|
||||||
|
keys: [],
|
||||||
|
span: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,210 @@
|
||||||
|
---
|
||||||
|
source: crates/uv-resolver/src/lock.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
Lock {
|
||||||
|
version: 1,
|
||||||
|
distributions: [
|
||||||
|
Distribution {
|
||||||
|
id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
sdist: Some(
|
||||||
|
Url {
|
||||||
|
url: Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"example.com",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
metadata: SourceDistMetadata {
|
||||||
|
hash: Some(
|
||||||
|
Hash(
|
||||||
|
HashDigest {
|
||||||
|
algorithm: Sha256,
|
||||||
|
digest: "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
size: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
wheels: [],
|
||||||
|
dependencies: [],
|
||||||
|
optional_dependencies: {},
|
||||||
|
dev_dependencies: {},
|
||||||
|
},
|
||||||
|
Distribution {
|
||||||
|
id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
sdist: Some(
|
||||||
|
Url {
|
||||||
|
url: Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"example.com",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
metadata: SourceDistMetadata {
|
||||||
|
hash: Some(
|
||||||
|
Hash(
|
||||||
|
HashDigest {
|
||||||
|
algorithm: Sha256,
|
||||||
|
digest: "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
size: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
wheels: [],
|
||||||
|
dependencies: [
|
||||||
|
Dependency {
|
||||||
|
distribution_id: DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
extra: None,
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
optional_dependencies: {},
|
||||||
|
dev_dependencies: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
requires_python: None,
|
||||||
|
by_id: {
|
||||||
|
DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}: 0,
|
||||||
|
DistributionId {
|
||||||
|
name: PackageName(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
version: "0.1.0",
|
||||||
|
source: Registry(
|
||||||
|
Url {
|
||||||
|
scheme: "https",
|
||||||
|
cannot_be_a_base: false,
|
||||||
|
username: "",
|
||||||
|
password: None,
|
||||||
|
host: Some(
|
||||||
|
Domain(
|
||||||
|
"pypi.org",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
port: None,
|
||||||
|
path: "/simple",
|
||||||
|
query: None,
|
||||||
|
fragment: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
Loading…
Add table
Add a link
Reference in a new issue