Render each docstring section only where it means something - #308
Merged
Conversation
`DocstringSectionTypeParameters` had no `render_docstring_section` registration, so it fell through to the base handler's `str(el.value)` and published `<griffe...DocstringTypeParameter object at 0x...>` reprs under a correct-looking `## Type Parameters` heading. The section is definition-shaped — name, optional bound, optional PEP 696 default, description — so route it through the definition-list rendering that `Parameters` already uses. `DocstringTypeParameter` exposes `.name`, `.annotation` and `.default`, and `.annotation` covers both a bound (`int`) and constraints (`(str, bytes)`), so no item-level code is needed. Split the one definition-section handler in two, by what the sections can describe. `Attributes` (classes and modules) and `Type Parameters` (classes, functions and type aliases) are not confined to callables, and `RenderDocModule` and `RenderDocTypeAlias` do not inherit `RenderDocCallMixin`; they reached the handler only because `singledispatchmethod` keeps one registry shared by every `RenderDoc` subclass. Those two now register on `RenderDoc` as `ObjectDefinitionSection`, while the nine call-only sections stay in `mixin_call.py` as `CallableDefinitionSection`. Both delegate to a shared `render_definition_items`, so nothing depends on the shared registry any more.
…registry `render_docstring_section` was a `singledispatchmethod`. That descriptor is created once, when the base class body runs, and holds ONE registry dict that every subclass inherits, so where a handler was written had no bearing on which classes could reach it. `RenderDocCallMixin` registered by reaching up into its parent, and `RenderDocAttribute` — which does not inherit that mixin — resolved `Parameters` to `__RenderDocCallMixin._`. Replace it with `SECTION_METHOD`, mapping each section type to the name of the method that renders it, resolved with `getattr(self, name, None)`. Scoping now follows the MRO: `render_parameters_section` lives on the call mixin, so a module renderer has no such attribute and takes the unhandled path. Overriding a handler is ordinary subclassing — no registry mutation, no import-order dependence. This deletes machinery rather than adding it. The `singledispatchmethod`, both runtime union aliases (`ObjectDefinitionSection`, `CallableDefinitionSection`, which existed only because `singledispatch` needed types at runtime) and the `pyright: ignore[reportFunctionMemberAccess]` all go. Three behaviour changes, all intended: - A section reaching a renderer with no method for it is omitted and logged, rather than rendered through the shared registry. - Unhandled sections are never stringified. This closes the repr leak that published `<griffe...DocstringTypeAlias object at 0x...>` into generated docs. - Deliberate suppression stays silent. `Methods`, `Functions`, `Classes`, `Modules` and `Type Aliases` are valid numpydoc that great-docs drops because it generates those groups from real members; warning on them would fire on well-formed docstrings. Each has its own method so one can be overridden without affecting the others. Tightening the scoping surfaced a latent bug. `Raises`, `Warns`, `Yields` and `Receives` had been treated as callable-only, but a property executes code on access and in griffe has `kind == "attribute"`, so it renders through `RenderDocAttribute`. Under the shared registry every renderer could reach every handler, so this was invisible. Those four now have property-gated renderers there, keyed on `"property" in obj.labels` rather than the display label, which annotation heuristics can shadow. `Receives` matters specifically because numpydoc requires it to accompany `Yields`.
… move Attributes and Type Parameters no longer belong to "any object" — they moved to the members mixin and the call mixin/type-alias renderer respectively. Update the SECTION_METHOD grouping comment and two test docstrings that still described the pre-move scoping.
The four snippets written in `type`-statement and type-parameter syntax are parsed by griffe with `ast`, so they raise SyntaxError on 3.11 rather than skipping. Gate them per-test, as test_type_alias_introspection.py does, so the rest of the file keeps running on the version floor.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR makes every docstring section render only on the objects that can actually have it. Type Parameters sections now work at all — a generic class, function or type alias can document what each parameter means; bounds and constraints both render, and omitting a bound fills it from the signature. Properties can document what they raise, warn, yield and receive, which was silently dropped. Nothing unhandled can leak into a page any more — a section no renderer claims is omitted with a warning naming the object.
Each section is also rendered by its own named method now, so customising one is a single override that changes that section, on that kind of object, and nothing else — previously every handler lived in one registry shared by all renderers, so neither of those could vary independently.
supersedes #303