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