1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::sync::Arc;
use futures::{TryStreamExt, stream::FuturesOrdered};
use uv_distribution::{DistributionDatabase, Reporter};
use uv_distribution_types::{Identifier, Requirement};
use uv_resolver::{InMemoryIndex, MetadataResponse};
use uv_types::{BuildContext, HashStrategy};
use crate::{Error, required_dist};
/// A resolver to expand the requested extras for a set of requirements to include all defined
/// extras.
pub struct ExtrasResolver<'a, Context: BuildContext> {
/// Whether to check hashes for distributions.
hasher: &'a HashStrategy,
/// The in-memory index for resolving dependencies.
index: &'a InMemoryIndex,
/// The database for fetching and building distributions.
database: DistributionDatabase<'a, Context>,
}
impl<'a, Context: BuildContext> ExtrasResolver<'a, Context> {
/// Instantiate a new [`ExtrasResolver`] for a given set of requirements.
pub fn new(
hasher: &'a HashStrategy,
index: &'a InMemoryIndex,
database: DistributionDatabase<'a, Context>,
) -> Self {
Self {
hasher,
index,
database,
}
}
/// Set the [`Reporter`] to use for this resolver.
#[must_use]
pub fn with_reporter(self, reporter: Arc<dyn Reporter>) -> Self {
Self {
database: self.database.with_reporter(reporter),
..self
}
}
/// Expand the set of available extras for a given set of requirements.
pub async fn resolve(
self,
requirements: impl Iterator<Item = Requirement>,
) -> Result<Vec<Requirement>, Error> {
let Self {
hasher,
index,
database,
} = self;
requirements
.map(async |requirement| {
Box::pin(Self::resolve_requirement(
requirement,
hasher,
index,
&database,
))
.await
})
.collect::<FuturesOrdered<_>>()
.try_collect()
.await
}
/// Expand the set of available extras for a given [`Requirement`].
async fn resolve_requirement(
requirement: Requirement,
hasher: &HashStrategy,
index: &InMemoryIndex,
database: &DistributionDatabase<'a, Context>,
) -> Result<Requirement, Error> {
// Determine whether the requirement represents a local distribution and convert to a
// buildable distribution.
let Some(dist) = required_dist(&requirement)? else {
return Ok(requirement);
};
// Fetch the metadata for the distribution.
let metadata = {
let id = dist.distribution_id();
if let Some(archive) = index
.distributions()
.get(&id)
.as_deref()
.and_then(|response| {
if let MetadataResponse::Found(archive, ..) = response {
Some(archive)
} else {
None
}
})
{
// If the metadata is already in the index, return it.
archive.metadata.clone()
} else {
// Run the PEP 517 build process to extract metadata from the source distribution.
let archive = database
.get_or_build_wheel_metadata(&dist, hasher.get(&dist))
.await
.map_err(|err| Error::from_dist(dist, err))?;
let metadata = archive.metadata.clone();
// Insert the metadata into the index.
index
.distributions()
.done(id, Arc::new(MetadataResponse::Found(archive)));
metadata
}
};
// Sort extras for consistency.
let extras = {
let mut extras = metadata.provides_extra.to_vec();
extras.sort_unstable();
extras
};
Ok(Requirement {
extras: extras.into_boxed_slice(),
..requirement
})
}
}