Skip to content

Commit 28988ab

Browse files
Merge pull request #4171 from aboutcode-org/fix-pypi-installed-wheel-assembly
Refactor and fix package assembly for installed wheels
2 parents e795bc6 + 0bd356c commit 28988ab

File tree

29 files changed

+1635
-8597
lines changed

29 files changed

+1635
-8597
lines changed

src/packagedcode/pypi.py

Lines changed: 23 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import json
1414
import logging
1515
import os
16+
import posixpath
1617
import re
1718
import sys
1819
import tempfile
@@ -390,19 +391,23 @@ def assign_package_to_resources(cls, package, resource, codebase, package_adder)
390391
Assign files to package for an installed wheel. This requires a bit
391392
of navigation around as the files can be in multiple places.
392393
"""
393-
site_packages = resource.parent(codebase).parent(codebase)
394+
dist_info_dir = resource.parent(codebase)
395+
if not dist_info_dir:
396+
return
397+
398+
# This could be a regular directory too which is not `site-packages`
399+
site_packages = dist_info_dir.parent(codebase)
394400
if not site_packages:
395401
return
402+
396403
package_data = resource.package_data
397404
assert len(resource.package_data) == 1, (
398405
f'Unsupported Pypi METADATA wheel structure: {resource.path!r} '
399406
f'with multiple {package_data!r}'
400407
)
401408

402409
package_data = models.PackageData.from_dict(package_data[0])
403-
404410
package_uid = package.package_uid
405-
406411
if package_uid:
407412
# save thyself!
408413
package_adder(package_uid, resource, codebase)
@@ -414,76 +419,33 @@ def assign_package_to_resources(cls, package, resource, codebase, package_adder)
414419
# relative paths need special treatment
415420
# most of thense are references to bin ../../../bin/wheel
416421
cannot_resolve = False
417-
ref_resource = None
422+
ref_resource = site_packages
423+
# note that resolving leading ".." always stays in the codebase
418424
while path_ref.startswith('..'):
419425
_, _, path_ref = path_ref.partition('../')
420-
ref_resource = site_packages.parent(codebase)
426+
ref_resource = ref_resource.parent(codebase)
421427
if not ref_resource:
422428
cannot_resolve = True
423429
break
430+
424431
if cannot_resolve or not ref_resource:
425432
# TODO:w e should log these kind of things
426433
continue
427434
else:
428-
if package_uid:
435+
ref_resource = codebase.get_resource(
436+
path=posixpath.join(ref_resource.path, path_ref)
437+
)
438+
if ref_resource and package_uid:
429439
package_adder(package_uid, ref_resource, codebase)
430440
else:
431-
ref_resource = get_resource_for_path(
432-
path=path_ref,
433-
root=site_packages,
434-
codebase=codebase,
441+
# These are absolute paths from the site-packages directory
442+
ref_resource = codebase.get_resource(
443+
path=posixpath.join(site_packages.path, path_ref)
435444
)
436445
if ref_resource and package_uid:
437446
package_adder(package_uid, ref_resource, codebase)
438447

439448

440-
def get_resource_for_path(path, root, codebase):
441-
"""
442-
Return a resource in ``codebase`` that has a ``path`` relative to the
443-
``root` Resource
444-
445-
For example, say we start from this:
446-
path: this/is/that therefore segments [this, is, that]
447-
root: /usr/foo
448-
449-
We would have these iterations:
450-
iteration1
451-
root = /usr/foo
452-
segments = [this, is, that]
453-
seg this
454-
segments = [is, that]
455-
children = [/usr/foo/this]
456-
root = /usr/foo/this
457-
458-
iteration2
459-
root = /usr/foo/this
460-
segments = [is, that]
461-
seg is
462-
segments = [that]
463-
children = [/usr/foo/this/is]
464-
root = /usr/foo/this/is
465-
466-
iteration3
467-
root = /usr/foo/this/is
468-
segments = [that]
469-
seg that
470-
segments = []
471-
children = [/usr/foo/this/is/that]
472-
root = /usr/foo/this/is/that
473-
474-
finally return root as /usr/foo/this/is/that
475-
"""
476-
segments = path.strip('/').split('/')
477-
while segments:
478-
seg = segments.pop(0)
479-
children = [c for c in root.children(codebase) if c.name == seg]
480-
if len(children) != 1:
481-
return
482-
else:
483-
root = children[0]
484-
return root
485-
486-
487449
class PyprojectTomlHandler(BaseExtractedPythonLayout):
488450
datasource_id = 'pypi_pyproject_toml'
489451
path_patterns = ('*pyproject.toml',)
@@ -1680,10 +1642,10 @@ def get_declared_license(metainfo):
16801642
# TODO: We should make the declared license as it is, this should be
16811643
# updated in scancode to parse a pure string
16821644
lic = get_attribute(metainfo, 'License')
1683-
1684-
license_file = None
1685-
if lic and 'file' in lic:
1686-
license_file = lic.pop('file')
1645+
license_file = get_attribute(metainfo, 'License-File')
1646+
if not license_file and lic:
1647+
if isinstance(lic, dict) and 'file' in lic.keys():
1648+
license_file = lic.pop('file')
16871649

16881650
if lic and not lic == 'UNKNOWN':
16891651
if 'text' in lic:

tests/packagedcode/data/conda/assembly-conda-scan.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@
177177
"is_private": false,
178178
"is_virtual": false,
179179
"extra_data": {
180-
"Documentation": "https://requests.readthedocs.io"
180+
"Documentation": "https://requests.readthedocs.io",
181+
"license_file": "LICENSE"
181182
},
182183
"repository_homepage_url": "https://pypi.org/project/requests",
183184
"repository_download_url": "https://pypi.org/packages/source/r/requests/requests-2.32.3.tar.gz",
@@ -491,7 +492,8 @@
491492
"is_private": false,
492493
"is_virtual": false,
493494
"extra_data": {
494-
"Documentation": "https://requests.readthedocs.io"
495+
"Documentation": "https://requests.readthedocs.io",
496+
"license_file": "LICENSE"
495497
},
496498
"dependencies": [
497499
{

tests/packagedcode/data/instance/python-package-instance-expected-with-test-manifests-with-license.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@
7777
"is_private": false,
7878
"is_virtual": false,
7979
"extra_data": {
80-
"Documentation": "https://setuptools.readthedocs.io/"
80+
"Documentation": "https://setuptools.readthedocs.io/",
81+
"license_file": "LICENSE"
8182
},
8283
"repository_homepage_url": "https://pypi.org/project/setuptools",
8384
"repository_download_url": "https://pypi.org/packages/source/s/setuptools/setuptools-58.2.0.tar.gz",
@@ -308,7 +309,8 @@
308309
"is_private": false,
309310
"is_virtual": false,
310311
"extra_data": {
311-
"Documentation": "https://setuptools.readthedocs.io/"
312+
"Documentation": "https://setuptools.readthedocs.io/",
313+
"license_file": "LICENSE"
312314
},
313315
"dependencies": [],
314316
"repository_homepage_url": "https://pypi.org/project/setuptools",

tests/packagedcode/data/instance/python-package-instance-expected-with-test-manifests.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@
7777
"is_private": false,
7878
"is_virtual": false,
7979
"extra_data": {
80-
"Documentation": "https://setuptools.readthedocs.io/"
80+
"Documentation": "https://setuptools.readthedocs.io/",
81+
"license_file": "LICENSE"
8182
},
8283
"repository_homepage_url": "https://pypi.org/project/setuptools",
8384
"repository_download_url": "https://pypi.org/packages/source/s/setuptools/setuptools-58.2.0.tar.gz",
@@ -194,7 +195,8 @@
194195
"is_private": false,
195196
"is_virtual": false,
196197
"extra_data": {
197-
"Documentation": "https://setuptools.readthedocs.io/"
198+
"Documentation": "https://setuptools.readthedocs.io/",
199+
"license_file": "LICENSE"
198200
},
199201
"dependencies": [],
200202
"repository_homepage_url": "https://pypi.org/project/setuptools",

tests/packagedcode/data/pypi/site-packages/codebase/bin/pip

Whitespace-only changes.

tests/packagedcode/data/pypi/site-packages/codebase/bin/pip-3.8

Whitespace-only changes.

tests/packagedcode/data/pypi/site-packages/codebase/bin/pip3

Whitespace-only changes.

tests/packagedcode/data/pypi/site-packages/codebase/lib/python3.9/site-packages/click-8.0.4.dist-info/RECORD

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,4 @@ click-8.0.4.dist-info/RECORD,,
55
click-8.0.4.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
66
click-8.0.4.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6
77
click/__init__.py,sha256=bOKrvMqmR9rN07vN_ycyrdF-EcTCl-EmuAjq-Fp4yPM,3243
8-
click/__pycache__/__init__.cpython-36.pyc,,
9-
click/__pycache__/_compat.cpython-36.pyc,,
10-
click/__pycache__/_termui_impl.cpython-36.pyc,,
11-
click/__pycache__/_textwrap.cpython-36.pyc,,
12-
click/__pycache__/_unicodefun.cpython-36.pyc,,
13-
click/__pycache__/_winconsole.cpython-36.pyc,,
14-
click/__pycache__/core.cpython-36.pyc,,
15-
click/__pycache__/decorators.cpython-36.pyc,,
16-
click/__pycache__/exceptions.cpython-36.pyc,,
17-
click/__pycache__/formatting.cpython-36.pyc,,
18-
click/__pycache__/globals.cpython-36.pyc,,
19-
click/__pycache__/parser.cpython-36.pyc,,
20-
click/__pycache__/shell_completion.cpython-36.pyc,,
21-
click/__pycache__/termui.cpython-36.pyc,,
22-
click/__pycache__/testing.cpython-36.pyc,,
23-
click/__pycache__/types.cpython-36.pyc,,
24-
click/__pycache__/utils.cpython-36.pyc,,
25-
click/_compat.py,sha256=JIHLYs7Jzz4KT9t-ds4o4jBzLjnwCiJQKqur-5iwCKI,18810
26-
click/_termui_impl.py,sha256=qK6Cfy4mRFxvxE8dya8RBhLpSC8HjF-lvBc6aNrPdwg,23451
27-
click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353
28-
click/_unicodefun.py,sha256=JKSh1oSwG_zbjAu4TBCa9tQde2P9FiYcf4MBfy5NdT8,3201
29-
click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860
30-
click/core.py,sha256=MYYmvqVa_dh4x4f-mr7VyNi6197ucYbJ0fsWW5EnCrA,111432
31-
click/decorators.py,sha256=5ZngOD72Flo8VLN29iarI0A5u_F-dxmqtUqx4KN8hOg,14879
32-
click/exceptions.py,sha256=7gDaLGuFZBeCNwY9ERMsF2-Z3R9Fvq09Zc6IZSKjseo,9167
33-
click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706
34-
click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961
35-
click/parser.py,sha256=cAEt1uQR8gq3-S9ysqbVU-fdAZNvilxw4ReJ_T1OQMk,19044
36-
click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37-
click/shell_completion.py,sha256=HHH0wMwlDW4WQhs8rRcS9M8MAo9gltGBN6V2PEbsTp0,18003
38-
click/termui.py,sha256=mZ12uc3-loFaV__vr8buxn9uIjAhy7QwVuZOQ8jDdjc,28873
39-
click/testing.py,sha256=ptpMYgRY7dVfE3UDgkgwayu9ePw98sQI3D7zZXiCpj4,16063
40-
click/types.py,sha256=rj2g3biAXKkNKV8vlwbIKSUlixhXybH84N84fwCYqUU,35092
41-
click/utils.py,sha256=M8tuplcFFHROha3vQ60ZRSakSB_ng6w9e8Uc1AugPZ0,18934
8+
click/core.py,sha256=MYYmvqVa_dh4x4f-mr7VyNi6197ucYbJ0fsWW5EnCrA,111432

0 commit comments

Comments
 (0)