Skip to content

Commit fc28934

Browse files
committed
Add API function for using cleanly as a library #39
Reference: #39 Signed-off-by: Tushar Goel <[email protected]>
1 parent 5b1d45c commit fc28934

File tree

6 files changed

+286
-26
lines changed

6 files changed

+286
-26
lines changed

CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Changelog
22
=========
33

4+
v0.6.6
5+
------
6+
7+
- Add API function for using cleanly as a library.
8+
49
v0.6.5
510
------
611

src/python_inspector/resolve_cli.py

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -207,16 +207,70 @@ def resolve_dependencies(
207207
208208
python-inspector --spec "flask==2.1.2" --json -
209209
"""
210-
if not (json_output or pdt_output):
211-
click.secho("No output file specified. Use --json or --json-pdt.", err=True)
210+
resolver_api(
211+
requirement_files=requirement_files,
212+
setup_py_file=setup_py_file,
213+
specifiers=specifiers,
214+
python_version=python_version,
215+
operating_system=operating_system,
216+
index_urls=index_urls,
217+
json_output=json_output,
218+
pdt_output=pdt_output,
219+
netrc_file=netrc_file,
220+
max_rounds=max_rounds,
221+
use_cached_index=use_cached_index,
222+
use_pypi_json_api=use_pypi_json_api,
223+
verbose=verbose,
224+
ctx=ctx,
225+
)
226+
227+
228+
def raise_error(message, ctx):
229+
"""Raise error."""
230+
if ctx:
231+
click.secho(message, err=True)
212232
ctx.exit(1)
233+
else:
234+
raise ValueError(message)
235+
236+
237+
def print_message(message, ctx):
238+
"""Print message."""
239+
if ctx:
240+
click.secho(message)
241+
else:
242+
print(message)
243+
244+
245+
def resolver_api(
246+
requirement_files=[],
247+
setup_py_file=None,
248+
specifiers=[],
249+
python_version=DEFAULT_PYTHON_VERSION,
250+
operating_system="linux",
251+
index_urls=tuple([PYPI_SIMPLE_URL]),
252+
json_output=None,
253+
pdt_output=None,
254+
netrc_file=None,
255+
max_rounds=200000,
256+
use_cached_index=False,
257+
use_pypi_json_api=False,
258+
verbose=False,
259+
ctx=None,
260+
):
261+
"""
262+
Resolve the dependencies for the package requirements listed in one or
263+
more ``requirement_files``, one or more ``specifiers`` and one setuptools
264+
``setup_py_file`` file and save the results as JSON to FILE.
265+
"""
266+
if not (json_output or pdt_output):
267+
raise_error("No output file specified. Use --json or --json-pdt.", ctx=ctx)
213268

214269
if json_output and pdt_output:
215-
click.secho("Only one of --json or --json-pdt can be used.", err=True)
216-
ctx.exit(1)
270+
raise_error("Only one of --json or --json-pdt can be used.", ctx=ctx)
217271

218272
if verbose:
219-
click.secho(f"Resolving dependencies...")
273+
print_message("Resolving dependencies...", ctx=ctx)
220274

221275
if netrc_file:
222276
if not os.path.exists(netrc_file):
@@ -231,7 +285,7 @@ def resolve_dependencies(
231285

232286
if netrc_file:
233287
if verbose:
234-
click.secho(f"Using netrc file {netrc_file}")
288+
print_message(f"Using netrc file {netrc_file}", ctx=ctx)
235289
netrc = Netrc(file=netrc_file)
236290
else:
237291
netrc = None
@@ -262,35 +316,33 @@ def resolve_dependencies(
262316
python_version=get_python_version_from_env_tag(python_version),
263317
python_requires=python_requires,
264318
):
265-
click.secho(
319+
raise_error(
266320
f"Python version {get_python_version_from_env_tag(python_version)} "
267321
f"is not compatible with setup.py {setup_py_file} "
268322
f"python_requires {python_requires}",
269-
err=True,
323+
ctx=ctx,
270324
)
271-
ctx.exit(1)
272325

273326
for dep in package_data.dependencies:
274327
# TODO : we need to handle to all the scopes
275328
if dep.scope == "install":
276329
direct_dependencies.append(dep)
277330

278331
if not direct_dependencies:
279-
click.secho("Error: no requirements requested.")
280-
ctx.exit(1)
332+
raise_error("Error: no requirements requested.", ctx=ctx)
281333

282334
if verbose:
283-
click.secho("direct_dependencies:")
335+
print_message("direct_dependencies:", ctx=ctx)
284336
for dep in direct_dependencies:
285-
click.secho(f" {dep}")
337+
print_message(f" {dep}", ctx=ctx)
286338

287339
# create a resolution environments
288340
environment = utils_pypi.Environment.from_pyver_and_os(
289341
python_version=python_version, operating_system=operating_system
290342
)
291343

292344
if verbose:
293-
click.secho(f"environment: {environment}")
345+
print_message(f"environment: {environment}", ctx=ctx)
294346

295347
repos = []
296348
if not use_pypi_json_api:
@@ -316,9 +368,9 @@ def resolve_dependencies(
316368
repos.append(repo)
317369

318370
if verbose:
319-
click.secho("repos:")
371+
print_message("repos:", ctx=ctx)
320372
for repo in repos:
321-
click.secho(f" {repo}")
373+
print_message(f" {repo}", ctx=ctx)
322374

323375
# resolve dependencies proper
324376
requirements, resolved_dependencies = resolve(
@@ -331,12 +383,20 @@ def resolve_dependencies(
331383
pdt_output=pdt_output,
332384
)
333385

334-
cli_options = [f"--requirement {rf}" for rf in requirement_files]
335-
cli_options += [f"--specifier {sp}" for sp in specifiers]
336-
cli_options += [f"--index-url {iu}" for iu in index_urls]
337-
cli_options += [f"--python-version {python_version}"]
338-
cli_options += [f"--operating-system {operating_system}"]
339-
cli_options += ["--json <file>"]
386+
if ctx:
387+
options = [f"--requirement {rf}" for rf in requirement_files]
388+
options += [f"--specifier {sp}" for sp in specifiers]
389+
options += [f"--index-url {iu}" for iu in index_urls]
390+
options += [f"--python-version {python_version}"]
391+
options += [f"--operating-system {operating_system}"]
392+
options += ["--json <file>"]
393+
else:
394+
options = [f"requirement_files- {rf}" for rf in requirement_files]
395+
options += [f"specifiers- {sp}" for sp in specifiers]
396+
options += [f"index_urls- {iu}" for iu in index_urls]
397+
options += [f"python-version {python_version}"]
398+
options += [f"operating-system {operating_system}"]
399+
options += [f"json "]
340400

341401
notice = (
342402
"Dependency tree generated with python-inspector.\n"
@@ -348,12 +408,18 @@ def resolve_dependencies(
348408
tool_name="python-inspector",
349409
tool_homepageurl="https://github.com/nexB/python-inspector",
350410
tool_version=__version__,
351-
options=cli_options,
411+
options=options,
352412
notice=notice,
353413
warnings=[],
354414
errors=[],
355415
)
356416

417+
if not ctx:
418+
if json_output:
419+
json_output = open(file=json_output, mode="w", encoding="utf-8")
420+
if pdt_output:
421+
pdt_output = open(file=pdt_output, mode="w", encoding="utf-8")
422+
357423
if json_output:
358424
write_output(
359425
headers=headers,
@@ -372,7 +438,7 @@ def resolve_dependencies(
372438
)
373439

374440
if verbose:
375-
click.secho("done!")
441+
print_message("done!", ctx=ctx)
376442

377443

378444
def resolve(
@@ -445,7 +511,6 @@ def write_output(headers, requirements, resolved_dependencies, json_output, pdt_
445511
)
446512
else:
447513
output = resolved_dependencies
448-
449514
json.dump(output, json_output, indent=2)
450515
return output
451516

tests/data/test-api-expected.json

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"headers": {
3+
"tool_name": "python-inspector",
4+
"tool_homepageurl": "https://github.com/nexB/python-inspector",
5+
"tool_version": "0.6.5",
6+
"options": [
7+
"specifiers- flask==2.1.2",
8+
"index_urls- https://pypi.org/simple",
9+
"python-version 3.10",
10+
"operating-system linux",
11+
"json "
12+
],
13+
"notice": "Dependency tree generated with python-inspector.\npython-inspector is a free software tool from nexB Inc. and others.\nVisit https://github.com/nexB/scancode-toolkit/ for support and download.",
14+
"warnings": [],
15+
"errors": []
16+
},
17+
"requirements": [
18+
{
19+
"purl": "pkg:pypi/[email protected]",
20+
"extracted_requirement": "flask==2.1.2",
21+
"scope": "install",
22+
"is_runtime": true,
23+
"is_optional": false,
24+
"is_resolved": true,
25+
"resolved_package": {},
26+
"extra_data": {}
27+
}
28+
],
29+
"resolved_dependencies": [
30+
{
31+
"package": "pkg:pypi/[email protected]",
32+
"dependencies": [],
33+
"wheel_urls": [
34+
"https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl"
35+
],
36+
"sdist_url": "https://files.pythonhosted.org/packages/59/87/84326af34517fca8c58418d148f2403df25303e02736832403587318e9e8/click-8.1.3.tar.gz"
37+
},
38+
{
39+
"package": "pkg:pypi/[email protected]",
40+
"dependencies": [
41+
"pkg:pypi/[email protected]",
42+
"pkg:pypi/[email protected]",
43+
"pkg:pypi/[email protected]",
44+
"pkg:pypi/[email protected]"
45+
],
46+
"wheel_urls": [
47+
"https://files.pythonhosted.org/packages/ba/76/e9580e494eaf6f09710b0f3b9000c9c0363e44af5390be32bb0394165853/Flask-2.1.2-py3-none-any.whl"
48+
],
49+
"sdist_url": "https://files.pythonhosted.org/packages/d3/3c/94f38d4db919a9326a706ad56f05a7e6f0c8f7b7d93e2997cca54d3bc14b/Flask-2.1.2.tar.gz"
50+
},
51+
{
52+
"package": "pkg:pypi/[email protected]",
53+
"dependencies": [],
54+
"wheel_urls": [
55+
"https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl"
56+
],
57+
"sdist_url": "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz"
58+
},
59+
{
60+
"package": "pkg:pypi/[email protected]",
61+
"dependencies": [
62+
"pkg:pypi/[email protected]"
63+
],
64+
"wheel_urls": [
65+
"https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl"
66+
],
67+
"sdist_url": "https://files.pythonhosted.org/packages/7a/ff/75c28576a1d900e87eb6335b063fab47a8ef3c8b4d88524c4bf78f670cce/Jinja2-3.1.2.tar.gz"
68+
},
69+
{
70+
"package": "pkg:pypi/[email protected]",
71+
"dependencies": [],
72+
"wheel_urls": [
73+
"https://files.pythonhosted.org/packages/9e/82/2e089c6f34e77c073aa5a67040d368aac0dfb9b8ccbb46d381452c26fc33/MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
74+
],
75+
"sdist_url": "https://files.pythonhosted.org/packages/1d/97/2288fe498044284f39ab8950703e88abbac2abbdf65524d576157af70556/MarkupSafe-2.1.1.tar.gz"
76+
},
77+
{
78+
"package": "pkg:pypi/[email protected]",
79+
"dependencies": [
80+
"pkg:pypi/[email protected]"
81+
],
82+
"wheel_urls": [
83+
"https://files.pythonhosted.org/packages/c8/27/be6ddbcf60115305205de79c29004a0c6bc53cec814f733467b1bb89386d/Werkzeug-2.2.2-py3-none-any.whl"
84+
],
85+
"sdist_url": "https://files.pythonhosted.org/packages/f8/c1/1c8e539f040acd80f844c69a5ef8e2fccdf8b442dabb969e497b55d544e1/Werkzeug-2.2.2.tar.gz"
86+
}
87+
]
88+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[
2+
{
3+
"key": "flask",
4+
"package_name": "flask",
5+
"installed_version": "2.1.2",
6+
"dependencies": [
7+
{
8+
"key": "click",
9+
"package_name": "click",
10+
"installed_version": "8.1.3",
11+
"dependencies": []
12+
},
13+
{
14+
"key": "itsdangerous",
15+
"package_name": "itsdangerous",
16+
"installed_version": "2.1.2",
17+
"dependencies": []
18+
},
19+
{
20+
"key": "jinja2",
21+
"package_name": "jinja2",
22+
"installed_version": "3.1.2",
23+
"dependencies": [
24+
{
25+
"key": "markupsafe",
26+
"package_name": "markupsafe",
27+
"installed_version": "2.1.1",
28+
"dependencies": []
29+
}
30+
]
31+
},
32+
{
33+
"key": "werkzeug",
34+
"package_name": "werkzeug",
35+
"installed_version": "2.2.2",
36+
"dependencies": [
37+
{
38+
"key": "markupsafe",
39+
"package_name": "markupsafe",
40+
"installed_version": "2.1.1",
41+
"dependencies": []
42+
}
43+
]
44+
}
45+
]
46+
}
47+
]

0 commit comments

Comments
 (0)