Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions src/fromager/pkgmetadata/pep639.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
- https://spdx.org/licenses/
"""

from license_expression import LicenseExpression, get_spdx_licensing
import re

from license_expression import ExpressionError, LicenseExpression, get_spdx_licensing
from packaging.metadata import Metadata


Expand Down Expand Up @@ -60,12 +62,25 @@ def license_from_metadata_values(

_SPDX = get_spdx_licensing()

# SPDX idstring: letters, digits, "." and "-"
_LICENSE_REF_RE = re.compile(r"LicenseRef-[A-Za-z0-9.\-]+")


def _parse_spdx(text: str, *, simplify: bool = False) -> LicenseExpression:
"""Parse, validate, and simplify a SPDX license expression"""
# LicenseRef are references to non-SPDX licenses
validate = not text.startswith("LicenseRef-")
expr = _SPDX.parse(text, validate=validate)
"""Parse, validate, and simplify a SPDX license expression

``LicenseRef-`` keys reference non-SPDX licenses (PEP 639) and may
appear anywhere in the expression, so they are exempt from the SPDX
license list check. Every other unknown key is an error.
"""
expr = _SPDX.parse(text, validate=False)
Comment thread
mprpic marked this conversation as resolved.
Resolved
unknown = [
key
for key in _SPDX.unknown_license_keys(expr)
if not _LICENSE_REF_RE.fullmatch(key)
]
if unknown:
raise ExpressionError(f"Unknown license key(s): {', '.join(unknown)}")
if simplify:
expr = expr.simplify()
return expr
Expand Down
10 changes: 9 additions & 1 deletion tests/test_pkgmetadata.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from license_expression import ExpressionError
from packaging.metadata import Metadata, RawMetadata

from fromager.pkgmetadata import (
Expand Down Expand Up @@ -82,6 +83,9 @@ def test_project_urls_from_metadata(
["metadata", "expected"],
[
[md("MIT AND MIT or MIT"), "MIT"],
# LicenseRef is valid anywhere in a PEP 639 expression
Comment thread
mprpic marked this conversation as resolved.
Resolved
[md("MIT OR LicenseRef-Proprietary"), "LicenseRef-Proprietary OR MIT"],
[md("LicenseRef-Proprietary"), "LicenseRef-Proprietary"],
[md(license="MIT License"), "MIT"],
[md(license="MIT and PSF-2.0"), "MIT AND PSF-2.0"],
[md(license="BSD 3-Clause License"), "BSD-3-Clause"],
Expand All @@ -107,6 +111,10 @@ def test_license_from_metadata(metadata: Metadata, expected: str) -> None:
[
(None, None, None),
(None, "invalid", None),
# a leading LicenseRef must not disable validation of other keys
("LicenseRef-Foo OR Apache 2", None, None),
# SPDX idstrings only allow letters, digits, "." and "-"
("LicenseRef-a_b", None, None),
(None, None, ["License :: OSI Approved :: BSD License"]),
# classifiers with no entry that maps to SPDX (see issue #1372)
(None, None, ["Programming Language :: Python :: 3"]),
Expand All @@ -127,7 +135,7 @@ def test_license_from_metadata_errors(
license: str | None,
classifiers: list[str] | None,
) -> None:
with pytest.raises((ValueError, ExceptionGroup)):
with pytest.raises((ValueError, ExceptionGroup, ExpressionError)):
Comment thread
mprpic marked this conversation as resolved.
Resolved
license_from_metadata_values(
license_expression=license_expression,
license_text=license,
Expand Down
Loading