5
5
6
6
from copy import deepcopy
7
7
from hashlib import sha256
8
+ from typing import Dict
8
9
from typing import Iterable
9
10
from typing import Iterator
10
11
from typing import List
11
12
from typing import Optional
12
13
from typing import Sequence
14
+ from typing import Set
15
+ from typing import Tuple
13
16
from typing import Union
14
17
15
18
from tomlkit import array
@@ -185,36 +188,107 @@ def locked_repository(
185
188
186
189
return packages
187
190
191
+ @staticmethod
192
+ def __get_locked_package (
193
+ _dependency , packages_by_name
194
+ ): # type: (Dependency, Dict[str, List[Package]]) -> Optional[Package]
195
+ """
196
+ Internal helper to identify corresponding locked package using dependency
197
+ version constraints.
198
+ """
199
+ for _package in packages_by_name .get (_dependency .name , []):
200
+ if _dependency .constraint .allows (_package .version ):
201
+ return _package
202
+ return None
203
+
204
+ @classmethod
205
+ def __walk_dependency_level (
206
+ cls ,
207
+ dependencies ,
208
+ level ,
209
+ pinned_versions ,
210
+ packages_by_name ,
211
+ project_level_dependencies ,
212
+ nested_dependencies ,
213
+ ): # type: (List[Dependency], int, bool, Dict[str, List[Package]], Set[str], Dict[Tuple[str, str], Dependency]) -> Dict[Tuple[str, str], Dependency]
214
+ if not dependencies :
215
+ return nested_dependencies
216
+
217
+ next_level_dependencies = []
218
+
219
+ for requirement in dependencies :
220
+ locked_package = cls .__get_locked_package (requirement , packages_by_name )
221
+
222
+ if locked_package :
223
+ for require in locked_package .requires :
224
+ if require .marker .is_empty ():
225
+ require .marker = requirement .marker
226
+ else :
227
+ require .marker = require .marker .intersect (requirement .marker )
228
+
229
+ require .marker = require .marker .intersect (locked_package .marker )
230
+ next_level_dependencies .append (require )
231
+
232
+ if requirement .name in project_level_dependencies and level == 0 :
233
+ # project level dependencies take precedence
234
+ continue
235
+
236
+ if locked_package :
237
+ # create dependency from locked package to retain dependency metadata
238
+ # if this is not done, we can end-up with incorrect nested dependencies
239
+ marker = requirement .marker
240
+ requirement = locked_package .to_dependency ()
241
+ requirement .marker = requirement .marker .intersect (marker )
242
+ else :
243
+ # we make a copy to avoid any side-effects
244
+ requirement = deepcopy (requirement )
245
+
246
+ if pinned_versions :
247
+ requirement .set_constraint (
248
+ cls .__get_locked_package (requirement , packages_by_name )
249
+ .to_dependency ()
250
+ .constraint
251
+ )
252
+
253
+ # dependencies use extra to indicate that it was activated via parent
254
+ # package's extras, this is not required for nested exports as we assume
255
+ # the resolver already selected this dependency
256
+ requirement .marker = requirement .marker .without_extras ()
257
+
258
+ key = (requirement .name , requirement .pretty_constraint )
259
+ if key not in nested_dependencies :
260
+ nested_dependencies [key ] = requirement
261
+ else :
262
+ nested_dependencies [key ].marker = nested_dependencies [
263
+ key
264
+ ].marker .intersect (requirement .marker )
265
+
266
+ return cls .__walk_dependency_level (
267
+ dependencies = next_level_dependencies ,
268
+ level = level + 1 ,
269
+ pinned_versions = pinned_versions ,
270
+ packages_by_name = packages_by_name ,
271
+ project_level_dependencies = project_level_dependencies ,
272
+ nested_dependencies = nested_dependencies ,
273
+ )
274
+
188
275
@classmethod
189
276
def get_project_dependencies (
190
277
cls , project_requires , locked_packages , pinned_versions = False , with_nested = False
191
278
): # type: (List[Dependency], List[Package], bool, bool) -> Iterable[Dependency]
192
- # group packages entries by name, this is required because requirement might use
193
- # different constraints
279
+ # group packages entries by name, this is required because requirement might use different constraints
194
280
packages_by_name = {}
195
281
for pkg in locked_packages :
196
282
if pkg .name not in packages_by_name :
197
283
packages_by_name [pkg .name ] = []
198
284
packages_by_name [pkg .name ].append (pkg )
199
285
200
- def __get_locked_package (
201
- _dependency ,
202
- ): # type: (Dependency) -> Optional[Package]
203
- """
204
- Internal helper to identify corresponding locked package using dependency
205
- version constraints.
206
- """
207
- for _package in packages_by_name .get (_dependency .name , []):
208
- if _dependency .constraint .allows (_package .version ):
209
- return _package
210
- return None
211
-
212
286
project_level_dependencies = set ()
213
287
dependencies = []
214
288
215
289
for dependency in project_requires :
216
290
dependency = deepcopy (dependency )
217
- locked_package = __get_locked_package (dependency )
291
+ locked_package = cls . __get_locked_package (dependency , packages_by_name )
218
292
if locked_package :
219
293
locked_dependency = locked_package .to_dependency ()
220
294
locked_dependency .marker = dependency .marker .intersect (
@@ -233,68 +307,14 @@ def __get_locked_package(
233
307
# return only with project level dependencies
234
308
return dependencies
235
309
236
- nested_dependencies = dict ()
237
-
238
- def __walk_level (
239
- __dependencies , __level
240
- ): # type: (List[Dependency], int) -> None
241
- if not __dependencies :
242
- return
243
-
244
- __next_level = []
245
-
246
- for requirement in __dependencies :
247
- __locked_package = __get_locked_package (requirement )
248
-
249
- if __locked_package :
250
- for require in __locked_package .requires :
251
- if require .marker .is_empty ():
252
- require .marker = requirement .marker
253
- else :
254
- require .marker = require .marker .intersect (
255
- requirement .marker
256
- )
257
-
258
- require .marker = require .marker .intersect (
259
- __locked_package .marker
260
- )
261
- __next_level .append (require )
262
-
263
- if requirement .name in project_level_dependencies and __level == 0 :
264
- # project level dependencies take precedence
265
- continue
266
-
267
- if __locked_package :
268
- # create dependency from locked package to retain dependency metadata
269
- # if this is not done, we can end-up with incorrect nested dependencies
270
- marker = requirement .marker
271
- requirement = __locked_package .to_dependency ()
272
- requirement .marker = requirement .marker .intersect (marker )
273
- else :
274
- # we make a copy to avoid any side-effects
275
- requirement = deepcopy (requirement )
276
-
277
- if pinned_versions :
278
- requirement .set_constraint (
279
- __get_locked_package (requirement ).to_dependency ().constraint
280
- )
281
-
282
- # dependencies use extra to indicate that it was activated via parent
283
- # package's extras, this is not required for nested exports as we assume
284
- # the resolver already selected this dependency
285
- requirement .marker = requirement .marker .without_extras ()
286
-
287
- key = (requirement .name , requirement .pretty_constraint )
288
- if key not in nested_dependencies :
289
- nested_dependencies [key ] = requirement
290
- else :
291
- nested_dependencies [key ].marker = nested_dependencies [
292
- key
293
- ].marker .intersect (requirement .marker )
294
-
295
- return __walk_level (__next_level , __level + 1 )
296
-
297
- __walk_level (dependencies , 0 )
310
+ nested_dependencies = cls .__walk_dependency_level (
311
+ dependencies = dependencies ,
312
+ level = 0 ,
313
+ pinned_versions = pinned_versions ,
314
+ packages_by_name = packages_by_name ,
315
+ project_level_dependencies = project_level_dependencies ,
316
+ nested_dependencies = dict (),
317
+ )
298
318
299
319
# Merge same dependencies using marker union
300
320
for requirement in dependencies :
0 commit comments