Skip to content

Commit 27b3068

Browse files
committed
#513 - attrib_from_spdx
* Introduce spdx_license_expression * Ability to transform spdx license key from spdx_license_expression to license_expression Signed-off-by: Chin Yeung Li <[email protected]>
1 parent 0c54f5c commit 27b3068

File tree

3 files changed

+95
-0
lines changed

3 files changed

+95
-0
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ Changelog
66

77
* Fixed `transform` with nested list #531
88
* Added curl dependency in Dockerfile #532
9+
* Introduce spdx_license_expression
10+
* Ability to transform spdx license key from spdx_license_expression to
11+
license_expression
912

1013
2023-08-20
1114
Release 10.0.0

src/attributecode/model.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
from attributecode.util import csv
5656
from attributecode.util import file_fields
5757
from attributecode.util import filter_errors
58+
from attributecode.util import get_spdx_key_and_lic_key_from_licdb
5859
from attributecode.util import is_valid_name
5960
from attributecode.util import on_windows
6061
from attributecode.util import norm
@@ -802,6 +803,7 @@ def set_standard_fields(self):
802803
('license_name', ListField()),
803804
('license_file', FileTextField()),
804805
('license_url', UrlListField()),
806+
('spdx_license_expression', StringField()),
805807
('spdx_license_key', ListField()),
806808
('copyright', StringField()),
807809
('notice_file', FileTextField()),
@@ -1764,6 +1766,7 @@ def pre_process_and_fetch_license_dict(abouts, from_check=False, api_url=None, a
17641766
if errors:
17651767
return key_text_dict, errors
17661768

1769+
spdx_sclickey_dict = get_spdx_key_and_lic_key_from_licdb()
17671770
for about in abouts:
17681771
# No need to go through all the about objects if '--api_key' is invalid
17691772
auth_error = Error(
@@ -1779,6 +1782,27 @@ def pre_process_and_fetch_license_dict(abouts, from_check=False, api_url=None, a
17791782
about.license_expression.value = lic_exp
17801783
about.license_expression.present = True
17811784

1785+
if not about.license_expression.value and about.spdx_license_expression.value:
1786+
lic_exp_value = ""
1787+
special_char_in_expression, lic_list = parse_license_expression(
1788+
about.spdx_license_expression.value)
1789+
if special_char_in_expression:
1790+
msg = (about.about_file_path + u": The following character(s) cannot be in the spdx_license_expression: " +
1791+
str(special_char_in_expression))
1792+
errors.append(Error(ERROR, msg))
1793+
else:
1794+
spdx_lic_exp_segment = about.spdx_license_expression.value.split()
1795+
for spdx_lic_key in spdx_lic_exp_segment:
1796+
if lic_exp_value:
1797+
lic_exp_value = lic_exp_value + " " + convert_spdx_expression_to_lic_expression(
1798+
spdx_lic_key, spdx_sclickey_dict)
1799+
else:
1800+
lic_exp_value = convert_spdx_expression_to_lic_expression(
1801+
spdx_lic_key, spdx_sclickey_dict)
1802+
if lic_exp_value:
1803+
about.license_expression.value = lic_exp_value
1804+
about.license_expression.present = True
1805+
17821806
if about.license_expression.value:
17831807
special_char_in_expression, lic_list = parse_license_expression(
17841808
about.license_expression.value)
@@ -1855,6 +1879,30 @@ def pre_process_and_fetch_license_dict(abouts, from_check=False, api_url=None, a
18551879
return key_text_dict, errors
18561880

18571881

1882+
def convert_spdx_expression_to_lic_expression(spdx_key, spdx_lic_dict):
1883+
"""
1884+
Translate the spdx_license_expression to license_expression and return
1885+
errors if spdx_license_key is not matched
1886+
"""
1887+
value = ""
1888+
if spdx_key in spdx_lic_dict:
1889+
value = spdx_lic_dict[spdx_key]
1890+
else:
1891+
if spdx_key.startswith('('):
1892+
mod_key = spdx_key.partition('(')[2]
1893+
value = '(' + \
1894+
convert_spdx_expression_to_lic_expression(
1895+
mod_key, spdx_lic_dict)
1896+
elif spdx_key.endswith(')'):
1897+
mod_key = spdx_key.rpartition(')')[0]
1898+
value = convert_spdx_expression_to_lic_expression(
1899+
mod_key, spdx_lic_dict) + ')'
1900+
else:
1901+
# This can be operator or key that don't have match
1902+
value = spdx_key
1903+
return value
1904+
1905+
18581906
def parse_license_expression(lic_expression):
18591907
licensing = Licensing()
18601908
lic_list = []

src/attributecode/util.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,50 @@ def norm(p):
192192
return p
193193

194194

195+
def get_spdx_key_and_lic_key_from_licdb():
196+
"""
197+
Return a dictionary list that fetch all licenses from licenseDB. The
198+
"spdx_license_key" will be the key of the dictionary and the "license_key"
199+
will be the value of the directionary
200+
"""
201+
import requests
202+
lic_dict = dict()
203+
204+
# URL of the license index
205+
url = "https://scancode-licensedb.aboutcode.org/index.json"
206+
207+
"""
208+
Sample of one of the license in the index.json
209+
{
210+
"license_key": "bsd-new",
211+
"category": "Permissive",
212+
"spdx_license_key": "BSD-3-Clause",
213+
"other_spdx_license_keys": [
214+
"LicenseRef-scancode-libzip"
215+
],
216+
"is_exception": false,
217+
"is_deprecated": false,
218+
"json": "bsd-new.json",
219+
"yaml": "bsd-new.yml",
220+
"html": "bsd-new.html",
221+
"license": "bsd-new.LICENSE"
222+
},
223+
"""
224+
response = requests.get(url)
225+
# Check if the request was successful (status code 200)
226+
if response.status_code == 200:
227+
# Retrieve the JSON data from the response
228+
licenses_index = response.json()
229+
230+
for license in licenses_index:
231+
lic_dict[license['spdx_license_key']] = license['license_key']
232+
if license['other_spdx_license_keys']:
233+
for other_spdx in license['other_spdx_license_keys']:
234+
lic_dict[other_spdx] = license['license_key']
235+
236+
return lic_dict
237+
238+
195239
def get_relative_path(base_loc, full_loc):
196240
"""
197241
Return a posix path for a given full location relative to a base location.

0 commit comments

Comments
 (0)