|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +Agent instructions for the [Iris](https://scitools-iris.readthedocs.io/) |
| 4 | +repository. |
| 5 | + |
| 6 | +Iris is a Python package for analysing and visualising Earth science data, |
| 7 | +built around CF-compliant multi-dimensional arrays ("Cubes"). |
| 8 | + |
| 9 | +Subdirectory AGENTS.md files take precedence for their subtrees: |
| 10 | +- [`changelog/AGENTS.md`](changelog/AGENTS.md) — documentation on changelog |
| 11 | +- [`docs/AGENTS.md`](docs/AGENTS.md) — documentation-specific rules |
| 12 | +- [`lib/iris/tests/AGENTS.md`](lib/iris/tests/AGENTS.md) — test-specific rules |
| 13 | + |
| 14 | + |
| 15 | +## Project Overview |
| 16 | + |
| 17 | +| | | |
| 18 | +|---|---| |
| 19 | +| **Language** | Python 3.12 / 3.13 / 3.14 | |
| 20 | +| **Licence** | BSD-3-Clause | |
| 21 | +| **Distribution** | conda-forge (`iris`), PyPI (`scitools-iris`) | |
| 22 | +| **Key dependencies** | NumPy, Dask, SciPy, Cartopy, CF-Python, NetCDF4 | |
| 23 | +| **Linter / formatter** | Ruff (88-char line length) | |
| 24 | +| **Test runner** | pytest + pytest-xdist (`-n auto`) | |
| 25 | +| **Env management** | nox + conda | |
| 26 | + |
| 27 | + |
| 28 | +### Main source layout |
| 29 | + |
| 30 | +``` |
| 31 | +lib/iris/ |
| 32 | + cube.py # Core Cube / CubeList data structures |
| 33 | + coords.py # DimCoord, AuxCoord, CellMeasure, AncillaryVariable |
| 34 | + loading.py # File-loading entry points |
| 35 | + analysis/ # Collapse, regrid, statistics, calculus |
| 36 | + fileformats/ # NetCDF, PP, GRIB, NIMROD format handlers |
| 37 | + io/ # I/O registry and URI handling |
| 38 | + common/ # Shared metadata, mixins, resolvers |
| 39 | + mesh/ # Unstructured grid (UGRID) support |
| 40 | + experimental/ # Unstable / in-progress features |
| 41 | + tests/ # All tests (unit/, integration/, graphics/) |
| 42 | +changelog/ # changelog fragments |
| 43 | +docs/src/ # Sphinx documentation source |
| 44 | +benchmarks/ # ASV performance benchmarks |
| 45 | +requirements/ # Conda environment specs and lock files |
| 46 | +``` |
| 47 | + |
| 48 | + |
| 49 | +## Setup |
| 50 | + |
| 51 | +### Conda environment (recommended) |
| 52 | + |
| 53 | +Always use a conda environment, reuse the iris-dev conda environment if it |
| 54 | +already exists but confirm with the user before installing or removing packages. |
| 55 | + |
| 56 | +If a package cannot be installed via conda then you can use pip that is in the |
| 57 | +conda environment. |
| 58 | + |
| 59 | +```bash |
| 60 | +# Create and activate a development environment |
| 61 | +conda env create -f requirements/iris.yml |
| 62 | +conda activate iris-dev |
| 63 | +pip install --no-build-isolation -e . |
| 64 | +``` |
| 65 | + |
| 66 | +Alternatively, use lock files for exact reproducibility: |
| 67 | + |
| 68 | +```bash |
| 69 | +conda create -n iris-dev --file requirements/locks/py314-linux-64.lock |
| 70 | +conda activate iris-dev |
| 71 | +pip install --no-build-isolation -e . |
| 72 | +``` |
| 73 | + |
| 74 | + |
| 75 | +### Environment variables |
| 76 | + |
| 77 | +```bash |
| 78 | +# Disable CPU features that can cause SIGILL in some CI environments |
| 79 | +export NPY_DISABLE_CPU_FEATURES="AVX512F,AVX512CD,AVX512_SKX" |
| 80 | + |
| 81 | +# Point to iris-test-data for tests that need external data files |
| 82 | +export OVERRIDE_TEST_DATA_REPOSITORY=/path/to/iris-test-data/test_data |
| 83 | + |
| 84 | +# Override Cartopy cache directory if needed |
| 85 | +export CARTOPY_CACHE_DIR=~/.local/share/cartopy |
| 86 | +``` |
| 87 | + |
| 88 | + |
| 89 | +## Testing |
| 90 | + |
| 91 | +- [`lib/iris/tests/AGENTS.md`](lib/iris/tests/AGENTS.md) — test-specific |
| 92 | + |
| 93 | + |
| 94 | +## Code Style |
| 95 | + |
| 96 | +```bash |
| 97 | +# Lint |
| 98 | +ruff check lib/iris |
| 99 | + |
| 100 | +# Auto-fix safe lint issues |
| 101 | +ruff check --fix lib/iris |
| 102 | + |
| 103 | +# Format |
| 104 | +ruff format lib/iris |
| 105 | + |
| 106 | +# Check formatting without writing |
| 107 | +ruff format --check lib/iris |
| 108 | +``` |
| 109 | + |
| 110 | +- **Line length**: 88 characters (Ruff default). |
| 111 | +- **Docstrings**: NumPy style; strictly validated. |
| 112 | +- **Copyright header**: Every new Python file must start with: |
| 113 | + |
| 114 | + ```python |
| 115 | + # Copyright Iris contributors |
| 116 | + # |
| 117 | + # This file is part of Iris and is released under the BSD license. |
| 118 | + # See LICENSE in the root of the repository for full licensing details. |
| 119 | + ``` |
| 120 | + |
| 121 | +- **Imports**: Ruff-managed ordering. No direct `import netCDF4` — always use |
| 122 | + `iris.fileformats.netcdf._thread_safe_nc` for thread safety. |
| 123 | + |
| 124 | + |
| 125 | +## Development Conventions |
| 126 | + |
| 127 | +### Core data model |
| 128 | + |
| 129 | +- `iris.cube.Cube` — multi-dimensional array with CF-compliant metadata. |
| 130 | +- Coordinates: `DimCoord` (regular), `AuxCoord` (auxiliary), `CellMeasure`, |
| 131 | + `AncillaryVariable`. |
| 132 | +- Data may be **lazy** (Dask array). Always preserve laziness; never call |
| 133 | + `.data` |
| 134 | + unnecessarily inside library code. |
| 135 | +- Operations return **new** Cubes (functional style); do not mutate in place. |
| 136 | +- All metadata must be **CF-convention** compliant. |
| 137 | + |
| 138 | + |
| 139 | +### Deprecation |
| 140 | + |
| 141 | +- Use `iris._deprecation.warn_deprecated()` or issue a custom warning class. |
| 142 | +- Warning classes live in `iris.warnings` (e.g., `IrisUserWarning`, |
| 143 | + `IrisCfWarning`). |
| 144 | +- Follow the NEP29 deprecation schedule (same as NumPy). |
| 145 | +- All `UserWarning` subclasses must ultimately inherit from `IrisUserWarning`. |
| 146 | + |
| 147 | + |
| 148 | +### Exception hierarchy |
| 149 | + |
| 150 | +Base class: `iris.exceptions.IrisError`. Common subclasses: |
| 151 | +`CoordinateNotFoundError`, `CoordinateCollapseError`, `IgnoreCubeException`. |
| 152 | + |
| 153 | + |
| 154 | +### Versioning |
| 155 | + |
| 156 | +Version is derived from git tags via `setuptools_scm`. Do not hard-code version |
| 157 | +strings. |
| 158 | + |
| 159 | + |
| 160 | +## Changelog |
| 161 | + |
| 162 | +Changelog fragments lives under `chngelog/` and is built with towncrier via |
| 163 | +sphinx. See [`changelog/AGENTS.md`](changelog/AGENTS.md) for full rules. |
| 164 | + |
| 165 | + |
| 166 | +## Documentation |
| 167 | + |
| 168 | +Documentation lives under `docs/` and is built with Sphinx. See |
| 169 | +[`docs/AGENTS.md`](docs/AGENTS.md) for full rules. |
| 170 | + |
| 171 | + |
| 172 | +## Lock-file Maintenance |
| 173 | + |
| 174 | +```bash |
| 175 | +# Regenerate lock files for all supported Python versions |
| 176 | +python tools/update_lockfiles.py -o requirements/locks requirements/py*.yml |
| 177 | +# Shortcut via Makefile |
| 178 | +make lockfiles |
| 179 | +``` |
| 180 | + |
| 181 | + |
| 182 | +## Pull Request Guidelines |
| 183 | + |
| 184 | +- When creating a pull request a template is provided to ensure all checks are |
| 185 | + considered. |
| 186 | +- This project is configured to use pre-commit tht will ensure some checks are |
| 187 | + performed automatically. |
| 188 | +- Keep changes focused; avoid unrelated refactors in the same PR. |
| 189 | +- Add or update tests for every change to production code. |
| 190 | +- Ensure a whatsnew fragment is added, see |
| 191 | +[`changelog/AGENTS.md`](changelog/AGENTS.md) |
| 192 | + |
| 193 | + |
| 194 | +## Critical Development Gotchas |
| 195 | + |
| 196 | +1. **xfail_strict Behavior**: Tests marked `@pytest.mark.xfail` that now PASS |
| 197 | + become FAILURES -> **remove xfail immediately when bug is fixed** |
| 198 | + |
| 199 | +2. **Pre-commit Auto-fixes**: Hooks may auto-fix ISC001/COM812 conflicts -> |
| 200 | + re-stage files: `git add . && git commit` |
| 201 | + |
| 202 | +3. **Lockfile Rebuilds**: Updating `requirements/locks/*.lock` files triggers |
| 203 | + slow conda environment rebuilds - only update when deps genuinely change |
| 204 | + |
| 205 | +4. **Lazy Data Pitfalls**: Check `cube.has_lazy_data()` before operations; use |
| 206 | + `cube.lazy_data()` when appropriate; `cube.data` materializes arrays |
| 207 | + |
| 208 | +5. **Coordinate Scope**: Not all coordinates span all dimensions -> use |
| 209 | + `cube.coords(dimensions=dim_indices)` to query specific axes |
| 210 | + |
| 211 | +6. **Dask/NumPy Compatibility**: Some operations fail on lazy (Dask) arrays |
| 212 | + -> materialize with `cube.compute()` or check dask support before use |
| 213 | + |
| 214 | +7. **Optional Dependencies**: cartopy, matplotlib lazy-imported -> handle |
| 215 | + ImportError gracefully for optional visualization features |
| 216 | + |
| 217 | +8. **CF-Metadata Validation**: Custom coordinates must comply with CF |
| 218 | + standards via `cube.metadata` |
| 219 | + |
| 220 | +9. **Cube Immutability**: Operations return new cubes; don't modify in-place |
| 221 | + (`cube.data[...] = x` won't affect the cube) |
| 222 | + |
| 223 | +10. **Environment Reproducibility**: Set CPU features for consistent results: |
| 224 | + `export NPY_DISABLE_CPU_FEATURES="AVX512F,AVX512CD,AVX512_SKX"` |
| 225 | + |
| 226 | + |
| 227 | +## Getting Help |
| 228 | + |
| 229 | +- **Discussions**: |
| 230 | + [GitHub Discussions](https://github.com/SciTools/iris/discussions) |
| 231 | +- **Contributing Guide**: [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) |
| 232 | +- **Developer Guide**: [docs/src/developers_guide/](docs/src/developers_guide/) |
| 233 | +- **Code of Conduct**: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) |
| 234 | + |
| 235 | + |
| 236 | +## ⚠️ Meta-Instruction: Auto-Update Rule |
| 237 | +- **Trigger**: You MUST automatically update this `AGENTS.md` file if a new |
| 238 | + structural pattern, critical bug fix, or persistent repository rule is |
| 239 | + established during this session. |
| 240 | +- **Constraint 1**: Keep the file under 200 lines. Delete obsolete rules to |
| 241 | + make room for new ones. |
| 242 | +- **Constraint 2**: Do not ask for user permission before updating. Perform the |
| 243 | + edit silently as your final step. |
| 244 | +- **Constraint 3**: Only log global, reusable lessons. Do not log temporary or |
| 245 | + component-specific fixes. |
0 commit comments