Skip to main content

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