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 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    /// Like [`Self::requirements`], but without the overrides.
124    pub fn requirements_no_overrides<'a>(
125        &'a self,
126        env: &'a ResolverEnvironment,
127        mode: DependencyMode,
128    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
129        match mode {
130            // Include all direct and transitive requirements, with constraints and overrides applied.
131            DependencyMode::Transitive => Either::Left(
132                self.lookaheads
133                    .iter()
134                    .flat_map(move |lookahead| {
135                        self.overrides
136                            .apply(lookahead.requirements())
137                            .filter(|requirement| !self.excludes.contains(&requirement.name))
138                            .filter(move |requirement| {
139                                requirement
140                                    .evaluate_markers(env.marker_environment(), lookahead.extras())
141                            })
142                    })
143                    .chain(
144                        self.overrides
145                            .apply(&self.requirements)
146                            .filter(|requirement| !self.excludes.contains(&requirement.name))
147                            .filter(move |requirement| {
148                                requirement.evaluate_markers(env.marker_environment(), &[])
149                            }),
150                    )
151                    .chain(
152                        self.constraints
153                            .requirements()
154                            .filter(|requirement| !self.excludes.contains(&requirement.name))
155                            .filter(move |requirement| {
156                                requirement.evaluate_markers(env.marker_environment(), &[])
157                            })
158                            .map(Cow::Borrowed),
159                    ),
160            ),
161            // Include direct requirements, with constraints and overrides applied.
162            DependencyMode::Direct => Either::Right(
163                self.overrides
164                    .apply(&self.requirements)
165                    .chain(self.constraints.requirements().map(Cow::Borrowed))
166                    .filter(|requirement| !self.excludes.contains(&requirement.name))
167                    .filter(move |requirement| {
168                        requirement.evaluate_markers(env.marker_environment(), &[])
169                    }),
170            ),
171        }
172    }
173
174    /// Only the overrides from [`Self::requirements`].
175    pub fn overrides<'a>(
176        &'a self,
177        env: &'a ResolverEnvironment,
178        mode: DependencyMode,
179    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
180        match mode {
181            // Include all direct and transitive requirements, with constraints and overrides applied.
182            DependencyMode::Transitive => Either::Left(
183                self.overrides
184                    .requirements()
185                    .filter(|requirement| !self.excludes.contains(&requirement.name))
186                    .filter(move |requirement| {
187                        requirement.evaluate_markers(env.marker_environment(), &[])
188                    })
189                    .map(Cow::Borrowed),
190            ),
191            // Include direct requirements, with constraints and overrides applied.
192            DependencyMode::Direct => Either::Right(
193                self.overrides
194                    .requirements()
195                    .filter(|requirement| !self.excludes.contains(&requirement.name))
196                    .filter(move |requirement| {
197                        requirement.evaluate_markers(env.marker_environment(), &[])
198                    })
199                    .map(Cow::Borrowed),
200            ),
201        }
202    }
203
204    /// Return an iterator over the names of all user-provided requirements.
205    ///
206    /// This includes:
207    /// - Direct requirements
208    /// - Dependencies of editable requirements
209    /// - Transitive dependencies of local package requirements
210    ///
211    /// At time of writing, this is used for:
212    /// - Determining which packages should use the "lowest-compatible version" of a package, when
213    ///   the `lowest-direct` strategy is in use.
214    pub fn user_requirements<'a>(
215        &'a self,
216        env: &'a ResolverEnvironment,
217        mode: DependencyMode,
218    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
219        match mode {
220            // Include direct requirements, dependencies of editables, and transitive dependencies
221            // of local packages.
222            DependencyMode::Transitive => Either::Left(
223                self.lookaheads
224                    .iter()
225                    .filter(|lookahead| lookahead.direct())
226                    .flat_map(move |lookahead| {
227                        self.overrides
228                            .apply(lookahead.requirements())
229                            .filter(move |requirement| {
230                                requirement
231                                    .evaluate_markers(env.marker_environment(), lookahead.extras())
232                            })
233                    })
234                    .chain(
235                        self.overrides
236                            .apply(&self.requirements)
237                            .filter(move |requirement| {
238                                requirement.evaluate_markers(env.marker_environment(), &[])
239                            }),
240                    ),
241            ),
242
243            // Restrict to the direct requirements.
244            DependencyMode::Direct => {
245                Either::Right(self.overrides.apply(self.requirements.iter()).filter(
246                    move |requirement| requirement.evaluate_markers(env.marker_environment(), &[]),
247                ))
248            }
249        }
250    }
251
252    /// Returns an iterator over the direct requirements, with overrides applied.
253    ///
254    /// At time of writing, this is used for:
255    /// - Determining which packages should have development dependencies included in the
256    ///   resolution (assuming the user enabled development dependencies).
257    pub fn direct_requirements<'a>(
258        &'a self,
259        env: &'a ResolverEnvironment,
260    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
261        self.overrides
262            .apply(self.requirements.iter())
263            .filter(move |requirement| requirement.evaluate_markers(env.marker_environment(), &[]))
264    }
265
266    /// Apply the overrides and constraints to a set of requirements.
267    ///
268    /// Constraints are always applied _on top_ of overrides, such that constraints are applied
269    /// even if a requirement is overridden.
270    pub fn apply<'a>(
271        &'a self,
272        requirements: impl IntoIterator<Item = &'a Requirement>,
273    ) -> impl Iterator<Item = Cow<'a, Requirement>> {
274        self.constraints.apply(self.overrides.apply(requirements))
275    }
276
277    /// Returns the number of input requirements.
278    pub fn num_requirements(&self) -> usize {
279        self.requirements.len()
280    }
281}