77# See https://aboutcode.org for more information about nexB OSS projects.
88#
99
10+ import logging
1011import os
1112import uuid
12- from fnmatch import fnmatchcase
13- import logging
1413import sys
1514
15+ from fnmatch import fnmatchcase
16+
1617import attr
17- from packageurl import normalize_qualifiers
18- from packageurl import PackageURL
1918import saneyaml
2019
2120from commoncode import filetype
21+ from commoncode .fileutils import as_posixpath
2222from commoncode .datautils import choices
2323from commoncode .datautils import Boolean
2424from commoncode .datautils import Date
2525from commoncode .datautils import Integer
2626from commoncode .datautils import List
2727from commoncode .datautils import Mapping
2828from commoncode .datautils import String
29- from commoncode .fileutils import as_posixpath
3029from commoncode .resource import Resource
3130from license_expression import combine_expressions
3231from license_expression import Licensing
32+ from packageurl import normalize_qualifiers
33+ from packageurl import PackageURL
3334
3435try :
3536 from typecode import contenttype
4142except ImportError :
4243 licensing = None
4344
45+ # FIXME: what if licensing is not importable?
4446from packagedcode .licensing import get_declared_license_expression_spdx
4547
4648"""
@@ -963,7 +965,7 @@ def get_license_detections_and_expression(self):
963965 return [], None
964966
965967 if self .datasource_id :
966- default_relation_license = get_default_relation_license (
968+ default_relation_license = get_default_relation_license (
967969 datasource_id = self .datasource_id ,
968970 )
969971 else :
@@ -1020,12 +1022,11 @@ def add_to_package(package_uid, resource, codebase):
10201022
10211023class DatafileHandler :
10221024 """
1023- A base handler class to handle any package manifests, lockfiles and data
1024- files. Each subclass handles a package datafile format to parse datafiles
1025- and assemble Package and Depdencies from these:
1025+ A base handler class to handle any package manifest, lockfile, package database
1026+ and related data files. Each subclass handles a package datafile format to parse
1027+ datafiles and assemble Package and Dependencies from these:
10261028
10271029 - parses a datafile format and yields package data.
1028-
10291030 - assembles this datafile package data in top-level packages and dependencies
10301031 - assigns package files to their package
10311032 """
@@ -1036,6 +1037,16 @@ class DatafileHandler:
10361037 # can only contain ASCII letters, digits and underscore. Must be lowercase
10371038 datasource_id = None
10381039
1040+ # style of package data processed by this handler, either app for application package like npm,
1041+ # sys for system packages like rpm, or info for informational data file that provides hints but
1042+ # is not a package manifest, like with a README file
1043+ # possible values are app, sys and info
1044+ datasource_type = 'app'
1045+
1046+ # tuple of specifically supported operating systems. If None or empty, all platforms are supported
1047+ # possible values are win, mac, linux, freebsd
1048+ supported_oses = tuple ()
1049+
10391050 # Sequence of known fnmatch-style case-insensitive glob patterns (e.g., Unix
10401051 # shell style patterns) that apply on the whole POSIX path for package
10411052 # datafiles recognized and parsed by this parser. See fnmatch.fnmatch().
@@ -1056,7 +1067,7 @@ class DatafileHandler:
10561067 # Informational: Default primary language for this parser.
10571068 default_primary_language = None
10581069
1059- # If the datafilehandler contains only resolved dependencies
1070+ # If the handler is for a lockfile that contains locked/pinned, pre- resolved dependencies
10601071 is_lockfile = False
10611072
10621073 # Informational: Description of this parser
@@ -1065,7 +1076,9 @@ class DatafileHandler:
10651076 # Informational: URL that documents this file format
10661077 documentation_url = None
10671078
1068- # Default Relation between license elements detected in an `extracted_license_statement`
1079+ # Default license expression relation between the license detected in an
1080+ # `extracted_license_statement` for this data file.
1081+ # This may vary for each data file based on conventions and specifications.
10691082 default_relation_license = None
10701083
10711084 @classmethod
@@ -1494,11 +1507,44 @@ def get_top_level_resources(cls, manifest_resource, codebase):
14941507 """
14951508 pass
14961509
1510+ @classmethod
1511+ def validate (cls ):
1512+ """
1513+ Validate this class.
1514+ Raise ImproperlyConfiguredDatafileHandler exception on errors.
1515+ """
1516+
1517+ did = cls .datasource_id
1518+ if not did :
1519+ raise ImproperlyConfiguredDatafileHandler (
1520+ f'The handler { cls !r} has an empty datasource_id { did !r} .' )
1521+
1522+ DATASOURCE_TYPES = 'app' , 'sys' , 'info' ,
1523+ dfs = cls .datasource_type
1524+ if dfs not in DATASOURCE_TYPES :
1525+ raise ImproperlyConfiguredDatafileHandler (
1526+ f'The handler { did !r} : { cls !r} has an invalid '
1527+ f'datasource_type: { dfs !r} : must be one of { DATASOURCE_TYPES !r} .'
1528+ )
1529+
1530+ oses = 'linux' , 'win' , 'max' , 'freebsd' ,
1531+ soses = cls .supported_oses
1532+ if soses and not all (s in oses for s in soses ):
1533+ raise ImproperlyConfiguredDatafileHandler (
1534+ f'The handler { cls .datasource_id !r} : { cls !r} has invalid '
1535+ f'supported_oses: { soses !r} : must be empty or among { oses !r} '
1536+ )
1537+
1538+
1539+ class ImproperlyConfiguredDatafileHandler (Exception ):
1540+ """ScanCode Package Datafile Handler is not properly configured"""
1541+ pass
1542+
14971543
14981544class NonAssemblableDatafileHandler (DatafileHandler ):
14991545 """
1500- A handler that has no default implmentation for the assemble method, e.g.,
1501- it will not alone trigger the creation of a top-level Pacakge .
1546+ A handler with a default implementation of an assemble method doing nothing , e.g.,
1547+ it will not alone trigger the creation of a top-level Package .
15021548 """
15031549
15041550 @classmethod
@@ -1531,8 +1577,8 @@ def build_purl(mapping):
15311577 subpath = mapping .get ('subpath' )
15321578 return PackageURL (
15331579 type = ptype ,
1534- name = name ,
15351580 namespace = namespace ,
1581+ name = name ,
15361582 version = version ,
15371583 qualifiers = qualifiers ,
15381584 subpath = subpath ,
@@ -1769,7 +1815,7 @@ def refresh_license_expressions(self, default_relation='AND'):
17691815 self .declared_license_expression_spdx = get_declared_license_expression_spdx (
17701816 declared_license_expression = self .declared_license_expression ,
17711817 )
1772-
1818+
17731819 if self .other_license_detections :
17741820 self .other_license_expression = str (combine_expressions (
17751821 expressions = [
0 commit comments