Skip to content

docs: Harden Python docstring validation - #121

Open
kylebarron wants to merge 14 commits into
mainfrom
kyle/docstring-hardening
Open

docs: Harden Python docstring validation#121
kylebarron wants to merge 14 commits into
mainfrom
kyle/docstring-hardening

Conversation

@kylebarron

@kylebarron kylebarron commented Jul 30, 2026

Copy link
Copy Markdown
Member

Closes #120

@kylebarron
kylebarron marked this pull request as ready for review July 30, 2026 18:42
Comment thread python/zarrista/_chunks.pyi Outdated
`(size, count)` pair meaning `count` consecutive chunks of `size`."""
"""One run of chunks along a rectilinear chunk edge.

Give a single chunk size, or a `(size, count)` pair. The pair means `count`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't like these as imperative mood because it's not a method, it's a type on an object. So these should be explaining a noun.

Suggested change
Give a single chunk size, or a `(size, count)` pair. The pair means `count`
A single chunk size, or a `(size, count)` pair. The pair means `count`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed. Both type alias docstrings are noun phrases now — _RunLength uses your suggestion verbatim, and _ChunkEdgeLengths became "An integer if the dimension has one chunk size, or a sequence of runs if the chunk sizes change along the dimension."

Added the general rule to CLAUDE.md so this holds for the remaining 17 files: imperative verb for functions and methods, noun phrase for type aliases, classes, attributes and properties.


Args:
array_shape: The shape of the array, in elements along each dimension.
chunk_shape: The shape of each chunk, in elements along each dimension.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't think this is the shape of each chunk? I think this is the shape of all chunks?

Check the upstream zarrs docstring

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Checked upstream — your original read was right, and the singular parameter name actually supports it rather than undermining it.

In zarrs 0.23.13, RegularChunkGrid stores a single chunk_shape and its accessor takes no chunk indices, so every chunk shares one shape:

// regular.rs
pub fn chunk_shape(&self) -> &[NonZeroU64] { self.chunk_shape.as_slice() }

// regular_bounded.rs
fn chunk_shape(&self, chunk_indices: &[u64]) -> Result<Option<ChunkShape>, _>

chunk_shape is singular precisely because all chunks share it. Changed to "The shape of all chunks".

regular_bounded is the opposite case and I had it wrong — it takes chunk indices and clamps to the array bounds, so chunk shapes genuinely differ there. That one now reads "The maximum shape of a chunk ... The grid clips the chunks at the array bounds to a smaller shape."

Comment thread CLAUDE.md Outdated
- **Write all documentation in ASD-STE100 Simplified Technical English.** This
applies to docstrings, `.pyi` stubs, Rust doc comments, Markdown pages, and the
README. Use one topic per sentence, keep sentences short (20 words or fewer for
descriptive text, 20 for instructions), use the active voice, use the approved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Use the active voice for functions and methods

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added as its own bullet, since it needs to say what the other case is too:

Use the active voice for functions and methods, and a noun phrase for everything else. A function or method docstring starts with an imperative verb: "Construct a regular grid...", "Return the store key...". A type alias, class, attribute, or property docstring names what the thing is: "The chunk sizes along one dimension."

Also fixed the sentence-length line you commented on — it said "20 words or fewer for descriptive text, 20 for instructions", which was both self-contradictory and wrong. STE is 20 max for procedural sentences, 25 for descriptive.

kylebarron and others added 7 commits July 30, 2026 15:39
Add Args/Returns/Raises sections to the small stub modules and the codec
modules, and apply the Simplified Technical English rules to their prose.

Every Raises entry is verified by calling the built extension rather than
inferred from the Rust source. That turned up several behaviours the old
docstrings did not record:

- gzip validates its level range but zstd does not; a negative level gives
  OverflowError rather than ValueError.
- blosc raises PluginCreateError (not ValueError) when shuffling is enabled
  without a typesize.
- FilesystemStore does not check that its path exists at construction.
- ArrayBytes does not check mask or offsets against the data buffer.

Also disable D413, which required a blank line after the final docstring
section. That is not Google style, and it is a rule that setting a pydocstyle
convention would have silently disabled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… group

Add Args/Returns/Raises sections and apply the Simplified Technical English
rules to the prose in these four stub modules.

Verified against the built extension rather than inferred. Notable findings:

- Group.open raises GroupCreateError for missing metadata but ValueError for a
  path that is not absolute; it does not use NodePathError.
- Group.child and Group.__getitem__ raise KeyError, not a zarrista exception.
- Tensor.__buffer__ raises BufferError only for PyBUF_WRITABLE; the previous
  wording was correct but untested.
- VariableArray.__arrow_c_array__ raises TypeError if requested_schema is
  neither None nor a PyCapsule.
- The existing "succeeds if it does not exist" claim on erase_metadata holds;
  calling it twice is fine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Complete the Google-style rewrite with _builder.pyi and _array.pyi, then turn
on the pydoclint settings that keep it that way:

- skip-checking-short-docstrings = false requires Args/Returns on every
  non-property method. The stubs are at zero violations under it.
- require-inline-class-var-docs = true keeps CodecOptions documenting its
  members inline, which is what mkdocstrings renders best.
- skip-checking-raises = true because stub bodies are `...`, so pydoclint can
  never see a raise statement and would reject every Raises section.

Raises entries come from calling the built extension. That recorded several
behaviours the previous "Raises if ..." wording left unnamed, and some that
contradict the obvious guess:

- Array.open raises ArrayCreateError for missing metadata but ValueError for a
  path that is not absolute.
- chunk_origin/chunk_shape/chunk_subset raise ArrayError for a wrong number of
  dimensions, but accept out-of-bounds indices.
- chunk_key validates nothing and returns a key for indices outside the grid.
- On a read-only array, store_chunk raises ArrayError while erase_chunk,
  erase_metadata and store_metadata raise StorageError.
- Unsupported indexing raises NotImplementedError (step != 1, np.newaxis) and
  too many indices raises IndexError.
- An unknown codec option raises TypeError.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrite the README, DEVELOP.md and the DecodedArray page in short active
sentences with one topic each, and keep the Zarr domain terms.

Fix two errors in the README example, both confirmed by running it:

- Indexing was documented as returning a `Data` buffer. No such type exists;
  a read returns a `DecodedArray`, so link to that instead.
- The `array.dtype` output comment read `DataType(float32)`. The actual repr
  is `DataType(float32 / <f4)`.

Also re-sync the `AsyncStore` and `DecodedArray` docstrings in the `.py` files
with the rewritten `.pyi` stubs, which had drifted apart, and record the new
pydoclint requirements in DEVELOP.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ensure that all docstrings follow google docstring conventions

1 participant