Refer to pre-commit for installation instructions.
TL;DR:
curl -LsSf https://astral.sh/uv/install.sh | sh # Install uv
uv tool install pre-commit # Install pre-commit
pre-commit install # Install hooksInstalling pre-commit ensures all contributions adhere to the project's code quality standards.
ruff and doc8 are triggered automatically by pre-commit.
To run checks manually:
make doc8
make ruffImport statements belong at module level. Avoid placing imports inside functions or methods unless absolutely necessary:
- Acceptable exceptions:
- Breaking circular dependencies
- Optional runtime dependencies (e.g., CLI-only imports)
- Heavy imports that are rarely used
- Why this matters:
- Improves code readability
- Makes dependencies explicit and discoverable
- Enables static analysis tools to work correctly
- Follows Python community best practices (PEP 8)
When in doubt, place imports at the top of the file.
make create-venvmake installNote
Python 3.15 is being tested on GitHub CI, but not inside a local Docker image.
All tests run inside Docker for platform independence and consistency:
make test # full matrix (Python 3.10-3.14)
make test-env ENV=py312 # single Python version
make shell # interactive shell in test container
make shell-env ENV=py312 # interactive shell for specific PythonFor faster iteration during development, you can run tests locally with uv:
make install # one-time setup
uv run pytest # run all tests
uv run pytest path/to/test_something.py # run specific testImportant: If you encounter tooling errors with local testing, fall back to Docker-based testing which is the canonical environment.
In any case, GitHub Actions runs the full matrix automatically on every push. Tests run on Python 3.10–3.15 (all non-EOL versions). See the versions manifest for the full list of available Python versions.
For a new alias or family override for an existing licence:
Add an entry to
src/licence_normaliser/data/aliases/aliases.json.Optionally, add an
aliasesarray to define additional lookup variants (e.g. hyphen vs space forms) that resolve to the same target:{ "cc by-nc": { "version_key": "cc-by-nc", "name_key": "cc-by-nc", "family_key": "cc", "aliases": ["cc-by-nc", "cc by nc", "cc-by nc"] } }Add a test in
src/licence_normaliser/tests/test_aliases.pyortest_alias_expansion.py.No Python changes needed.
For a new prose pattern (regex matching free-text descriptions):
- Add an entry to
src/licence_normaliser/data/prose/prose_patterns.json. - Add a test in
src/licence_normaliser/tests/test_prose.py. - No Python changes needed.
For a new URL mapping:
- Add an entry to
src/licence_normaliser/data/aliases/aliases.jsonunder the appropriate entry'surlsarray. - Add a test in
src/licence_normaliser/tests/test_aliases.pyortest_integration.py. - No Python changes needed.
For a brand-new licence key (SPDX, OpenDefinition, OSI, CC, or ScanCode):
- The upstream data source must be updated first
(
licence-normaliser update-data --forcefor SPDX/OpenDefinition, or edit the upstream source for OSI/CC/ScanCode). - The parser will pick it up automatically on the next import.
- Add an alias in
aliases.jsonif needed. - Add family override in
aliases.jsonif needed. - Add tests.
For a new parser (new upstream data source):
- Create
src/licence_normaliser/parsers/my_parser.pyimplementingBasePluginplus one or more plugin interfaces (e.g.,RegistryPlugin,URLPlugin). - Register it in
src/licence_normaliser/defaults.pyin the appropriate_load_*_plugins()function. - Set
is_registry_entry = Falseif the parser only contributes aliases/URLs/patterns (not new licence keys). - Add tests.
Build the package for releasing:
make package-buildTest the built package:
make check-package-buildMake a test release (test.pypi.org):
make test-releaseRelease (pypi.org):
make release- Every new normalisation rule must have a corresponding test.
- Tests should cover both successful normalisation and edge cases.
Open a pull request to the dev branch only. Never directly to main.
Note
Create pull requests to the dev branch only!
Examples of welcome contributions:
- Fixing documentation typos or improving explanations.
- Adding test cases for new edge cases.
- Extending support for additional licence formats.
- Improving error messages.
- Does your change require documentation updates (
README.rst,AGENTS.md,ARCHITECTURE.rst,CONTRIBUTING.rst)? - Does your change require new tests?
- Does your change add any external dependencies?
If so, reconsider:
licence-normalisershould have minimal dependencies.
- Add a regression test that reproduces the bug before your fix.
- Update
README.rst,AGENTS.md, andARCHITECTURE.rstif applicable. - Add appropriate tests.
Ask on GitHub discussions.
Report bugs or request features on GitHub issues.