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, DependencyModifierScope, DependencyModifiers};
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 dependency modifiers for the project.
27    pub(super) modifiers: DependencyModifiers,
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    /// Record which settings are consulted during resolution, excluding package selection setup.
58    #[must_use]
59    pub fn with_recorder(mut self, recorder: Option<ResolutionRecorder>) -> Self {
60        self.recorder = recorder;
61        self
62    }
63
64    pub fn new(
65        requirements: Vec<Requirement>,
66        constraints: Constraints,
67        modifiers: DependencyModifiers,
68        preferences: Preferences,
69        project: Option<PackageName>,
70        workspace_members: BTreeSet<PackageName>,
71        exclusions: Exclusions,
72        lookaheads: Vec<RequestedRequirements>,
73    ) -> Self {
74        Self {
75            recorder: None,
76            requirements,
77            constraints,
78            modifiers,
79            preferences,
80            project,
81            workspace_members,
82            exclusions,
83            lookaheads: Vec::new(),
84        }
85        .with_lookaheads(lookaheads)
86    }
87
88    pub fn simple(requirements: Vec<Requirement>) -> Self {
89        Self {
90            recorder: None,
91            requirements,
92            constraints: Constraints::default(),
93            modifiers: DependencyModifiers::default(),
94            preferences: Preferences::default(),
95            project: None,
96            exclusions: Exclusions::default(),
97            workspace_members: BTreeSet::new(),
98            lookaheads: Vec::new(),
99        }
100    }
101
102    #[must_use]
103    pub fn with_constraints(mut self, constraints: Constraints) -> Self {
104        self.constraints = constraints;
105        self
106    }
107
108    #[must_use]
109    pub fn with_lookaheads(mut self, mut lookaheads: Vec<RequestedRequirements>) -> Self {
110        // Package metadata defaults to forced-relative paths. Restore the user's path preference for
111        // the current project and workspace members before merging requirement URLs.
112        for lookahead in &mut lookaheads {
113            if self.workspace_members.contains(lookahead.package())
114                || self.project.as_ref() == Some(lookahead.package())
115            {
116                for requirement in lookahead.requirements_mut() {
117                    requirement.set_force_relative(false);
118                }
119            }
120        }
121        self.lookaheads = lookaheads;
122        self
123    }
124
125    /// Return an iterator over all requirements, constraints, and overrides, in priority order,
126    /// such that requirements come first, followed by constraints, followed by overrides.
127    ///
128    /// At time of writing, this is used for:
129    /// - Determining which requirements should allow yanked versions.
130    /// - Determining which requirements should allow pre-release versions (e.g., `torch>=2.2.0a1`).
131    /// - Determining which requirements should allow direct URLs (e.g., `torch @ https://...`).
132    pub(crate) fn requirements<'a>(
133        &'a self,
134        env: &'a ResolverEnvironment,
135        mode: DependencyMode,
136    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
137        self.requirements_no_overrides(env, mode)
138            .chain(self.overrides(env))
139    }
140
141    /// Return all requirements that affect manifest-wide candidate selection policy.
142    ///
143    /// Scoped overrides are included even when their scope is not selected. Whether a scoped
144    /// override applies is only known during resolution, after yanked-version policy has already
145    /// been initialized.
146    pub(crate) fn candidate_selection_requirements<'a>(
147        &'a self,
148        env: &'a ResolverEnvironment,
149        mode: DependencyMode,
150    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
151        self.requirements(env, mode).chain(
152            self.modifiers
153                .scoped_overrides()
154                .map(Cow::Borrowed)
155                .filter(move |requirement| {
156                    requirement.evaluate_markers(env.marker_environment(), &[])
157                }),
158        )
159    }
160
161    /// Like [`Self::requirements`], but without the overrides.
162    pub(crate) fn requirements_no_overrides<'a>(
163        &'a self,
164        env: &'a ResolverEnvironment,
165        mode: DependencyMode,
166    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
167        match mode {
168            // Include all direct and transitive requirements, with constraints and overrides applied.
169            DependencyMode::Transitive => Either::Left(
170                self.lookaheads
171                    .iter()
172                    .flat_map(move |lookahead| {
173                        self.modifiers
174                            .apply(
175                                DependencyModifierScope::Package(
176                                    lookahead.package(),
177                                    lookahead.version(),
178                                ),
179                                lookahead.requirements(),
180                            )
181                            .filter(move |requirement| {
182                                requirement
183                                    .evaluate_markers(env.marker_environment(), lookahead.extras())
184                            })
185                    })
186                    .chain(
187                        self.modifiers
188                            .apply(DependencyModifierScope::Global, &self.requirements)
189                            .filter(move |requirement| {
190                                requirement.evaluate_markers(env.marker_environment(), &[])
191                            }),
192                    )
193                    .chain(
194                        self.constraints
195                            .requirements()
196                            .filter(|requirement| !self.modifiers.is_excluded(&requirement.name))
197                            .filter(move |requirement| {
198                                requirement.evaluate_markers(env.marker_environment(), &[])
199                            })
200                            .map(Cow::Borrowed),
201                    ),
202            ),
203            // Include direct requirements, with constraints and overrides applied.
204            DependencyMode::Direct => Either::Right(
205                self.modifiers
206                    .apply(DependencyModifierScope::Global, &self.requirements)
207                    .chain(self.constraints.requirements().map(Cow::Borrowed))
208                    .filter(|requirement| !self.modifiers.is_excluded(&requirement.name))
209                    .filter(move |requirement| {
210                        requirement.evaluate_markers(env.marker_environment(), &[])
211                    }),
212            ),
213        }
214    }
215
216    /// Only the overrides from [`Self::requirements`].
217    pub(crate) fn overrides<'a>(
218        &'a self,
219        env: &'a ResolverEnvironment,
220    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
221        self.modifiers
222            .global_overrides()
223            .filter(move |requirement| requirement.evaluate_markers(env.marker_environment(), &[]))
224            .map(Cow::Borrowed)
225    }
226
227    /// Return an iterator over the names of all user-provided requirements.
228    ///
229    /// This includes:
230    /// - Direct requirements
231    /// - Dependencies of editable requirements
232    /// - Transitive dependencies of local package requirements
233    ///
234    /// At time of writing, this is used for:
235    /// - Determining which packages should use the "lowest-compatible version" of a package, when
236    ///   the `lowest-direct` strategy is in use.
237    pub(crate) fn user_requirements<'a>(
238        &'a self,
239        env: &'a ResolverEnvironment,
240        mode: DependencyMode,
241    ) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
242        match mode {
243            // Include direct requirements, dependencies of editables, and transitive dependencies
244            // of local packages.
245            DependencyMode::Transitive => Either::Left(
246                self.lookaheads
247                    .iter()
248                    .filter(|lookahead| lookahead.direct())
249                    .flat_map(move |lookahead| {
250                        self.modifiers
251                            .apply(
252                                DependencyModifierScope::Package(
253                                    lookahead.package(),
254                                    lookahead.version(),
255                                ),
256                                lookahead.requirements(),
257                            )
258                            .filter(move |requirement| {
259                                requirement
260                                    .evaluate_markers(env.marker_environment(), lookahead.extras())
261                            })
262                    })
263                    .chain(
264                        self.modifiers
265                            .apply(DependencyModifierScope::Global, &self.requirements)
266                            .filter(move |requirement| {
267                                requirement.evaluate_markers(env.marker_environment(), &[])
268                            }),
269                    ),
270            ),
271
272            // Restrict to the direct requirements.
273            DependencyMode::Direct => Either::Right(
274                self.modifiers
275                    .apply(DependencyModifierScope::Global, self.requirements.iter())
276                    .filter(move |requirement| {
277                        requirement.evaluate_markers(env.marker_environment(), &[])
278                    }),
279            ),
280        }
281    }
282
283    /// Returns the number of input requirements.
284    pub fn num_requirements(&self) -> usize {
285        self.requirements.len()
286    }
287}