Skip to content

Commit eb22af3

Browse files
authored
Merge pull request #2939 from nexB/2929-populate-for-packages-field
Populate for packages field correctly #2929
2 parents 3b7fe81 + 8453448 commit eb22af3

File tree

107 files changed

+2109
-997
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+2109
-997
lines changed

src/packagedcode/npm.py

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
from packagedcode import models
2020
from packagedcode.utils import combine_expressions
2121
from packagedcode.utils import normalize_vcs_url
22+
from packagedcode.utils import yield_dependencies_from_package_data
23+
from packagedcode.utils import yield_dependencies_from_package_resource
2224
import saneyaml
2325

2426
"""
@@ -40,31 +42,84 @@ class BaseNpmHandler(models.DatafileHandler):
4042

4143
@classmethod
4244
def assemble(cls, package_data, resource, codebase):
45+
"""
46+
If ``resource``, or one of its siblings, is a package.json file, use it
47+
to create and yield the package, the package dependencies, and the
48+
package resources.
49+
50+
When reporting the resources of a package, we alk the codebase, skipping
51+
the node_modules directory, assign resources to the package and yield
52+
resources.
53+
54+
For each lock file, assign dependencies to package instances and yield dependencies.
55+
56+
If there is no package.json, we do not have a package instance. In this
57+
case, we yield each of the dependencies in each lock file.
58+
"""
4359
datafile_name_patterns = (
44-
'package.json',
4560
'package-lock.json',
4661
'.package-lock.json',
4762
'npm-shrinkwrap.json',
4863
'yarn.lock',
4964
)
5065

51-
if resource.has_parent():
52-
dir_resource=resource.parent(codebase)
53-
else:
54-
dir_resource=resource
55-
56-
for assembled in cls.assemble_from_many_datafiles(
57-
datafile_name_patterns=datafile_name_patterns,
58-
directory=dir_resource,
59-
codebase=codebase,
60-
):
61-
if isinstance(assembled, models.Package):
62-
cls.assign_package_to_resources(
63-
package=assembled,
64-
resource=resource,
65-
codebase=codebase,
66+
package_resource = None
67+
if resource.name == 'package.json':
68+
package_resource = resource
69+
elif resource.name in datafile_name_patterns:
70+
if resource.has_parent():
71+
siblings = resource.siblings(codebase)
72+
package_resource = [r for r in siblings if r.name == 'package.json']
73+
if package_resource:
74+
package_resource = package_resource[0]
75+
76+
if package_resource:
77+
# do we have enough to create a package?
78+
if package_data.purl:
79+
package = models.Package.from_package_data(
80+
package_data=package_data,
81+
datafile_path=package_resource.path,
6682
)
67-
yield assembled
83+
package_uid = package.package_uid
84+
85+
if not package.license_expression:
86+
package.license_expression = compute_normalized_license(package.declared_license)
87+
88+
root = resource.parent(codebase)
89+
if root:
90+
for npm_res in cls.walk_npm(resource=root, codebase=codebase):
91+
if package_uid not in npm_res.for_packages:
92+
npm_res.for_packages.append(package_uid)
93+
npm_res.save(codebase)
94+
yield npm_res
95+
elif codebase.has_single_resource:
96+
if package_uid not in package_resource.for_packages:
97+
package_resource.for_packages.append(package_uid)
98+
package_resource.save(codebase)
99+
yield package_resource
100+
101+
yield package
102+
else:
103+
# we have no package, so deps are not for a specific package uid
104+
package_uid = None
105+
106+
# in all cases yield possible dependencies
107+
yield from yield_dependencies_from_package_data(package_data, package_resource.path, package_uid)
108+
109+
# we yield this as we do not want this further processed
110+
yield package_resource
111+
112+
for sibling in package_resource.siblings(codebase):
113+
if sibling.name in datafile_name_patterns:
114+
yield from yield_dependencies_from_package_resource(sibling, package_uid)
115+
116+
if package_uid not in sibling.for_packages:
117+
sibling.for_packages.append(package_uid)
118+
sibling.save(codebase)
119+
yield sibling
120+
else:
121+
# we do not have a package.json
122+
yield from yield_dependencies_from_package_resource(resource)
68123

69124
@classmethod
70125
def walk_npm(cls, resource, codebase, depth=0):
@@ -84,19 +139,9 @@ def walk_npm(cls, resource, codebase, depth=0):
84139

85140
if child.is_dir:
86141
depth += 1
87-
for subchild in cls.walk_skip(child, codebase, depth=depth):
142+
for subchild in cls.walk_npm(child, codebase, depth=depth):
88143
yield subchild
89144

90-
# TODO: this MUST BE USED
91-
@classmethod
92-
def assign_package_to_resources(cls, package, resource, codebase):
93-
"""
94-
Yield the Resources of an npm Package, ignoring nested mode_modules.
95-
"""
96-
root = resource.parent(codebase)
97-
if root:
98-
yield from cls.walk_npm(resource=root, codebase=codebase)
99-
100145

101146
def get_urls(namespace, name, version):
102147
return dict(

src/packagedcode/pypi.py

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import re
1717
import sys
1818
import zipfile
19+
from configparser import ConfigParser
20+
from io import StringIO
1921
from pathlib import Path
2022

2123
import dparse2
@@ -30,6 +32,8 @@
3032
from packagedcode import models
3133
from packagedcode.utils import build_description
3234
from packagedcode.utils import combine_expressions
35+
from packagedcode.utils import yield_dependencies_from_package_data
36+
from packagedcode.utils import yield_dependencies_from_package_resource
3337

3438
# FIXME: we always want to use the external library rather than the built-in for now
3539
import importlib_metadata
@@ -135,12 +139,55 @@ def assemble(cls, package_data, resource, codebase):
135139
'Pipfile',
136140
) + PipRequirementsFileHandler.path_patterns
137141

138-
parent = resource.parent(codebase)
139-
yield from cls.assemble_from_many_datafiles(
140-
datafile_name_patterns=datafile_name_patterns,
141-
directory=parent,
142-
codebase=codebase,
143-
)
142+
package_resource = None
143+
if resource.name in datafile_name_patterns:
144+
package_resource = resource
145+
146+
if package_resource:
147+
# do we have enough to create a package?
148+
if package_data.purl:
149+
package = models.Package.from_package_data(
150+
package_data=package_data,
151+
datafile_path=package_resource.path,
152+
)
153+
package_uid = package.package_uid
154+
155+
if not package.license_expression:
156+
package.license_expression = compute_normalized_license(package.declared_license)
157+
158+
root = package_resource.parent(codebase)
159+
if root:
160+
for py_res in root.walk(codebase):
161+
if py_res.is_dir:
162+
continue
163+
if package_uid not in py_res.for_packages:
164+
py_res.for_packages.append(package_uid)
165+
py_res.save(codebase)
166+
yield py_res
167+
elif codebase.has_single_resource:
168+
if package_uid not in package_resource.for_packages:
169+
package_resource.for_packages.append(package_uid)
170+
package_resource.save(codebase)
171+
yield package_resource
172+
yield package
173+
else:
174+
# we have no package, so deps are not for a specific package uid
175+
package_uid = None
176+
177+
# in all cases yield possible dependencies
178+
yield from yield_dependencies_from_package_data(package_data, package_resource.path, package_uid)
179+
yield package_resource
180+
181+
for sibling in package_resource.siblings(codebase):
182+
if sibling.name in datafile_name_patterns:
183+
yield from yield_dependencies_from_package_resource(sibling, package_uid)
184+
185+
if package_uid not in sibling.for_packages:
186+
sibling.for_packages.append(package_uid)
187+
sibling.save(codebase)
188+
yield sibling
189+
else:
190+
yield from yield_dependencies_from_package_resource(resource)
144191

145192
@classmethod
146193
def assign_package_to_resources(cls, package, resource, codebase):
@@ -547,6 +594,63 @@ class SetupCfgHandler(BaseExtractedPythonLayout):
547594
description = 'Python setup.cfg'
548595
documentation_url = 'https://peps.python.org/pep-0390/'
549596

597+
@classmethod
598+
def parse(cls, location):
599+
file_name = fileutils.file_name(location)
600+
601+
with open(location) as f:
602+
content = f.read()
603+
604+
metadata = {}
605+
parser = ConfigParser()
606+
parser.readfp(StringIO(content))
607+
for section in parser.values():
608+
if section.name == 'metadata':
609+
options = (
610+
'name',
611+
'version',
612+
'license',
613+
'url',
614+
'author',
615+
'author_email',
616+
)
617+
for name in options:
618+
content = section.get(name)
619+
if not content:
620+
continue
621+
metadata[name] = content
622+
623+
parties = []
624+
author = metadata.get('author')
625+
if author:
626+
parties = [
627+
models.Party(
628+
type=models.party_person,
629+
name=author,
630+
role='author',
631+
email=metadata.get('author_email'),
632+
)
633+
]
634+
635+
dependency_type = get_dparse2_supported_file_name(file_name)
636+
if not dependency_type:
637+
return
638+
639+
dependencies = parse_with_dparse2(
640+
location=location,
641+
file_name=dependency_type,
642+
)
643+
yield models.PackageData(
644+
datasource_id=cls.datasource_id,
645+
type=cls.default_package_type,
646+
name=metadata.get('name'),
647+
version=metadata.get('version'),
648+
parties=parties,
649+
homepage_url=metadata.get('url'),
650+
primary_language=cls.default_primary_language,
651+
dependencies=dependencies,
652+
)
653+
550654

551655
class PipfileHandler(BaseDependencyFileHandler):
552656
datasource_id = 'pipfile'

src/packagedcode/utils.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,26 @@ def find_root_resource(path, resource, codebase):
184184
return resource
185185

186186

187+
def yield_dependencies_from_package_data(package_data, datafile_path, package_uid):
188+
"""
189+
Yield a Dependency for each dependency from ``package_data.dependencies``
190+
"""
191+
from packagedcode import models
192+
dependent_packages = package_data.dependencies
193+
if dependent_packages:
194+
yield from models.Dependency.from_dependent_packages(
195+
dependent_packages=dependent_packages,
196+
datafile_path=datafile_path,
197+
datasource_id=package_data.datasource_id,
198+
package_uid=package_uid,
199+
)
200+
201+
202+
def yield_dependencies_from_package_resource(resource, package_uid=None):
203+
"""
204+
Yield a Dependency for each dependency from each package from``resource.package_data``
205+
"""
206+
from packagedcode import models
207+
for pkg_data in resource.package_data:
208+
pkg_data = models.PackageData.from_dict(pkg_data)
209+
yield from yield_dependencies_from_package_data(pkg_data, resource.path, package_uid)

tests/licensedcode/data/plugin_license/package/package.expected.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@
1212
"output_format_version": "2.0.0",
1313
"message": null,
1414
"errors": [],
15+
"warnings": [],
1516
"extra_data": {
17+
"system_environment": {
18+
"operating_system": "linux",
19+
"cpu_architecture": "64",
20+
"platform": "Linux-5.4.0-107-generic-x86_64-with-Ubuntu-18.04-bionic",
21+
"platform_version": "#121~18.04.1-Ubuntu SMP Thu Mar 24 17:21:33 UTC 2022",
22+
"python_version": "3.6.9 (default, Mar 15 2022, 13:55:28) \n[GCC 8.4.0]"
23+
},
1624
"spdx_license_list_version": "3.16",
1725
"files_count": 1
1826
}

tests/licensedcode/data/plugin_licenses_reference/scan.expected.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@
1313
"output_format_version": "2.0.0",
1414
"message": null,
1515
"errors": [],
16+
"warnings": [],
1617
"extra_data": {
18+
"system_environment": {
19+
"operating_system": "linux",
20+
"cpu_architecture": "64",
21+
"platform": "Linux-5.4.0-107-generic-x86_64-with-Ubuntu-18.04-bionic",
22+
"platform_version": "#121~18.04.1-Ubuntu SMP Thu Mar 24 17:21:33 UTC 2022",
23+
"python_version": "3.6.9 (default, Mar 15 2022, 13:55:28) \n[GCC 8.4.0]"
24+
},
1725
"spdx_license_list_version": "3.16",
1826
"files_count": 2
1927
}
@@ -314,7 +322,9 @@
314322
],
315323
"percentage_of_license_text": 100.0,
316324
"package_data": [],
317-
"for_packages": [],
325+
"for_packages": [
326+
"pkg:npm/[email protected]?uuid=fixed-uid-done-for-testing-5642512d1758"
327+
],
318328
"scan_errors": []
319329
},
320330
{

tests/packagedcode/data/about/aboutfiles.expected.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
"errors": [],
1414
"warnings": [],
1515
"extra_data": {
16+
"system_environment": {
17+
"operating_system": "linux",
18+
"cpu_architecture": "64",
19+
"platform": "Linux-5.4.0-107-generic-x86_64-with-Ubuntu-18.04-bionic",
20+
"platform_version": "#121~18.04.1-Ubuntu SMP Thu Mar 24 17:21:33 UTC 2022",
21+
"python_version": "3.6.9 (default, Mar 15 2022, 13:55:28) \n[GCC 8.4.0]"
22+
},
1623
"spdx_license_list_version": "3.16",
1724
"files_count": 3
1825
}

tests/packagedcode/data/bower/scan-expected.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
"errors": [],
1414
"warnings": [],
1515
"extra_data": {
16+
"system_environment": {
17+
"operating_system": "linux",
18+
"cpu_architecture": "64",
19+
"platform": "Linux-5.4.0-107-generic-x86_64-with-Ubuntu-18.04-bionic",
20+
"platform_version": "#121~18.04.1-Ubuntu SMP Thu Mar 24 17:21:33 UTC 2022",
21+
"python_version": "3.6.9 (default, Mar 15 2022, 13:55:28) \n[GCC 8.4.0]"
22+
},
1623
"spdx_license_list_version": "3.16",
1724
"files_count": 1
1825
}

tests/packagedcode/data/build/bazel/end2end-expected.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
"errors": [],
1414
"warnings": [],
1515
"extra_data": {
16+
"system_environment": {
17+
"operating_system": "linux",
18+
"cpu_architecture": "64",
19+
"platform": "Linux-5.4.0-107-generic-x86_64-with-Ubuntu-18.04-bionic",
20+
"platform_version": "#121~18.04.1-Ubuntu SMP Thu Mar 24 17:21:33 UTC 2022",
21+
"python_version": "3.6.9 (default, Mar 15 2022, 13:55:28) \n[GCC 8.4.0]"
22+
},
1623
"spdx_license_list_version": "3.16",
1724
"files_count": 6
1825
}

0 commit comments

Comments
 (0)