Skip to main content

Comprehensive licence normalisation with a three-level hierarchy.

Project description

licence-normaliser logo

Comprehensive licence normalsation with a three-level hierarchy.

PyPI Version Supported Python versions Build Status Documentation Status llms.txt - documentation for LLMs MIT Coverage

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:

  1. LicenceFamily - broad bucket: "cc", "osi", "copyleft", "publisher-tdm", …

  2. LicenceName - version-free: "cc-by", "cc-by-nc-nd", "mit", "wiley-tdm"

  3. 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

Author

Artur Barseghyan <artur.barseghyan@gmail.com>

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

licence_normaliser-0.4.tar.gz (174.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

licence_normaliser-0.4-py3-none-any.whl (187.5 kB view details)

Uploaded Python 3

File details

Details for the file licence_normaliser-0.4.tar.gz.

File metadata

  • Download URL: licence_normaliser-0.4.tar.gz
  • Upload date:
  • Size: 174.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for licence_normaliser-0.4.tar.gz
Algorithm Hash digest
SHA256 11eab8d1b42382ecd53d08cd3e5e908377048a841af83ad2d038ca420dfb62b6
MD5 aeefa5a9e95118bc4bfff2f730d54ee5
BLAKE2b-256 ec76c4dbd5755e5ddfc2a00863a6d4cb69c0c2f5ab4b410b78f8922cfd3bf53e

See more details on using hashes here.

File details

Details for the file licence_normaliser-0.4-py3-none-any.whl.

File metadata

File hashes

Hashes for licence_normaliser-0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 3f6e249e70a5495ea0121fc4d1196eaee824c1fdd7f7de91cb7eea75968c337e
MD5 97c1b81acf1aebe43a5361d66a05d8ea
BLAKE2b-256 4dc53ff1d399ef53f7e78032a6b196de2758bdcf5760f27aa27e35b0d2f30462

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page