7
7
# See https://aboutcode.org for more information about nexB OSS projects.
8
8
#
9
9
10
+ import logging
10
11
import os
11
12
import uuid
12
- from fnmatch import fnmatchcase
13
- import logging
14
13
import sys
15
14
15
+ from fnmatch import fnmatchcase
16
+
16
17
import attr
17
- from packageurl import normalize_qualifiers
18
- from packageurl import PackageURL
19
18
import saneyaml
20
19
21
20
from commoncode import filetype
21
+ from commoncode .fileutils import as_posixpath
22
22
from commoncode .datautils import choices
23
23
from commoncode .datautils import Boolean
24
24
from commoncode .datautils import Date
25
25
from commoncode .datautils import Integer
26
26
from commoncode .datautils import List
27
27
from commoncode .datautils import Mapping
28
28
from commoncode .datautils import String
29
- from commoncode .fileutils import as_posixpath
30
29
from commoncode .resource import Resource
31
30
from license_expression import combine_expressions
32
31
from license_expression import Licensing
32
+ from packageurl import normalize_qualifiers
33
+ from packageurl import PackageURL
33
34
34
35
try :
35
36
from typecode import contenttype
41
42
except ImportError :
42
43
licensing = None
43
44
45
+ # FIXME: what if licensing is not importable?
44
46
from packagedcode .licensing import get_declared_license_expression_spdx
45
47
46
48
"""
@@ -963,7 +965,7 @@ def get_license_detections_and_expression(self):
963
965
return [], None
964
966
965
967
if self .datasource_id :
966
- default_relation_license = get_default_relation_license (
968
+ default_relation_license = get_default_relation_license (
967
969
datasource_id = self .datasource_id ,
968
970
)
969
971
else :
@@ -1017,12 +1019,11 @@ def add_to_package(package_uid, resource, codebase):
1017
1019
1018
1020
class DatafileHandler :
1019
1021
"""
1020
- A base handler class to handle any package manifests, lockfiles and data
1021
- files. Each subclass handles a package datafile format to parse datafiles
1022
- and assemble Package and Depdencies from these:
1022
+ A base handler class to handle any package manifest, lockfile, package database
1023
+ and related data files. Each subclass handles a package datafile format to parse
1024
+ datafiles and assemble Package and Dependencies from these:
1023
1025
1024
1026
- parses a datafile format and yields package data.
1025
-
1026
1027
- assembles this datafile package data in top-level packages and dependencies
1027
1028
- assigns package files to their package
1028
1029
"""
@@ -1033,6 +1034,16 @@ class DatafileHandler:
1033
1034
# can only contain ASCII letters, digits and underscore. Must be lowercase
1034
1035
datasource_id = None
1035
1036
1037
+ # style of package data processed by this handler, either app for application package like npm,
1038
+ # sys for system packages like rpm, or info for informational data file that provides hints but
1039
+ # is not a package manifest, like with a README file
1040
+ # possible values are app, sys and info
1041
+ datasource_type = 'app'
1042
+
1043
+ # tuple of specifically supported operating systems. If None or empty, all platforms are supported
1044
+ # possible values are win, mac, linux, freebsd
1045
+ supported_oses = tuple ()
1046
+
1036
1047
# Sequence of known fnmatch-style case-insensitive glob patterns (e.g., Unix
1037
1048
# shell style patterns) that apply on the whole POSIX path for package
1038
1049
# datafiles recognized and parsed by this parser. See fnmatch.fnmatch().
@@ -1053,7 +1064,7 @@ class DatafileHandler:
1053
1064
# Informational: Default primary language for this parser.
1054
1065
default_primary_language = None
1055
1066
1056
- # If the datafilehandler contains only resolved dependencies
1067
+ # If the handler is for a lockfile that contains locked/pinned, pre- resolved dependencies
1057
1068
is_lockfile = False
1058
1069
1059
1070
# Informational: Description of this parser
@@ -1062,7 +1073,9 @@ class DatafileHandler:
1062
1073
# Informational: URL that documents this file format
1063
1074
documentation_url = None
1064
1075
1065
- # Default Relation between license elements detected in an `extracted_license_statement`
1076
+ # Default license expression relation between the license detected in an
1077
+ # `extracted_license_statement` for this data file.
1078
+ # This may vary for each data file based on conventions and specifications.
1066
1079
default_relation_license = None
1067
1080
1068
1081
@classmethod
@@ -1491,11 +1504,44 @@ def get_top_level_resources(cls, manifest_resource, codebase):
1491
1504
"""
1492
1505
pass
1493
1506
1507
+ @classmethod
1508
+ def validate (cls ):
1509
+ """
1510
+ Validate this class.
1511
+ Raise ImproperlyConfiguredDatafileHandler exception on errors.
1512
+ """
1513
+
1514
+ did = cls .datasource_id
1515
+ if not did :
1516
+ raise ImproperlyConfiguredDatafileHandler (
1517
+ f'The handler { cls !r} has an empty datasource_id { did !r} .' )
1518
+
1519
+ DATASOURCE_TYPES = 'app' , 'sys' , 'info' ,
1520
+ dfs = cls .datasource_type
1521
+ if dfs not in DATASOURCE_TYPES :
1522
+ raise ImproperlyConfiguredDatafileHandler (
1523
+ f'The handler { did !r} : { cls !r} has an invalid '
1524
+ f'datasource_type: { dfs !r} : must be one of { DATASOURCE_TYPES !r} .'
1525
+ )
1526
+
1527
+ oses = 'linux' , 'win' , 'max' , 'freebsd' ,
1528
+ soses = cls .supported_oses
1529
+ if soses and not all (s in oses for s in soses ):
1530
+ raise ImproperlyConfiguredDatafileHandler (
1531
+ f'The handler { cls .datasource_id !r} : { cls !r} has invalid '
1532
+ f'supported_oses: { soses !r} : must be empty or among { oses !r} '
1533
+ )
1534
+
1535
+
1536
+ class ImproperlyConfiguredDatafileHandler (Exception ):
1537
+ """ScanCode Package Datafile Handler is not properly configured"""
1538
+ pass
1539
+
1494
1540
1495
1541
class NonAssemblableDatafileHandler (DatafileHandler ):
1496
1542
"""
1497
- A handler that has no default implmentation for the assemble method, e.g.,
1498
- it will not alone trigger the creation of a top-level Pacakge .
1543
+ A handler with a default implementation of an assemble method doing nothing , e.g.,
1544
+ it will not alone trigger the creation of a top-level Package .
1499
1545
"""
1500
1546
1501
1547
@classmethod
@@ -1528,8 +1574,8 @@ def build_purl(mapping):
1528
1574
subpath = mapping .get ('subpath' )
1529
1575
return PackageURL (
1530
1576
type = ptype ,
1531
- name = name ,
1532
1577
namespace = namespace ,
1578
+ name = name ,
1533
1579
version = version ,
1534
1580
qualifiers = qualifiers ,
1535
1581
subpath = subpath ,
@@ -1601,10 +1647,10 @@ def from_package_data(cls, package_data, datafile_path, package_only=False):
1601
1647
license_match ['from_file' ] = datafile_path
1602
1648
1603
1649
package = cls .from_dict (package_data_mapping )
1604
-
1650
+
1605
1651
if not package .package_uid :
1606
1652
package .package_uid = build_package_uid (package .purl )
1607
-
1653
+
1608
1654
if not package_only :
1609
1655
package .populate_license_fields ()
1610
1656
package .populate_holder_field ()
@@ -1763,7 +1809,7 @@ def refresh_license_expressions(self, default_relation='AND'):
1763
1809
self .declared_license_expression_spdx = get_declared_license_expression_spdx (
1764
1810
declared_license_expression = self .declared_license_expression ,
1765
1811
)
1766
-
1812
+
1767
1813
if self .other_license_detections :
1768
1814
self .other_license_expression = str (combine_expressions (
1769
1815
expressions = [
0 commit comments