Skip to content

Commit e06f9fd

Browse files
authored
feat: complete SPDX license expression (#425)
Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
1 parent 13e441d commit e06f9fd

File tree

4 files changed

+55
-11
lines changed

4 files changed

+55
-11
lines changed

cyclonedx/spdx.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@
1414
#
1515
# SPDX-License-Identifier: Apache-2.0
1616

17-
__all__ = ['is_supported_id', 'fixup_id', 'is_compound_expression']
17+
__all__ = [
18+
'is_supported_id', 'fixup_id',
19+
'is_compound_expression'
20+
]
1821

1922
from json import load as json_load
2023
from os.path import dirname, join as path_join
21-
from typing import Dict, Optional, Set
24+
from typing import TYPE_CHECKING, Dict, Optional, Set
25+
26+
from license_expression import get_spdx_licensing # type: ignore
27+
28+
if TYPE_CHECKING:
29+
from license_expression import Licensing
2230

2331
# region init
2432
# python's internal module loader will assure that this init-part runs only once.
@@ -30,9 +38,11 @@
3038

3139
__IDS_LOWER_MAP: Dict[str, str] = dict((id_.lower(), id_) for id_ in __IDS)
3240

41+
__SPDX_EXPRESSION_LICENSING: 'Licensing' = get_spdx_licensing()
3342

3443
# endregion
3544

45+
3646
def is_supported_id(value: str) -> bool:
3747
"""Validate a SPDX-ID according to current spec."""
3848
return value in __IDS
@@ -50,11 +60,12 @@ def is_compound_expression(value: str) -> bool:
5060
"""Validate compound expression.
5161
5262
.. note::
53-
Uses a best-effort detection of SPDX compound expression according to `SPDX license expression spec`_.
63+
Utilizes `license-expression library`_ to
64+
validate SPDX compound expression according to `SPDX license expression spec`_.
5465
5566
.. _SPDX license expression spec: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
67+
.. _license-expression library: https://github.com/nexB/license-expression
5668
"""
57-
# shortest known valid expression: (A or B) - 8 characters long
58-
return len(value) >= 8 \
59-
and value.startswith('(') \
60-
and value.endswith(')')
69+
return 0 == len(
70+
__SPDX_EXPRESSION_LICENSING.validate(value).errors
71+
)

poetry.lock

Lines changed: 32 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ python = "^3.7"
5555
packageurl-python = ">= 0.11"
5656
py-serializable = "^0.11.1"
5757
sortedcontainers = "^2.4.0"
58+
license-expression = "^30"
5859

5960
[tool.poetry.dev-dependencies]
6061
ddt = "^1.6.0"

tests/test_spdx.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333

3434
VALID_COMPOUND_EXPRESSIONS = {
3535
# for valid test data see the spec: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
36-
'(MIT WITH Apache-2.0)',
37-
'(BSD-2-Clause OR Apache-2.0)',
36+
'(MIT AND Apache-2.0)',
37+
'BSD-2-Clause OR Apache-2.0',
3838
}
3939

4040

@@ -89,8 +89,9 @@ def test_positive(self, valid_expression: str) -> None:
8989
self.assertTrue(actual)
9090

9191
@data(
92+
'MIT AND Apache-2.0 OR something-unknown'
9293
'something invalid',
93-
'(c) John Doe'
94+
'(c) John Doe',
9495
)
9596
def test_negative(self, invalid_expression: str) -> None:
9697
actual = spdx.is_compound_expression(invalid_expression)

0 commit comments

Comments
 (0)