Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
AAriam committed Oct 11, 2024
1 parent 8211bf3 commit 996b150
Show file tree
Hide file tree
Showing 9 changed files with 776 additions and 146 deletions.
2 changes: 2 additions & 0 deletions docs/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Notify SPDX about mismatches and missing info in license data (build db and run verification)
- Notify PyPA trove-classifiers about LicenseMan (https://github.com/pypa/trove-classifiers/issues/17)
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ namespaces = true
# ----------------------------------------- Project Metadata -------------------------------------
#
[project]
version = "0.0.0.dev1"
version = "0.0.0.dev2"
name = "LicenseMan"
requires-python = ">=3.10"
dependencies = [
"LoggerMan == 0.0.0.dev49",
"PyLinks",
"PkgData",
"PySerials",
"MDit == 0.0.0.dev20",
"ExceptionMan == 0.0.0.dev20",
"platformdirs >= 4.3, < 5",
]
3 changes: 3 additions & 0 deletions src/licenseman/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from loggerman import logger

from licenseman import spdx
12 changes: 10 additions & 2 deletions src/licenseman/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
from pathlib import Path as _Path

import pkgdata as _pkgdata
import pyserials as _ps

__all__ = ["get"]
__all__ = ["get_filepath"]


def get(relative_path: str) -> _Path:
def get_filepath(relative_path: str) -> _Path:
"""Get the absolute path to a package data file.
Parameters
Expand All @@ -27,3 +28,10 @@ def get(relative_path: str) -> _Path:
path_absolute=filepath,
)
return filepath


def spdx_to_trove_mapping() -> dict[str, str]:
"""Get the SPDX to Trove classifier mapping."""
rel_path = "spdx/trove_classifiers.yaml"
abs_path = get_filepath(rel_path)
return _ps.read.yaml_from_file(abs_path)
152 changes: 152 additions & 0 deletions src/licenseman/spdx/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
from pathlib import Path as _Path
import json as _json
import platformdirs as _platdir
import pylinks as _pl
from pylinks.exception.api import WebAPIError as _WebAPIError

from licenseman.spdx.license_db import SPDXLicenseDB
from licenseman.spdx.license_list import SPDXLicenseList
from licenseman.spdx.license import SPDXLicense
from licenseman import logger


URL_TEMPLATE_LICENSE_XML = "https://raw.githubusercontent.com/spdx/license-list-data/refs/heads/main/license-list-XML/{}.xml"
URL_TEMPLATE_LICENSE_JSON = "https://raw.githubusercontent.com/spdx/license-list-data/refs/heads/main/json/details/{}.json"
URL_LICENSE_LIST = "https://spdx.org/licenses/licenses.json"


def license_db(
path: str | _Path | None = _platdir.site_cache_path(
appauthor="RepoDynamics",
appname="LicenseMan",
) / "SPDX_DB",
force_update: bool = False,
verify_updates: bool = True,
) -> SPDXLicenseDB:
db_path = _Path(path)
db_license_path = db_path / "licenses"
license_list_ = _get_global_license_list()
license_ids = license_list_.license_ids
if force_update or not db_path.is_dir():
missing_ids = license_ids
intro = "Force update is enabled" if force_update else f"SPDX license database not found at {db_path}"
logger.log(
"info" if force_update else "notice",
"SPDX License Database Load",
f"{intro}; downloading all latest SPDX license data."
)
else:
missing_ids = []
for license_id in license_ids:
if not (db_license_path / f"{license_id}.json").is_file():
missing_ids.append(license_id)
if not missing_ids:
logger.success(
"SPDX License Database Load",
f"Loaded database from {db_path}; all {len(license_ids)} license files found."
)
return SPDXLicenseDB(license_list_, db_path)
num_missing = len(missing_ids)
num_available = len(license_ids) - num_missing
logger.log(
"notice",
"SPDX License Database Load",
f"Loaded database from {db_path}; "
f"found {num_missing} missing license files (available: {num_available})."
)
db_license_path.mkdir(parents=True, exist_ok=True)
for missing_id in missing_ids:
output_path = db_license_path / f"{missing_id}.json"
license_data = license(missing_id, verify=verify_updates)
with open(output_path, "w") as f:
_json.dump(license_data.raw_data, f)
logger.success(
"SPDX License Database Update",
f"Downloaded '{missing_id}' to 'file://{output_path}'.",
)
return SPDXLicenseDB(license_list_, db_path)


def license_list() -> SPDXLicenseList:
"""Get the latest version of the [SPDX license list](https://spdx.org/licenses/) from SPDX website."""
data = _pl.http.request(URL_LICENSE_LIST, response_type="json")
return SPDXLicenseList(data)


def license(license_id: str, verify: bool = True) -> SPDXLicense:
"""Get an SPDX license.
Parameters
----------
license_id
SPDX license ID, e.g., 'MIT', 'GPL-2.0-or-later'.
"""
data = license_json(license_id)
data["xml"] = license_xml(license_id)
license_list_ = _get_global_license_list()
for list_entry_key, list_entry_val in license_list_[license_id].items():
# 'detailsUrl', 'reference', 'referenceNumber' are not present in JSON data
if list_entry_key not in data:
data[list_entry_key] = list_entry_val
logger.info(
"SPDX JSON License Load",
f"Added missing '{list_entry_key}' entry to '{license_id}' JSON data from license list."
)
elif data[list_entry_key] != list_entry_val:
logger.warning(
"SPDX JSON License Load",
f"Mismatched '{list_entry_key}' entry in '{license_id}' JSON data.",
"JSON content:",
logger.pretty(data[list_entry_key]),
"License list content:",
logger.pretty(list_entry_val),
)
return SPDXLicense(data, verify=verify)


def license_xml(license_id: str) -> str:
"""Get an SPDX license definition in XML format from SPDX
[license-list-data](https://github.com/spdx/license-list-data) repository.
Parameters
----------
license_id
SPDX license ID, e.g., 'MIT', 'GPL-2.0-or-later'.
"""
try:
xml_str = _pl.http.request(
URL_TEMPLATE_LICENSE_XML.format(license_id),
response_type="str"
)
except _WebAPIError as e:
raise Exception(f"Error downloading license XML for ID '{license_id}") from e
return xml_str


def license_json(license_id: str) -> dict:
"""Get an SPDX license definition in XML format from SPDX
[license-list-data](https://github.com/spdx/license-list-data) repository.
Parameters
----------
license_id
SPDX license ID, e.g., 'MIT', 'GPL-2.0-or-later'.
"""
try:
json_data = _pl.http.request(
URL_TEMPLATE_LICENSE_JSON.format(license_id),
response_type="json"
)
except _WebAPIError as e:
raise Exception(f"Error downloading license JSON for ID '{license_id}") from e
return json_data


def _get_global_license_list() -> SPDXLicenseList:
global _LICENSE_LIST
if _LICENSE_LIST is None:
_LICENSE_LIST = license_list()
return _LICENSE_LIST


_LICENSE_LIST: SPDXLicenseList | None = None
Loading

0 comments on commit 996b150

Please sign in to comment.