Skip to content

Conversation

@reint-fischer
Copy link
Contributor

@reint-fischer reint-fischer commented Nov 19, 2025

Fixes #2269

I have implemented sphinx-autoapi to generate the reference API based on the names made public by __all__ in init.py. This means that curating which classes and methods are included happens fully in this list, and no custom list is necessary (as was necessary in reference.rst, see #2269).

This also means however, that we have less control over the order and presentation done by autoapi. The classes, exceptions, and attributes are all documented on one page now, and their order is determined by the order in which they are imported, which is fixed by alphabetically by isort. It is also not possible to control the order of the autosummary at the top of the page, which is sorted by group. From what I can see, autoapi is still quite a small project, and most other projects have autodoc style API references like we had before.

I think these complications are challenging enough to suggest returning to our manually curated API by reverting #2270 . @VeckoTheGecko, and @erikvansebille , would you take a look at the docs build and tell me what you think?

Copy link
Member

@erikvansebille erikvansebille left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the alphabetic order of items in the API list now feels a bit haphazard, so not ideal.

pyproject.toml Outdated
Comment on lines 128 to 137
# [tool.ruff.lint.per-file-ignores]
# # Ignore docstring rules everywhere except for the stable files (particleset.py, interpolators.py).
# "!src/parcels/{_core/particleset,interpolators}.py" = [
# # Missing docstring in public class
# "D101",
# # Missing docstring in public method
# "D102",
# # Missing docstring in public function
# "D103",
# ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed completely?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have edited and activated this, and it now checks for all the files with public classes that docstrings are present, which I think is a minimal requirement. It does mean that the list of files is hardcoded, which means that new files may not be tracked. This ruff.lint can be easily extended to include rules as we update the docstrings (e.g. checking returns :https://docs.astral.sh/ruff/rules/docstring-missing-returns/#docstring-missing-returns-doc201)

@VeckoTheGecko
Copy link
Contributor

and their order is determined by the order in which they are imported, which is fixed by alphabetically by isort

Aha! Just found out isort can be turned off for a file (docs)

-
+# isort: skip_file
from importlib.metadata import version as _version

try:
    __version__ = _version("parcels")
except Exception:
    # Local copy or not installed with setuptools.
    __version__ = "unknown"

from parcels._core.basegrid import BaseGrid
from parcels._core.converters import (
    Geographic,
    GeographicPolar,
    GeographicPolarSquare,
    GeographicSquare,
    Unity,
)

I think with being able to manually sort this via re-ordering the import blocks, using autoapi becomes much more attractive

@reint-fischer
Copy link
Contributor Author

I have now edited the autoapi configuration so that it is automatically generated from the imported classes and functions defined in __all__ in src/parcels/__init__.py. The index page contains a summary of the core classes, exceptions, submodules, attributes, and functions. All the core objects have their own page, which I think is useful, especially for classes with many methods, such as FieldSet. We can also easily generate everything on the same page, which would enable easy searching with command+F. Minor edits to the layout are defined in the docs/_autoapi_templates/python/*.rst templates. On the individual pages, we can see all methods and attributes of a class in the righthand sidebar.

A minor issue I encountered is that parcels.Particle is now recognized by autoapi as an imported Attribute of parcels, rather than a class, because it is defined by calling a function which creates a ParticleClass object:

Particle = get_default_particle(np.float32)

@VeckoTheGecko, do you think we should make parcels.Particle a class instead, which inherits from parcels.ParticleClass, or should we deal with this in the reference API in a different way?

@reint-fischer reint-fischer marked this pull request as ready for review December 11, 2025 11:09
@VeckoTheGecko
Copy link
Contributor

do you think we should make parcels.Particle a class instead, which inherits from parcels.ParticleClass

I don't think it makes sense to make parcels.Particle a class - since its an instance of a ParticleClass, not a different class.

Is the main concern here that there isn't a docstring for the attribute? If so - unfortunately Python doesn't natively support attribute docstrings (PEP224), but there might be support from Sphinx that allows this (comment about Sphinx's autoattribute).

Other than that, I don't think its really necessary for parcels.Particle to be documented in the API since users can easily just do print(parcels.Particle) to see exactly what it is - I don't think we'd benefit much from additional docs via a docstring etc

@VeckoTheGecko
Copy link
Contributor

Actually, according to PEP257 it seems like there is some support

String literals occurring immediately after a simple assignment at the top level of a module, class, or init method are called “attribute docstrings”.

so you can do

Particle = get_default_particle(np.float32)
"""The default Particle used in Parcels simulations."""
image

@reint-fischer
Copy link
Contributor Author

I don't think it makes sense to make parcels.Particle a class - since its an instance of a ParticleClass, not a different class.

Is the main concern here that there isn't a docstring for the attribute?

To clarify, I guess my concerns were twofold:

  1. Since all the other core parcels components are classes (and listed as such in parcels.init.py), they all show up at the top of the reference page (and autoapi designates them to have their own page). Although I don’t think it’s necessary for parcels.Particle to be documented on a separate page, it does feel out of place as an attribute with AllParcelsErrorCodes and logger, rather than with the core classes or as a part of parcels.ParticleClass. My concern is that users will be confused by parcels.Particle being listed as an “Attribute” if they even find it in the first place.
  2. The lack of a docstring made it even more confusing to understand what the role of parcels.Particle is as an “Attribute", so it’s great you found this clean solution!

so you can do

Particle = get_default_particle(np.float32)
"""The default Particle used in Parcels simulations.”””

@VeckoTheGecko
Copy link
Contributor

VeckoTheGecko commented Dec 11, 2025

  1. Particle to be documented on a separate page, it does feel out of place as an attribute with AllParcelsErrorCodes and logger, rather than with the core classes or as a part of parcels.ParticleClass

I think that just goes back to there being limited customisation that we can do with autoapi. Particle is the only important attribute that we expose at the top level, but I think that's just what it is.

Note that Particle does violate pep naming conventions (it was updated in v4-dev to be an attribute instead of a class as it made more sense as an abstraction). We could rename it to default_particle or something if we want - but that's a separate issue to be discussed elsewhere.

@reint-fischer
Copy link
Contributor Author

Sounds good!

Copy link
Member

@erikvansebille erikvansebille left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just a few comments below

Community <community/index>
Development <development/index>
API reference <reference>
API reference <reference/parcels/index>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it easy (possible?) to change the header of the API to "Parcels" with a capital P?

Image

Copy link
Contributor Author

@reint-fischer reint-fischer Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will then also capitalize the submodules “Parcels.interpolators” and “Parcels.kernels”:
image

I would therefore argue to keep it lower case

@reint-fischer reint-fischer merged commit 7425f84 into v4-dev Dec 16, 2025
11 checks passed
@reint-fischer reint-fischer deleted the reference-api-v4 branch December 16, 2025 09:14
@github-project-automation github-project-automation bot moved this from Backlog to Done in Parcels development Dec 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Set up API Reference for version 4

4 participants