Comprehensive licence normalsation with a three-level hierarchy.
licence-normaliser is a comprehensive licence normalisation library that maps any licence representation (SPDX tokens, URLs, prose descriptions) to a canonical three-level hierarchy.
Features
Three-level hierarchy - LicenceFamily → LicenceName → LicenceVersion.
Wide format support - SPDX tokens, URLs, prose descriptions.
Creative Commons support - Full CC family with versions and IGO variants.
Publisher-specific licences - Springer, Nature, Elsevier, Wiley, ACS, and more.
File-driven data - Add aliases, URLs, and patterns by editing JSON files. No Python code changes required for new synonyms.
Pluggable parsers - Drop in a new parser class to ingest any external licence registry. Parsers implement plugin interfaces (RegistryPlugin, URLPlugin, etc.).
Strict mode - Raise LicenceNotFoundError instead of silently returning "unknown".
Caching - LRU caching for performance.
CLI - Command-line interface with --strict and --explain support.
Hierarchy
The library uses a three-level hierarchy:
LicenceFamily - broad bucket: "cc", "osi", "copyleft", "publisher-tdm", …
LicenceName - version-free: "cc-by", "cc-by-nc-nd", "mit", "wiley-tdm"
LicenceVersion - fully resolved: "cc-by-3.0", "cc-by-nc-nd-4.0"
Installation
With uv:
uv pip install licence-normaliser
Or with pip:
pip install licence-normaliser
Quick start
from licence_normaliser import normalise_licence
v = normalise_licence("CC BY-NC-ND 4.0")
str(v) # "cc-by-nc-nd-4.0" ← LicenceVersion
str(v.licence) # "cc-by-nc-nd" ← LicenceName
str(v.licence.family) # "cc" ← LicenceFamily
Strict mode
By default, unresolvable inputs return an "unknown" result. Pass strict=True to raise LicenceNotFoundError instead:
from licence_normaliser import normalise_licence
from licence_normaliser.exceptions import LicenceNotFoundError
# Silent fallback (default)
v = normalise_licence("some-unknown-string")
v.family.key # "unknown"
# Strict: raises on unresolvable input
try:
v = normalise_licence("some-unknown-string", strict=True)
except LicenceNotFoundError as exc:
print(exc.raw) # original input
print(exc.cleaned) # cleaned form that failed lookup
Trace / Explain
Set ENABLE_LICENCE_NORMALISER_TRACE=1 or pass trace=True to get resolution traces showing how the licence was matched:
from licence_normaliser import normalise_licence
# Via function
v = normalise_licence("cc by-nc-nd 3.0 igo", trace=True)
print(v.explain())
# Via class
from licence_normaliser import LicenceNormaliser
ln = LicenceNormaliser(trace=True)
v = ln.normalise_licence("MIT")
print(v.explain())
Output shows the resolution pipeline (alias → registry → url → prose → fallback) and which source file + line matched:
Input: 'cc by-nc-nd 3.0 igo' → 'cc by-nc-nd 3.0 igo'
[✓] alias: 'cc by-nc-nd 3.0 igo' → 'cc-by-nc-nd-3.0-igo' (line 139 in aliases.json)
Result:
version_key: 'cc-by-nc-nd-3.0-igo'
name_key: 'cc-by-nc-nd'
family_key: 'cc'
The trace can also be accessed via v._trace for programmatic use.
Batch normalisation
from licence_normaliser import normalise_licences
results = normalise_licences(["MIT", "Apache-2.0", "CC BY 4.0"])
for r in results:
print(r.key)
# Strict batch - raises on first unresolvable
results = normalise_licences(["MIT", "Apache-2.0"], strict=True)
Custom plugins
The LicenceNormaliser class lets you inject custom plugin classes for specialised use cases:
from licence_normaliser import LicenceNormaliser
from licence_normaliser.parsers.alias import AliasParser
from licence_normaliser.parsers.spdx import SPDXParser
# Use only SPDX + Alias plugins (no CC, no publisher URLs)
ln = LicenceNormaliser(
registry=[SPDXParser],
alias=[AliasParser],
family=[AliasParser],
name=[AliasParser],
cache=True,
cache_maxsize=8192,
)
# MIT resolves via SPDX parser
assert str(ln.normalise_licence("MIT")) == "mit"
# CC BY resolves via Alias
assert str(ln.normalise_licence("CC BY-NC-ND 4.0")) == "cc-by-nc-nd-4.0"
For caching, LicenceNormaliser wraps the resolution method with lru_cache. Disable it by passing cache=False for debugging:
from licence_normaliser import LicenceNormaliser
ln = LicenceNormaliser(cache=False)
result = ln.normalise_licence("MIT")
Update data (CLI)
licence-normaliser update-data --force
# Fetches fresh SPDX, OpenDefinition, OSI, CreativeCommons, and ScanCode JSONs
Integration tests (public API only)
All integration tests live in src/licence_normaliser/tests/test_integration.py and only import the public API.
CLI usage
Normalise a single licence:
licence-normaliser normalise "MIT"
# Output: mit
licence-normaliser normalise --full "CC BY 4.0"
# Output:
# Key: cc-by-4.0
# URL: https://creativecommons.org/licenses/by/4.0/
# Licence: cc-by
# Family: cc
licence-normaliser normalise --strict "totally-unknown"
# Exits with code 1 and prints an error
Batch normalise:
licence-normaliser batch MIT "Apache-2.0" "CC BY 4.0"
licence-normaliser batch --strict MIT "Apache-2.0"
Exceptions
from licence_normaliser.exceptions import (
LicenceNormaliserError, # base class
LicenceNotFoundError, # raised by strict mode
)
Testing
All tests run inside Docker:
make test
To test a specific Python version:
make test-env ENV=py312
Licence
MIT
Release files for license-normalizer 0.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| license_normalizer-0.4.tar.gz | 174.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| license_normalizer-0.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 362.4 kB
Release files / license_normalizer-0.4.tar.gz
| Download URL | license_normalizer-0.4.tar.gz |
|---|---|
| Size | 174.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f885b3f3ba88ff260f041c25b6192b91ad2e3aed20086424c2b7c678ae5f23fe
|
|
BLAKE2b-256 checksum How to use checksums |
4c13b57fd5d459178f0d6ac9967c44a73ebe222dc6735cd5f84380082c16e418
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.11
|
Release files / license_normalizer-0.4-py3-none-any.whl
| Download URL | license_normalizer-0.4-py3-none-any.whl |
|---|---|
| Size | 187.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8b4424ec5740316029881f4ab90ed117822bc76fe3475610f241961cbeab74bb
|
|
BLAKE2b-256 checksum How to use checksums |
aed7ca54fdfbf2d559dcf37af57f4b14d594d40a888d70d90030cbf3053ad188
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.11
|