Skip to content

Commit e7ec8f7

Browse files
committed
Support both wheels and sdist
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent 1cbebd5 commit e7ec8f7

4 files changed

Lines changed: 118 additions & 73 deletions

File tree

src/_packagedcode/pypi.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -453,8 +453,9 @@ def parse_metadata(location, datasource_id, package_type):
453453
type=package_type,
454454
primary_language='Python',
455455
name=name,
456-
version=version,
457-
description=get_description(meta, location),
456+
version=version, #TODO: https://github.com/nexB/scancode-toolkit/issues/3014
457+
# description=get_description(meta, location),
458+
description = "",
458459
declared_license=get_declared_license(meta),
459460
keywords=get_keywords(meta),
460461
parties=get_parties(meta),
@@ -1301,7 +1302,6 @@ def parse_with_dparse2(location, file_name=None):
13011302
dependent_packages = []
13021303

13031304
for dependency in dep_file.dependencies:
1304-
# print(dependency.serialize())
13051305
name = dependency.name
13061306
is_resolved = False
13071307
purl = PackageURL(type='pypi', name=dependency.name)

src/python_inspector/resolution.py

Lines changed: 114 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from resolvelib.reporters import BaseReporter
2828

2929
from _packagedcode.pypi import PipRequirementsFileHandler
30+
from _packagedcode.pypi import PypiWheelHandler
3031
from _packagedcode.pypi import PythonSdistPkgInfoFile
3132
from _packagedcode.pypi import PythonSetupPyHandler
3233
from _packagedcode.pypi import SetupCfgHandler
@@ -74,6 +75,7 @@ def __init__(self, environment=None, repos=tuple()):
7475
self.repos = repos or []
7576
self.versions_by_package = {}
7677
self.dependencies_by_purl = {}
78+
self.wheel_or_sdist_by_package = {}
7779

7880
def identify(self, requirement_or_candidate):
7981
"""Given a requirement, return an identifier for it. Overridden."""
@@ -109,17 +111,37 @@ def get_versions_for_package_from_repo(self, name, repo):
109111
"""
110112
Return a list of versions for a package name from a repo
111113
"""
114+
versions = []
112115
for version, package in repo.get_package_versions(name).items():
116+
name = packaging.utils.canonicalize_name(package.name)
117+
purl = PackageURL(type="pypi", name=name, version=version)
118+
python_version = packaging.version.parse(
119+
get_python_version_from_env_tag(self.environment.python_version)
120+
)
121+
wheels = list(package.get_supported_wheels(environment=self.environment))
122+
if wheels:
123+
valid_wheel_present = False
124+
for wheel in wheels:
125+
if wheel.requires_python and python_version in SpecifierSet(
126+
wheel.requires_python
127+
):
128+
valid_wheel_present = True
129+
if not wheel.requires_python:
130+
valid_wheel_present = True
131+
if valid_wheel_present:
132+
self.wheel_or_sdist_by_package[str(purl)] = "Wheel"
133+
versions.append(version)
134+
continue
113135
if package.sdist:
114-
python_version = packaging.version.parse(
115-
get_python_version_from_env_tag(self.environment.python_version)
116-
)
117136
if package.sdist.requires_python and python_version in SpecifierSet(
118137
package.sdist.requires_python
119138
):
120-
yield version
139+
self.wheel_or_sdist_by_package[str(purl)] = "Sdist"
140+
versions.append(version)
121141
if not package.sdist.requires_python:
122-
yield version
142+
self.wheel_or_sdist_by_package[str(purl)] = "Sdist"
143+
versions.append(version)
144+
return versions
123145

124146
def get_versions_for_package_from_pypi_json_api(self, name):
125147
"""
@@ -145,74 +167,98 @@ def get_requirements_for_package(self, purl, candidate):
145167
return self.get_requirements_for_package_from_pypi_json_api(purl)
146168

147169
def get_requirements_for_package_from_pypi_simple(self, candidate):
148-
sdist = utils_pypi.download_sdist(
149-
name=candidate.name,
150-
version=str(candidate.version),
151-
repos=self.repos,
152-
)
153170

154-
if sdist.endswith(".tar.gz"):
155-
sdist_file = sdist.rstrip(".tar.gz")
156-
file = tarfile.open(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, sdist))
157-
file.extractall(
158-
os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file)
171+
purl = PackageURL(type="pypi", name=candidate.name, version=str(candidate.version))
172+
173+
wheel_or_sdist = self.wheel_or_sdist_by_package[str(purl)]
174+
175+
if wheel_or_sdist == "Wheel":
176+
wheels = utils_pypi.download_wheel(
177+
name=candidate.name,
178+
version=str(candidate.version),
179+
environment=self.environment,
180+
repos=self.repos,
159181
)
160-
file.close()
161-
if sdist.endswith(".zip"):
162-
sdist_file = sdist.rstrip(".zip")
163-
with ZipFile(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, sdist), "r") as zip:
164-
zip.extractall(
165-
os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file)
182+
for wheel in wheels:
183+
deps = list(
184+
PypiWheelHandler.parse(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, wheel))
166185
)
167-
setup_py_path = os.path.join(
168-
utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file, sdist_file, "setup.py"
169-
)
170-
setup_cfg_path = os.path.join(
171-
utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file, sdist_file, "setup.cfg"
172-
)
173-
pkg_info_path = os.path.join(
174-
utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file
175-
)
176-
requirement_path = os.path.join(
177-
utils_pypi.CACHE_THIRDPARTY_DIR,
178-
"extracted_sdists",
179-
sdist_file,
180-
sdist_file,
181-
"requirements.txt",
182-
)
183-
pyproject_path = os.path.join(
184-
utils_pypi.CACHE_THIRDPARTY_DIR,
185-
"extracted_sdists",
186-
sdist_file,
187-
sdist_file,
188-
"pyproject.toml",
189-
)
190-
191-
path_by_format = {
192-
"pkginfo": pkg_info_path,
193-
"setup-py": setup_py_path,
194-
"setup-cfg": setup_cfg_path,
195-
"requirement": requirement_path,
196-
}
197-
198-
handler_by_format = {
199-
"pkginfo": PythonSdistPkgInfoFile,
200-
"setup-py": PythonSetupPyHandler,
201-
"setup-cfg": SetupCfgHandler,
202-
"requirement": PipRequirementsFileHandler,
203-
}
204-
for format in ["pkginfo", "setup-py", "setup-cfg", "requirement"]:
205-
path = path_by_format[format]
206-
if os.path.exists(path):
207-
handler = handler_by_format[format]
208-
deps = list(handler.parse(path))
209186
assert len(deps) == 1
210-
dependencies = deps[0].dependencies
211-
for dep in dependencies:
187+
deps = deps[0].dependencies
188+
for dep in deps:
212189
if dep.scope == "install":
213190
yield packaging.requirements.Requirement(str(dep.extracted_requirement))
214-
if dependencies:
215-
break
191+
192+
if wheel_or_sdist == "Sdist":
193+
sdist = utils_pypi.download_sdist(
194+
name=candidate.name,
195+
version=str(candidate.version),
196+
repos=self.repos,
197+
)
198+
199+
if sdist.endswith(".tar.gz"):
200+
sdist_file = sdist.rstrip(".tar.gz")
201+
file = tarfile.open(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, sdist))
202+
file.extractall(
203+
os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file)
204+
)
205+
file.close()
206+
if sdist.endswith(".zip"):
207+
sdist_file = sdist.rstrip(".zip")
208+
with ZipFile(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, sdist), "r") as zip:
209+
zip.extractall(
210+
os.path.join(
211+
utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file
212+
)
213+
)
214+
setup_py_path = os.path.join(
215+
utils_pypi.CACHE_THIRDPARTY_DIR,
216+
"extracted_sdists",
217+
sdist_file,
218+
sdist_file,
219+
"setup.py",
220+
)
221+
setup_cfg_path = os.path.join(
222+
utils_pypi.CACHE_THIRDPARTY_DIR,
223+
"extracted_sdists",
224+
sdist_file,
225+
sdist_file,
226+
"setup.cfg",
227+
)
228+
pkg_info_path = os.path.join(
229+
utils_pypi.CACHE_THIRDPARTY_DIR, "extracted_sdists", sdist_file
230+
)
231+
requirement_path = os.path.join(
232+
utils_pypi.CACHE_THIRDPARTY_DIR,
233+
"extracted_sdists",
234+
sdist_file,
235+
sdist_file,
236+
"requirements.txt",
237+
)
238+
239+
path_by_format = {
240+
"pkginfo": pkg_info_path,
241+
"setup-py": setup_py_path,
242+
"setup-cfg": setup_cfg_path,
243+
"requirement": requirement_path,
244+
}
245+
246+
handler_by_format = {
247+
"pkginfo": PythonSdistPkgInfoFile,
248+
"setup-py": PythonSetupPyHandler,
249+
"setup-cfg": SetupCfgHandler,
250+
"requirement": PipRequirementsFileHandler,
251+
}
252+
for format in ["pkginfo", "setup-py", "setup-cfg", "requirement"]:
253+
path = path_by_format[format]
254+
if os.path.exists(path):
255+
handler = handler_by_format[format]
256+
deps = list(handler.parse(path))
257+
assert len(deps) == 1
258+
dependencies = deps[0].dependencies
259+
for dep in dependencies:
260+
if dep.scope == "install":
261+
yield packaging.requirements.Requirement(str(dep.extracted_requirement))
216262

217263
def get_requirements_for_package_from_pypi_json_api(self, purl):
218264

@@ -242,7 +288,7 @@ def _iter_matches(self, identifier, requirements, incompatibilities):
242288
"""
243289
Yield candidates for the given identifier, requirements and incompatibilities
244290
"""
245-
name, _, _extras = identifier.partition("[")
291+
name, _, _ = identifier.partition("[")
246292
bad_versions = {c.version for c in incompatibilities[identifier]}
247293
extras = {e for r in requirements[identifier] for e in r.extras}
248294
if not self.repos:

src/python_inspector/utils_pypi.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,7 +1426,6 @@ def _get_package_versions_map(
14261426
verbose=verbose,
14271427
echo_func=echo_func,
14281428
)
1429-
# print(links)
14301429
# note that this is sorted so the mapping is also sorted
14311430
versions = {
14321431
package.version: package
@@ -1510,7 +1509,6 @@ def fetch_links(
15101509
verbose=verbose,
15111510
echo_func=echo_func,
15121511
)
1513-
# print(text)
15141512
soup = BeautifulSoup(text, features="html.parser")
15151513
anchor_tags = soup.find_all("a")
15161514
links_with_requires_python = []

tests/test_resolution.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ def test_without_supported_wheels():
137137
"pkg:pypi/cryptography@37.0.3",
138138
"pkg:pypi/hyperlink@21.0.0",
139139
"pkg:pypi/idna@3.3",
140+
"pkg:pypi/pycparser@2.21",
140141
"pkg:pypi/setuptools@62.6.0",
141142
"pkg:pypi/txaio@22.2.1",
142143
]

0 commit comments

Comments
 (0)