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