Skip to content

Commit e094f3c

Browse files
committed
Address review comments
Signed-off-by: Tushar Goel <[email protected]>
1 parent 567d343 commit e094f3c

File tree

6 files changed

+90
-97
lines changed

6 files changed

+90
-97
lines changed

src/python_inspector/resolution.py

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from typing import Generator
1515
from typing import List
1616
from typing import NamedTuple
17-
from typing import Sequence
1817
from typing import Tuple
1918
from typing import Union
2019
from zipfile import ZipFile
@@ -27,8 +26,6 @@
2726
from packaging.version import Version
2827
from packaging.version import parse as parse_version
2928
from resolvelib import AbstractProvider
30-
from resolvelib import Resolver
31-
from resolvelib.reporters import BaseReporter
3229
from resolvelib.structs import DirectedGraph
3330

3431
from _packagedcode.models import DependentPackage
@@ -40,7 +37,6 @@
4037
from _packagedcode.pypi import can_process_dependent_package
4138
from python_inspector import utils_pypi
4239
from python_inspector.setup_py_live_eval import iter_requirements
43-
from python_inspector.utils_pypi import Environment
4440
from python_inspector.utils_pypi import PypiSimpleRepository
4541

4642

@@ -741,49 +737,3 @@ def get_setup_dependencies(location, analyze_setup_py_insecurely=False, use_requ
741737
yield from parse_reqs_from_setup_py_insecurely(setup_py=setup_py_location)
742738
else:
743739
raise Exception("Unable to collect setup.py dependencies securely")
744-
745-
746-
def get_resolved_dependencies(
747-
requirements: List[Requirement],
748-
environment: Environment = None,
749-
repos: Sequence[PypiSimpleRepository] = tuple(),
750-
as_tree: bool = False,
751-
max_rounds: int = 200000,
752-
verbose: bool = False,
753-
pdt_output: bool = False,
754-
analyze_setup_py_insecurely: bool = False,
755-
ctx=None,
756-
):
757-
"""
758-
Return resolved dependencies of a ``requirements`` list of Requirement for
759-
an ``enviroment`` Environment. The resolved dependencies are formatted as
760-
parent/children or a nested tree if ``as_tree`` is True.
761-
762-
Used the provided ``repos`` list of PypiSimpleRepository.
763-
If empty, use instead the PyPI.org JSON API exclusively instead
764-
"""
765-
try:
766-
resolver = Resolver(
767-
provider=PythonInputProvider(
768-
environment=environment,
769-
repos=repos,
770-
analyze_setup_py_insecurely=analyze_setup_py_insecurely,
771-
),
772-
reporter=BaseReporter(),
773-
)
774-
resolver_results = resolver.resolve(requirements=requirements, max_rounds=max_rounds)
775-
package_list = get_package_list(results=resolver_results)
776-
if pdt_output:
777-
return (format_pdt_tree(resolver_results), package_list)
778-
return (
779-
format_resolution(resolver_results, as_tree=as_tree),
780-
package_list,
781-
)
782-
except Exception as e:
783-
if verbose:
784-
if ctx:
785-
import click
786-
787-
click.secho(f"{e!r}", err=True)
788-
else:
789-
print(f"{e!r}")

src/python_inspector/resolve_cli.py

Lines changed: 87 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@
1111

1212
import json
1313
import os
14+
from functools import partial
1415
from typing import Dict
1516
from typing import List
17+
from typing import Sequence
1618

1719
import click
1820
from packaging.requirements import Requirement
21+
from resolvelib import Resolver
22+
from resolvelib.reporters import BaseReporter
1923
from tinynetrc import Netrc
2024

2125
from _packagedcode.models import DependentPackage
@@ -27,12 +31,16 @@
2731
from python_inspector import utils_pypi
2832
from python_inspector.cli_utils import FileOptionType
2933
from python_inspector.package_data import get_pypi_data_from_purl
34+
from python_inspector.resolution import PythonInputProvider
3035
from python_inspector.resolution import contain_string
36+
from python_inspector.resolution import format_pdt_tree
37+
from python_inspector.resolution import format_resolution
3138
from python_inspector.resolution import get_deps_from_distribution
3239
from python_inspector.resolution import get_environment_marker_from_environment
40+
from python_inspector.resolution import get_package_list
3341
from python_inspector.resolution import get_python_version_from_env_tag
34-
from python_inspector.resolution import get_resolved_dependencies
3542
from python_inspector.resolution import parse_deps_from_setup_py_insecurely
43+
from python_inspector.utils_pypi import Environment
3644

3745
TRACE = False
3846

@@ -227,6 +235,13 @@ def resolve_dependencies(
227235
click.secho("Only one of --json or --json-pdt can be used.", err=True)
228236
ctx.exit(1)
229237

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+
230245
resolver_api(
231246
requirement_files=requirement_files,
232247
setup_py_file=setup_py_file,
@@ -242,25 +257,23 @@ def resolve_dependencies(
242257
use_pypi_json_api=use_pypi_json_api,
243258
verbose=verbose,
244259
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,
246264
)
247265

248266

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)
256270

257271

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)
264277

265278

266279
def resolver_api(
@@ -278,7 +291,10 @@ def resolver_api(
278291
use_pypi_json_api=False,
279292
verbose=False,
280293
analyze_setup_py_insecurely=False,
281-
ctx=None,
294+
printer=print,
295+
raise_error=raise_exception,
296+
options=[],
297+
write_output=False,
282298
):
283299
"""
284300
Resolve the dependencies for the package requirements listed in one or
@@ -287,7 +303,7 @@ def resolver_api(
287303
"""
288304

289305
if verbose:
290-
print_message("Resolving dependencies...", ctx=ctx)
306+
printer("Resolving dependencies...")
291307

292308
if netrc_file:
293309
if not os.path.exists(netrc_file):
@@ -302,7 +318,7 @@ def resolver_api(
302318

303319
if netrc_file:
304320
if verbose:
305-
print_message(f"Using netrc file {netrc_file}", ctx=ctx)
321+
printer(f"Using netrc file {netrc_file}")
306322
netrc = Netrc(file=netrc_file)
307323
else:
308324
netrc = None
@@ -349,7 +365,6 @@ def resolver_api(
349365
f"Python version {get_python_version_from_env_tag(python_version)} "
350366
f"is not compatible with setup.py {setup_py_file} "
351367
f"python_requires {python_requires}",
352-
ctx=ctx,
353368
)
354369

355370
setup_py_file_deps = package_data.dependencies
@@ -399,20 +414,20 @@ def resolver_api(
399414
)
400415

401416
if not direct_dependencies:
402-
raise_error("Error: no requirements requested.", ctx=ctx)
417+
raise_error("Error: no requirements requested.")
403418

404419
if verbose:
405-
print_message("direct_dependencies:", ctx=ctx)
420+
printer("direct_dependencies:")
406421
for dep in direct_dependencies:
407-
print_message(f" {dep}", ctx=ctx)
422+
printer(f" {dep}")
408423

409424
# create a resolution environments
410425
environment = utils_pypi.Environment.from_pyver_and_os(
411426
python_version=python_version, operating_system=operating_system
412427
)
413428

414429
if verbose:
415-
print_message(f"environment: {environment}", ctx=ctx)
430+
printer(f"environment: {environment}")
416431

417432
repos = []
418433
if not use_pypi_json_api:
@@ -438,9 +453,9 @@ def resolver_api(
438453
repos.append(repo)
439454

440455
if verbose:
441-
print_message("repos:", ctx=ctx)
456+
printer("repos:")
442457
for repo in repos:
443-
print_message(f" {repo}", ctx=ctx)
458+
printer(f" {repo}")
444459

445460
# resolve dependencies proper
446461
resolved_dependencies, purls = resolve(
@@ -449,20 +464,12 @@ def resolver_api(
449464
repos=repos,
450465
as_tree=False,
451466
max_rounds=max_rounds,
452-
verbose=verbose,
453467
pdt_output=pdt_output,
454468
analyze_setup_py_insecurely=analyze_setup_py_insecurely,
455-
ctx=ctx,
469+
raise_error=raise_error,
456470
)
457471

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:
466473
options = [f"requirement_files- {rf}" for rf in requirement_files]
467474
options += [f"specifiers- {sp}" for sp in specifiers]
468475
options += [f"index_urls- {iu}" for iu in index_urls]
@@ -500,16 +507,16 @@ def resolver_api(
500507
packages=packages,
501508
)
502509

503-
if ctx:
504-
write_output(
510+
if write_output:
511+
write_output_in_file(
505512
json_output=json_output or pdt_output,
506513
output=output,
507514
)
508515
else:
509516
return output
510517

511518
if verbose:
512-
print_message("done!", ctx=ctx)
519+
printer("done!")
513520

514521

515522
def resolve(
@@ -518,10 +525,9 @@ def resolve(
518525
repos=tuple(),
519526
as_tree=False,
520527
max_rounds=200000,
521-
verbose=False,
522528
pdt_output=False,
523529
analyze_setup_py_insecurely=False,
524-
ctx=None,
530+
raise_error=raise_exception,
525531
):
526532
"""
527533
Resolve dependencies given a ``direct_dependencies`` list of
@@ -545,15 +551,53 @@ def resolve(
545551
repos=repos,
546552
as_tree=as_tree,
547553
max_rounds=max_rounds,
548-
verbose=verbose,
549554
pdt_output=pdt_output,
550555
analyze_setup_py_insecurely=analyze_setup_py_insecurely,
551-
ctx=ctx,
556+
raise_error=raise_error,
552557
)
553558

554559
return resolved_dependencies, packages
555560

556561

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+
557601
def get_requirements_from_direct_dependencies(
558602
direct_dependencies: List[DependentPackage], environment_marker: Dict
559603
) -> List[Requirement]:
@@ -571,7 +615,7 @@ def get_requirements_from_direct_dependencies(
571615
yield req
572616

573617

574-
def write_output(output, json_output):
618+
def write_output_in_file(output, json_output):
575619
"""
576620
Write headers, requirements and resolved_dependencies as JSON to ``json_output``.
577621
Return the output data.

tests/data/azure-devops.req-310-expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"headers": {
33
"tool_name": "python-inspector",
44
"tool_homepageurl": "https://github.com/nexB/python-inspector",
5-
"tool_version": "0.8.3",
5+
"tool_version": "0.9.0",
66
"options": [
77
"--requirement /home/tg1999/Desktop/python-inspector-1/tests/data/azure-devops.req.txt",
88
"--index-url https://pypi.org/simple",

tests/data/azure-devops.req-38-expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"headers": {
33
"tool_name": "python-inspector",
44
"tool_homepageurl": "https://github.com/nexB/python-inspector",
5-
"tool_version": "0.8.3",
5+
"tool_version": "0.9.0",
66
"options": [
77
"--requirement /home/tg1999/Desktop/python-inspector-1/tests/data/azure-devops.req.txt",
88
"--index-url https://pypi.org/simple",

tests/data/single-url-except-simple-expected.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"tool_version": "0.9.0",
66
"options": [
77
"--specifier flask",
8-
"--index-url https://pypi.org/simple",
98
"--index-url https://thirdparty.aboutcode.org/pypi/simple/",
109
"--python-version 38",
1110
"--operating-system linux",

tests/test_resolution.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717

1818
from _packagedcode import models
1919
from python_inspector.resolution import get_requirements_from_dependencies
20-
from python_inspector.resolution import get_resolved_dependencies
2120
from python_inspector.resolution import is_valid_version
2221
from python_inspector.resolution import parse_reqs_from_setup_py_insecurely
22+
from python_inspector.resolve_cli import get_resolved_dependencies
2323
from python_inspector.utils_pypi import PYPI_PUBLIC_REPO
2424
from python_inspector.utils_pypi import Environment
2525

0 commit comments

Comments
 (0)