Developing

This guide covers setting up a development environment, running tests, building documentation, and enforcing code quality.

Prerequisites

  • Python 3.12+

  • uv — package manager and virtual environment manager (https://github.com/astral-sh/uv). Install with:

    curl -LsSf https://astral.sh/uv/install.sh | sh
    

Project structure

proyecto_aemet_api/
|-- pyproject.toml          # Project metadata and dependencies
|-- src/aemetxfb/           # Package source code
|   |-- clim/               # Climate data module
|   |-- obs/                # Observations module
|   |-- pred/               # Predictions (hourly forecasts) module
|   |-- utils.py            # Shared utilities and configuration
|   `-- cache.py            # Optional disk cache
|-- tests/                  # Unit tests (mirrors src/ structure)
|-- docs/source/            # Sphinx documentation source
`-- scripts/                # Utility scripts

Setting up the environment

The project uses uv to manage dependencies and the virtual environment. All commands below assume you are in the project root directory.

Install dependencies

# Core dependencies (numpy, pandas)
uv sync

# With test dependencies (pytest, ruff)
uv sync --extra tests

# With documentation dependencies (sphinx, sphinx-rtd-theme)
uv sync --extra docs

# With all extras (tests, docs, dev tools)
uv sync --all-extras

Running tests

The test suite uses pytest and covers all public functions. Tests include unit tests, mocked HTTP calls, and doctests embedded in docstrings.

# Run all tests
uv run pytest

# Run a specific test file
uv run pytest tests/pred/test_pred.py

# Run a specific test class
uv run pytest tests/pred/test_pred.py::TestGetForecast

# Run a specific test method
uv run pytest tests/pred/test_pred.py::TestGetForecast::test_by_name_returns_forecast_instance

# Run with verbose output
uv run pytest -v

# Stop on first failure
uv run pytest -x

# Show captured stdout/stderr
uv run pytest -s

Test structure

Tests mirror the package structure under tests/:

tests/
|-- test_utils.py           # Tests for shared utilities
|-- clim/                   # Tests for climate module
|   |-- conftest.py
|   |-- test_clim_ephem.py
|   |-- test_clim_extremes.py
|   |-- test_clim_normals.py
|   |-- test_clim_thresholds.py
|   `-- test_masts.py
|-- obs/                    # Tests for observations module
|   |-- test_chem.py
|   |-- test_lightning.py
|   |-- test_obs.py
|   |-- test_radar.py
|   |-- test_radiation.py
|   `-- test_satellite.py
`-- pred/                   # Tests for predictions module
    |-- test_pred.py
    `-- test_stations.py

Some tests use unittest.mock.patch to avoid real network calls. Tests that require live AEMet endpoints are marked with @pytest.mark.skip by default.

Linting and formatting

The project uses ruff for linting and formatting. All code must pass both checks before committing.

# Check for linting issues
uv run ruff check

# Auto-fix fixable issues
uv run ruff check --fix

# Check formatting
uv run ruff format --check

# Format all files
uv run ruff format

# Check a specific directory
uv run ruff check src/aemetxfb/pred/
uv run ruff format --check src/aemetxfb/pred/

Style rules

  • Line length: 79 characters (enforced by ruff format)

  • Import order: standard library, third-party, local (enforced by ruff)

  • Type annotations: required for all public functions and classes

  • Docstrings: NumPy/SciPy style with Parameters, Returns, Raises, and Examples sections

  • Naming: snake_case for functions and variables, PascalCase for classes

Building documentation

Documentation is written in reStructuredText and built with Sphinx. Source files live in docs/source/.

# Install documentation dependencies
uv sync --extra docs

# Build HTML documentation
uv run sphinx-build docs/source docs/source/build/html

# Or use the provided script
./scripts/build_docs.sh

# Build with warnings as errors (CI mode)
uv run sphinx-build -W docs/source docs/source/build/html

The generated HTML is written to docs/source/build/html/. Open index.html in a browser to view it.

Documentation structure

docs/source/
|-- conf.py                 # Sphinx configuration
|-- index.rst               # Documentation home page
|-- quickstart.rst          # Quick start guide
|-- configuration.rst       # Runtime configuration guide
|-- caching.rst             # Disk cache guide
|-- developing.rst          # This page
|-- modules/
|   |-- climate.rst         # Climate module guide
|   |-- observations.rst    # Observations module guide
|   `-- predictions.rst     # Predictions module guide
`-- api/
    |-- aemetxfb.rst        # Top-level package reference
    |-- aemetxfb.clim.rst   # Climate API reference
    |-- aemetxfb.obs.rst    # Observations API reference
    |-- aemetxfb.pred.rst   # Predictions API reference
    |-- aemetxfb.utils.rst  # Utilities API reference
    `-- aemetxfb.cache.rst  # Cache API reference

Writing documentation

  • Use :py:func:\`~module.func\, :py:class:\`~module.Class\, and :py:mod:\`~module\ cross-references to link to API elements.

  • Use .. code-block:: python for Python code examples.

  • Use .. list-table:: for structured data (columns, types, descriptions).

  • Use .. note::, .. warning::, and .. seealso:: directives for callouts.

  • Docstrings in source code use NumPy/SciPy style and are automatically included in the API reference via sphinx.ext.autodoc.

Continuous integration

The project uses the following checks in CI:

  1. Tests: uv run pytest — all tests must pass

  2. Linting: uv run ruff check — no lint errors

  3. Formatting: uv run ruff format --check — code must be formatted

  4. Documentation: uv run sphinx-build -W docs/source docs/source/build/html — no Sphinx warnings

Pre-commit checklist

Before committing changes, run:

uv run pytest
uv run ruff check
uv run ruff format --check

If all three pass, the changes are ready to commit.