# Developer Notes ## Contributing Contributions to `xyzpy` are very welcome, whether they are bug reports, documentation fixes, examples, tests, or new features. If you are planning a larger change, opening an issue first is often the easiest way to check the approach before spending too much time on implementation. Please also read the [`xyzpy` Code of Conduct](https://github.com/jcmgray/xyzpy/blob/main/CODE_OF_CONDUCT.md). Things to check if new functionality is added: 1. Ensure functions are unit tested. Tests under `tests/test_gen/` mirror the `xyzpy/gen/` package structure; a new module in `xyzpy/gen/` should get a corresponding `tests/test_gen/test_.py`. 2. Ensure functions have [NumPy-style docstrings](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html). 3. Ensure code is formatted and linted with `pixi run lint`. 4. Add to `xyzpy/__init__.py` and `"__all__"` if appropriate. 5. If a new entry-point script is added (e.g. under `[project.scripts]`), keep it as a top-level module (`xyzpy_*.py`) rather than inside the `xyzpy` package. This guarantees that env-var flags such as `OMP_NUM_THREADS` are set before numpy / xarray are eagerly imported. 6. Do not manually edit `xyzpy/_version.py` — it is generated by `hatch-vcs` from git tags. 7. Add to changelog and elsewhere in docs. ### AI Policy Please treat the [numpy AI policy](https://numpy.org/devdocs/dev/ai_policy.html) as a rough guide. ## Development Setup `xyzpy` uses [pixi](https://pixi.sh) to manage development environments and reproducible tasks. The environments and tasks are defined in `pyproject.toml`, which is the source of truth for the commands below. After cloning the repository, install the pixi environments from the project root: ```bash git clone https://github.com/jcmgray/xyzpy.git cd xyzpy pixi install ``` You can then run project tasks with `pixi run ...`. For example, to run a short Python command inside the default test environment: ```bash pixi run -e testpymid python -c "import xyzpy; print(xyzpy.__version__)" ``` ## Running the Tests Testing `xyzpy` is handled by pixi tasks. The most common commands are: ```bash pixi run -e testpymid test # full suite with coverage, matches CI ``` The `test` task expands to: ```bash pytest tests/ --cov=xyzpy --cov-report=xml --verbose --durations=10 ``` For a narrower check, use the `pytest` task (which runs in the `testpymid` environment) and forward arguments after `--`: ```bash pixi run pytest -- tests/test_manage.py pixi run pytest -- tests/test_gen/test_cropping.py -k "test_name" -v pixi run pytest -- -k "test_combo" -v ``` To run the full suite in a specific environment, use `-e`: ```bash pixi run -e testpyold test pixi run -e testpymid test pixi run -e testpynew test ``` ## Formatting the Code `xyzpy` uses [`ruff`](https://docs.astral.sh/ruff/) to format imports and code style. Use the predefined pixi tasks rather than running the tools directly: ```bash pixi run lint pixi run format ``` The `format-all` task also runs notebook cleanup with `squeaky`: ```bash pixi run format-all ``` ## Building the docs locally The documentation dependencies are managed by pixi. To build, clean, and serve the docs locally, use: ```bash pixi run docs pixi run docs-clean pixi run docs-serve ``` The local server hosts the built docs at `http://localhost:8000/`. The generated HTML is in `docs/_build/html/`. On ReadTheDocs, the build is driven by `.readthedocs.yml` and uses the dedicated `readthedocs` pixi task. ### Referencing functions and methods The API docs are generated with [`sphinx-autoapi`](https://sphinx-autoapi.readthedocs.io/en/latest/), which documents each object only where it is *defined*, not where it is re-exported. References should either use the full path or a short suffix-matching form: - In MyST markdown, use a link with a leading `#`: `` [`cultivate`](#farming.cultivate) ``, `` [`grow`](#Crop.grow) ``. This is the preferred form, since it also renders cleanly as plain markdown. - In a docutils role, use a leading `.`: `` {func}`.farming.cultivate` ``, `` {meth}`.Crop.grow` ``. Roles are typed, so prefer them in docstrings and to disambiguate a name that matches more than one object. Keep enough trailing components to be unambiguous. If a bare name still matches several objects, qualify it further or give an explicit title. ## Minting a release `xyzpy` uses [`hatch-vcs`](https://github.com/ofek/hatch-vcs) to derive the version from git tags, and [GitHub Actions](https://github.com/jcmgray/xyzpy/actions) to publish to [PyPI](https://pypi.org/project/xyzpy/). To mint a new release: 1. Make sure all the [tests are passing on CI](https://github.com/jcmgray/xyzpy/actions/workflows/tests.yml). 2. `git tag` the release with the next `vX.Y.Z`. 3. Push the tag to GitHub: `git push --tags`. The release workflow will build the sdist and wheel and upload them to the [PyPI **test** server](https://test.pypi.org/project/xyzpy/). 4. If the test-pypi build looks good, create a GitHub release from the tag. Publishing the release triggers the same workflow to upload to the [PyPI **production** server](https://pypi.org/project/xyzpy/). 5. The [`conda-forge/xyzpy-feedstock`](https://github.com/conda-forge/xyzpy-feedstock) repo should automatically pick up the new PyPI release and build a new [conda package](https://anaconda.org/conda-forge/xyzpy); the recipe should only need to be manually updated if there are, for example, new dependencies. Alternate manual release steps (after tagging): 1. Remove any old builds: `rm -rf dist/*` 2. Build the sdist and wheel: `python -m build` 3. Upload using twine: `twine upload dist/*`