Code Quality and Style

We enforce consistent code style using pre-commit hooks. All code must follow these guidelines:

Code Formatting

  1. We follow PEP 8 with some modifications:

    • Line length is 120 characters (not 79).

    • Uses black for consistent formatting.

    • Uses isort for import sorting with: - Single line imports. - Black-compatible formatting. - Project imports grouped under anemoi.

  2. Import organization:

    • Imports are sorted using isort.

    • Force single-line imports.

    • Group order: standard library, third-party, anemoi packages.

Linting

We use ruff for code linting, which checks for:

  • Code style violations.

  • Potential bugs.

  • Complexity issues.

  • Best practices.

Documentation Style

  1. Documentation follows strict guidelines:

    • RST files are formatted using rstfmt.

    • Docstrings must match function signatures (checked by docsig).

    • Sphinx documentation is linted (sphinx-lint).

Pre-commit Checks

All code is automatically checked using pre-commit hooks that verify:

  1. Code formatting: Black formatting. Import sorting. Line endings and trailing whitespace.

  2. Code quality: No debugger statements. No merge conflicts. Type annotations. No blanket noqa statements.

  3. Documentation: Docstring validation. RST formatting. Sphinx linting.

File Organization

Proper file organization is crucial for maintaining a clean and maintainable codebase. Follow these guidelines when adding new files or modifying existing ones:

Directory Structure

Place new files in the appropriate package directory:

  • Core functionality goes in src/anemoi/<package_name>/.

  • Tests go in tests/.

  • Documentation in docs/.

  • Group related functionality together in the same module for better organisation and maintainability.

Note

When adding new files, ensure they are properly included in __init__.py files if they should be part of the public API. Keep it minimal. Use __init__.py to define package-level exports using __all__.

Utility Functions Organization:
  • Use utils.py only for package-specific helper functions that don’t fit in other modules.

  • If a utility function could be useful across multiple packages: - Move it to anemoi-utils package. - Document its general-purpose nature. - Ensure it remains stateless and reusable.

  • Avoid using utils.py as a catch-all; if multiple related utilities emerge, consider creating a dedicated module.

File Structure

Within each file:

  1. Start with the license header and imports:

    • Anemoi contributors license header.

    • Standard library imports.

    • Third-party imports.

    • Local imports.

  2. Follow with any module-level constants or configurations.

  3. Define classes and functions in a logical order:

    • Base classes before derived classes.

    • Related functions grouped together.

    • Public API before private implementations.

Note

Use absolute imports within the package. Avoid wildcard (*) imports.

Naming Conventions

  1. Use descriptive names that clearly indicate purpose or functionality.

  2. Files and Modules:

    • Use lowercase with underscores

    • Examples:

      • reduced_gaussian_grid.py

      • ReducedGaussianGrid.py

      • rgrid.py ❌ (too vague)

  3. Classes:

    • Use PascalCase (CapWords)

    • Examples:

      • ReducedGaussianGridNodes

      • MultiScaleEdges

      • reduced_gaussian_grid_nodes

      • Rgn ❌ (too cryptic)

  4. Functions and Variables:

    • Use snake_case

    • Use verbs for functions, nouns for variables

    • Examples:

      • calculate_edge_weights()

      • get_coordinates()

      • node_attributes

      • calculateEdgeWeights()

      • crds ❌ (too vague)

  5. Constants:

    • Use uppercase with underscores

    • Examples:

      • MAX_GRID_RESOLUTION

      • DEFAULT_BATCH_SIZE

      • MaxGridResolution

  6. Private Names:

    • Prefix with single underscore for internal use

    • Examples:

      • _validate_input()

      • _cached_result

  7. Type Variables:

    • Use CamelCase, preferably single letters or short names

    • Examples:

      • T ✅ (for generic type)

      • NodeType

      • EdgeAttr

  8. Enums:

    • Use CamelCase for enum class names

    • Use UPPERCASE for enum members

    • Examples:

      • class NodeType(Enum):

      • SOURCE = "source"

      • TARGET = "target"

  9. Test Names:

    • Prefix with test_ (methods) or Test (classes).

    • Be descriptive about what is being tested.

    • Include the scenario and expected outcome.

    • Examples:

      • test_reduced_gaussian_grid_with_invalid_resolution

      • test_edge_builder_handles_empty_graph

      • test_coordinates_are_in_radians

      • testGrid ❌ (too vague)

      • test1 ❌ (meaningless)

Note

Avoid abbreviations unless they are widely understood in the domain (e.g., lat, lon for latitude/longitude). Clarity is more important than brevity.