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(super) requirements: Vec<Requirement>,
19
20 /// The constraints for the project.
21 pub(super) constraints: Constraints,
22
23 /// The overrides for the project.
24 pub(super) overrides: Overrides,
25
26 /// The dependency excludes for the project.
27 pub(super) 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(super) preferences: Preferences,
35
36 /// The name of the project.
37 pub(super) project: Option<PackageName>,
38
39 /// Members of the project's workspace.
40 pub(super) 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(super) 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(super) 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: Vec::new(),
78 }
79 .with_lookaheads(lookaheads)
80 }
81
82 pub fn simple(requirements: Vec<Requirement>) -> Self {
83 Self {
84 requirements,
85 constraints: Constraints::default(),
86 overrides: Overrides::default(),
87 excludes: Excludes::default(),
88 preferences: Preferences::default(),
89 project: None,
90 exclusions: Exclusions::default(),
91 workspace_members: BTreeSet::new(),
92 lookaheads: Vec::new(),
93 }
94 }
95
96 #[must_use]
97 pub fn with_constraints(mut self, constraints: Constraints) -> Self {
98 self.constraints = constraints;
99 self
100 }
101
102 #[must_use]
103 pub fn with_lookaheads(mut self, mut lookaheads: Vec<RequestedRequirements>) -> Self {
104 // Package metadata defaults to forced-relative paths. Restore the user's path preference for
105 // the current project and workspace members before merging requirement URLs.
106 for lookahead in &mut lookaheads {
107 if self.workspace_members.contains(lookahead.package())
108 || self.project.as_ref() == Some(lookahead.package())
109 {
110 for requirement in lookahead.requirements_mut() {
111 requirement.set_force_relative(false);
112 }
113 }
114 }
115 self.lookaheads = lookaheads;
116 self
117 }
118
119 /// Return an iterator over all requirements, constraints, and overrides, in priority order,
120 /// such that requirements come first, followed by constraints, followed by overrides.
121 ///
122 /// At time of writing, this is used for:
123 /// - Determining which requirements should allow yanked versions.
124 /// - Determining which requirements should allow pre-release versions (e.g., `torch>=2.2.0a1`).
125 /// - Determining which requirements should allow direct URLs (e.g., `torch @ https://...`).
126 pub(crate) fn requirements<'a>(
127 &'a self,
128 env: &'a ResolverEnvironment,
129 mode: DependencyMode,
130 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
131 self.requirements_no_overrides(env, mode)
132 .chain(self.overrides(env, mode))
133 }
134
135 /// Return all requirements that affect manifest-wide candidate selection policy.
136 ///
137 /// Scoped overrides are included even when their scope is not selected. Whether a scoped
138 /// override applies is only known during resolution, after yanked-version policy has already
139 /// been initialized.
140 pub(crate) fn candidate_selection_requirements<'a>(
141 &'a self,
142 env: &'a ResolverEnvironment,
143 mode: DependencyMode,
144 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
145 self.requirements(env, mode).chain(
146 self.overrides
147 .scoped_requirements()
148 .filter(|(package, version, requirement)| {
149 !self.excludes.contains_for_scope(
150 &self.overrides,
151 package,
152 *version,
153 &requirement.name,
154 )
155 })
156 .map(|(_, _, requirement)| Cow::Borrowed(requirement))
157 .filter(move |requirement| {
158 requirement.evaluate_markers(env.marker_environment(), &[])
159 }),
160 )
161 }
162
163 /// Like [`Self::requirements`], but without the overrides.
164 pub(crate) fn requirements_no_overrides<'a>(
165 &'a self,
166 env: &'a ResolverEnvironment,
167 mode: DependencyMode,
168 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
169 match mode {
170 // Include all direct and transitive requirements, with constraints and overrides applied.
171 DependencyMode::Transitive => Either::Left(
172 self.lookaheads
173 .iter()
174 .flat_map(move |lookahead| {
175 self.overrides
176 .apply_for(
177 lookahead.package(),
178 lookahead.version(),
179 lookahead.requirements(),
180 )
181 .filter(|requirement| {
182 !self.excludes.contains_for(
183 lookahead.package(),
184 lookahead.version(),
185 &requirement.name,
186 )
187 })
188 .filter(move |requirement| {
189 requirement
190 .evaluate_markers(env.marker_environment(), lookahead.extras())
191 })
192 })
193 .chain(
194 self.overrides
195 .apply(&self.requirements)
196 .filter(|requirement| !self.excludes.contains(&requirement.name))
197 .filter(move |requirement| {
198 requirement.evaluate_markers(env.marker_environment(), &[])
199 }),
200 )
201 .chain(
202 self.constraints
203 .requirements()
204 .filter(|requirement| !self.excludes.contains(&requirement.name))
205 .filter(move |requirement| {
206 requirement.evaluate_markers(env.marker_environment(), &[])
207 })
208 .map(Cow::Borrowed),
209 ),
210 ),
211 // Include direct requirements, with constraints and overrides applied.
212 DependencyMode::Direct => Either::Right(
213 self.overrides
214 .apply(&self.requirements)
215 .chain(self.constraints.requirements().map(Cow::Borrowed))
216 .filter(|requirement| !self.excludes.contains(&requirement.name))
217 .filter(move |requirement| {
218 requirement.evaluate_markers(env.marker_environment(), &[])
219 }),
220 ),
221 }
222 }
223
224 /// Only the overrides from [`Self::requirements`].
225 pub(crate) fn overrides<'a>(
226 &'a self,
227 env: &'a ResolverEnvironment,
228 mode: DependencyMode,
229 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
230 match mode {
231 // Include all direct and transitive requirements, with constraints and overrides applied.
232 DependencyMode::Transitive => Either::Left(
233 self.overrides
234 .global_requirements()
235 .filter(|requirement| !self.excludes.contains(&requirement.name))
236 .filter(move |requirement| {
237 requirement.evaluate_markers(env.marker_environment(), &[])
238 })
239 .map(Cow::Borrowed),
240 ),
241 // Include direct requirements, with constraints and overrides applied.
242 DependencyMode::Direct => Either::Right(
243 self.overrides
244 .global_requirements()
245 .filter(|requirement| !self.excludes.contains(&requirement.name))
246 .filter(move |requirement| {
247 requirement.evaluate_markers(env.marker_environment(), &[])
248 })
249 .map(Cow::Borrowed),
250 ),
251 }
252 }
253
254 /// Return an iterator over the names of all user-provided requirements.
255 ///
256 /// This includes:
257 /// - Direct requirements
258 /// - Dependencies of editable requirements
259 /// - Transitive dependencies of local package requirements
260 ///
261 /// At time of writing, this is used for:
262 /// - Determining which packages should use the "lowest-compatible version" of a package, when
263 /// the `lowest-direct` strategy is in use.
264 pub(crate) fn user_requirements<'a>(
265 &'a self,
266 env: &'a ResolverEnvironment,
267 mode: DependencyMode,
268 ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
269 match mode {
270 // Include direct requirements, dependencies of editables, and transitive dependencies
271 // of local packages.
272 DependencyMode::Transitive => Either::Left(
273 self.lookaheads
274 .iter()
275 .filter(|lookahead| lookahead.direct())
276 .flat_map(move |lookahead| {
277 self.overrides
278 .apply_for(
279 lookahead.package(),
280 lookahead.version(),
281 lookahead.requirements(),
282 )
283 .filter(|requirement| {
284 !self.excludes.contains_for(
285 lookahead.package(),
286 lookahead.version(),
287 &requirement.name,
288 )
289 })
290 .filter(move |requirement| {
291 requirement
292 .evaluate_markers(env.marker_environment(), lookahead.extras())
293 })
294 })
295 .chain(
296 self.overrides
297 .apply(&self.requirements)
298 .filter(move |requirement| {
299 requirement.evaluate_markers(env.marker_environment(), &[])
300 }),
301 ),
302 ),
303
304 // Restrict to the direct requirements.
305 DependencyMode::Direct => {
306 Either::Right(self.overrides.apply(self.requirements.iter()).filter(
307 move |requirement| requirement.evaluate_markers(env.marker_environment(), &[]),
308 ))
309 }
310 }
311 }
312
313 /// Returns the number of input requirements.
314 pub fn num_requirements(&self) -> usize {
315 self.requirements.len()
316 }
317}