@@ -9,7 +9,7 @@ use miette::{Diagnostic, IntoDiagnostic, Report, WrapErr};
9
9
use pep440_rs:: { Version , VersionSpecifiers } ;
10
10
use pep508_rs:: Requirement ;
11
11
use pixi_spec:: PixiSpec ;
12
- use pyproject_toml:: { self , pep735_resolve :: Pep735Error , Contact } ;
12
+ use pyproject_toml:: { self , has_recursion :: RecursionResolutionError , Contact } ;
13
13
use rattler_conda_types:: { PackageName , ParseStrictness :: Lenient , VersionSpec } ;
14
14
use thiserror:: Error ;
15
15
use toml_span:: Spanned ;
@@ -22,7 +22,7 @@ use crate::{
22
22
error:: { DependencyError , GenericError } ,
23
23
manifests:: PackageManifest ,
24
24
toml:: {
25
- pyproject:: { TomlContact , TomlDependencyGroups , TomlProject } ,
25
+ pyproject:: { TomlContact , TomlDependencyGroups , TomlOptionalDependencies , TomlProject } ,
26
26
ExternalPackageProperties , ExternalWorkspaceProperties , FromTomlStr , PyProjectToml ,
27
27
TomlManifest ,
28
28
} ,
@@ -97,11 +97,6 @@ impl PyProjectManifest {
97
97
None
98
98
}
99
99
100
- /// Returns the project name as PEP508 name
101
- fn package_name ( & self ) -> Option < pep508_rs:: PackageName > {
102
- pep508_rs:: PackageName :: new ( self . name ( ) ?. to_string ( ) ) . ok ( )
103
- }
104
-
105
100
fn tool ( & self ) -> Option < & Tool > {
106
101
self . tool . as_ref ( )
107
102
}
@@ -124,19 +119,18 @@ impl PyProjectManifest {
124
119
125
120
/// Returns optional dependencies from the `[project.optional-dependencies]`
126
121
/// table
127
- fn optional_dependencies ( & self ) -> Option < IndexMap < String , Vec < Requirement > > > {
122
+ fn optional_dependencies (
123
+ & self ,
124
+ ) -> Option < Result < IndexMap < String , Vec < Requirement > > , RecursionResolutionError > > {
128
125
let project = self . project . project . as_ref ( ) ?;
129
126
let optional_dependencies = project. optional_dependencies . as_ref ( ) ?;
130
- Some (
131
- optional_dependencies
132
- . iter ( )
133
- . map ( |( k, v) | ( k. clone ( ) , v. iter ( ) . cloned ( ) . map ( Spanned :: take) . collect ( ) ) )
134
- . collect ( ) ,
135
- )
127
+ Some ( optional_dependencies. value . 0 . resolve ( ) )
136
128
}
137
129
138
130
/// Returns dependency groups from the `[dependency-groups]` table
139
- fn dependency_groups ( & self ) -> Option < Result < IndexMap < String , Vec < Requirement > > , Pep735Error > > {
131
+ fn dependency_groups (
132
+ & self ,
133
+ ) -> Option < Result < IndexMap < String , Vec < Requirement > > , RecursionResolutionError > > {
140
134
let dg = self . project . dependency_groups . as_ref ( ) ?;
141
135
Some ( dg. value . 0 . resolve ( ) )
142
136
}
@@ -145,37 +139,23 @@ impl PyProjectManifest {
145
139
/// dependencies and/or dependency groups:
146
140
/// - one environment is created per group with the same name
147
141
/// - each environment includes the feature of the same name
148
- /// - it will also include other features inferred from any self references
149
- /// to other groups of optional dependencies (but won't for dependency
150
- /// groups, as recursion between groups is resolved upstream)
151
- pub fn environments_from_extras ( & self ) -> Result < HashMap < String , Vec < String > > , Pep735Error > {
142
+ pub fn environments_from_dependency_groups (
143
+ & self ,
144
+ ) -> Result < HashMap < String , Vec < String > > , RecursionResolutionError > {
152
145
let mut environments = HashMap :: new ( ) ;
153
- if let Some ( extras) = self . optional_dependencies ( ) {
154
- let pname = self . package_name ( ) ;
155
- for ( extra, reqs) in extras {
156
- let mut features = vec ! [ extra. to_string( ) ] ;
157
- // Add any references to other groups of extra dependencies
158
- for req in reqs. iter ( ) {
159
- if pname. as_ref ( ) == Some ( & req. name ) {
160
- for extra in & req. extras {
161
- features. push ( extra. to_string ( ) )
162
- }
163
- }
164
- }
165
- // Environments can only contain number, strings and dashes
166
- environments. insert ( extra. replace ( '_' , "-" ) . clone ( ) , features) ;
167
- }
168
- }
169
146
170
- if let Some ( groups) = self . dependency_groups ( ) . transpose ( ) ? {
171
- for group in groups. into_keys ( ) {
172
- let normalised = group. replace ( '_' , "-" ) ;
173
- // Nothing to do if a group of optional dependencies has the same name as the
174
- // dependency group
175
- if !environments. contains_key ( & normalised) {
176
- environments. insert ( normalised. clone ( ) , vec ! [ normalised] ) ;
177
- }
178
- }
147
+ let groups = self
148
+ . optional_dependencies ( )
149
+ . transpose ( ) ?
150
+ . unwrap_or_default ( )
151
+ . into_iter ( )
152
+ . chain ( self . dependency_groups ( ) . transpose ( ) ?. unwrap_or_default ( ) ) ;
153
+
154
+ for ( group, _) in groups {
155
+ let normalised = group. replace ( '_' , "-" ) ;
156
+ environments
157
+ . entry ( normalised. clone ( ) )
158
+ . or_insert_with ( || vec ! [ group] ) ;
179
159
}
180
160
181
161
Ok ( environments)
@@ -187,7 +167,7 @@ pub enum PyProjectToManifestError {
187
167
#[ error( "Unsupported pep508 requirement: '{0}'" ) ]
188
168
DependencyError ( Requirement , #[ source] DependencyError ) ,
189
169
#[ error( transparent) ]
190
- DependencyGroupError ( #[ from] Pep735Error ) ,
170
+ DependencyGroupError ( #[ from] RecursionResolutionError ) ,
191
171
#[ error( transparent) ]
192
172
TomlError ( #[ from] TomlError ) ,
193
173
}
@@ -200,7 +180,7 @@ pub struct PyProjectFields {
200
180
pub authors : Option < Vec < Spanned < TomlContact > > > ,
201
181
pub requires_python : Option < Spanned < VersionSpecifiers > > ,
202
182
pub dependencies : Option < Vec < Spanned < Requirement > > > ,
203
- pub optional_dependencies : Option < IndexMap < String , Vec < Spanned < Requirement > > > > ,
183
+ pub optional_dependencies : Option < Spanned < TomlOptionalDependencies > > ,
204
184
}
205
185
206
186
impl From < TomlProject > for PyProjectFields {
@@ -391,12 +371,7 @@ impl PyProjectManifest {
391
371
}
392
372
393
373
// For each group of optional dependency or dependency group, add pypi
394
- // dependencies, filtering out self-references in optional dependencies
395
- let project_name = workspace_manifest
396
- . workspace
397
- . name
398
- . clone ( )
399
- . and_then ( |name| pep508_rs:: PackageName :: new ( name) . ok ( ) ) ;
374
+ // dependencies
400
375
for ( group, reqs) in pypi_dependency_groups {
401
376
let feature_name = FeatureName :: from ( group. to_string ( ) ) ;
402
377
let target = workspace_manifest
@@ -406,16 +381,13 @@ impl PyProjectManifest {
406
381
. targets
407
382
. default_mut ( ) ;
408
383
for requirement in reqs. iter ( ) {
409
- // filter out any self references in groups of extra dependencies
410
- if project_name. as_ref ( ) != Some ( & requirement. name ) {
411
- target
412
- . try_add_pep508_dependency (
413
- requirement,
414
- None ,
415
- DependencyOverwriteBehavior :: Error ,
416
- )
417
- . map_err ( |err| GenericError :: new ( format ! ( "{}" , err) ) ) ?;
418
- }
384
+ target
385
+ . try_add_pep508_dependency (
386
+ requirement,
387
+ None ,
388
+ DependencyOverwriteBehavior :: Error ,
389
+ )
390
+ . map_err ( |err| GenericError :: new ( format ! ( "{}" , err) ) ) ?;
419
391
}
420
392
}
421
393
@@ -424,31 +396,27 @@ impl PyProjectManifest {
424
396
425
397
fn extract_dependency_groups (
426
398
dependency_groups : Option < Spanned < TomlDependencyGroups > > ,
427
- optional_dependencies : Option < IndexMap < String , Vec < Spanned < Requirement > > > > ,
399
+ optional_dependencies : Option < Spanned < TomlOptionalDependencies > > ,
428
400
) -> Result < Vec < ( String , Vec < Requirement > ) > , TomlError > {
429
- Ok ( optional_dependencies
430
- . map ( |deps| {
431
- deps. into_iter ( )
432
- . map ( |( group, reqs) | {
433
- (
434
- group,
435
- reqs. into_iter ( ) . map ( Spanned :: take) . collect :: < Vec < _ > > ( ) ,
436
- )
437
- } )
438
- . collect ( )
439
- } )
440
- . into_iter ( )
441
- . chain (
442
- dependency_groups
443
- . map ( |Spanned { span, value } | {
444
- value. 0 . resolve ( ) . map_err ( |err| {
445
- GenericError :: new ( format ! ( "{}" , err) ) . with_span ( span. into ( ) )
446
- } )
447
- } )
448
- . transpose ( ) ?,
449
- )
450
- . flat_map ( |map| map. into_iter ( ) )
451
- . collect :: < Vec < _ > > ( ) )
401
+ let mut result = Vec :: new ( ) ;
402
+
403
+ if let Some ( Spanned { span, value } ) = optional_dependencies {
404
+ let resolved = value
405
+ . 0
406
+ . resolve ( )
407
+ . map_err ( |err| GenericError :: new ( err. to_string ( ) ) . with_span ( span. into ( ) ) ) ?;
408
+ result. extend ( resolved) ;
409
+ }
410
+
411
+ if let Some ( Spanned { span, value } ) = dependency_groups {
412
+ let resolved = value
413
+ . 0
414
+ . resolve ( )
415
+ . map_err ( |err| GenericError :: new ( err. to_string ( ) ) . with_span ( span. into ( ) ) ) ?;
416
+ result. extend ( resolved) ;
417
+ }
418
+
419
+ Ok ( result)
452
420
}
453
421
}
454
422
0 commit comments