Skip to content

Commit c09309f

Browse files
authored
Merge pull request #2710 from nexB/2098-top-level-packages
Report `packages` at top level with file level `package_manifests`
2 parents 152abda + c90dedb commit c09309f

92 files changed

Lines changed: 20999 additions & 13248 deletions

File tree

Some content is hidden

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

CHANGELOG.rst

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ Changelog
88
Important API changes:
99
~~~~~~~~~~~~~~~~~~~~~~~~
1010

11+
- Main package API function `get_package_infos` is now deprecated, and is replaced by
12+
`get_package_manifests`.
13+
1114
- The data structure of the JSON output has changed for copyrights, authors
1215
and holders: we now use proper name for attributes and not a generic "value".
1316

@@ -54,7 +57,14 @@ Package detection:
5457
- OpenWRT packages.
5558
- Yocto/BitBake .bb recipes.
5659

57-
- We now support track the files of Package types.
60+
- Major changes in packages detection and reporting, codebase-level attribute `packages`
61+
with one or more package_manifests and files for the packages will be reported.
62+
The specific changes made are:
63+
64+
- The resource level attribute `packages` has been renamed to `package_manifests`,
65+
as these are really package manifests that are being detected.
66+
- A new codebase level attribute `packages` has been added which contains package
67+
instances created from package_manifests detected in the codebase.
5868

5969

6070
Outputs:
@@ -63,6 +73,16 @@ Outputs:
6373
- There is a new CycloneDX 1.2 output as XML and JSON.
6474

6575

76+
Output version
77+
--------------
78+
79+
Scancode Data Output Version is now 2.0.0.
80+
81+
Changes:
82+
83+
- rename resource level attribute `packages` to `package_manifests`.
84+
- add codebase level attribute `packages`.
85+
6686

6787
30.1.0 - 2021-09-25
6888
--------------------

src/formattedcode/output_csv.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def collect_keys(mapping, key_group):
194194
collect_keys(url_info, 'url')
195195
yield url_info
196196

197-
for package in scanned_file.get('packages', []):
197+
for package in scanned_file.get('package_manifests', []):
198198
flat = flatten_package(package, path)
199199
collect_keys(flat, 'package')
200200
yield flat

src/formattedcode/output_html.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def generate_output(results, version, template):
154154

155155
LICENSES = 'licenses'
156156
COPYRIGHTS = 'copyrights'
157-
PACKAGES = 'packages'
157+
PACKAGES = 'package_manifests'
158158

159159
# Create a flattened data dict keyed by path
160160
for scanned_file in results:
@@ -207,7 +207,7 @@ def generate_output(results, version, template):
207207
files = {
208208
'license_copyright': converted,
209209
'infos': converted_infos,
210-
'packages': converted_packages
210+
'package_manifests': converted_packages
211211
}
212212

213213
return template.generate(files=files, licenses=licenses, version=version)

src/formattedcode/templates/html/template.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@
233233
</table>
234234
{% endif %}
235235

236-
{% if files.packages %}
236+
{% if files.package_manifests %}
237237
<table>
238238
<caption>Package Information</caption>
239239
<thead>
@@ -245,7 +245,7 @@
245245
</tr>
246246
</thead>
247247
<tbody>
248-
{% for path, data in files.packages.items() %}
248+
{% for path, data in files.package_manifests.items() %}
249249
{% for row in data %}
250250
<tr>
251251
<td>{{ path }}</td>

src/packagedcode/maven.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def get_package_root(cls, manifest_resource, codebase):
6666
if manifest_resource.name.endswith(('pom.xml', '.pom',)):
6767
# the root is either the parent or further up for poms stored under
6868
# a META-INF dir
69-
package_data = manifest_resource.packages
69+
package_data = manifest_resource.package_manifests
7070
if not package_data:
7171
return manifest_resource
7272
package_data = package_data[0]

src/packagedcode/plugin_package.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ class PackageScanner(ScanPlugin):
5050
"""
5151

5252
resource_attributes = {}
53-
resource_attributes['packages'] = attr.ib(default=attr.Factory(list), repr=False)
53+
codebase_attributes = {}
54+
resource_attributes['package_manifests'] = attr.ib(default=attr.Factory(list), repr=False)
55+
codebase_attributes['packages'] = attr.ib(default=attr.Factory(list), repr=False)
5456

5557
sort_order = 6
5658

@@ -78,13 +80,15 @@ def get_scanner(self, **kwargs):
7880
"""
7981
Return a scanner callable to scan a Resource for packages.
8082
"""
81-
from scancode.api import get_package_info
82-
return get_package_info
83+
from scancode.api import get_package_manifests
84+
return get_package_manifests
8385

8486
def process_codebase(self, codebase, **kwargs):
8587
"""
8688
Set the package root given a package "type".
8789
"""
90+
create_packages_from_manifests(codebase, **kwargs)
91+
8892
if codebase.has_single_resource:
8993
# What if we scanned a single file and we do not have a root proper?
9094
return
@@ -93,6 +97,19 @@ def process_codebase(self, codebase, **kwargs):
9397
set_packages_root(resource, codebase)
9498

9599

100+
def create_packages_from_manifests(codebase, **kwargs):
101+
"""
102+
Create package instances from package manifests present in the codebase.
103+
"""
104+
package_manifests = []
105+
106+
for resource in codebase.walk(topdown=False):
107+
if resource.package_manifests:
108+
package_manifests.extend(resource.package_manifests)
109+
110+
codebase.attributes.packages.extend(package_manifests)
111+
112+
96113
def set_packages_root(resource, codebase):
97114
"""
98115
Set the root_path attribute as the path to the root Resource for a given
@@ -102,25 +119,25 @@ def set_packages_root(resource, codebase):
102119
if not resource.is_file:
103120
return
104121

105-
packages = resource.packages
106-
if not packages:
122+
package_manifests = resource.package_manifests
123+
if not package_manifests:
107124
return
108125
# NOTE: we are dealing with a single file therefore there should be only be
109126
# a single package detected. But some package manifests can document more
110127
# than one package at a time such as multiple arches/platforms for a gempsec
111128
# or multiple sub package (with "%package") in an RPM .spec file.
112129

113130
modified = False
114-
for package in packages:
115-
package_instance = get_package_instance(package)
131+
for package_manifest in package_manifests:
132+
package_instance = get_package_instance(package_manifest)
116133
package_root = package_instance.get_package_root(resource, codebase)
117134
if not package_root:
118135
# this can happen if we scan a single resource that is a package package
119136
continue
120137
# What if the target resource (e.g. a parent) is the root and we are in stripped root mode?
121138
if package_root.is_root and codebase.strip_root:
122139
continue
123-
package['root_path'] = package_root.path
140+
package_manifest['root_path'] = package_root.path
124141
modified = True
125142

126143
if modified:

src/packagedcode/recognize.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ def logger_debug(*args):
4343
"""
4444

4545

46-
def recognize_packages(location):
46+
def recognize_package_manifests(location):
4747
"""
48-
Return a list of Package object if any packages were recognized for this
48+
Return a list of Package objects if any package_manifests were recognized for this
4949
`location`, or None if there were no Packages found. Raises Exceptions on errors.
5050
"""
5151

@@ -67,7 +67,7 @@ def recognize_packages(location):
6767
'fname:', filename, 'ext:', extension,
6868
)
6969

70-
recognized_packages = []
70+
recognized_package_manifests = []
7171
for package_type in PACKAGE_TYPES:
7272
# Note: default to True if there is nothing to match against
7373
metafiles = package_type.metafiles
@@ -86,8 +86,8 @@ def recognize_packages(location):
8686
'recognize_packages: recognized.license_expression:',
8787
recognized.license_expression,
8888
)
89-
recognized_packages.append(recognized)
90-
return recognized_packages
89+
recognized_package_manifests.append(recognized)
90+
return recognized_package_manifests
9191

9292
type_matched = False
9393
if package_type.filetypes:
@@ -124,19 +124,19 @@ def recognize_packages(location):
124124
if TRACE:
125125
logger_debug('recognize_packages: recognized', recognized)
126126

127-
recognized_packages.append(recognized)
127+
recognized_package_manifests.append(recognized)
128128

129129
except NotImplementedError:
130130
# build a plain package if recognize is not yet implemented
131131
recognized = package_type()
132132
if TRACE:
133133
logger_debug('recognize_packages: recognized', recognized)
134134

135-
recognized_packages.append(recognized)
135+
recognized_package_manifests.append(recognized)
136136

137137
if SCANCODE_DEBUG_PACKAGE_API:
138138
raise
139139

140-
return recognized_packages
140+
return recognized_package_manifests
141141

142142
if TRACE: logger_debug('recognize_packages: no match for type:', package_type)

src/scancode/api.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -287,32 +287,68 @@ def _licenses_data_from_match(
287287
SCANCODE_DEBUG_PACKAGE_API = os.environ.get('SCANCODE_DEBUG_PACKAGE_API', False)
288288

289289

290-
def get_package_info(location, **kwargs):
290+
def _get_package_manifests(location):
291291
"""
292-
Return a mapping of package manifest information detected in the
293-
file at `location`.
292+
Return a mapping of package manifest information detected in the file at `location`.
294293
295294
Note that all exceptions are caught if there are any errors while parsing a
296295
package manifest.
297296
"""
298-
from packagedcode.recognize import recognize_packages
297+
from packagedcode.recognize import recognize_package_manifests
299298
try:
300-
recognized_packages = recognize_packages(location)
301-
if recognized_packages:
302-
return dict(packages=[package.to_dict() for package in recognized_packages])
299+
recognized_package_manifests = recognize_package_manifests(location)
300+
if recognized_package_manifests:
301+
return recognized_package_manifests
303302
except Exception as e:
304303
if TRACE:
305-
logger.error('get_package_info: {}: Exception: {}'.format(location, e))
304+
logger.error('_get_package_manifests: {}: Exception: {}'.format(location, e))
306305

307306
if SCANCODE_DEBUG_PACKAGE_API:
308307
raise
309308
else:
310309
# attention: we are swallowing ALL exceptions here!
311310
pass
312311

312+
313+
def get_package_info(location, **kwargs):
314+
"""
315+
Return a mapping of package information detected in the file at `location`.
316+
317+
This API function is DEPRECATED, use `get_package_manifests` instead.
318+
"""
319+
import warnings
320+
warnings.warn(
321+
"`get_package_info` is deprecated. Use `get_package_manifests` instead.",
322+
DeprecationWarning,
323+
stacklevel=1
324+
)
325+
326+
recognized_packages = _get_package_manifests(location)
327+
328+
if recognized_packages:
329+
return dict(packages=[
330+
packages.to_dict()
331+
for packages in recognized_packages
332+
])
333+
313334
return dict(packages=[])
314335

315336

337+
def get_package_manifests(location, **kwargs):
338+
"""
339+
Return a mapping of package manifest information detected in the file at `location`.
340+
"""
341+
recognized_package_manifests = _get_package_manifests(location)
342+
343+
if recognized_package_manifests:
344+
return dict(package_manifests=[
345+
package_manifests.to_dict()
346+
for package_manifests in recognized_package_manifests
347+
])
348+
349+
return dict(package_manifests=[])
350+
351+
316352
def get_file_info(location, **kwargs):
317353
"""
318354
Return a mapping of file information collected for the file at `location`.

src/scancode_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def _create_dir(location):
8484

8585
# See https://github.com/nexB/scancode-toolkit/issues/2653 for more information
8686
# on the data format version
87-
__output_format_version__ = '1.0.0'
87+
__output_format_version__ = '2.0.0'
8888

8989
#
9090
spdx_license_list_version = '3.14'

src/summarycode/classify.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,25 +169,25 @@ def process_codebase(self, codebase, classify, **kwargs):
169169

170170
root_path = codebase.root.path
171171

172-
has_packages = hasattr(codebase.root, 'packages')
173-
if not has_packages:
172+
has_package_manifests = hasattr(codebase.root, 'package_manifests')
173+
if not has_package_manifests:
174174
# FIXME: this is not correct... we may still have cases where this
175175
# is wrong: e.g. a META-INF directory and we may not have a package
176176
return
177177

178178

179179
for resource in codebase.walk(topdown=True):
180-
packages_info = resource.packages or []
180+
package_manifests_info = resource.package_manifests or []
181181

182-
if not packages_info:
182+
if not package_manifests_info:
183183
continue
184184
if not resource.has_children():
185185
continue
186186

187187
descendants = None
188188

189-
for package_info in packages_info:
190-
package_class = get_package_class(package_info)
189+
for package_manifest_info in package_manifests_info:
190+
package_class = get_package_class(package_manifest_info)
191191
extra_root_dirs = package_class.extra_root_dirs()
192192
extra_key_files = package_class.extra_key_files()
193193
if TRACE:

0 commit comments

Comments
 (0)