Skip to content

Commit 0faa6da

Browse files
committed
improved documentation in pep621.py, moved test_pep621.py to integration/test_utils_pep621.py, and improved test coverage
Signed-off-by: Manav Gupta <[email protected]>
1 parent 90b85af commit 0faa6da

File tree

3 files changed

+81
-48
lines changed

3 files changed

+81
-48
lines changed

cyclonedx_py/_internal/utils/pep621.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,13 @@ def project2licenses(project: dict[str, Any], lfac: 'LicenseFactory', *,
6161
# https://packaging.python.org/en/latest/specifications/core-metadata/#classifier-multiple-use
6262
yield from classifiers2licenses(classifiers, lfac, lack)
6363
if isinstance(plicense := project.get('license'), dict):
64+
# https://packaging.python.org/en/latest/specifications/pyproject-toml/#license
6465
if 'file' in plicense and 'text' in plicense:
6566
raise ValueError('`license.file` and `license.text` are mutually exclusive,')
6667
if 'file' in plicense:
68+
# per spec:
69+
# > [...] a string value that is a relative file path [...].
70+
# > Tools MUST assume the file’s encoding is UTF-8.
6771
with open(join(dirname(fpath), plicense['file']), 'rb') as plicense_fileh:
6872
yield DisjunctiveLicense(name=f"declared license of '{project['name']}'",
6973
acknowledgement=lack,
@@ -73,6 +77,7 @@ def project2licenses(project: dict[str, Any], lfac: 'LicenseFactory', *,
7377
license = lfac.make_from_string(plicense_text,
7478
license_acknowledgement=lack)
7579
if isinstance(license, DisjunctiveLicense) and license.id is None:
80+
# per spec, `License` is either a SPDX ID/Expression, or a license text(not name!)
7681
yield DisjunctiveLicense(name=f"declared license of '{project['name']}'",
7782
acknowledgement=lack,
7883
text=AttachedText(content=plicense_text))
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# This file is part of CycloneDX Python
2+
# SPDX-License-Identifier: Apache-2.0
3+
# Copyright (c) OWASP Foundation. All Rights Reserved.
4+
5+
import os
6+
import tempfile
7+
import unittest
8+
9+
from cyclonedx.factory.license import LicenseFactory
10+
from cyclonedx.model.license import DisjunctiveLicense, LicenseAcknowledgement
11+
12+
from cyclonedx_py._internal.utils.pep621 import project2licenses
13+
14+
15+
class TestUtilsPEP621(unittest.TestCase):
16+
17+
def test_license_dict_text_pep621(self) -> None:
18+
lfac = LicenseFactory()
19+
fpath = tempfile.mktemp()
20+
project = {
21+
'name': 'testpkg',
22+
'license': {'text': 'This is the license text.'},
23+
}
24+
licenses = list(project2licenses(project, lfac, fpath=fpath))
25+
self.assertEqual(len(licenses), 1)
26+
lic = licenses[0]
27+
self.assertIsInstance(lic, DisjunctiveLicense)
28+
self.assertIsNone(lic.id)
29+
self.assertEqual(lic.text.content, 'This is the license text.')
30+
self.assertEqual(lic.acknowledgement, LicenseAcknowledgement.DECLARED)
31+
32+
def test_license_dict_file_pep621(self) -> None:
33+
lfac = LicenseFactory()
34+
with tempfile.NamedTemporaryFile('w+', delete=True) as tf:
35+
tf.write('File license text')
36+
tf.flush()
37+
project = {
38+
'name': 'testpkg',
39+
'license': {'file': os.path.basename(tf.name)},
40+
}
41+
# fpath should be the file path so dirname(fpath) resolves to the correct directory
42+
licenses = list(project2licenses(project, lfac, fpath=tf.name))
43+
44+
self.assertEqual(len(licenses), 1)
45+
lic = licenses[0]
46+
self.assertIsInstance(lic, DisjunctiveLicense)
47+
self.assertIsNotNone(lic.text.content)
48+
self.assertEqual(lic.acknowledgement, LicenseAcknowledgement.DECLARED)
49+
50+
def test_license_non_dict_pep621(self) -> None:
51+
lfac = LicenseFactory()
52+
fpath = tempfile.mktemp()
53+
54+
# Test with string license (should be silently skipped)
55+
project = {
56+
'name': 'testpkg',
57+
'license': 'MIT',
58+
}
59+
licenses = list(project2licenses(project, lfac, fpath=fpath))
60+
self.assertEqual(len(licenses), 0)
61+
62+
# Test with None license (should be silently skipped)
63+
project = {
64+
'name': 'testpkg',
65+
'license': None,
66+
}
67+
licenses = list(project2licenses(project, lfac, fpath=fpath))
68+
self.assertEqual(len(licenses), 0)
69+
70+
# Test with list license (should be silently skipped)
71+
project = {
72+
'name': 'testpkg',
73+
'license': ['MIT', 'Apache-2.0'],
74+
}
75+
licenses = list(project2licenses(project, lfac, fpath=fpath))
76+
self.assertEqual(len(licenses), 0)

tests/unit/test_pep621.py

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)