| """
|
| License utility functions for normalising and verifying SPDX license IDs.
|
| """
|
| import logging
|
| from typing import Optional, Dict
|
|
|
| logger = logging.getLogger(__name__)
|
|
|
|
|
| LICENSE_URLS: Dict[str, str] = {
|
| "Apache-2.0": "https://www.apache.org/licenses/LICENSE-2.0.txt",
|
| "MIT": "https://opensource.org/licenses/MIT",
|
| "BSD-3-Clause": "https://opensource.org/licenses/BSD-3-Clause",
|
| "BSD-2-Clause": "https://opensource.org/licenses/BSD-2-Clause",
|
| "GPL-3.0-only": "https://www.gnu.org/licenses/gpl-3.0.txt",
|
| "GPL-2.0-only": "https://www.gnu.org/licenses/gpl-2.0.txt",
|
| "LGPL-3.0-only": "https://www.gnu.org/licenses/lgpl-3.0.txt",
|
| "CC-BY-4.0": "https://creativecommons.org/licenses/by/4.0/legalcode",
|
| "CC-BY-SA-4.0": "https://creativecommons.org/licenses/by-sa/4.0/legalcode",
|
| "CC-BY-NC-4.0": "https://creativecommons.org/licenses/by-nc/4.0/legalcode",
|
| "CC-BY-ND-4.0": "https://creativecommons.org/licenses/by-nd/4.0/legalcode",
|
| "CC-BY-NC-SA-4.0": "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode",
|
| "CC-BY-NC-ND-4.0": "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode",
|
| "CC0-1.0": "https://creativecommons.org/publicdomain/zero/1.0/legalcode",
|
| "MPL-2.0": "https://www.mozilla.org/en-US/MPL/2.0/",
|
| "Unlicense": "https://unlicense.org/",
|
| "nvidia-open-model-license": "https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/",
|
| }
|
|
|
|
|
| LICENSE_MAPPING: Dict[str, str] = {
|
| "apache license 2.0": "Apache-2.0",
|
| "apache-2.0": "Apache-2.0",
|
| "mit": "MIT",
|
| "mit license": "MIT",
|
| "bsd-3-clause": "BSD-3-Clause",
|
| "cc-by-4.0": "CC-BY-4.0",
|
| "cc-by-nc-4.0": "CC-BY-NC-4.0",
|
| "cc0-1.0": "CC0-1.0",
|
| "gpl-3.0": "GPL-3.0-only",
|
| "nvidia open model license agreement": "nvidia-open-model-license",
|
|
|
| }
|
|
|
| def normalize_license_id(license_id: str) -> Optional[str]:
|
| """
|
| Normalize a license string to a valid SPDX ID if possible.
|
| Returns None if no clear mapping is found.
|
| """
|
| if not license_id:
|
| return None
|
|
|
|
|
| if license_id in LICENSE_URLS:
|
| return license_id
|
|
|
| lower_id = license_id.lower()
|
|
|
|
|
| if lower_id in LICENSE_MAPPING:
|
| return LICENSE_MAPPING[lower_id]
|
|
|
|
|
| for valid_id in LICENSE_URLS:
|
| if valid_id.lower() == lower_id:
|
| return valid_id
|
|
|
|
|
| if " " not in license_id and len(license_id) < 50:
|
|
|
| return license_id
|
|
|
| return None
|
|
|
| def get_license_url(license_id: str, fallback: bool = True) -> Optional[str]:
|
| """Get the URL for a license based on its ID.
|
| If fallback is False, returns None if not in known list.
|
| """
|
| if license_id in LICENSE_URLS:
|
| return LICENSE_URLS[license_id]
|
|
|
|
|
| lower_id = license_id.lower()
|
| for valid_id, url in LICENSE_URLS.items():
|
| if valid_id.lower() == lower_id:
|
| return url
|
|
|
| return f"https://spdx.org/licenses/{license_id}.html" if fallback else None
|
|
|
|
|
| _licensing = None
|
|
|
| def is_valid_spdx_license_id(license_id: str) -> bool:
|
| """Check if the license ID is a valid SPDX ID"""
|
| global _licensing
|
| try:
|
| from license_expression import get_spdx_licensing
|
| if _licensing is None:
|
| _licensing = get_spdx_licensing()
|
|
|
|
|
| res = _licensing.validate(license_id)
|
| if len(res.errors) > 0:
|
| return False
|
|
|
|
|
| parsed = _licensing.parse(license_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| return hasattr(parsed, "key") and not hasattr(parsed, "children")
|
| except ImportError:
|
| logger.warning("license-expression library not found, skipping validation")
|
| return True
|
| except Exception as e:
|
| logger.debug(f"License validation error: {e}")
|
| return False
|
|
|