Skip to content

Commit d9508cc

Browse files
committed
Add tests
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent ee35e70 commit d9508cc

4 files changed

Lines changed: 73 additions & 22 deletions

File tree

src/python_inspector/resolution.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,19 @@
3636

3737

3838
def get_response(url):
39+
"""
40+
Return a response for the given url.
41+
"""
3942
resp = requests.get(url)
4043
if resp.status_code == 200:
4144
return resp.json()
4245
return None
4346

4447

4548
def is_valid_version(parsed_version, requirements, identifier, bad_versions):
49+
"""
50+
Return True if the parsed_version is valid for the given identifier.
51+
"""
4652
if (
4753
any(parsed_version not in r.specifier for r in requirements[identifier])
4854
or parsed_version in bad_versions
@@ -52,7 +58,7 @@ def is_valid_version(parsed_version, requirements, identifier, bad_versions):
5258

5359

5460
class PythonInputProvider(AbstractProvider):
55-
def __init__(self, environment, repos):
61+
def __init__(self, environment=None, repos=[]):
5662
self.environment = environment
5763
self.repos = repos
5864
self.versions_by_package = {}
@@ -81,7 +87,7 @@ def get_versions_for_package(self, name, repo=None):
8187
Return a list of versions for a package.
8288
"""
8389
versions = []
84-
if repo:
90+
if repo and self.environment:
8591
for version, package in repo._get_package_versions_map(name).items():
8692
wheels = package.get_supported_wheels(environment=self.environment)
8793
if list(wheels):
@@ -99,9 +105,9 @@ def get_versions_for_package(self, name, repo=None):
99105

100106
def get_requirements_for_package(self, purl, candidate):
101107
"""
102-
Generate requirements for a package.
108+
Yield requirements for a package.
103109
"""
104-
if self.repos:
110+
if self.repos and self.environment:
105111
wheels = download_wheel(
106112
name=candidate.name,
107113
version=str(candidate.version),
@@ -157,9 +163,6 @@ def _iter_matches(self, identifier, requirements, incompatibilities):
157163
)
158164

159165
def find_matches(self, identifier, requirements, incompatibilities):
160-
"""
161-
Return a sorted list of candidates for the given identifier.
162-
"""
163166
candidates = sorted(
164167
self._iter_matches(identifier, requirements, incompatibilities),
165168
key=operator.attrgetter("version"),
@@ -168,9 +171,6 @@ def find_matches(self, identifier, requirements, incompatibilities):
168171
return candidates
169172

170173
def is_satisfied_by(self, requirement, candidate):
171-
"""
172-
Return True if the given requirement is satisfied by the given candidate.
173-
"""
174174
return candidate.version in requirement.specifier
175175

176176
def _iter_dependencies(self, candidate):
@@ -194,9 +194,6 @@ def _iter_dependencies(self, candidate):
194194
yield r
195195

196196
def get_dependencies(self, candidate):
197-
"""
198-
Return a list of dependencies for the given candidate.
199-
"""
200197
return list(self._iter_dependencies(candidate))
201198

202199

@@ -228,7 +225,7 @@ def dfs(mapping, graph, src):
228225
)
229226

230227

231-
def _format_resolution(result):
228+
def format_resolution(result):
232229
"""
233230
Return a formatted resolution.
234231
"""
@@ -289,19 +286,20 @@ def pypi_simple_repo_in_repos(repos: PypiSimpleRepository):
289286

290287
def resolution(
291288
requirements: List[Requirement],
292-
environment: Environment,
289+
environment: Environment = None,
293290
repos: List[PypiSimpleRepository] = [],
294291
return_as_parent_children: bool = True,
295292
return_as_tree: bool = False,
296293
return_as_list: bool = False,
297294
):
298295
"""
299296
Return a resolution for the given requirements.
297+
If environment is not None, it is used to resolve the requirements.
300298
"""
301299
if repos and not pypi_simple_repo_in_repos(repos):
302300
repos.append(PYPI_PUBLIC_REPO)
303301
resolver = Resolver(PythonInputProvider(environment, repos), BaseReporter())
304-
as_list, as_parent_children, as_tree = _format_resolution(resolver.resolve(requirements))
302+
as_list, as_parent_children, as_tree = format_resolution(resolver.resolve(requirements))
305303
if return_as_parent_children:
306304
return as_parent_children
307305
if return_as_tree:

src/python_inspector/resolve_cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,19 @@ def resolve_dependencies(
117117
Download from the provided PyPI simple --index-url INDEX(s) URLs.
118118
Error and progress are printed to stderr.
119119
120+
Default environment is the Python version - 3.8 and OS - linux.
121+
122+
1) If no index_url is provided, the PyPI JSON API is used and environment will be ignored in that case.
123+
124+
For example:
125+
dad --spec "flask==2.1.2" --json -
126+
127+
2) If an index_url is provided, the environment will be used to resolve the dependencies.
128+
(If no environment is provided default environment will be used.)
129+
130+
For example:
131+
dad --spec "flask==2.1.2" --index-url https://pypi.org/simple --json -
132+
120133
For example::
121134
dad --spec "flask" --requirement etc/scripts/requirements.txt --json -
122135
"""

tests/test_cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ def test_cli_with_single_index_url():
3636

3737

3838
@pytest.mark.online
39-
def test_cli_with_multiple_index_url():
39+
def test_cli_with_multiple_index_url_and_tilde_req():
4040
runner = CliRunner()
4141
result = runner.invoke(
4242
cli,
4343
[
4444
"--spec",
45-
"zipp==3.8.0",
45+
"zipp~=3.8.0",
4646
"--index-url",
4747
"https://pypi.org/simple",
4848
"--index-url",

tests/test_resolution.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
# See https://github.com/nexB/python-inspector for support or download.
99
# See https://aboutcode.org for more information about nexB OSS projects.
1010
#
11+
import packaging
1112
import pytest
1213
from packaging.requirements import Requirement
1314

15+
from python_inspector.resolution import is_valid_version
16+
from python_inspector.resolution import pypi_simple_repo_in_repos
1417
from python_inspector.resolution import resolution
1518
from python_inspector.utils_pypi import PYPI_PUBLIC_REPO
1619
from python_inspector.utils_pypi import Environment
@@ -29,7 +32,7 @@ def test_resolvelib_with_flask_and_python_310():
2932
return_as_parent_children=False,
3033
return_as_list=True,
3134
)
32-
assert [
35+
assert as_list == [
3336
"pkg:pypi/click@8.1.3",
3437
"pkg:pypi/flask@2.1.2",
3538
"pkg:pypi/importlib-metadata@4.11.4",
@@ -38,7 +41,7 @@ def test_resolvelib_with_flask_and_python_310():
3841
"pkg:pypi/markupsafe@2.1.1",
3942
"pkg:pypi/werkzeug@2.1.2",
4043
"pkg:pypi/zipp@3.8.0",
41-
] == as_list
44+
]
4245

4346

4447
@pytest.mark.online
@@ -55,7 +58,7 @@ def test_resolvelib_with_flask_and_python_36():
5558
return_as_list=True,
5659
)
5760

58-
assert [
61+
assert as_list == [
5962
"pkg:pypi/click@8.1.3",
6063
"pkg:pypi/flask@2.1.2",
6164
"pkg:pypi/importlib-metadata@4.11.4",
@@ -64,4 +67,41 @@ def test_resolvelib_with_flask_and_python_36():
6467
"pkg:pypi/markupsafe@2.0.1",
6568
"pkg:pypi/werkzeug@2.1.2",
6669
"pkg:pypi/zipp@3.8.0",
67-
] == as_list
70+
]
71+
72+
73+
@pytest.mark.online
74+
def test_resolvelib_with_tilde_requirement_using_json_api():
75+
req = [Requirement("flask~=2.1.2")]
76+
as_list = resolution(
77+
requirements=req,
78+
return_as_parent_children=False,
79+
return_as_list=True,
80+
)
81+
82+
assert as_list == [
83+
"pkg:pypi/click@8.1.3",
84+
"pkg:pypi/flask@2.1.2",
85+
"pkg:pypi/importlib-metadata@4.11.4",
86+
"pkg:pypi/itsdangerous@2.1.2",
87+
"pkg:pypi/jinja2@3.1.2",
88+
"pkg:pypi/markupsafe@2.1.1",
89+
"pkg:pypi/werkzeug@2.1.2",
90+
"pkg:pypi/zipp@3.8.0",
91+
]
92+
93+
94+
def test_pypi_simple_repo_in_repos():
95+
assert pypi_simple_repo_in_repos(repos=[PYPI_PUBLIC_REPO]) == True
96+
97+
98+
def test_pypi_simple_repo_in_repos_not_present():
99+
assert pypi_simple_repo_in_repos(repos=[]) == False
100+
101+
102+
def test_is_valid_version():
103+
parsed_version = packaging.version.parse("2.1.2")
104+
requirements = {"flask": [Requirement("flask>2.0.0")]}
105+
bad_versions = []
106+
identifier = "flask"
107+
assert is_valid_version(parsed_version, requirements, identifier, bad_versions) == True

0 commit comments

Comments
 (0)