Module continuous_delivery_scripts.utils.third_party_licences

Third party licences.

Functions

def cleanse_licence_descriptor(licence_descriptor: str) ‑> str
Expand source code
def cleanse_licence_descriptor(licence_descriptor: str) -> str:
    """Cleanses the licence descriptor to only keep words describing the licence."""
    cleansed_descriptor = licence_descriptor.strip()
    cleansed_descriptor = re.sub(r"OSI\s?[Aa]pproved[\:]*", "", cleansed_descriptor)
    cleansed_descriptor = re.sub(r"[pP]ublic [dD]omain", "", cleansed_descriptor)
    cleansed_descriptor = re.sub(r"BSD[-\s][Ll]ike", "BSD", cleansed_descriptor)
    cleansed_descriptor = re.sub(r"BSD [lL]icen[sc]e", "BSD", cleansed_descriptor)
    cleansed_descriptor = re.sub(r"MIT [lL]icen[sc]e", "MIT", cleansed_descriptor)
    cleansed_descriptor = _handle_special_licence_entries(cleansed_descriptor.strip())
    if cleansed_descriptor.lower().startswith("the"):
        cleansed_descriptor = cleansed_descriptor[3:].strip()
    return cleansed_descriptor

Cleanses the licence descriptor to only keep words describing the licence.

def cleanse_licence_expression(licence_expression: str) ‑> str
Expand source code
def cleanse_licence_expression(licence_expression: str) -> str:
    """Cleanses a licence expression by using SPDX identifiers when possible.

    A licence expression can be a combination of licences and in a lot of cases is free-form text.
    The idea is to return an equivalent expression but using SPDX identifiers when possible.
    """
    simplified_expression = _parse_licence_expression(
        Licensing(), normalise_proprietary_licence(licence_expression)
    ).simplify()
    for s in simplified_expression.symbols:
        if isinstance(s, LicenseWithExceptionSymbol):
            # An exception changes the obligations: do not treat it as the base licence.
            continue
        if s.key.startswith("LicenseRef-"):
            continue
        corresponding_licence = OPENSOURCE_LICENCES.get_licence(s.key)
        if corresponding_licence:
            s.key = corresponding_licence.identifier
    return simplify_licence_expression(str(simplified_expression))

Cleanses a licence expression by using SPDX identifiers when possible.

A licence expression can be a combination of licences and in a lot of cases is free-form text. The idea is to return an equivalent expression but using SPDX identifiers when possible.

def determine_allowed_opensource_licences_from_string(allowed_licences: Any) ‑> Iterable[Licence]
Expand source code
def determine_allowed_opensource_licences_from_string(
    allowed_licences: Any,
) -> Iterable[Licence]:
    """Determines all the third party licences allowed as set in the input parameter."""
    if isinstance(allowed_licences, str):
        allowed_licences = allowed_licences.split(", ")
    if isinstance(allowed_licences, (list, dict, tuple, set)):
        yield from _retrieve_licences_from_identifier_list(allowed_licences)

Determines all the third party licences allowed as set in the input parameter.

def determine_licence_compound(main_licence: str, additional_licences: List[str]) ‑> str
Expand source code
def determine_licence_compound(main_licence: str, additional_licences: List[str]) -> str:
    """Determines the overall licence based on main licence and additional licences."""
    overall_licence = f"({main_licence}) AND ({') AND ('.join(additional_licences)})"
    return str(_parse_licence_expression(Licensing(), overall_licence).simplify())

Determines the overall licence based on main licence and additional licences.

def determine_licences_not_in_list(licence_expression: str, licence_list: Iterator[str]) ‑> Iterator[str]
Expand source code
def determine_licences_not_in_list(licence_expression: str, licence_list: Iterator[str]) -> Iterator[str]:
    """Determines all the licences in an expression which are not in list."""
    licensing_util = Licensing()
    licence_keys = licensing_util.license_keys(_parse_licence_expression(licensing_util, licence_expression))
    for licence in licence_keys:
        if licence not in licence_list:
            yield licence

Determines all the licences in an expression which are not in list.

def determine_whether_licence_expression_is_compliant(licence_expression: str, licence_list: list) ‑> bool
Expand source code
def determine_whether_licence_expression_is_compliant(licence_expression: str, licence_list: list) -> bool:
    """Checks whether an expression is compliant with a list of licences."""
    licensing_util = Licensing()
    for licence in licence_list:
        if licensing_util.contains(licence_expression, licence):
            return True
    return False

Checks whether an expression is compliant with a list of licences.

def get_allowed_opensource_licences() ‑> Iterable[Licence]
Expand source code
def get_allowed_opensource_licences() -> Iterable[Licence]:
    """Determines all the third party licences allowed for a given project."""
    yield from determine_allowed_opensource_licences_from_string(
        configuration.get_value(ConfigurationVariable.ACCEPTED_THIRD_PARTY_LICENCES)
    )

Determines all the third party licences allowed for a given project.

def is_licence_accepted(licence_expression: str, additional_identifiers: Iterable[str] = ()) ‑> bool
Expand source code
def is_licence_accepted(licence_expression: str, additional_identifiers: Iterable[str] = ()) -> bool:
    """Determines whether the licence expressed is valid with regards to project's accepted licences."""
    authorised_licences = [licence.identifier for licence in get_allowed_opensource_licences()]
    authorised_licences.extend(additional_identifiers)
    is_or = _is_expression_or(licence_expression)
    if bool([licence for licence in determine_licences_not_in_list(licence_expression, iter(authorised_licences))]):
        return (
            determine_whether_licence_expression_is_compliant(licence_expression, authorised_licences)
            if is_or
            else False
        )
    return True

Determines whether the licence expressed is valid with regards to project's accepted licences.

def is_proprietary_licence(licence_expression: str) ‑> bool
Expand source code
def is_proprietary_licence(licence_expression: str) -> bool:
    """Return whether a licence declaration resolves to the proprietary SPDX reference."""
    return normalise_proprietary_licence(licence_expression.strip()) == LICENSE_REF_PROPRIETARY

Return whether a licence declaration resolves to the proprietary SPDX reference.

def iter_licenses(licence_info: dict) ‑> Iterable[Licence]
Expand source code
def iter_licenses(licence_info: dict) -> Iterable[Licence]:
    """Gets a generator over all the licences present in licenses.json."""
    licences = cast(Iterable[dict], licence_info.get("licenses", []))
    for licence_info in licences:
        yield parse_licence(licence_info)

Gets a generator over all the licences present in licenses.json.

def normalise_proprietary_licence(licence_expression: str) ‑> str
Expand source code
def normalise_proprietary_licence(licence_expression: str) -> str:
    """Use a stable SPDX licence reference for proprietary declarations."""
    return re.sub(
        r"(?<![\w-])(?:LicenseRef-)?Proprietary(?:\s+Licen[cs]e)?(?![\w-])",
        LICENSE_REF_PROPRIETARY,
        licence_expression,
        flags=re.IGNORECASE,
    )

Use a stable SPDX licence reference for proprietary declarations.

def parse_licence(licence_info: dict) ‑> Licence
Expand source code
def parse_licence(licence_info: dict) -> Licence:
    """Parses a licence entry in the Json file and translates it into a licence object."""
    return Licence(
        reference_number=licence_info.get("referenceNumber", ""),
        identifier=licence_info.get("licenseId", ""),
        name=licence_info.get("name", ""),
        url=licence_info.get("detailsUrl", ""),
        reference=licence_info.get("reference", ""),
        is_deprecated=bool(licence_info.get("isDeprecatedLicenseId", False)),
        is_osi_approved=bool(licence_info.get("isOsiApproved", True)),
    )

Parses a licence entry in the Json file and translates it into a licence object.

def simplify_licence_expression(licence_expression: str) ‑> str
Expand source code
def simplify_licence_expression(licence_expression: str) -> str:
    """Simplifies a licence expression."""
    return str(_parse_licence_expression(Licensing(), normalise_proprietary_licence(licence_expression)).simplify())

Simplifies a licence expression.

Classes

class Licence (reference_number: str,
identifier: str,
name: str,
is_deprecated: bool,
is_osi_approved: bool,
url: str,
reference: str)
Expand source code
@dataclass(order=True, frozen=True)
class Licence:
    """Licence descriptor.

    Maps to what is defined in licenses.json in
    https://github.com/spdx/license-list-data/blob/master/json/licenses.json
    """

    reference_number: str
    identifier: str
    name: str
    is_deprecated: bool
    is_osi_approved: bool
    url: str
    reference: str

Licence descriptor.

Maps to what is defined in licenses.json in https://github.com/spdx/license-list-data/blob/master/json/licenses.json

Instance variables

var identifier : str

The type of the None singleton.

var is_deprecated : bool

The type of the None singleton.

var is_osi_approved : bool

The type of the None singleton.

var name : str

The type of the None singleton.

var reference : str

The type of the None singleton.

var reference_number : str

The type of the None singleton.

var url : str

The type of the None singleton.

class OpenSourceLicences
Expand source code
class OpenSourceLicences:
    """All the opensource licences known."""

    def __init__(self) -> None:
        """Initialiser."""
        self._licence_store: Optional[dict] = None
        self._licence_list: Optional[list] = None

    def _store_licence(self, licence: Licence, aliases: Iterable[str] = ()) -> None:
        if not self._licence_store or self._licence_list is None:
            return
        entries = [licence.identifier, licence.name, *aliases]
        for entry in entries:
            if not entry:
                continue
            self._licence_store[entry] = licence
            self._licence_list.append(entry)

    def load(self) -> None:
        """Loads licence data from internal Json file."""
        if self._licence_list and self._licence_store:
            return
        self._licence_store = {UNKNOWN_LICENCE.identifier: UNKNOWN_LICENCE}
        self._licence_list = [UNKNOWN_LICENCE.identifier]
        try:
            with open(_get_spdx_licenses_path(), "r", encoding="utf8") as f:
                for licence in iter_licenses(json.load(f)):
                    self._store_licence(licence)
            return
        except (FileNotFoundError, ModuleNotFoundError):
            pass

        for licence, aliases in _iter_fallback_licences():
            self._store_licence(licence, aliases)

    def get_licences_from_pattern(self, licence_descriptor_pattern: Pattern) -> Optional[List[Licence]]:
        """Determines all the licences following a certain pattern."""
        self.load()
        if not self._licence_store or not self._licence_list:
            return None
        matching_licences = [licence for licence in self._licence_list if licence_descriptor_pattern.match(licence)]
        return (
            [cast(Licence, self._licence_store.get(licence)) for licence in matching_licences]
            if matching_licences
            else None
        )

    def get_licence(self, licence_descriptor: Optional[str], allow_fuzzy: bool = True) -> Optional[Licence]:
        """Determines a licence from its identifier or name, optionally without approximate matching."""
        if licence_descriptor and normalise_proprietary_licence(licence_descriptor) == LICENSE_REF_PROPRIETARY:
            return PROPRIETARY_LICENCE
        if licence_descriptor and licence_descriptor.lower().startswith("licenseref-"):
            return None
        self.load()
        if not self._licence_store or not self._licence_list or not licence_descriptor:
            return None
        cleansed_descriptor = cleanse_licence_descriptor(licence_descriptor)
        exact_match = self._licence_store.get(cleansed_descriptor)
        if exact_match:
            return cast(Licence, exact_match)

        normalised_map: Dict[str, Licence] = {}
        for name in self._licence_list:
            licence = cast(Licence, self._licence_store.get(name))
            if licence:
                normalised_map[_normalise_licence_text(name)] = licence

        normalised_descriptor = _normalise_licence_text(cleansed_descriptor)
        normalised_exact_match = normalised_map.get(normalised_descriptor)
        if normalised_exact_match:
            return normalised_exact_match

        if not allow_fuzzy:
            return None
        likelihood, matched_key = determine_similar_string_from_list(normalised_descriptor, normalised_map.keys())
        return normalised_map.get(matched_key) if likelihood > LICENCE_LIKELIHOOD_THRESHOLD else None

All the opensource licences known.

Initialiser.

Methods

def get_licence(self, licence_descriptor: str | None, allow_fuzzy: bool = True) ‑> Licence | None
Expand source code
def get_licence(self, licence_descriptor: Optional[str], allow_fuzzy: bool = True) -> Optional[Licence]:
    """Determines a licence from its identifier or name, optionally without approximate matching."""
    if licence_descriptor and normalise_proprietary_licence(licence_descriptor) == LICENSE_REF_PROPRIETARY:
        return PROPRIETARY_LICENCE
    if licence_descriptor and licence_descriptor.lower().startswith("licenseref-"):
        return None
    self.load()
    if not self._licence_store or not self._licence_list or not licence_descriptor:
        return None
    cleansed_descriptor = cleanse_licence_descriptor(licence_descriptor)
    exact_match = self._licence_store.get(cleansed_descriptor)
    if exact_match:
        return cast(Licence, exact_match)

    normalised_map: Dict[str, Licence] = {}
    for name in self._licence_list:
        licence = cast(Licence, self._licence_store.get(name))
        if licence:
            normalised_map[_normalise_licence_text(name)] = licence

    normalised_descriptor = _normalise_licence_text(cleansed_descriptor)
    normalised_exact_match = normalised_map.get(normalised_descriptor)
    if normalised_exact_match:
        return normalised_exact_match

    if not allow_fuzzy:
        return None
    likelihood, matched_key = determine_similar_string_from_list(normalised_descriptor, normalised_map.keys())
    return normalised_map.get(matched_key) if likelihood > LICENCE_LIKELIHOOD_THRESHOLD else None

Determines a licence from its identifier or name, optionally without approximate matching.

def get_licences_from_pattern(self, licence_descriptor_pattern: Pattern) ‑> List[Licence] | None
Expand source code
def get_licences_from_pattern(self, licence_descriptor_pattern: Pattern) -> Optional[List[Licence]]:
    """Determines all the licences following a certain pattern."""
    self.load()
    if not self._licence_store or not self._licence_list:
        return None
    matching_licences = [licence for licence in self._licence_list if licence_descriptor_pattern.match(licence)]
    return (
        [cast(Licence, self._licence_store.get(licence)) for licence in matching_licences]
        if matching_licences
        else None
    )

Determines all the licences following a certain pattern.

def load(self) ‑> None
Expand source code
def load(self) -> None:
    """Loads licence data from internal Json file."""
    if self._licence_list and self._licence_store:
        return
    self._licence_store = {UNKNOWN_LICENCE.identifier: UNKNOWN_LICENCE}
    self._licence_list = [UNKNOWN_LICENCE.identifier]
    try:
        with open(_get_spdx_licenses_path(), "r", encoding="utf8") as f:
            for licence in iter_licenses(json.load(f)):
                self._store_licence(licence)
        return
    except (FileNotFoundError, ModuleNotFoundError):
        pass

    for licence, aliases in _iter_fallback_licences():
        self._store_licence(licence, aliases)

Loads licence data from internal Json file.