Skip to content

Commit 36e7ffc

Browse files
authored
Merge pull request #3036 from nexB/release-31-rc4-prep
Release 31 rc4 prep
2 parents 04d67da + 1862c2b commit 36e7ffc

File tree

651 files changed

+20309
-6902
lines changed

Some content is hidden

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

651 files changed

+20309
-6902
lines changed

CHANGELOG.rst

+5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Important API changes:
3434

3535
- There is a a new top-level "dependencies" attribute that contains each
3636
dependency instance, these can be standalone or releated to a package.
37+
These contain a new "extra_data" object.
3738

3839
- There is a new resource-level attribute "for_packages" which refers to
3940
packages through package_uuids (pURL + uuid string).
@@ -172,6 +173,10 @@ Package detection:
172173
- The package_data attribute `dependencies` (which is a list of DependentPackages),
173174
now has a new attribute `resolved_package` with a package data mapping.
174175
Also the `requirement` attribute is renamed to `extracted_requirement`.
176+
There is a new `extra_data` to collect extra data as needed.
177+
178+
- For Pypi packages, python_requires is treated as a package dependency.
179+
175180

176181

177182
License Clarity Scoring Update

src/packagedcode/alpine.py

+6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ def parse(cls, location):
5858
package_type=cls.default_package_type,
5959
)
6060

61+
@classmethod
62+
def compute_normalized_license(cls, package):
63+
_declared, detected = detect_declared_license(package.declared_license)
64+
return detected
65+
6166
@classmethod
6267
def assemble(cls, package_data, resource, codebase):
6368
# get the root resource of the rootfs
@@ -76,6 +81,7 @@ def assemble(cls, package_data, resource, codebase):
7681

7782
package.license_expression = cls.compute_normalized_license(package)
7883

84+
7985
dependent_packages = package_data.dependencies
8086
if dependent_packages:
8187
yield from models.Dependency.from_dependent_packages(

src/packagedcode/debian.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828

2929
TRACE = SCANCODE_DEBUG_PACKAGE_API
3030

31+
3132
def logger_debug(*args):
3233
pass
3334

35+
3436
logger = logging.getLogger(__name__)
3537

3638
if TRACE:
@@ -43,7 +45,6 @@ def logger_debug(*args):
4345
' '.join(isinstance(a, str) and a or repr(a) for a in args)
4446
)
4547

46-
4748
# TODO: add dependencies
4849

4950

@@ -282,14 +283,27 @@ def assemble(cls, package_data, resource, codebase):
282283
resources = []
283284
# TODO: keep track of missing files
284285
for res in root_resource.walk(codebase):
286+
if TRACE:
287+
logger_debug(f' debian: assemble: root_walk: res: {res}')
285288
if not res.path.endswith(assemblable_paths):
286289
continue
287290

288291
for pkgdt in res.package_data:
289292
package_data = models.PackageData.from_dict(pkgdt)
293+
if TRACE:
294+
# logger_debug(f' debian: assemble: root_walk: package_data: {package_data}')
295+
logger_debug(f' debian: assemble: root_walk: package_data: {package_data.license_expression}')
296+
297+
# Most debian secondary files are only specific to a name. We
298+
# have a few cases where the arch is included in the lists and
299+
# md5sums.
290300
package.update(
291301
package_data=package_data,
292302
datafile_path=res.path,
303+
replace=False,
304+
include_version=False,
305+
include_qualifiers=False,
306+
include_subpath=False,
293307
)
294308
package_file_references.extend(package_data.file_references)
295309

src/packagedcode/models.py

+107-9
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
# See https://aboutcode.org for more information about nexB OSS projects.
88
#
99

10+
import logging
1011
import os
1112
import uuid
1213
from fnmatch import fnmatchcase
13-
import logging
1414

1515
import attr
1616
from packageurl import normalize_qualifiers
@@ -26,7 +26,15 @@
2626
from commoncode.datautils import String
2727
from commoncode.fileutils import as_posixpath
2828
from commoncode.resource import Resource
29-
from typecode import contenttype
29+
try:
30+
from typecode import contenttype
31+
except ImportError:
32+
contenttype = None
33+
34+
try:
35+
from packagedcode import licensing
36+
except ImportError:
37+
licensing = None
3038

3139
"""
3240
This module contain data models for package and dependencies, abstracting and
@@ -366,6 +374,11 @@ class DependentPackage(ModelMixin):
366374
'lockfiles for Composer or Cargo contain extra dependency data.'
367375
)
368376

377+
extra_data = Mapping(
378+
label='extra data',
379+
help='A mapping of arbitrary extra data.',
380+
)
381+
369382

370383
@attr.attributes(slots=True)
371384
class Dependency(DependentPackage):
@@ -771,7 +784,13 @@ def compute_normalized_license(declared_license, expression_symbols=None):
771784
if not declared_license:
772785
return
773786

774-
from packagedcode import licensing
787+
if not licensing:
788+
if TRACE:
789+
logger_debug(
790+
f'Failed to compute license for {declared_license!r}: '
791+
'cannot import packagedcode.licensing')
792+
return 'unknown'
793+
775794
try:
776795
return licensing.get_normalized_expression(
777796
query_string=declared_license,
@@ -781,7 +800,6 @@ def compute_normalized_license(declared_license, expression_symbols=None):
781800
# we never fail just for this
782801
if TRACE:
783802
logger_debug(f'Failed to compute license for {declared_license!r}: {e!r}')
784-
# FIXME: add logging
785803
return 'unknown'
786804

787805

@@ -852,7 +870,8 @@ def is_datafile(cls, location, filetypes=tuple(), _bare_filename=False):
852870
filetypes = filetypes or cls.filetypes
853871
if not filetypes:
854872
return True
855-
else:
873+
# we check for contenttype IFF this is available
874+
if contenttype:
856875
T = contenttype.get_type(location)
857876
actual_type = T.filetype_file.lower()
858877
return any(ft in actual_type for ft in filetypes)
@@ -1207,6 +1226,13 @@ def __attrs_post_init__(self, *args, **kwargs):
12071226
def to_dict(self):
12081227
return super().to_dict(with_details=False)
12091228

1229+
def to_package_data(self):
1230+
mapping = super().to_dict(with_details=True)
1231+
mapping.pop('package_uid', None)
1232+
mapping.pop('datafile_paths', None)
1233+
mapping.pop('datasource_ids', None)
1234+
return PackageData.from_dict(mapping)
1235+
12101236
@classmethod
12111237
def from_package_data(cls, package_data, datafile_path):
12121238
"""
@@ -1255,7 +1281,15 @@ def is_compatible(self, package_data, include_qualifiers=True):
12551281
and self.primary_language == package_data.primary_language
12561282
)
12571283

1258-
def update(self, package_data, datafile_path, replace=False):
1284+
def update(
1285+
self,
1286+
package_data,
1287+
datafile_path,
1288+
replace=False,
1289+
include_version=True,
1290+
include_qualifiers=False,
1291+
include_subpath=False,
1292+
):
12591293
"""
12601294
Update this Package with data from the ``package_data`` PackageData.
12611295
@@ -1281,9 +1315,15 @@ def update(self, package_data, datafile_path, replace=False):
12811315
if isinstance(package_data, dict):
12821316
package_data = PackageData.from_dict(package_data)
12831317

1284-
if not self.is_compatible(package_data, include_qualifiers=False):
1318+
if not is_compatible(
1319+
purl1=self,
1320+
purl2=package_data,
1321+
include_version=include_version,
1322+
include_qualifiers=include_qualifiers,
1323+
include_subpath=include_subpath,
1324+
):
12851325
if TRACE_UPDATE:
1286-
logger_debug(f'update: {self.purl} not compatible with: {package_data.purl}')
1326+
logger_debug(f'update: skipping: {self.purl} is not compatible with: {package_data.purl}')
12871327
return False
12881328

12891329
# always append these new items
@@ -1345,6 +1385,56 @@ def get_packages_files(self, codebase):
13451385
yield resource
13461386

13471387

1388+
def is_compatible(
1389+
purl1,
1390+
purl2,
1391+
include_version=True,
1392+
include_qualifiers=True,
1393+
include_subpath=True,
1394+
):
1395+
"""
1396+
Return True if the ``purl1`` PackageURL-like object is compatible with
1397+
the ``purl2`` PackageURL-like object, e.g. it is about the same package.
1398+
PackageData objectys are PackageURL-like.
1399+
1400+
For example::
1401+
>>> p1 = PackageURL.from_string('pkg:deb/[email protected]?arch=arm64')
1402+
>>> p2 = PackageURL.from_string('pkg:deb/[email protected]')
1403+
>>> p3 = PackageURL.from_string('pkg:deb/libssl')
1404+
>>> p4 = PackageURL.from_string('pkg:deb/libncurses5')
1405+
>>> p5 = PackageURL.from_string('pkg:deb/[email protected]?arch=arm64#/sbin')
1406+
>>> is_compatible(p1, p2)
1407+
False
1408+
>>> is_compatible(p1, p2, include_qualifiers=False)
1409+
True
1410+
>>> is_compatible(p1, p4)
1411+
False
1412+
>>> is_compatible(p1, p4, include_version=False, include_qualifiers=False)
1413+
True
1414+
>>> is_compatible(p3, p4)
1415+
False
1416+
>>> is_compatible(p1, p5)
1417+
False
1418+
>>> is_compatible(p1, p5, include_subpath=False)
1419+
True
1420+
"""
1421+
is_compatible = (
1422+
purl1.type == purl2.type
1423+
and purl1.namespace == purl2.namespace
1424+
and purl1.name == purl2.name
1425+
)
1426+
if include_version:
1427+
is_compatible = is_compatible and (purl1.version == purl2.version)
1428+
1429+
if include_qualifiers:
1430+
is_compatible = is_compatible and (purl1.qualifiers == purl2.qualifiers)
1431+
1432+
if include_subpath:
1433+
is_compatible = is_compatible and (purl1.subpath == purl2.subpath)
1434+
1435+
return is_compatible
1436+
1437+
13481438
@attr.attributes(slots=True)
13491439
class PackageWithResources(Package):
13501440
"""
@@ -1384,7 +1474,15 @@ def merge_sequences(list1, list2, **kwargs):
13841474
merged = []
13851475
existing = set()
13861476
for item in list1 + list2:
1387-
key = item.to_tuple(**kwargs)
1477+
try:
1478+
if hasattr(item, 'to_tuple'):
1479+
key = item.to_tuple(**kwargs)
1480+
else:
1481+
key = to_tuple(kwargs)
1482+
1483+
except Exception as e:
1484+
raise Exception(f'Failed to merge sequences: {item}', f'kwargs: {kwargs}') from e
1485+
13881486
if not key in existing:
13891487
merged.append(item)
13901488
existing.add(key)

src/packagedcode/plugin_package.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ def process_codebase(self, codebase, strip_root=False, **kwargs):
156156

157157
def get_installed_packages(root_dir, processes=2, **kwargs):
158158
"""
159-
Yield Package and their Resources as they are found in `root_dir`
159+
Detect and yield Package mappings with their assigned Resource in a ``resources``
160+
attribute as they are found in `root_dir`.
160161
"""
161162
from scancode import cli
162163

0 commit comments

Comments
 (0)