Module continuous_delivery_scripts.spdx_report.spdx_helpers

Facilities regarding SPDX.

SPDX file (i.e. tag-value format) https://github.com/OpenChain-Project/curriculum/blob/master/guides/including_license_info.rst https://github.com/david-a-wheeler/spdx-tutorial#spdx-files https://github.com/OpenChain-Project/curriculum/blob/master/guides/reusing_software.md https://github.com/vmware/tern/blob/c9a0c83369b92df58f7f80842aa15da5f63ed983/docs/spdx-tag-value-overview.md

Examples -----= - https://spdx.org/spdx-tagvalue-example - https://github.com/spdx/tools/blob/master/Examples/SPDXTagExample-v2.1.spdx

Functions

def determine_checked_packages_from_configuration_entry(checked_packages: Any) ‑> dict
Expand source code
def determine_checked_packages_from_configuration_entry(checked_packages: Any) -> dict:
    """Determines the list of packages for which the licence has been manually checked."""
    if isinstance(checked_packages, str):
        checked_packages = checked_packages.split(", ")
    if isinstance(checked_packages, (list, tuple, set)):
        return _convert_list_into_dict(checked_packages)
    if isinstance(checked_packages, dict):
        return checked_packages
    return dict()

Determines the list of packages for which the licence has been manually checked.

Expand source code
def determine_file_copyright_text(path: Path) -> Optional[str]:
    """Determines the copyright text of a file."""
    match = scan_file_for_pattern(path, COPYRIGHT_REGEX_PATTERN)
    if not match:
        return None
    return str(match.group(0).strip())

Determines the copyright text of a file.

def determine_file_licence(path: pathlib.Path) ‑> str | None
Expand source code
def determine_file_licence(path: Path) -> Optional[str]:
    """Determines the licence of a file based on the SPDX identifier."""
    licence = None
    try:
        match = scan_file_for_pattern(path, SPDX_IDENTIFIER_REGEX_PATTERN)
        if not match:
            return None
        licence = match.group(1).strip()
        return str(simplify_licence_expression(licence))
    except UnicodeDecodeError as e:
        logger.warning(
            "Could not screen file [%s] for an embedded SPDX licence identifier because it appears to be binary. "
            "Binary files cannot be screened for inline licence metadata.",
            path,
        )
        logger.info("Binary screening failure for [%s]: %s", path, e)
        return None
    except Exception as e:
        logger.error(f"Could not determine the licence of file [{path}] from identifier '{licence}'. Reason: {e}.")
        return None

Determines the licence of a file based on the SPDX identifier.

def determine_spdx_value(value: str | None) ‑> str | UnKnown | SPDXNone
Expand source code
def determine_spdx_value(value: Optional[str]) -> Union[str, UnKnown, SPDXNone]:
    """Determines the correct SPDX value.

    Args:
        value: a value
    Returns:
        correct SPDX value a string, UnKnown or SPDXNone
    """
    if not value:
        return SPDXNone()
    if value == UNKNOWN:
        return UnKnown()

    return value

Determines the correct SPDX value.

Args
-----=
value
a value

Returns -----= correct SPDX value a string, UnKnown or SPDXNone

def get_package_manual_check(package_name: str) ‑> Tuple[bool, str | None]
Expand source code
def get_package_manual_check(package_name: str) -> Tuple[bool, Optional[str]]:
    """Return whether a licence was checked and the recorded explanation."""
    record = get_package_manual_record(package_name)
    return record.checked, record.reason

Return whether a licence was checked and the recorded explanation.

def get_package_manual_licence(package_name: str) ‑> str | None
Expand source code
def get_package_manual_licence(package_name: str) -> Optional[str]:
    """Return the manually verified licence candidate, distinct from prose."""
    return get_package_manual_record(package_name).licence

Return the manually verified licence candidate, distinct from prose.

def get_package_manual_record(package_name: str) ‑> ManualLicenceCheck
Expand source code
def get_package_manual_record(package_name: str) -> ManualLicenceCheck:
    """Parse a flat or structured manual licence entry once into a model."""
    checked_packages = get_packages_with_checked_licence()
    name = package_name.strip()
    if name not in checked_packages:
        name = name.replace(".", "-")
    checked = name in checked_packages
    entry = checked_packages.get(name)
    record = ManualLicenceCheck(checked=checked)
    if isinstance(entry, dict):
        record.licence = entry.get("licence")
        record.reason = entry.get("reason") or record.licence
    elif isinstance(entry, str):
        record.licence = entry
        record.reason = entry
    return record

Parse a flat or structured manual licence entry once into a model.

def get_packages_with_checked_licence() ‑> dict
Expand source code
def get_packages_with_checked_licence() -> dict:
    """Determines the list of packages for which the licence has been checked from configuration."""
    return determine_checked_packages_from_configuration_entry(
        configuration.get_value(ConfigurationVariable.PACKAGES_WITH_CHECKED_LICENCE)
    )

Determines the list of packages for which the licence has been checked from configuration.

def get_project_namespace(project_config_path: pathlib.Path, document_name: str) ‑> str
Expand source code
def get_project_namespace(project_config_path: Path, document_name: str) -> str:
    """Determines the project namespace from configuration."""
    with open(str(project_config_path), "r", encoding="utf8") as f:
        config = toml.load(f).get(THIRD_PARTY_CONFIG_NAMESPACE, dict())
    protocol = "http://"
    path_part = f"{config.get('CreatorWebsite')}/{config.get('PathToSpdx')}"
    name_part = f"{document_name}-{config.get('UUID')}"
    return f"{protocol}{path_part}/{name_part}"

Determines the project namespace from configuration.

def is_package_licence_manually_checked(package_name: str) ‑> bool
Expand source code
def is_package_licence_manually_checked(package_name: str) -> bool:
    """States whether the licence of a package has been manually checked and hence, that its licence is compliant."""
    checked, _ = get_package_manual_check(package_name)
    if not checked:
        checked, _ = get_package_manual_check(package_name.replace(".", "-"))
    return checked

States whether the licence of a package has been manually checked and hence, that its licence is compliant.

def list_project_files_for_licensing(project_root: pathlib.Path) ‑> Iterator[pathlib.Path]
Expand source code
def list_project_files_for_licensing(project_root: Path) -> Iterator[Path]:
    """Gets a generator over all the project's files needing licensing."""

    def ignore_path(p: Path) -> bool:
        return True if p.name.startswith(".") else should_exclude_path(p, PATHS_TO_EXCLUDE)

    for path in list_all_files(project_root, ignore_path):
        yield path

Gets a generator over all the project's files needing licensing.

def parse_spdx_licence(licence: str) ‑> Expression | SpdxNoAssertion | ModelSpdxNone
Expand source code
def parse_spdx_licence(licence: str) -> Union["Expression", "SpdxNoAssertion", "ModelSpdxNone"]:
    """Convert a project licence expression to the SPDX SDK's model.

    An unknown licence in the audit is not an SPDX licence identifier. It is
    represented as NOASSERTION in the SPDX document instead.
    """
    from license_expression import get_spdx_licensing
    from spdx_tools.spdx.model.spdx_no_assertion import SpdxNoAssertion
    from spdx_tools.spdx.model.spdx_none import SpdxNone

    if not licence or licence.upper() in ("UNKNOWN", "NOASSERTION"):
        return SpdxNoAssertion()
    if licence.upper() == "NONE":
        return SpdxNone()
    return cast("Expression", get_spdx_licensing().parse(licence))

Convert a project licence expression to the SPDX SDK's model.

An unknown licence in the audit is not an SPDX licence identifier. It is represented as NOASSERTION in the SPDX document instead.

Classes

class ManualLicenceCheck (checked: bool = False)
Expand source code
@dataclass
class ManualLicenceCheck:
    """A project's manually checked licence and its separate explanation."""

    checked: bool = False
    _licence: Optional[str] = None
    _reason: Optional[str] = None

    @property
    def licence(self) -> Optional[str]:
        """Get the licence proposed for assessment, if supplied."""
        return self._licence

    @licence.setter
    def licence(self, value: Optional[str]) -> None:
        """Set the manually verified licence candidate."""
        if value is not None and not isinstance(value, str):
            raise ValueError("A manually checked licence must be text")
        self._licence = value

    @property
    def reason(self) -> Optional[str]:
        """Get the recorded explanation for the licence-policy check."""
        return self._reason

    @reason.setter
    def reason(self, value: Optional[str]) -> None:
        """Set the manually recorded review explanation."""
        if value is not None and not isinstance(value, str):
            raise ValueError("A manual licence review reason must be text")
        self._reason = value

A project's manually checked licence and its separate explanation.

Instance variables

var checked : bool

The type of the None singleton.

prop licence : str | None
Expand source code
@property
def licence(self) -> Optional[str]:
    """Get the licence proposed for assessment, if supplied."""
    return self._licence

Get the licence proposed for assessment, if supplied.

prop reason : str | None
Expand source code
@property
def reason(self) -> Optional[str]:
    """Get the recorded explanation for the licence-policy check."""
    return self._reason

Get the recorded explanation for the licence-policy check.

class SPDXNone
Expand source code
class SPDXNone(object):
    """Represent SPDX NONE value without importing spdx-tools."""

    def to_value(self) -> str:
        """Return the SPDX serialised value."""
        return "NONE"

    def __str__(self) -> str:
        """Return the SPDX serialised value as text."""
        return self.to_value()

Represent SPDX NONE value without importing spdx-tools.

Methods

def to_value(self) ‑> str
Expand source code
def to_value(self) -> str:
    """Return the SPDX serialised value."""
    return "NONE"

Return the SPDX serialised value.

class UnKnown
Expand source code
class UnKnown(object):
    """Represent SPDX UNKNOWN value without importing spdx-tools."""

    def to_value(self) -> str:
        """Return the SPDX serialised value."""
        return "UNKNOWN"

    def __str__(self) -> str:
        """Return the SPDX serialised value as text."""
        return self.to_value()

Represent SPDX UNKNOWN value without importing spdx-tools.

Methods

def to_value(self) ‑> str
Expand source code
def to_value(self) -> str:
    """Return the SPDX serialised value."""
    return "UNKNOWN"

Return the SPDX serialised value.