8
8
# See https://aboutcode-orgnexB/python-inspector for support or download.
9
9
# See https://aboutcode.org for more information about nexB OSS projects.
10
10
#
11
-
11
+ import asyncio
12
12
import os
13
13
from netrc import netrc
14
14
from typing import Dict
15
15
from typing import List
16
16
from typing import NamedTuple
17
17
from typing import Sequence
18
+ from typing import Tuple
18
19
19
20
from packageurl import PackageURL
20
21
from packvers .requirements import Requirement
26
27
from _packagedcode .pypi import PipRequirementsFileHandler
27
28
from _packagedcode .pypi import PythonSetupPyHandler
28
29
from _packagedcode .pypi import can_process_dependent_package
29
- from python_inspector import DEFAULT_PYTHON_VERSION
30
+ from _packagedcode . pypi import get_resolved_purl
30
31
from python_inspector import dependencies
31
32
from python_inspector import utils
32
33
from python_inspector import utils_pypi
39
40
from python_inspector .resolution import get_python_version_from_env_tag
40
41
from python_inspector .resolution import get_reqs_insecurely
41
42
from python_inspector .resolution import get_requirements_from_python_manifest
43
+ from python_inspector .utils import Candidate
42
44
from python_inspector .utils_pypi import PLATFORMS_BY_OS
43
45
from python_inspector .utils_pypi import PYPI_SIMPLE_URL
44
46
from python_inspector .utils_pypi import Environment
@@ -54,7 +56,7 @@ class Resolution(NamedTuple):
54
56
``files`` is a parsed list of input file data.
55
57
"""
56
58
57
- resolution : Dict
59
+ resolution : List [ Dict ]
58
60
packages : List [PackageData ]
59
61
files : List [Dict ]
60
62
@@ -286,21 +288,27 @@ def resolve_dependencies(
286
288
pdt_output = pdt_output ,
287
289
analyze_setup_py_insecurely = analyze_setup_py_insecurely ,
288
290
ignore_errors = ignore_errors ,
291
+ verbose = verbose ,
292
+ printer = printer ,
289
293
)
290
294
291
- packages = []
295
+ async def gather_pypi_data ():
296
+ async def get_pypi_data (package ):
297
+ data = await get_pypi_data_from_purl (
298
+ package , repos = repos , environment = environment , prefer_source = prefer_source
299
+ )
292
300
293
- for package in purls :
294
- packages . extend (
295
- [
296
- pkg . to_dict ()
297
- for pkg in list (
298
- get_pypi_data_from_purl (
299
- package , repos = repos , environment = environment , prefer_source = prefer_source
300
- )
301
- )
302
- ],
303
- )
301
+ if verbose :
302
+ printer ( f" retrieved package ' { package } '" )
303
+
304
+ return data
305
+
306
+ if verbose :
307
+ printer ( f"retrieve package data from pypi:" )
308
+
309
+ return await asyncio . gather ( * [ get_pypi_data ( package ) for package in purls ] )
310
+
311
+ packages = [ pkg . to_dict () for pkg in asyncio . run ( gather_pypi_data ()) if pkg is not None ]
304
312
305
313
if verbose :
306
314
printer ("done!" )
@@ -316,14 +324,16 @@ def resolve_dependencies(
316
324
317
325
318
326
def resolve (
319
- direct_dependencies ,
320
- environment ,
321
- repos = tuple (),
322
- as_tree = False ,
323
- max_rounds = 200000 ,
324
- pdt_output = False ,
325
- analyze_setup_py_insecurely = False ,
326
- ignore_errors = False ,
327
+ direct_dependencies : List [DependentPackage ],
328
+ environment : Environment ,
329
+ repos : Sequence [utils_pypi .PypiSimpleRepository ] = tuple (),
330
+ as_tree : bool = False ,
331
+ max_rounds : int = 200000 ,
332
+ pdt_output : bool = False ,
333
+ analyze_setup_py_insecurely : bool = False ,
334
+ ignore_errors : bool = False ,
335
+ verbose : bool = False ,
336
+ printer = print ,
327
337
):
328
338
"""
329
339
Resolve dependencies given a ``direct_dependencies`` list of
@@ -350,6 +360,8 @@ def resolve(
350
360
pdt_output = pdt_output ,
351
361
analyze_setup_py_insecurely = analyze_setup_py_insecurely ,
352
362
ignore_errors = ignore_errors ,
363
+ verbose = verbose ,
364
+ printer = printer ,
353
365
)
354
366
355
367
return resolved_dependencies , packages
@@ -364,32 +376,77 @@ def get_resolved_dependencies(
364
376
pdt_output : bool = False ,
365
377
analyze_setup_py_insecurely : bool = False ,
366
378
ignore_errors : bool = False ,
367
- ):
379
+ verbose : bool = False ,
380
+ printer = print ,
381
+ ) -> Tuple [List [Dict ], List [str ]]:
368
382
"""
369
383
Return resolved dependencies of a ``requirements`` list of Requirement for
370
- an ``enviroment `` Environment. The resolved dependencies are formatted as
384
+ an ``environment `` Environment. The resolved dependencies are formatted as
371
385
parent/children or a nested tree if ``as_tree`` is True.
372
386
373
387
Used the provided ``repos`` list of PypiSimpleRepository.
374
- If empty, use instead the PyPI.org JSON API exclusively instead
388
+ If empty, use instead the PyPI.org JSON API exclusively instead.
375
389
"""
390
+ provider = PythonInputProvider (
391
+ environment = environment ,
392
+ repos = repos ,
393
+ analyze_setup_py_insecurely = analyze_setup_py_insecurely ,
394
+ ignore_errors = ignore_errors ,
395
+ )
396
+
397
+ # gather version data for all requirements concurrently in advance.
398
+
399
+ async def gather_version_data ():
400
+ async def get_version_data (name : str ):
401
+ versions = await provider .fill_versions_for_package (name )
402
+
403
+ if verbose :
404
+ printer (f" retrieved versions for package '{ name } '" )
405
+
406
+ return versions
407
+
408
+ if verbose :
409
+ printer (f"versions:" )
410
+
411
+ return await asyncio .gather (
412
+ * [get_version_data (requirement .name ) for requirement in requirements ]
413
+ )
414
+
415
+ asyncio .run (gather_version_data ())
416
+
417
+ # gather dependencies for all pinned requirements concurrently in advance.
418
+
419
+ async def gather_dependencies ():
420
+ async def get_dependencies (requirement : Requirement ):
421
+ purl = PackageURL (type = "pypi" , name = requirement .name )
422
+ resolved_purl = get_resolved_purl (purl = purl , specifiers = requirement .specifier )
423
+
424
+ if resolved_purl :
425
+ purl = resolved_purl .purl
426
+ candidate = Candidate (requirement .name , purl .version , requirement .extras )
427
+ await provider .fill_requirements_for_package (purl , candidate )
428
+
429
+ if verbose :
430
+ printer (f" retrieved dependencies for requirement '{ str (purl )} '" )
431
+
432
+ if verbose :
433
+ printer (f"dependencies:" )
434
+
435
+ return await asyncio .gather (
436
+ * [get_dependencies (requirement ) for requirement in requirements ]
437
+ )
438
+
439
+ asyncio .run (gather_dependencies ())
440
+
376
441
resolver = Resolver (
377
- provider = PythonInputProvider (
378
- environment = environment ,
379
- repos = repos ,
380
- analyze_setup_py_insecurely = analyze_setup_py_insecurely ,
381
- ignore_errors = ignore_errors ,
382
- ),
442
+ provider = provider ,
383
443
reporter = BaseReporter (),
384
444
)
385
445
resolver_results = resolver .resolve (requirements = requirements , max_rounds = max_rounds )
386
446
package_list = get_package_list (results = resolver_results )
387
447
if pdt_output :
388
- return (format_pdt_tree (resolver_results ), package_list )
389
- return (
390
- format_resolution (resolver_results , as_tree = as_tree ),
391
- package_list ,
392
- )
448
+ return format_pdt_tree (resolver_results ), package_list
449
+ return format_resolution (resolver_results , as_tree = as_tree ), package_list
393
450
394
451
395
452
def get_requirements_from_direct_dependencies (
0 commit comments