uv_resolver/manifest.rs
1use std::borrow::Cow;
2use std::collections::BTreeSet;
3
4use either::Either;
5
6use uv_configuration::{Constraints, Excludes, Overrides};
7use uv_distribution_types::Requirement;
8use uv_normalize::PackageName;
9use uv_types::RequestedRequirements;
10
11use crate::preferences::Preferences;
12use crate::{DependencyMode, Exclusions, ResolverEnvironment};
13
14/// A manifest of requirements, constraints, and preferences.
15#[derive(Clone, Debug)]
16pub struct Manifest {
17 /// The direct requirements for the project.
18 pub(crate) requirements: Vec<Requirement>,
19
20 /// The constraints for the project.
21 pub(crate) constraints: Constraints,
22
23 /// The overrides for the project.
24 pub(crate) overrides: Overrides,
25
26 /// The dependency excludes for the project.
27 pub(crate) excludes: Excludes,
28
29 /// The preferences for the project.
30 ///
31 /// These represent "preferred" versions of a given package. For example, they may be the
32 /// versions that are already installed in the environment, or already pinned in an existing
33 /// lockfile.
34 pub(crate) preferences: Preferences,
35
36 /// The name of the project.
37 pub(crate) project: Option<PackageName>,
38
39 /// Members of the project's workspace.
40 pub(crate) workspace_members: BTreeSet<PackageName>,
41
42 /// The installed packages to exclude from consideration during resolution.
43 ///
44 /// These typically represent packages that are being upgraded or reinstalled
45 /// and should be pulled from a remote source like a package index.
46 pub(crate) exclusions: Exclusions,
47
48 /// The lookahead requirements for the project.
49 ///
50 /// These represent transitive dependencies that should be incorporated when making
51 /// determinations around "allowed" versions (for example, "allowed" URLs or "allowed"
52 /// pre-release versions).
53 pub(crate) lookaheads: Vec<RequestedRequirements>,
54}
55
56impl Manifest {
57 pub fn new(
58 requirements: Vec<Requirement>,
59 constraints: Constraints,
60 overrides: Overrides,
61 excludes: Excludes,
62 preferences: Preferences,
63 project: Option<PackageName>,
64 workspace_members: BTreeSet<PackageName>,
65 exclusions: Exclusions,
66 lookaheads: Vec<RequestedRequirements>,
67 ) -> Self {
68 Self {
69 requirements,
70 constraints,
71 overrides,
72 excludes,
73 preferences,
74 project,
75 workspace_members,
76 exclusions,
77 lookaheads,
78 }
79 }
80
81 pub fn simple(requirements: Vec<Requirement>) -> Self {
82 Self {
83 requirements,
84 constraints: Constraints::default(),
85 overrides: Overrides::default(),
86 excludes: Excludes::default(),
87 preferences: Preferences::default(),
88 project: None,
89 exclusions: Exclusions::default(),
90 workspace_members: BTreeSet::new(),
91 lookaheads: Vec::new(),
92 }
93 }
94
95 #[must_use]
96 pub fn with_constraints(mut self, constraints: Constraints) -> Self {
97 self.constraints = constraints;
98 self
99 }
100
101 #[must_use]
102 pub fn with_lookaheads(mut self, lookaheads: Vec<RequestedRequirements>) -> Self {
103 self.lookaheads = lookaheads;
104 self
105 }
106
107 /// Return an iterator over all requirements, constraints, and overrides, in priority order,
108 /// such that requirements come first, followed by constraints, followed by overrides.
109 ///
110 /// At time of writing, this is used for:
111 /// - Determining which requirements should allow yanked versions.
112 /// - Determining which requirements should allow pre-release versions (e.g., `torch>=2.2.0a1`).
113 /// - Determining which requirements should allow direct URLs (e.g., `torch @ https://...`).
114 pub(crate) fn requirements<'a>(
115 &'a self,
116 env: &'a ResolverEnvironment,
117 mode: DependencyMode,
118 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
119 self.requirements_no_overrides(env, mode)
120 .chain(self.overrides(env, mode))
121 }
122
123 /// Return all requirements that affect manifest-wide candidate selection policy.
124 ///
125 /// Scoped overrides are included even when their scope is not selected. Whether a scoped
126 /// override applies is only known during resolution, after pre-release and yanked-version
127 /// policy has already been initialized.
128 pub(crate) fn candidate_selection_requirements<'a>(
129 &'a self,
130 env: &'a ResolverEnvironment,
131 mode: DependencyMode,
132 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
133 self.requirements(env, mode).chain(
134 self.overrides
135 .scoped_requirements()
136 .filter(|(package, version, requirement)| {
137 !self.excludes.contains_for_scope(
138 &self.overrides,
139 package,
140 *version,
141 &requirement.name,
142 )
143 })
144 .map(|(_, _, requirement)| Cow::Borrowed(requirement))
145 .filter(move |requirement| {
146 requirement.evaluate_markers(env.marker_environment(), &[])
147 }),
148 )
149 }
150
151 /// Like [`Self::requirements`], but without the overrides.
152 pub(crate) fn requirements_no_overrides<'a>(
153 &'a self,
154 env: &'a ResolverEnvironment,
155 mode: DependencyMode,
156 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
157 match mode {
158 // Include all direct and transitive requirements, with constraints and overrides applied.
159 DependencyMode::Transitive => Either::Left(
160 self.lookaheads
161 .iter()
162 .flat_map(move |lookahead| {
163 self.overrides
164 .apply_for(
165 lookahead.package(),
166 lookahead.version(),
167 lookahead.requirements(),
168 )
169 .filter(|requirement| {
170 !self.excludes.contains_for(
171 lookahead.package(),
172 lookahead.version(),
173 &requirement.name,
174 )
175 })
176 .filter(move |requirement| {
177 requirement
178 .evaluate_markers(env.marker_environment(), lookahead.extras())
179 })
180 })
181 .chain(
182 self.overrides
183 .apply(&self.requirements)
184 .filter(|requirement| !self.excludes.contains(&requirement.name))
185 .filter(move |requirement| {
186 requirement.evaluate_markers(env.marker_environment(), &[])
187 }),
188 )
189 .chain(
190 self.constraints
191 .requirements()
192 .filter(|requirement| !self.excludes.contains(&requirement.name))
193 .filter(move |requirement| {
194 requirement.evaluate_markers(env.marker_environment(), &[])
195 })
196 .map(Cow::Borrowed),
197 ),
198 ),
199 // Include direct requirements, with constraints and overrides applied.
200 DependencyMode::Direct => Either::Right(
201 self.overrides
202 .apply(&self.requirements)
203 .chain(self.constraints.requirements().map(Cow::Borrowed))
204 .filter(|requirement| !self.excludes.contains(&requirement.name))
205 .filter(move |requirement| {
206 requirement.evaluate_markers(env.marker_environment(), &[])
207 }),
208 ),
209 }
210 }
211
212 /// Only the overrides from [`Self::requirements`].
213 pub(crate) fn overrides<'a>(
214 &'a self,
215 env: &'a ResolverEnvironment,
216 mode: DependencyMode,
217 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
218 match mode {
219 // Include all direct and transitive requirements, with constraints and overrides applied.
220 DependencyMode::Transitive => Either::Left(
221 self.overrides
222 .global_requirements()
223 .filter(|requirement| !self.excludes.contains(&requirement.name))
224 .filter(move |requirement| {
225 requirement.evaluate_markers(env.marker_environment(), &[])
226 })
227 .map(Cow::Borrowed),
228 ),
229 // Include direct requirements, with constraints and overrides applied.
230 DependencyMode::Direct => Either::Right(
231 self.overrides
232 .global_requirements()
233 .filter(|requirement| !self.excludes.contains(&requirement.name))
234 .filter(move |requirement| {
235 requirement.evaluate_markers(env.marker_environment(), &[])
236 })
237 .map(Cow::Borrowed),
238 ),
239 }
240 }
241
242 /// Return an iterator over the names of all user-provided requirements.
243 ///
244 /// This includes:
245 /// - Direct requirements
246 /// - Dependencies of editable requirements
247 /// - Transitive dependencies of local package requirements
248 ///
249 /// At time of writing, this is used for:
250 /// - Determining which packages should use the "lowest-compatible version" of a package, when
251 /// the `lowest-direct` strategy is in use.
252 pub(crate) fn user_requirements<'a>(
253 &'a self,
254 env: &'a ResolverEnvironment,
255 mode: DependencyMode,
256 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
257 match mode {
258 // Include direct requirements, dependencies of editables, and transitive dependencies
259 // of local packages.
260 DependencyMode::Transitive => Either::Left(
261 self.lookaheads
262 .iter()
263 .filter(|lookahead| lookahead.direct())
264 .flat_map(move |lookahead| {
265 self.overrides
266 .apply_for(
267 lookahead.package(),
268 lookahead.version(),
269 lookahead.requirements(),
270 )
271 .filter(|requirement| {
272 !self.excludes.contains_for(
273 lookahead.package(),
274 lookahead.version(),
275 &requirement.name,
276 )
277 })
278 .filter(move |requirement| {
279 requirement
280 .evaluate_markers(env.marker_environment(), lookahead.extras())
281 })
282 })
283 .chain(
284 self.overrides
285 .apply(&self.requirements)
286 .filter(move |requirement| {
287 requirement.evaluate_markers(env.marker_environment(), &[])
288 }),
289 ),
290 ),
291
292 // Restrict to the direct requirements.
293 DependencyMode::Direct => {
294 Either::Right(self.overrides.apply(self.requirements.iter()).filter(
295 move |requirement| requirement.evaluate_markers(env.marker_environment(), &[]),
296 ))
297 }
298 }
299 }
300
301 /// Returns the number of input requirements.
302 pub fn num_requirements(&self) -> usize {
303 self.requirements.len()
304 }
305}