Contributor guidelines ********************** Developer prerequisites ======================= pre-commit ---------- 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 hooks Installing pre-commit ensures all contributions adhere to the project's code quality standards. Code standards ============== ruff and doc8 are triggered automatically by pre-commit. To run checks manually: make doc8 make ruff Import conventions ------------------ **Import 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. Virtual environment =================== make create-venv Installation ============ make install Testing ======= Note: Python 3.15 is being tested on GitHub CI, but not inside a local Docker image. Docker-based testing (recommended) ---------------------------------- 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 Python Local testing (alternative) --------------------------- For 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 test **Important**: If you encounter tooling errors with local testing, fall back to Docker-based testing which is the canonical environment. GitHub Actions -------------- 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. Adding new normalisation rules ============================== For a new **alias** or **family override** for an *existing* license: 1. Add an entry to "src/licence_normaliser/data/aliases/aliases.json". 2. Optionally, add an "aliases" array 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"] } } 3. Add a test in "src/licence_normaliser/tests/test_aliases.py" or "test_alias_expansion.py". 4. No Python changes needed. For a new **prose pattern** (regex matching free-text descriptions): 1. Add an entry to "src/licence_normaliser/data/prose/prose_patterns.json". 2. Add a test in "src/licence_normaliser/tests/test_prose.py". 3. No Python changes needed. For a new **URL mapping**: 1. Add an entry to "src/licence_normaliser/data/urls/url_map.json" or "src/licence_normaliser/data/publishers/publishers.json". 2. Add a test in "src/licence_normaliser/tests/test_publisher.py". 3. No Python changes needed. For a **brand-new license key** (SPDX, OpenDefinition, OSI, CC, or ScanCode): 1. The upstream data source must be updated first ("licence-normaliser update-data --force" for SPDX/OpenDefinition, or edit the upstream source for OSI/CC/ScanCode). 2. The parser will pick it up automatically on the next import. 3. Add an alias in "aliases.json" if needed. 4. Add family override in "aliases.json" if needed. 5. Add tests. For a **new parser** (new upstream data source): 1. Create "src/licence_normaliser/parsers/my_parser.py" implementing "BasePlugin". 2. Register it in "src/licence_normaliser/parsers/__init__.py". 3. Set "is_registry_entry = False" if the parser only contributes aliases/URLs/patterns (not new license keys). 4. Add tests. Releases ======== **Build the package for releasing:** make package-build ====================================================================== **Test the built package:** make check-package-build ====================================================================== **Make a test release (test.pypi.org):** make test-release ====================================================================== **Release (pypi.org):** make release Adding tests ============ * Every new normalisation rule must have a corresponding test. * Tests should cover both successful normalisation and edge cases. Pull requests ============= 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 license formats. * Improving error messages. General checklist ----------------- * 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-normaliser" should have minimal dependencies. When fixing bugs ---------------- * Add a regression test that reproduces the bug before your fix. When adding a new feature ------------------------- * Update "README.rst", "AGENTS.md", and "ARCHITECTURE.rst" if applicable. * Add appropriate tests. Questions ========= Ask on GitHub discussions. Issues ====== Report bugs or request features on GitHub issues.