11
11
12
12
import json
13
13
import os
14
+ from functools import partial
14
15
from typing import Dict
15
16
from typing import List
17
+ from typing import Sequence
16
18
17
19
import click
18
20
from packaging .requirements import Requirement
21
+ from resolvelib import Resolver
22
+ from resolvelib .reporters import BaseReporter
19
23
from tinynetrc import Netrc
20
24
21
25
from _packagedcode .models import DependentPackage
27
31
from python_inspector import utils_pypi
28
32
from python_inspector .cli_utils import FileOptionType
29
33
from python_inspector .package_data import get_pypi_data_from_purl
34
+ from python_inspector .resolution import PythonInputProvider
30
35
from python_inspector .resolution import contain_string
36
+ from python_inspector .resolution import format_pdt_tree
37
+ from python_inspector .resolution import format_resolution
31
38
from python_inspector .resolution import get_deps_from_distribution
32
39
from python_inspector .resolution import get_environment_marker_from_environment
40
+ from python_inspector .resolution import get_package_list
33
41
from python_inspector .resolution import get_python_version_from_env_tag
34
- from python_inspector .resolution import get_resolved_dependencies
35
42
from python_inspector .resolution import parse_deps_from_setup_py_insecurely
43
+ from python_inspector .utils_pypi import Environment
36
44
37
45
TRACE = False
38
46
@@ -227,6 +235,13 @@ def resolve_dependencies(
227
235
click .secho ("Only one of --json or --json-pdt can be used." , err = True )
228
236
ctx .exit (1 )
229
237
238
+ options = [f"--requirement { rf } " for rf in requirement_files ]
239
+ options += [f"--specifier { sp } " for sp in specifiers ]
240
+ options += [f"--index-url { iu } " for iu in index_urls ]
241
+ options += [f"--python-version { python_version } " ]
242
+ options += [f"--operating-system { operating_system } " ]
243
+ options += ["--json <file>" ]
244
+
230
245
resolver_api (
231
246
requirement_files = requirement_files ,
232
247
setup_py_file = setup_py_file ,
@@ -242,25 +257,23 @@ def resolve_dependencies(
242
257
use_pypi_json_api = use_pypi_json_api ,
243
258
verbose = verbose ,
244
259
analyze_setup_py_insecurely = analyze_setup_py_insecurely ,
245
- ctx = ctx ,
260
+ printer = click .secho ,
261
+ raise_error = partial (raise_exception_from_cli , ctx ),
262
+ options = options ,
263
+ write_output = True ,
246
264
)
247
265
248
266
249
- def raise_error (message , ctx ):
250
- """Raise error."""
251
- if ctx :
252
- click .secho (message , err = True )
253
- ctx .exit (1 )
254
- else :
255
- raise ValueError (message )
267
+ def raise_exception_from_cli (ctx , message ):
268
+ click .secho (message , err = True )
269
+ ctx .exit (1 )
256
270
257
271
258
- def print_message (message , ctx ):
259
- """Print message."""
260
- if ctx :
261
- click .secho (message )
262
- else :
263
- print (message )
272
+ def raise_exception (message ):
273
+ """
274
+ Raise error.
275
+ """
276
+ raise Exception (message )
264
277
265
278
266
279
def resolver_api (
@@ -278,7 +291,10 @@ def resolver_api(
278
291
use_pypi_json_api = False ,
279
292
verbose = False ,
280
293
analyze_setup_py_insecurely = False ,
281
- ctx = None ,
294
+ printer = print ,
295
+ raise_error = raise_exception ,
296
+ options = [],
297
+ write_output = False ,
282
298
):
283
299
"""
284
300
Resolve the dependencies for the package requirements listed in one or
@@ -287,7 +303,7 @@ def resolver_api(
287
303
"""
288
304
289
305
if verbose :
290
- print_message ("Resolving dependencies..." , ctx = ctx )
306
+ printer ("Resolving dependencies..." )
291
307
292
308
if netrc_file :
293
309
if not os .path .exists (netrc_file ):
@@ -302,7 +318,7 @@ def resolver_api(
302
318
303
319
if netrc_file :
304
320
if verbose :
305
- print_message (f"Using netrc file { netrc_file } " , ctx = ctx )
321
+ printer (f"Using netrc file { netrc_file } " )
306
322
netrc = Netrc (file = netrc_file )
307
323
else :
308
324
netrc = None
@@ -349,7 +365,6 @@ def resolver_api(
349
365
f"Python version { get_python_version_from_env_tag (python_version )} "
350
366
f"is not compatible with setup.py { setup_py_file } "
351
367
f"python_requires { python_requires } " ,
352
- ctx = ctx ,
353
368
)
354
369
355
370
setup_py_file_deps = package_data .dependencies
@@ -399,20 +414,20 @@ def resolver_api(
399
414
)
400
415
401
416
if not direct_dependencies :
402
- raise_error ("Error: no requirements requested." , ctx = ctx )
417
+ raise_error ("Error: no requirements requested." )
403
418
404
419
if verbose :
405
- print_message ("direct_dependencies:" , ctx = ctx )
420
+ printer ("direct_dependencies:" )
406
421
for dep in direct_dependencies :
407
- print_message (f" { dep } " , ctx = ctx )
422
+ printer (f" { dep } " )
408
423
409
424
# create a resolution environments
410
425
environment = utils_pypi .Environment .from_pyver_and_os (
411
426
python_version = python_version , operating_system = operating_system
412
427
)
413
428
414
429
if verbose :
415
- print_message (f"environment: { environment } " , ctx = ctx )
430
+ printer (f"environment: { environment } " )
416
431
417
432
repos = []
418
433
if not use_pypi_json_api :
@@ -438,9 +453,9 @@ def resolver_api(
438
453
repos .append (repo )
439
454
440
455
if verbose :
441
- print_message ("repos:" , ctx = ctx )
456
+ printer ("repos:" )
442
457
for repo in repos :
443
- print_message (f" { repo } " , ctx = ctx )
458
+ printer (f" { repo } " )
444
459
445
460
# resolve dependencies proper
446
461
resolved_dependencies , purls = resolve (
@@ -449,20 +464,12 @@ def resolver_api(
449
464
repos = repos ,
450
465
as_tree = False ,
451
466
max_rounds = max_rounds ,
452
- verbose = verbose ,
453
467
pdt_output = pdt_output ,
454
468
analyze_setup_py_insecurely = analyze_setup_py_insecurely ,
455
- ctx = ctx ,
469
+ raise_error = raise_error ,
456
470
)
457
471
458
- if ctx :
459
- options = [f"--requirement { rf } " for rf in requirement_files ]
460
- options += [f"--specifier { sp } " for sp in specifiers ]
461
- options += [f"--index-url { iu } " for iu in index_urls ]
462
- options += [f"--python-version { python_version } " ]
463
- options += [f"--operating-system { operating_system } " ]
464
- options += ["--json <file>" ]
465
- else :
472
+ if not options :
466
473
options = [f"requirement_files- { rf } " for rf in requirement_files ]
467
474
options += [f"specifiers- { sp } " for sp in specifiers ]
468
475
options += [f"index_urls- { iu } " for iu in index_urls ]
@@ -500,16 +507,16 @@ def resolver_api(
500
507
packages = packages ,
501
508
)
502
509
503
- if ctx :
504
- write_output (
510
+ if write_output :
511
+ write_output_in_file (
505
512
json_output = json_output or pdt_output ,
506
513
output = output ,
507
514
)
508
515
else :
509
516
return output
510
517
511
518
if verbose :
512
- print_message ("done!" , ctx = ctx )
519
+ printer ("done!" )
513
520
514
521
515
522
def resolve (
@@ -518,10 +525,9 @@ def resolve(
518
525
repos = tuple (),
519
526
as_tree = False ,
520
527
max_rounds = 200000 ,
521
- verbose = False ,
522
528
pdt_output = False ,
523
529
analyze_setup_py_insecurely = False ,
524
- ctx = None ,
530
+ raise_error = raise_exception ,
525
531
):
526
532
"""
527
533
Resolve dependencies given a ``direct_dependencies`` list of
@@ -545,15 +551,53 @@ def resolve(
545
551
repos = repos ,
546
552
as_tree = as_tree ,
547
553
max_rounds = max_rounds ,
548
- verbose = verbose ,
549
554
pdt_output = pdt_output ,
550
555
analyze_setup_py_insecurely = analyze_setup_py_insecurely ,
551
- ctx = ctx ,
556
+ raise_error = raise_error ,
552
557
)
553
558
554
559
return resolved_dependencies , packages
555
560
556
561
562
+ def get_resolved_dependencies (
563
+ requirements : List [Requirement ],
564
+ environment : Environment = None ,
565
+ repos : Sequence [utils_pypi .PypiSimpleRepository ] = tuple (),
566
+ as_tree : bool = False ,
567
+ max_rounds : int = 200000 ,
568
+ pdt_output : bool = False ,
569
+ analyze_setup_py_insecurely : bool = False ,
570
+ raise_error = raise_exception ,
571
+ ):
572
+ """
573
+ Return resolved dependencies of a ``requirements`` list of Requirement for
574
+ an ``enviroment`` Environment. The resolved dependencies are formatted as
575
+ parent/children or a nested tree if ``as_tree`` is True.
576
+
577
+ Used the provided ``repos`` list of PypiSimpleRepository.
578
+ If empty, use instead the PyPI.org JSON API exclusively instead
579
+ """
580
+ try :
581
+ resolver = Resolver (
582
+ provider = PythonInputProvider (
583
+ environment = environment ,
584
+ repos = repos ,
585
+ analyze_setup_py_insecurely = analyze_setup_py_insecurely ,
586
+ ),
587
+ reporter = BaseReporter (),
588
+ )
589
+ resolver_results = resolver .resolve (requirements = requirements , max_rounds = max_rounds )
590
+ package_list = get_package_list (results = resolver_results )
591
+ if pdt_output :
592
+ return (format_pdt_tree (resolver_results ), package_list )
593
+ return (
594
+ format_resolution (resolver_results , as_tree = as_tree ),
595
+ package_list ,
596
+ )
597
+ except Exception as e :
598
+ raise_error (f"{ e !r} " )
599
+
600
+
557
601
def get_requirements_from_direct_dependencies (
558
602
direct_dependencies : List [DependentPackage ], environment_marker : Dict
559
603
) -> List [Requirement ]:
@@ -571,7 +615,7 @@ def get_requirements_from_direct_dependencies(
571
615
yield req
572
616
573
617
574
- def write_output (output , json_output ):
618
+ def write_output_in_file (output , json_output ):
575
619
"""
576
620
Write headers, requirements and resolved_dependencies as JSON to ``json_output``.
577
621
Return the output data.
0 commit comments