Contribution guideΒΆ

To add a new Linux distribution, you can get inspiration from these pull requests:

  • https://github.com/kdeldycke/extra-platforms/pull/156

  • https://github.com/kdeldycke/extra-platforms/pull/94

claude.md fileΒΆ

This file provides guidance to Claude Code when working with code in this repository.

Project overviewΒΆ

Extra Platforms is a Python library for detecting and managing platform/OS information.

It provides:

  • Detection of architectures, platforms (operating systems), and CI systems

  • Grouping of platforms into families (e.g., LINUX, BSD, UNIX)

  • Pytest decorators for conditional test skipping (@skip_<id>, @unless_<id>)

Upstream conventionsΒΆ

This repository uses reusable workflows from kdeldycke/workflows and follows the conventions established there. For code style, documentation, testing, and design principles, refer to the upstream claude.md as the canonical reference.

Contributing upstream: If you spot inefficiencies, improvements, or missing features in the reusable workflows, propose changes via a pull request or issue at kdeldycke/workflows.

CommandsΒΆ

TestingΒΆ

# Run all tests with coverage.
$ uv run --group test pytest

# Run a single test file.
$ uv run --group test pytest tests/test_platform_data.py

# Run a specific test.
$ uv run --group test pytest tests/test_platform_data.py::test_function_name

# Run tests in parallel.
$ uv run --group test pytest -n auto

Type checkingΒΆ

$ uv run --group typing mypy extra_platforms

DocumentationΒΆ

Build Sphinx documentation locally:

$ uv run sphinx-build -b html ./docs ./docs/html

ArchitectureΒΆ

Core classesΒΆ

All core classes are defined in trait.py:

Trait (ABC) - Base class for all detectable traits
β”œβ”€β”€ Platform - Operating systems
β”œβ”€β”€ Architecture - CPU architectures
└── CI - CI/CD systems

Group - Collection of Traits with set-like operations (group.py)

Module layoutΒΆ

Module

Purpose

trait.py

Base classes: Trait, Platform, Architecture, CI

detection.py

All is_<id>() detection functions

group.py

Group class, reduce(), traits_from_ids(), groups_from_ids()

platform_data.py

All Platform instances (MACOS, UBUNTU, WINDOWS, etc.)

architecture_data.py

All Architecture instances (X86_64, AARCH64, etc.)

ci_data.py

All CI instances (GITHUB_CI, GITLAB_CI, etc.)

group_data.py

All Group instances and ID collections

pytest.py

Generates @skip_<id> and @unless_<id> decorators

_utils.py

Internal utilities

_types.py

Type aliases

Detection patternΒΆ

Each trait has a corresponding is_<id>() function in detection.py. The Trait.current cached property calls detection.is_{self.id}() to check if the trait matches the current environment.

Dynamic code generationΒΆ

  • __init__.py generates is_<group_id>() functions for all groups at import time

  • pytest.py generates skip_<id> and unless_<id> decorators for all traits and groups

Documentation requirementsΒΆ

Changelog and readme updatesΒΆ

Always update documentation when making changes:

  • changelog.md: Add a bullet point describing user-facing changes (new features, bug fixes, behavior changes).

  • readme.md: Update relevant sections when adding/modifying public API, classes, or functions.

Code styleΒΆ

Comments and docstringsΒΆ

  • All comments in Python files must end with a period.

  • Docstrings use reStructuredText format (vanilla style, not Google/NumPy).

  • Documentation in ./docs/ uses MyST markdown format where possible. Fallback to reStructuredText if necessary.

  • Keep lines within 88 characters in Python files, including docstrings and comments (ruff default). Markdown files have no line-length limit.

  • Titles in markdown use sentence case.

Documenting code decisionsΒΆ

Document design decisions, trade-offs, and non-obvious implementation choices directly in the code:

  • Use docstring admonitions for important notes:

    """Extract metadata from repository.
    
    .. warning::
        This method temporarily modifies repository state during execution.
    
    .. note::
        The commit range is inclusive on both ends.
    """
    
  • Use inline comments for explaining specific code blocks:

    # We use a frozenset for O(1) lookups and immutability.
    SKIP_BRANCHES: Final[frozenset[str]] = frozenset(("branch-a", "branch-b"))
    

TYPE_CHECKING blockΒΆ

Place a module-level TYPE_CHECKING block immediately after the module docstring:

TYPE_CHECKING = False
if TYPE_CHECKING:
    from collections.abc import Iterator
    from ._types import _T, _TNestedReferences

ImportsΒΆ

  • Import from the root package (from extra_platforms import CI), not submodules (from extra_platforms.trait import CI).

  • Place imports at the top of the file, unless avoiding circular imports or improving data registry clarity.

Testing guidelinesΒΆ

  • Use @pytest.mark.parametrize when testing the same logic for multiple traits/groups.

  • Keep test logic simple with straightforward asserts.

  • Tests should be sorted logically and alphabetically where applicable.

  • Enforce naming conventions for traits and groups via tests.

Design principlesΒΆ

PhilosophyΒΆ

  1. Create something that works (to provide business value).

  2. Create something that’s beautiful (to lower maintenance costs).

  3. Work on performance.

Linting and formattingΒΆ

Linting and formatting are automated via GitHub workflows. Developers don’t need to run these manually during development, but are still expected to do best effort. Push your changes and the workflows will catch any issues.

Data registry priorityΒΆ

The *_data.py files (trait and group definitions) should be clean and easy to maintain. It’s acceptable to use indirections elsewhere (like function-level imports) to achieve this.

Ordering and uniquenessΒΆ

  • All IDs must be unique across traits and groups.

  • High-level objects in data files must be sorted alphabetically by ID.

  • Tests should verify this ordering.

CachingΒΆ

  • Detection functions are cached with @cache decorator.

  • Use invalidate_caches() to reset all cached detection results.

Optional dependenciesΒΆ

Pytest integration requires the extra_platforms[pytest] extra.