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;
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}