1use std::collections::BTreeMap;
2
3use uv_auth::CredentialsCache;
4use uv_cache::Cache;
5use uv_configuration::{NoSources, Override, PackageOverride};
6use uv_distribution::{LoweredExtraBuildDependencies, LoweredRequirement, LoweringError};
7use uv_distribution_types::{
8 ExtraBuildRequirement, ExtraBuildRequires, IndexLocations, IndexUrlError,
9};
10use uv_scripts::Pep723ItemRef;
11use uv_workspace::WorkspaceCache;
12use uv_workspace::pyproject::ExtraBuildDependency;
13
14use crate::RequirementsSpecification;
15
16#[derive(Debug, thiserror::Error)]
18pub enum ScriptRequirementsError {
19 #[error(transparent)]
20 Io(#[from] std::io::Error),
21
22 #[error(transparent)]
23 IndexUrl(#[from] IndexUrlError),
24
25 #[error(transparent)]
26 Lowering(#[from] Box<LoweringError>),
27}
28
29impl From<LoweringError> for ScriptRequirementsError {
30 fn from(error: LoweringError) -> Self {
31 Self::Lowering(Box::new(error))
32 }
33}
34
35pub async fn script_specification(
37 script: Pep723ItemRef<'_>,
38 sources: &NoSources,
39 index_locations: &IndexLocations,
40 cache: &Cache,
41 workspace_cache: &WorkspaceCache,
42 credentials_cache: &CredentialsCache,
43) -> Result<Option<RequirementsSpecification>, ScriptRequirementsError> {
44 let Some(dependencies) = script.metadata().dependencies.as_ref() else {
45 return Ok(None);
46 };
47
48 let script_dir = script.directory()?;
49 let script_indexes = script
50 .indexes(sources)
51 .iter()
52 .cloned()
53 .map(|index| index.relative_to(&script_dir))
54 .collect::<Result<Vec<_>, _>>()?;
55 let script_sources = script.sources(sources);
56
57 let mut requirements = Vec::new();
58 for requirement in dependencies.iter().cloned() {
59 requirements.extend(
60 LoweredRequirement::from_non_workspace_requirement(
61 requirement,
62 script_dir.as_ref(),
63 script_sources.as_ref(),
64 &script_indexes,
65 index_locations,
66 cache,
67 workspace_cache,
68 credentials_cache,
69 )
70 .await
71 .map(|requirement| requirement.map(LoweredRequirement::into_inner))
72 .collect::<Result<Vec<_>, _>>()?,
73 );
74 }
75 let constraint_dependencies = script
76 .metadata()
77 .tool
78 .as_ref()
79 .and_then(|tool| tool.uv.as_ref())
80 .and_then(|uv| uv.constraint_dependencies.as_ref())
81 .into_iter()
82 .flatten()
83 .cloned();
84 let mut constraints = Vec::new();
85 for requirement in constraint_dependencies {
86 constraints.extend(
87 LoweredRequirement::from_non_workspace_requirement(
88 requirement,
89 script_dir.as_ref(),
90 script_sources.as_ref(),
91 &script_indexes,
92 index_locations,
93 cache,
94 workspace_cache,
95 credentials_cache,
96 )
97 .await
98 .map(|requirement| requirement.map(LoweredRequirement::into_inner))
99 .collect::<Result<Vec<_>, _>>()?,
100 );
101 }
102 let overrides = {
103 let override_entries = script
104 .metadata()
105 .tool
106 .as_ref()
107 .and_then(|tool| tool.uv.as_ref())
108 .and_then(|uv| uv.override_dependencies.as_ref())
109 .into_iter()
110 .flatten()
111 .cloned();
112 let mut overrides = Vec::new();
113 for entry in override_entries {
114 match entry {
115 Override::Requirement(requirement) => {
116 overrides.extend(
117 LoweredRequirement::from_non_workspace_requirement(
118 requirement,
119 script_dir.as_ref(),
120 script_sources.as_ref(),
121 &script_indexes,
122 index_locations,
123 cache,
124 workspace_cache,
125 credentials_cache,
126 )
127 .await
128 .map(|requirement| {
129 requirement
130 .map(|requirement| Override::Requirement(requirement.into_inner()))
131 })
132 .collect::<Result<Vec<_>, _>>()?,
133 );
134 }
135 Override::Package(package) => {
136 let mut dependencies = Vec::new();
137 for requirement in package.dependencies.into_vec() {
138 dependencies.extend(
139 LoweredRequirement::from_non_workspace_requirement(
140 requirement,
141 script_dir.as_ref(),
142 script_sources.as_ref(),
143 &script_indexes,
144 index_locations,
145 cache,
146 workspace_cache,
147 credentials_cache,
148 )
149 .await
150 .map(|requirement| requirement.map(LoweredRequirement::into_inner))
151 .collect::<Result<Vec<_>, _>>()?,
152 );
153 }
154 overrides.push(Override::Package(PackageOverride {
155 package: package.package,
156 dependencies: dependencies.into_boxed_slice(),
157 }));
158 }
159 }
160 }
161 overrides
162 };
163 let excludes = script
164 .metadata()
165 .tool
166 .as_ref()
167 .and_then(|tool| tool.uv.as_ref())
168 .and_then(|uv| uv.exclude_dependencies.as_ref())
169 .into_iter()
170 .flatten()
171 .cloned()
172 .collect::<Vec<_>>();
173
174 let mut specification =
175 RequirementsSpecification::from_excludes(requirements, constraints, Vec::new(), Vec::new());
176 specification.override_dependencies = overrides;
177 specification.excludes = excludes;
178 Ok(Some(specification))
179}
180
181pub async fn script_extra_build_requires(
183 script: Pep723ItemRef<'_>,
184 sources: &NoSources,
185 index_locations: &IndexLocations,
186 cache: &Cache,
187 workspace_cache: &WorkspaceCache,
188 credentials_cache: &CredentialsCache,
189) -> Result<LoweredExtraBuildDependencies, ScriptRequirementsError> {
190 let script_dir = script.directory()?;
191 let script_indexes = script
192 .indexes(sources)
193 .iter()
194 .cloned()
195 .map(|index| index.relative_to(&script_dir))
196 .collect::<Result<Vec<_>, _>>()?;
197 let script_sources = script.sources(sources);
198
199 let empty = BTreeMap::default();
201 let script_extra_build_dependencies = script
202 .metadata()
203 .tool
204 .as_ref()
205 .and_then(|tool| tool.uv.as_ref())
206 .and_then(|uv| uv.extra_build_dependencies.as_ref())
207 .unwrap_or(&empty);
208
209 let mut extra_build_requires = ExtraBuildRequires::default();
211 for (name, requirements) in script_extra_build_dependencies {
212 let mut lowered_requirements = Vec::new();
213 for ExtraBuildDependency {
214 requirement,
215 match_runtime,
216 } in requirements.iter().cloned()
217 {
218 lowered_requirements.extend(
219 LoweredRequirement::from_non_workspace_requirement(
220 requirement,
221 script_dir.as_ref(),
222 script_sources.as_ref(),
223 &script_indexes,
224 index_locations,
225 cache,
226 workspace_cache,
227 credentials_cache,
228 )
229 .await
230 .map(|requirement| {
231 requirement.map(|requirement| ExtraBuildRequirement {
232 requirement: requirement.into_inner(),
233 match_runtime,
234 })
235 })
236 .collect::<Result<Vec<_>, _>>()?,
237 );
238 }
239 extra_build_requires.insert(name.clone(), lowered_requirements);
240 }
241
242 Ok(LoweredExtraBuildDependencies::from_lowered(
243 extra_build_requires,
244 ))
245}