Contributing#

This guide covers the development workflow, testing, code style, and contribution requirements.

Note

A shorter quick-reference version is available in the repository’s CONTRIBUTING.md file for GitHub.

Getting Started#

Fork and Clone#

Fork the repository on GitHub, then clone your fork:

git clone https://github.com/YOUR-USERNAME/icalendar-anonymizer.git
cd icalendar-anonymizer

Install Development Dependencies#

Install in editable mode with dev extras:

pip install -e ".[dev]"

This installs:

  • pytest - Testing framework

  • ruff - Linting and code formatting

  • pre-commit - Git hook management

  • commitizen - Conventional commit enforcement

  • reuse - License compliance checking

  • build and twine - Package building and publishing

For documentation building, also install:

pip install -e ".[doc]"

Development Workflow#

  1. Create a Feature Branch

    Create a branch from main:

    git checkout main
    git pull origin main
    git checkout -b feature-name
    
  2. Make Changes with Tests

    Write your code changes and corresponding tests. All new features must include tests.

  3. Run Tests and Linting

    pytest                     # Run tests
    ruff check .               # Check for linting errors
    ruff check . --fix         # Auto-fix linting errors
    ruff format .              # Format code
    
  4. Commit with Conventional Format

    Follow Commit Message Format for commit messages:

    git add .
    git commit -m "feat: add new feature description"
    
  5. Push and Open Pull Request

    git push origin feature-name
    

    Then open a pull request on GitHub.

Running Tests#

Run All Tests#

pytest

Run with Coverage#

pytest --cov=src/icalendar_anonymizer --cov-report=html

Coverage report will be in htmlcov/index.html.

Test Requirements#

  • 90% minimum coverage required - PRs fail if coverage drops below this threshold

  • All tests must pass before merge

  • Add tests for all new features and bug fixes

  • Use parametrized tests to reduce duplication (see Test Organization)

CI Test Matrix#

Continuous integration runs tests on:

  • Python versions: 3.11, 3.12, 3.13

  • Operating systems: Ubuntu, Windows, macOS

This creates 9 test jobs total. All must pass before merge.

Code Quality#

Linting with Ruff#

We use Ruff for linting and code formatting with a 100-character line length.

Check for Errors#

ruff check .

Auto-Fix Errors#

ruff check . --fix

Format Code#

ruff format .

Configuration#

Ruff settings are in pyproject.toml under [tool.ruff]. The CI enforces the same Ruff version (>=0.14.0) for consistency.

Code Style Guidelines#

Docstrings#

Use Google-style docstrings with multi-line format:

def function(arg1: str, arg2: int) -> str:
    """Brief description on first line.

    More detailed explanation if needed. Can span multiple paragraphs.

    Args:
        arg1: Description of first argument
        arg2: Description of second argument

    Returns:
        Description of return value

    Raises:
        ValueError: When invalid input provided
    """

Don’t include Examples sections unless they contain real, testable doctests.

Test Organization#

Use pytest.mark.parametrize for duplicate test patterns:

@pytest.mark.parametrize(
    ("property_name", "expected_value"),
    [
        ("status", "CONFIRMED"),
        ("priority", 1),
    ],
)
def test_preserves_metadata(property_name, expected_value):
    """Test implementation."""

Organize tests into logical groups with clear section comments.

Imports#

  • Standard library imports first

  • Third-party imports second

  • Local imports third

  • Sort alphabetically within each group

# Standard library
import hashlib
from datetime import datetime

# Third-party
from icalendar import Calendar

# Local
from icalendar_anonymizer import anonymize

Line Length#

100 characters maximum - enforced by Ruff.

Documentation#

API Documentation#

Use autodoc for API function signatures in Sphinx documentation:

.. autofunction:: icalendar_anonymizer.anonymize

This ensures documentation stays in sync with code. Don’t manually copy function signatures.

Code Examples#

Use doctest format for Python examples in documentation:

.. doctest::

    >>> from icalendar import Calendar
    >>> from icalendar_anonymizer import anonymize
    >>> # Example code here

This allows examples to be automatically tested for correctness.

Pull Request Process#

Requirements#

  • One approval required before merge

  • All tests must pass (9 test jobs)

  • Coverage must be ≥90%

  • PR title must follow Commit Message Format

  • Update CHANGES.rst with your changes

PR Title Format#

PR titles must follow conventional commits because we use squash merge:

feat: add preserve parameter to anonymize function
fix: correct UID uniqueness handling
docs: update installation instructions

The PR title becomes the commit message on main.

Update CHANGES.rst#

Add your changes to CHANGES.rst following the formatting rules documented in the file header.

See CHANGES.rst Formatting below.

CHANGES.rst Formatting#

Add entries under the appropriate category in CHANGES.rst:

Categories#

  • Breaking changes - Incompatible API changes

  • New features - New functionality

  • Minor changes - Small improvements

  • Bug fixes - Bug fixes

Formatting Rules#

Use these RST formatting conventions:

Inline Literals#

Use double backticks for property names and inline code:

``PROPERTY``
``preserve`` parameter

Python Objects#

Use Python domain roles:

:py:func:`function_name`
:py:class:`ClassName`
:py:meth:`method_name`

Files#

Use the :file: directive:

:file:`docs/conf.py`
:file:`pyproject.toml`

Verbs#

Start entries with past tense verbs:

  • Added

  • Fixed

  • Updated

  • Removed

  • Deprecated

Example Entry#

- Added ``preserve`` parameter to :py:func:`anonymize` function. Accepts optional
  set of property names to preserve beyond defaults. Case-insensitive. Allows
  preserving properties like ``CATEGORIES`` or ``COMMENT`` for bug reproduction
  when user confirms no sensitive data. Added 7 tests for preserve functionality.
  See `Issue 53 <https://github.com/mergecal/icalendar-anonymizer/issues/53>`_.

See the CHANGES.rst file header for complete formatting guidelines.

Licensing and REUSE Compliance#

This project follows the REUSE specification for clear licensing.

License#

The project is licensed under AGPL-3.0-or-later.

SPDX Headers and REUSE.toml#

All new source files must include SPDX headers. Auto-generated files that cannot have persistent headers may instead rely on entries in REUSE.toml as a fallback.

In-file headers (required for all source files):

Python Files#

# SPDX-FileCopyrightText: 2025 icalendar-anonymizer contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

RST Files#

.. SPDX-FileCopyrightText: 2025 icalendar-anonymizer contributors
.. SPDX-License-Identifier: AGPL-3.0-or-later

Markdown Files#

<!--- SPDX-FileCopyrightText: 2025 icalendar-anonymizer contributors -->
<!--- SPDX-License-Identifier: AGPL-3.0-or-later -->

REUSE.toml (fallback only):

Use REUSE.toml only as a fallback for auto-generated files that cannot reasonably include headers; it must not be treated as a substitute for adding headers to regular source files.

Checking Compliance#

Pre-commit hooks automatically check REUSE compliance. You can also run manually:

reuse lint

All files must pass REUSE compliance before merge.

Getting Help#

  • Check the Issue Tracker

  • Open a new issue for bugs or feature requests

  • For major changes, open an issue for discussion before starting work

Reference#