uv/crates/puffin-resolver/src/overrides.rs
Charlie Marsh 69581c03c3
Enable package overrides in pip-compile (#631)
## Summary

This PR enables overrides to be passed to `pip-compile` and
`pip-install` via a new `--overrides` flag.

When overrides are provided, we effectively replace any requirements
that are overridden with the overridden versions. This is applied at all
depths of the tree.

The merge semantics are such that we replace _all_ requirements of a
package with _all_ requirements from the overrides files. So, for
example, if a package declares:

```
foo >= 1.0; python_version < '3.11'
foo < 1.0; python_version >= '3.11'
```

And the user provides an override like:
```
foo >= 2.0
```

Then _both_ of the `foo` requirements in the package will be replaced
with the override.

If instead, the user provided an override like:
```
foo >= 2.0; python_version < '3.11'
foo < 3.0; python_version >= '3.11'
```

Then we'd replace _both_ of the original `foo` requirements with both of
these overrides. (In technical terms, for each package in the
requirements file, we flat-map over its overrides.)

Closes https://github.com/astral-sh/puffin/issues/511.
2023-12-13 15:03:38 +00:00

45 lines
1.4 KiB
Rust

use itertools::Either;
use std::hash::BuildHasherDefault;
use rustc_hash::FxHashMap;
use pep508_rs::Requirement;
use puffin_normalize::PackageName;
/// A set of overrides for a set of requirements.
#[derive(Debug, Default, Clone)]
pub(crate) struct Overrides(FxHashMap<PackageName, Vec<Requirement>>);
impl Overrides {
/// Create a new set of overrides from a set of requirements.
pub(crate) fn from_requirements(requirements: Vec<Requirement>) -> Self {
let mut overrides: FxHashMap<PackageName, Vec<Requirement>> =
FxHashMap::with_capacity_and_hasher(requirements.len(), BuildHasherDefault::default());
for requirement in requirements {
overrides
.entry(requirement.name.clone())
.or_default()
.push(requirement);
}
Self(overrides)
}
/// Get the overrides for a package.
pub(crate) fn get(&self, name: &PackageName) -> Option<&Vec<Requirement>> {
self.0.get(name)
}
/// Apply the overrides to a set of requirements.
pub(crate) fn apply<'a>(
&'a self,
requirements: &'a [Requirement],
) -> impl Iterator<Item = &Requirement> {
requirements.iter().flat_map(|requirement| {
if let Some(overrides) = self.get(&requirement.name) {
Either::Left(overrides.iter())
} else {
Either::Right(std::iter::once(requirement))
}
})
}
}