Skip to content

Commit ce9d58a

Browse files
committed
Add tests and address review comments
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent a6cb7d4 commit ce9d58a

10 files changed

Lines changed: 3022 additions & 132 deletions

File tree

src/_packagedcode/pypi.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,9 @@ def parse(cls, location):
741741

742742
@classmethod
743743
def parse_reqs(cls, reqs, scope):
744+
"""
745+
Parse a list of requirements and return a list of dependencies
746+
"""
744747
dependent_packages = []
745748
for req in reqs:
746749
is_resolved = False
@@ -1303,10 +1306,10 @@ def parse_with_dparse2(location, file_name=None):
13031306
dependent_packages = []
13041307

13051308
for dependency in dep_file.dependencies:
1306-
name = dependency.name
1309+
requirement = dependency.name
13071310
is_resolved = False
13081311
purl = PackageURL(type='pypi', name=dependency.name)
1309-
extracted_requirement = name
1312+
13101313
# note: dparse2.dependencies.Dependency.specs comes from
13111314
# packaging.requirements.Requirement.specifier
13121315
# which in turn is a packaging.specifiers.SpecifierSet objects
@@ -1329,8 +1332,6 @@ def parse_with_dparse2(location, file_name=None):
13291332
is_resolved = True
13301333
purl = purl._replace(version=specifier.version)
13311334

1332-
extracted_requirement = f"{name}{requirement}"
1333-
13341335
dependent_packages.append(
13351336
models.DependentPackage(
13361337
purl=purl.to_string(),
@@ -1339,7 +1340,7 @@ def parse_with_dparse2(location, file_name=None):
13391340
is_runtime=True,
13401341
is_optional=False,
13411342
is_resolved=is_resolved,
1342-
extracted_requirement=extracted_requirement
1343+
extracted_requirement=requirement
13431344
)
13441345
)
13451346

@@ -1936,22 +1937,22 @@ def get_requirement_from_section(section, sub_section):
19361937
"""
19371938
Generate requirements from the `sub_section`
19381939
"""
1939-
content = section.get(sub_section)
1940-
if content:
1941-
for req in content.splitlines():
1942-
if req:
1943-
#pytest-mypy >= 0.9.1; \
1944-
req = req.replace("; \\", "")
1945-
# pip>=19.1 # For proper file:// URLs support.
1946-
if "#" in req:
1947-
req , _ = req.rsplit("#")
1948-
#pure-eval; black; tox;
1949-
req_split_by_semi_colon = req.split(";")
1950-
req_split_by_semi_colon = [req.strip() for req in req_split_by_semi_colon]
1951-
if len(req_split_by_semi_colon) >= 2 and not(req_split_by_semi_colon[1].startswith("python_version") # pip>=19.1 ;python_version > 3.7
1952-
or req_split_by_semi_colon[1].startswith("sys_platform") # pip>=19.1 ;sys_platform = "Windows"
1953-
or req_split_by_semi_colon[1].startswith("platform_system")): # pip>=19.1 ;platform_system = "Windows"
1954-
for temp_req in req_split_by_semi_colon:
1955-
yield temp_req
1956-
else:
1957-
yield req
1940+
content = section.get(sub_section) or ""
1941+
for req in content.splitlines():
1942+
if req:
1943+
#pytest-mypy >= 0.9.1; \
1944+
req = req.replace("; \\", "")
1945+
# pip>=19.1 # For proper file:// URLs support.
1946+
if "#" in req:
1947+
req , _ = req.rsplit("#")
1948+
#pure-eval; black; tox
1949+
req_split_by_semi_colon = req.split(";")
1950+
req_split_by_semi_colon = [req.strip() for req in req_split_by_semi_colon if req]
1951+
if len(req_split_by_semi_colon) >= 2 and not(req_split_by_semi_colon[1].startswith("python_version") # pip>=19.1 ;python_version > 3.7
1952+
or req_split_by_semi_colon[1].startswith("sys_platform") # pip>=19.1 ;sys_platform = "Windows"
1953+
or req_split_by_semi_colon[1].startswith("platform_system") # pip>=19.1 ;platform_system = "Windows"
1954+
or req_split_by_semi_colon[1].startswith("platform_python_implementation")):#pytest-black>=0.3.7; platform_python_implementation != "PyPy"
1955+
for temp_req in req_split_by_semi_colon:
1956+
yield temp_req
1957+
else:
1958+
yield req

src/python_inspector/resolution.py

Lines changed: 62 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -194,28 +194,7 @@ def get_requirements_for_package_from_pypi_simple(self, candidate):
194194
yield packaging.requirements.Requirement(str(dep.extracted_requirement))
195195

196196
if format == "Sdist":
197-
sdist = utils_pypi.download_sdist(
198-
name=candidate.name,
199-
version=str(candidate.version),
200-
repos=self.repos,
201-
)
202-
if sdist.endswith(".tar.gz"):
203-
sdist_file = sdist.rstrip(".tar.gz")
204-
file = tarfile.open(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, sdist))
205-
file.extractall(
206-
os.path.join(
207-
utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file
208-
)
209-
)
210-
file.close()
211-
if sdist.endswith(".zip"):
212-
sdist_file = sdist.rstrip(".zip")
213-
with ZipFile(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, sdist), "r") as zip:
214-
zip.extractall(
215-
os.path.join(
216-
utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file
217-
)
218-
)
197+
sdist_file = self.get_sdist_file(candidate)
219198
setup_py_path = os.path.join(
220199
utils_pypi.CACHE_THIRDPARTY_DIR,
221200
"extracted_sdists",
@@ -241,45 +220,67 @@ def get_requirements_for_package_from_pypi_simple(self, candidate):
241220
"requirements.txt",
242221
)
243222

244-
path_by_format = {
245-
"pkginfo": pkg_info_path,
246-
"setup-py": setup_py_path,
247-
"setup-cfg": setup_cfg_path,
248-
"requirement": requirement_path,
223+
path_by_sdist_parser = {
224+
PythonSdistPkgInfoFile: pkg_info_path,
225+
PythonSetupPyHandler: setup_py_path,
226+
SetupCfgHandler: setup_cfg_path,
227+
PipRequirementsFileHandler: requirement_path,
249228
}
250229

251-
handler_by_format = {
252-
"pkginfo": PythonSdistPkgInfoFile,
253-
"setup-py": PythonSetupPyHandler,
254-
"setup-cfg": SetupCfgHandler,
255-
"requirement": PipRequirementsFileHandler,
256-
}
257-
for format in ["pkginfo", "setup-py", "setup-cfg", "requirement"]:
258-
path = path_by_format[format]
259-
if os.path.exists(path):
260-
handler = handler_by_format[format]
261-
deps = list(handler.parse(path))
262-
assert len(deps) == 1
263-
dependencies = deps[0].dependencies
264-
for dep in dependencies:
265-
# skip if no purl can be extracted for dependency
266-
if dep.purl:
267-
dep_purl = PackageURL.from_string(dep.purl)
268-
if dep.scope == "install" and (
269-
not (dep.is_resolved)
270-
or (
271-
dep.is_resolved
272-
and dep_purl.name not in self.resolved_requirements
273-
)
274-
):
275-
if dep.is_resolved:
276-
self.resolved_requirements.append(dep_purl)
277-
# skip the requirement starting with -- like
278-
# --editable, --requirement
279-
if not dep.extracted_requirement.startswith("--"):
280-
yield packaging.requirements.Requirement(
281-
str(dep.extracted_requirement)
282-
)
230+
for handler, path in path_by_sdist_parser.items():
231+
if not os.path.exists(path):
232+
continue
233+
234+
deps = list(handler.parse(path))
235+
assert len(deps) == 1
236+
dependencies = deps[0].dependencies
237+
for dep in dependencies:
238+
if not dep.purl:
239+
continue
240+
241+
dep_purl = PackageURL.from_string(dep.purl)
242+
if not (
243+
dep.scope == "install"
244+
and (
245+
not (dep.is_resolved)
246+
or (
247+
dep.is_resolved
248+
and dep_purl.name not in self.resolved_requirements
249+
)
250+
)
251+
):
252+
continue
253+
if dep.is_resolved:
254+
self.resolved_requirements.append(dep_purl)
255+
# skip the requirement starting with -- like
256+
# --editable, --requirement
257+
if not dep.extracted_requirement.startswith("--"):
258+
yield packaging.requirements.Requirement(str(dep.extracted_requirement))
259+
260+
def get_sdist_file(self, candidate):
261+
sdist = utils_pypi.download_sdist(
262+
name=candidate.name,
263+
version=str(candidate.version),
264+
repos=self.repos,
265+
)
266+
sdist_file = None
267+
if sdist.endswith(".tar.gz"):
268+
sdist_file = sdist.rstrip(".tar.gz")
269+
file = tarfile.open(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, sdist))
270+
file.extractall(
271+
os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file)
272+
)
273+
file.close()
274+
if sdist.endswith(".zip"):
275+
sdist_file = sdist.rstrip(".zip")
276+
with ZipFile(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, sdist), "r") as zip:
277+
zip.extractall(
278+
os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file)
279+
)
280+
281+
if not sdist_file:
282+
raise Exception(f"Unable to extract sdist {sdist}")
283+
return sdist_file
283284

284285
def get_requirements_for_package_from_pypi_json_api(self, purl):
285286

@@ -455,11 +456,10 @@ def get_resolved_dependencies(
455456
If empty, use instead the PyPI.org JSON API exclusively instead
456457
"""
457458
resolved_requirements = [
458-
packaging.utils.canonicalize_name(r["requirement"].name)
459+
packaging.utils.canonicalize_name(r.name)
459460
for r in requirements
460-
if r["is_requirement_resolved"]
461+
if getattr(r, "is_requirement_resolved", False)
461462
]
462-
requirements = [r["requirement"] for r in requirements]
463463
resolver = Resolver(
464464
provider=PythonInputProvider(
465465
environment=environment, repos=repos, resolved_requirements=resolved_requirements

src/python_inspector/resolve_cli.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ def resolve_dependencies(
167167
# TODO: deduplicate me
168168
direct_dependencies = []
169169

170+
if PYPI_SIMPLE_URL not in index_urls:
171+
index_urls = (*index_urls, PYPI_SIMPLE_URL)
172+
170173
for req_file in requirement_files:
171174
deps = dependencies.get_dependencies_from_requirements(requirements_file=req_file)
172175
for extra_data in dependencies.get_extra_data_from_requirements(requirements_file=req_file):
@@ -273,13 +276,14 @@ def resolve(direct_dependencies, environment, repos=tuple(), as_tree=False, max_
273276
Used the provided ``repos`` list of PypiSimpleRepository.
274277
If empty, use instead the PyPI.org JSON API exclusively.
275278
"""
276-
requirements = [
277-
dict(
278-
requirement=Requirement(requirement_string=d.extracted_requirement),
279-
is_requirement_resolved=d.is_resolved,
280-
)
281-
for d in direct_dependencies
282-
]
279+
280+
requirements = []
281+
282+
for dependency in direct_dependencies:
283+
requirement = Requirement(requirement_string=dependency.extracted_requirement)
284+
requirement.is_requirement_resolved = dependency.is_resolved
285+
requirements.append(requirement)
286+
283287
resolved_dependencies = get_resolved_dependencies(
284288
requirements=requirements,
285289
environment=environment,

0 commit comments

Comments
 (0)