| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | from __future__ import annotations |
| |
|
| | import re |
| | from typing import NewType, cast |
| |
|
| | from packaging.licenses._spdx import EXCEPTIONS, LICENSES |
| |
|
| | __all__ = [ |
| | "InvalidLicenseExpression", |
| | "NormalizedLicenseExpression", |
| | "canonicalize_license_expression", |
| | ] |
| |
|
| | license_ref_allowed = re.compile("^[A-Za-z0-9.-]*$") |
| |
|
| | NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str) |
| |
|
| |
|
| | class InvalidLicenseExpression(ValueError): |
| | """Raised when a license-expression string is invalid |
| | |
| | >>> canonicalize_license_expression("invalid") |
| | Traceback (most recent call last): |
| | ... |
| | packaging.licenses.InvalidLicenseExpression: Invalid license expression: 'invalid' |
| | """ |
| |
|
| |
|
| | def canonicalize_license_expression( |
| | raw_license_expression: str, |
| | ) -> NormalizedLicenseExpression: |
| | if not raw_license_expression: |
| | message = f"Invalid license expression: {raw_license_expression!r}" |
| | raise InvalidLicenseExpression(message) |
| |
|
| | |
| | |
| | license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ") |
| | licenseref_prefix = "LicenseRef-" |
| | license_refs = { |
| | ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :] |
| | for ref in license_expression.split() |
| | if ref.lower().startswith(licenseref_prefix.lower()) |
| | } |
| |
|
| | |
| | |
| | license_expression = license_expression.lower() |
| |
|
| | tokens = license_expression.split() |
| |
|
| | |
| | |
| | |
| | python_tokens = [] |
| | for token in tokens: |
| | if token not in {"or", "and", "with", "(", ")"}: |
| | python_tokens.append("False") |
| | elif token == "with": |
| | python_tokens.append("or") |
| | elif token == "(" and python_tokens and python_tokens[-1] not in {"or", "and"}: |
| | message = f"Invalid license expression: {raw_license_expression!r}" |
| | raise InvalidLicenseExpression(message) |
| | else: |
| | python_tokens.append(token) |
| |
|
| | python_expression = " ".join(python_tokens) |
| | try: |
| | invalid = eval(python_expression, globals(), locals()) |
| | except Exception: |
| | invalid = True |
| |
|
| | if invalid is not False: |
| | message = f"Invalid license expression: {raw_license_expression!r}" |
| | raise InvalidLicenseExpression(message) from None |
| |
|
| | |
| | normalized_tokens = [] |
| | for token in tokens: |
| | if token in {"or", "and", "with", "(", ")"}: |
| | normalized_tokens.append(token.upper()) |
| | continue |
| |
|
| | if normalized_tokens and normalized_tokens[-1] == "WITH": |
| | if token not in EXCEPTIONS: |
| | message = f"Unknown license exception: {token!r}" |
| | raise InvalidLicenseExpression(message) |
| |
|
| | normalized_tokens.append(EXCEPTIONS[token]["id"]) |
| | else: |
| | if token.endswith("+"): |
| | final_token = token[:-1] |
| | suffix = "+" |
| | else: |
| | final_token = token |
| | suffix = "" |
| |
|
| | if final_token.startswith("licenseref-"): |
| | if not license_ref_allowed.match(final_token): |
| | message = f"Invalid licenseref: {final_token!r}" |
| | raise InvalidLicenseExpression(message) |
| | normalized_tokens.append(license_refs[final_token] + suffix) |
| | else: |
| | if final_token not in LICENSES: |
| | message = f"Unknown license: {final_token!r}" |
| | raise InvalidLicenseExpression(message) |
| | normalized_tokens.append(LICENSES[final_token]["id"] + suffix) |
| |
|
| | normalized_expression = " ".join(normalized_tokens) |
| |
|
| | return cast( |
| | NormalizedLicenseExpression, |
| | normalized_expression.replace("( ", "(").replace(" )", ")"), |
| | ) |
| |
|