Development¶
Environment¶
Use the devcontainer (Installation). CI builds and tests in the same image, so a local run gives the same answers as CI.
uv sync --extra dev --extra docs
uv run pytest tests/test_environment.py # check the environment first
Tests and checks¶
# The usual run: everything except the exhaustive dataset sweep (about 1,900 tests).
uv run pytest
# Everything, including the sweep of tests/testdataset (about 15,000 tests),
# on all logical cores. Arguments are passed through to pytest.
scripts/run_all_tests.sh
# What CI runs besides the tests.
uv run ruff check geodetic_engine tests
uv run ruff format --check geodetic_engine tests
uv run mypy geodetic_engine
pyproject.toml deselects the dataset marker by default. The sweep is worth
running before a release, or after changing operation selection. It takes
minutes, not seconds, which is why it is opt-in. Tests marked network need a
reachable Georepository instance and credentials.
CI runs these as two separate GitHub workflows, so a red check says which kind of problem it is:
Lint and type check (
.github/workflows/lint.yml):ruffandmypy.Tests (
.github/workflows/tests.yml): the unit and integration tests, then the dataset sweep, with a coverage floor of 80 % across both.
Building the documentation¶
The documentation is built with Sphinx. Pages are written in MyST Markdown, a Markdown dialect with Sphinx’s cross-referencing, so there is no reStructuredText to learn.
uv run --extra docs sphinx-build -W --keep-going -b html docs docs/_build/html
Or build and serve it on http://127.0.0.1:8000 in one step
(--help lists the options, including --live for rebuild-on-save):
scripts/build-docs.sh
Open docs/_build/html/index.html in a browser. For a live preview that
rebuilds on save:
uv run --extra docs sphinx-autobuild docs docs/_build/html
-W turns every warning into an error, and cross-references are checked
strictly (nitpicky = True). A broken link to a class or a page fails the
build, just as a failing test would. Run the docstring examples with:
uv run pytest --doctest-modules geodetic_engine -o addopts=""
If sphinx-build fails at startup with unsupported locale setting, set
LC_ALL=C.UTF-8. The devcontainer sets it for you.
How the site is put together¶
Path |
What it is |
|---|---|
|
Sphinx configuration: the Furo theme, extensions, cross-reference checking |
|
Site styling on top of Furo, and the logo |
|
Local extension: generates the API pages from |
|
Layout of each generated API page |
|
Hand-written pages |
|
Generated on every build from each subpackage’s |
|
Copied from |
|
Entries of the version switcher, reserved for when a |
Pages with a file_format: mystnb header are notebooks written as Markdown.
Their {code-cell} blocks run during the build, and the output is included in
the page. A cell expected to raise is tagged raises-exception. Any other
error fails the build. Results are cached in docs/_build/.jupyter_cache, so
only changed pages are re-run.
Adding to the API reference¶
Nothing to do. Export the name from its subpackage’s __all__ and it gets a
page. A type that public functions return or accept but that is not exported
can be listed in SUPPORTING in docs/_ext/geodetic_docs.py.
Adding a page¶
Create a .md file in the relevant section and add it to that section’s
toctree. Use an executed page (file_format: mystnb) for anything that shows
coordinates, so the numbers cannot go stale.
Docstring style¶
Docstrings use Google style,
rendered by sphinx.ext.napoleon:
def transform(source_crs, target_crs, x, y=None, z=None, *, operation=None):
"""Transform points between two CRSs in one call.
Longer description: when to use it, what it checks, what it refuses.
Args:
source_crs: CRS the input coordinates are in.
operation: EPSG coordinate operation to apply, for example
``"EPSG:15670"``.
Returns:
The transformed coordinates and their provenance.
Raises:
AmbiguousOperationError: If no operation was given and the
transformation involves an unbound datum change.
Example:
>>> result = transform("EPSG:4258", "EPSG:25832", (10.75, 59.91),
... operation="EPSG:16032")
>>> result.operation.authority_code
'EPSG:16032'
"""
Every public name gets a one-line summary, then a description.
Functions and methods list
Args,Returns, andRaiseswith the package’s own exceptions.Dataclasses list their fields under
Attributes.Cross-reference with Sphinx roles:
:class:`Transformation`,:func:`~geodetic_engine.geodesy.transform`. Refer to private helpers in double backticks, not with a role, because they have no page to link to.An
Exampleis a doctest. It must run offline against the pinned PROJ. Mark a line# doctest: +SKIPonly if it needs a network, credentials or a custom database. Use...for float tails;ELLIPSISis enabled.
Publishing the documentation¶
.github/workflows/docs.yml builds the site in the devcontainer image, like
the lint and test workflows:
Pull requests: build with warnings as errors, run the doctests, run the link check (reported, not blocking), and upload the HTML as an artifact to download and review.
Push to
main: the same, then deploy to GitHub Pages under/dev/.
The site root redirects to /dev/. When the first release is tagged, add a
stable/ build and an entry per release to docs/_static/switcher.json. The
URLs are already versioned, so no page moves.
One-time repository setting: Settings → Pages → Build and deployment → Source: GitHub Actions.