1
1
use std:: collections:: BTreeMap ;
2
2
use std:: path:: { Path , PathBuf } ;
3
3
4
+ use itertools:: Either ;
5
+
4
6
use uv_configuration:: { LowerBound , SourceStrategy } ;
7
+ use uv_distribution:: LoweredRequirement ;
5
8
use uv_distribution_types:: IndexLocations ;
6
9
use uv_normalize:: { GroupName , PackageName } ;
7
10
use uv_pep508:: RequirementOrigin ;
8
11
use uv_pypi_types:: { Conflicts , Requirement , SupportedEnvironments , VerbatimParsedUrl } ;
9
12
use uv_resolver:: { Lock , LockVersion , RequiresPython , VERSION } ;
13
+ use uv_scripts:: Pep723Script ;
10
14
use uv_workspace:: dependency_groups:: DependencyGroupError ;
11
15
use uv_workspace:: { Workspace , WorkspaceMember } ;
12
16
@@ -16,6 +20,7 @@ use crate::commands::project::{find_requires_python, ProjectError};
16
20
#[ derive( Debug , Copy , Clone ) ]
17
21
pub ( crate ) enum LockTarget < ' lock > {
18
22
Workspace ( & ' lock Workspace ) ,
23
+ Script ( & ' lock Pep723Script ) ,
19
24
}
20
25
21
26
impl < ' lock > From < & ' lock Workspace > for LockTarget < ' lock > {
@@ -24,26 +29,53 @@ impl<'lock> From<&'lock Workspace> for LockTarget<'lock> {
24
29
}
25
30
}
26
31
32
+ impl < ' lock > From < & ' lock Pep723Script > for LockTarget < ' lock > {
33
+ fn from ( script : & ' lock Pep723Script ) -> Self {
34
+ LockTarget :: Script ( script)
35
+ }
36
+ }
37
+
27
38
impl < ' lock > LockTarget < ' lock > {
28
39
/// Return the set of requirements that are attached to the target directly, as opposed to being
29
40
/// attached to any members within the target.
30
41
pub ( crate ) fn requirements ( self ) -> Vec < uv_pep508:: Requirement < VerbatimParsedUrl > > {
31
42
match self {
32
43
Self :: Workspace ( workspace) => workspace. requirements ( ) ,
44
+ Self :: Script ( script) => script. metadata . dependencies . clone ( ) . unwrap_or_default ( ) ,
33
45
}
34
46
}
35
47
36
48
/// Returns the set of overrides for the [`LockTarget`].
37
49
pub ( crate ) fn overrides ( self ) -> Vec < uv_pep508:: Requirement < VerbatimParsedUrl > > {
38
50
match self {
39
51
Self :: Workspace ( workspace) => workspace. overrides ( ) ,
52
+ Self :: Script ( script) => script
53
+ . metadata
54
+ . tool
55
+ . as_ref ( )
56
+ . and_then ( |tool| tool. uv . as_ref ( ) )
57
+ . and_then ( |uv| uv. override_dependencies . as_ref ( ) )
58
+ . into_iter ( )
59
+ . flatten ( )
60
+ . cloned ( )
61
+ . collect ( ) ,
40
62
}
41
63
}
42
64
43
65
/// Returns the set of constraints for the [`LockTarget`].
44
66
pub ( crate ) fn constraints ( self ) -> Vec < uv_pep508:: Requirement < VerbatimParsedUrl > > {
45
67
match self {
46
68
Self :: Workspace ( workspace) => workspace. constraints ( ) ,
69
+ Self :: Script ( script) => script
70
+ . metadata
71
+ . tool
72
+ . as_ref ( )
73
+ . and_then ( |tool| tool. uv . as_ref ( ) )
74
+ . and_then ( |uv| uv. constraint_dependencies . as_ref ( ) )
75
+ . into_iter ( )
76
+ . flatten ( )
77
+ . cloned ( )
78
+ . collect ( ) ,
47
79
}
48
80
}
49
81
@@ -57,20 +89,23 @@ impl<'lock> LockTarget<'lock> {
57
89
> {
58
90
match self {
59
91
Self :: Workspace ( workspace) => workspace. dependency_groups ( ) ,
92
+ Self :: Script ( _) => Ok ( BTreeMap :: new ( ) ) ,
60
93
}
61
94
}
62
95
63
96
/// Returns the set of all members within the target.
64
97
pub ( crate ) fn members_requirements ( self ) -> impl Iterator < Item = Requirement > + ' lock {
65
98
match self {
66
- Self :: Workspace ( workspace) => workspace. members_requirements ( ) ,
99
+ Self :: Workspace ( workspace) => Either :: Left ( workspace. members_requirements ( ) ) ,
100
+ Self :: Script ( _) => Either :: Right ( std:: iter:: empty ( ) ) ,
67
101
}
68
102
}
69
103
70
104
/// Returns the set of all dependency groups within the target.
71
105
pub ( crate ) fn group_requirements ( self ) -> impl Iterator < Item = Requirement > + ' lock {
72
106
match self {
73
- Self :: Workspace ( workspace) => workspace. group_requirements ( ) ,
107
+ Self :: Workspace ( workspace) => Either :: Left ( workspace. group_requirements ( ) ) ,
108
+ Self :: Script ( _) => Either :: Right ( std:: iter:: empty ( ) ) ,
74
109
}
75
110
}
76
111
@@ -90,48 +125,74 @@ impl<'lock> LockTarget<'lock> {
90
125
91
126
members
92
127
}
128
+ Self :: Script ( _) => Vec :: new ( ) ,
93
129
}
94
130
}
95
131
96
132
/// Return the list of packages.
97
133
pub ( crate ) fn packages ( self ) -> & ' lock BTreeMap < PackageName , WorkspaceMember > {
98
134
match self {
99
135
Self :: Workspace ( workspace) => workspace. packages ( ) ,
136
+ Self :: Script ( _) => {
137
+ static EMPTY : BTreeMap < PackageName , WorkspaceMember > = BTreeMap :: new ( ) ;
138
+ & EMPTY
139
+ }
100
140
}
101
141
}
102
142
103
143
/// Returns the set of supported environments for the [`LockTarget`].
104
144
pub ( crate ) fn environments ( self ) -> Option < & ' lock SupportedEnvironments > {
105
145
match self {
106
146
Self :: Workspace ( workspace) => workspace. environments ( ) ,
147
+ Self :: Script ( _) => {
148
+ // TODO(charlie): Add support for environments in scripts.
149
+ None
150
+ }
107
151
}
108
152
}
109
153
110
154
/// Returns the set of conflicts for the [`LockTarget`].
111
155
pub ( crate ) fn conflicts ( self ) -> Conflicts {
112
156
match self {
113
157
Self :: Workspace ( workspace) => workspace. conflicts ( ) ,
158
+ Self :: Script ( _) => Conflicts :: empty ( ) ,
114
159
}
115
160
}
116
161
117
162
/// Return the `Requires-Python` bound for the [`LockTarget`].
118
163
pub ( crate ) fn requires_python ( self ) -> Option < RequiresPython > {
119
164
match self {
120
165
Self :: Workspace ( workspace) => find_requires_python ( workspace) ,
166
+ Self :: Script ( script) => script
167
+ . metadata
168
+ . requires_python
169
+ . as_ref ( )
170
+ . map ( RequiresPython :: from_specifiers) ,
121
171
}
122
172
}
123
173
124
174
/// Return the path to the lock root.
125
175
pub ( crate ) fn install_path ( self ) -> & ' lock Path {
126
176
match self {
127
177
Self :: Workspace ( workspace) => workspace. install_path ( ) ,
178
+ Self :: Script ( script) => script. path . parent ( ) . unwrap ( ) ,
128
179
}
129
180
}
130
181
131
182
/// Return the path to the lockfile.
132
183
pub ( crate ) fn lock_path ( self ) -> PathBuf {
133
184
match self {
185
+ // `uv.lock`
134
186
Self :: Workspace ( workspace) => workspace. install_path ( ) . join ( "uv.lock" ) ,
187
+ // `script.py.lock`
188
+ Self :: Script ( script) => {
189
+ let mut file_name = match script. path . file_name ( ) {
190
+ Some ( f) => f. to_os_string ( ) ,
191
+ None => panic ! ( "Script path has no file name" ) ,
192
+ } ;
193
+ file_name. push ( ".lock" ) ;
194
+ script. path . with_file_name ( file_name)
195
+ }
135
196
}
136
197
}
137
198
@@ -223,6 +284,55 @@ impl<'lock> LockTarget<'lock> {
223
284
. map ( |requirement| requirement. with_origin ( RequirementOrigin :: Workspace ) )
224
285
. collect :: < Vec < _ > > ( ) )
225
286
}
287
+ Self :: Script ( script) => {
288
+ // Collect any `tool.uv.index` from the script.
289
+ let empty = Vec :: default ( ) ;
290
+ let indexes = match sources {
291
+ SourceStrategy :: Enabled => script
292
+ . metadata
293
+ . tool
294
+ . as_ref ( )
295
+ . and_then ( |tool| tool. uv . as_ref ( ) )
296
+ . and_then ( |uv| uv. top_level . index . as_deref ( ) )
297
+ . unwrap_or ( & empty) ,
298
+ SourceStrategy :: Disabled => & empty,
299
+ } ;
300
+
301
+ // Collect any `tool.uv.sources` from the script.
302
+ let empty = BTreeMap :: default ( ) ;
303
+ let sources = match sources {
304
+ SourceStrategy :: Enabled => script
305
+ . metadata
306
+ . tool
307
+ . as_ref ( )
308
+ . and_then ( |tool| tool. uv . as_ref ( ) )
309
+ . and_then ( |uv| uv. sources . as_ref ( ) )
310
+ . unwrap_or ( & empty) ,
311
+ SourceStrategy :: Disabled => & empty,
312
+ } ;
313
+
314
+ Ok ( requirements
315
+ . into_iter ( )
316
+ . flat_map ( |requirement| {
317
+ let requirement_name = requirement. name . clone ( ) ;
318
+ LoweredRequirement :: from_non_workspace_requirement (
319
+ requirement,
320
+ script. path . parent ( ) . unwrap ( ) ,
321
+ sources,
322
+ indexes,
323
+ locations,
324
+ LowerBound :: Allow ,
325
+ )
326
+ . map ( move |requirement| match requirement {
327
+ Ok ( requirement) => Ok ( requirement. into_inner ( ) ) ,
328
+ Err ( err) => Err ( uv_distribution:: MetadataError :: LoweringError (
329
+ requirement_name. clone ( ) ,
330
+ Box :: new ( err) ,
331
+ ) ) ,
332
+ } )
333
+ } )
334
+ . collect :: < Result < _ , _ > > ( ) ?)
335
+ }
226
336
}
227
337
}
228
338
}
0 commit comments