Curated autodoc rendering: defaults, xrefs, and responsive layout#36
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #36 +/- ##
==========================================
+ Coverage 91.57% 91.86% +0.28%
==========================================
Files 205 219 +14
Lines 16814 18035 +1221
==========================================
+ Hits 15398 16567 +1169
- Misses 1416 1468 +52 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
9d2d8b7 to
8dc90a0
Compare
Code reviewFound 1 issue:
gp-sphinx/tests/ext/typehints_gp/test_default_xref_integration.py Lines 294 to 335 in 8dc90a0 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
why: PR #36 review caught a function-scoped Sphinx build inside `test_unsupported_default_falls_back_to_plain_text` — CLAUDE.md requires every integration test's Sphinx build to live in a module- or session-scoped fixture so the build cost is shared across runs. The three sibling tests in the file already follow the rule; this one was the outlier because it was the last test added and used a one-off scenario. what: - Hoist the lambda fixture project's module source into a module-level `_LAMBDA_MODULE_SOURCE` constant alongside the existing `_DATA_ATTRIBUTE_MODULE_SOURCE` and `_CROSS_MODULE_*` constants. - Add `lambda_default_html_result` as a `@pytest.fixture(scope="module")` wrapping `build_shared_sphinx_result`, mirroring the existing three fixtures in shape (parameter list, scenario construction, `purge_modules=("lambda_demo",)` argument). - Convert `test_unsupported_default_falls_back_to_plain_text` to consume the fixture parameter and call `read_output` directly; the two assertions are unchanged so the test still pins the plain-text-fallback contract for unparseable lambda defaults.
why: PR #36 review caught a function-scoped Sphinx build inside `test_unsupported_default_falls_back_to_plain_text` — CLAUDE.md requires every integration test's Sphinx build to live in a module- or session-scoped fixture so the build cost is shared across runs. The three sibling tests in the file already follow the rule; this one was the outlier because it was the last test added and used a one-off scenario. what: - Hoist the lambda fixture project's module source into a module-level `_LAMBDA_MODULE_SOURCE` constant alongside the existing `_DATA_ATTRIBUTE_MODULE_SOURCE` and `_CROSS_MODULE_*` constants. - Add `lambda_default_html_result` as a `@pytest.fixture(scope="module")` wrapping `build_shared_sphinx_result`, mirroring the existing three fixtures in shape (parameter list, scenario construction, `purge_modules=("lambda_demo",)` argument). - Convert `test_unsupported_default_falls_back_to_plain_text` to consume the fixture parameter and call `read_output` directly; the two assertions are unchanged so the test still pins the plain-text-fallback contract for unparseable lambda defaults.
49ed000 to
d08129a
Compare
Code reviewFound 1 issue:
CLAUDE.md rule: https://github.com/git-pull/gp-sphinx/blob/d08129a03d466a5246e51ebbfaabad2519c09c0f/CLAUDE.md#L520-L526 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
…ources why: gp-sphinx PR git-pull/gp-sphinx#36 ships curated parameter and data-attribute default rendering (source-text reprs, dataclass factory resolution, long-value truncation, and `:py:class:`-styled cross-reference links inside default values). Pinning the gp-sphinx-family deps to that branch via `[tool.uv.sources]` lets this repo's docs preview the user-visible win before the workspace release bump propagates the changes via PyPI. After the audit, libtmux's `<libtmux.constants._DefaultOptionScope object>` defaults across `Pane._show_option`, `Window._show_option`, `Session._show_option`, hook dataclasses, etc. drop from 171 ugly sig-params to 0; `scope=` renders as `DEFAULT_OPTION_SCOPE` linked to the documented constant. what: - Add `[tool.uv.sources]` overrides for gp-sphinx, sphinx-autodoc-typehints-gp, sphinx-autodoc-api-style, and sphinx-autodoc-pytest-fixtures, all pointing at the `improved-defaults-reprs` branch with the appropriate monorepo subdirectory. - Regenerate `uv.lock`; uv resolves all transitive workspace siblings (sphinx-fonts, sphinx-gp-opengraph, sphinx-ux-autodoc-layout, etc.) from the same commit. - Revert this commit when gp-sphinx>=0.0.1a18 lands and the per-package version pins move forward in the usual workspace bump.
why: PR #36 review caught a function-scoped Sphinx build inside `test_unsupported_default_falls_back_to_plain_text` — CLAUDE.md requires every integration test's Sphinx build to live in a module- or session-scoped fixture so the build cost is shared across runs. The three sibling tests in the file already follow the rule; this one was the outlier because it was the last test added and used a one-off scenario. what: - Hoist the lambda fixture project's module source into a module-level `_LAMBDA_MODULE_SOURCE` constant alongside the existing `_DATA_ATTRIBUTE_MODULE_SOURCE` and `_CROSS_MODULE_*` constants. - Add `lambda_default_html_result` as a `@pytest.fixture(scope="module")` wrapping `build_shared_sphinx_result`, mirroring the existing three fixtures in shape (parameter list, scenario construction, `purge_modules=("lambda_demo",)` argument). - Convert `test_unsupported_default_falls_back_to_plain_text` to consume the fixture parameter and call `read_output` directly; the two assertions are unchanged so the test still pins the plain-text-fallback contract for unparseable lambda defaults.
2d22ead to
3f669e1
Compare
…ources why: gp-sphinx PR git-pull/gp-sphinx#36 ships curated parameter and data-attribute default rendering (source-text reprs, dataclass factory resolution, long-value truncation, and `:py:class:`-styled cross-reference links inside default values). Pinning the gp-sphinx-family deps to that branch via `[tool.uv.sources]` lets this repo's docs preview the user-visible win before the workspace release bump propagates the changes via PyPI. After the audit, libtmux's `<libtmux.constants._DefaultOptionScope object>` defaults across `Pane._show_option`, `Window._show_option`, `Session._show_option`, hook dataclasses, etc. drop from 171 ugly sig-params to 0; `scope=` renders as `DEFAULT_OPTION_SCOPE` linked to the documented constant. what: - Add `[tool.uv.sources]` overrides for gp-sphinx, sphinx-autodoc-typehints-gp, sphinx-autodoc-api-style, and sphinx-autodoc-pytest-fixtures, all pointing at the `improved-defaults-reprs` branch with the appropriate monorepo subdirectory. - Regenerate `uv.lock`; uv resolves all transitive workspace siblings (sphinx-fonts, sphinx-gp-opengraph, sphinx-ux-autodoc-layout, etc.) from the same commit. - Revert this commit when gp-sphinx>=0.0.1a18 lands and the per-package version pins move forward in the usual workspace bump.
627c7e8 to
25efab5
Compare
…ources why: gp-sphinx PR git-pull/gp-sphinx#36 ships curated parameter and data-attribute default rendering. Pinning the gp-sphinx-family deps to that branch via `[tool.uv.sources]` lets this repo's docs preview the user-visible win before the workspace release bump propagates the changes via PyPI. `RetryMiddleware.__init__(retry_exceptions=(<class 'libtmux.exc. LibTmuxException'>,))` now renders as `retry_exceptions=(libtmux_exc.LibTmuxException,)` with `LibTmuxException` linked to its documented exception class in the same `<a class="reference internal"><code class="xref py py-class">…</code></a>` shape that inline `:py:class:` roles produce in body prose. what: - Add `[tool.uv.sources]` overrides for gp-sphinx, sphinx-autodoc-typehints-gp, sphinx-autodoc-api-style, and sphinx-autodoc-fastmcp, all pointing at the `improved-defaults-reprs` branch with the appropriate monorepo subdirectory. - Regenerate `uv.lock`; uv resolves all transitive workspace siblings from the same commit (8c3a1418). - Revert this commit when gp-sphinx>=0.0.1a18 lands and the per-package version pins move forward in the usual workspace bump.
why: PR #36 review caught a function-scoped Sphinx build inside `test_unsupported_default_falls_back_to_plain_text` — CLAUDE.md requires every integration test's Sphinx build to live in a module- or session-scoped fixture so the build cost is shared across runs. The three sibling tests in the file already follow the rule; this one was the outlier because it was the last test added and used a one-off scenario. what: - Hoist the lambda fixture project's module source into a module-level `_LAMBDA_MODULE_SOURCE` constant alongside the existing `_DATA_ATTRIBUTE_MODULE_SOURCE` and `_CROSS_MODULE_*` constants. - Add `lambda_default_html_result` as a `@pytest.fixture(scope="module")` wrapping `build_shared_sphinx_result`, mirroring the existing three fixtures in shape (parameter list, scenario construction, `purge_modules=("lambda_demo",)` argument). - Convert `test_unsupported_default_falls_back_to_plain_text` to consume the fixture parameter and call `read_output` directly; the two assertions are unchanged so the test still pins the plain-text-fallback contract for unparseable lambda defaults.
42ced5b to
b8fdc67
Compare
…ources why: gp-sphinx PR git-pull/gp-sphinx#36 ships curated parameter and data-attribute default rendering. Pinning the gp-sphinx-family deps to that branch via `[tool.uv.sources]` lets this repo's docs preview the user-visible win before the workspace release bump propagates the changes via PyPI. `RetryMiddleware.__init__(retry_exceptions=(<class 'libtmux.exc. LibTmuxException'>,))` now renders as `retry_exceptions=(libtmux_exc.LibTmuxException,)` with `LibTmuxException` linked to its documented exception class in the same `<a class="reference internal"><code class="xref py py-class">…</code></a>` shape that inline `:py:class:` roles produce in body prose. what: - Add `[tool.uv.sources]` overrides for gp-sphinx, sphinx-autodoc-typehints-gp, sphinx-autodoc-api-style, and sphinx-autodoc-fastmcp, all pointing at the `improved-defaults-reprs` branch with the appropriate monorepo subdirectory. - Regenerate `uv.lock`; uv resolves all transitive workspace siblings from the same commit (8c3a1418). - Revert this commit when gp-sphinx>=0.0.1a18 lands and the per-package version pins move forward in the usual workspace bump.
a0d3576 to
2ebd33b
Compare
why: PR #36 review caught a function-scoped Sphinx build inside `test_unsupported_default_falls_back_to_plain_text` — CLAUDE.md requires every integration test's Sphinx build to live in a module- or session-scoped fixture so the build cost is shared across runs. The three sibling tests in the file already follow the rule; this one was the outlier because it was the last test added and used a one-off scenario. what: - Hoist the lambda fixture project's module source into a module-level `_LAMBDA_MODULE_SOURCE` constant alongside the existing `_DATA_ATTRIBUTE_MODULE_SOURCE` and `_CROSS_MODULE_*` constants. - Add `lambda_default_html_result` as a `@pytest.fixture(scope="module")` wrapping `build_shared_sphinx_result`, mirroring the existing three fixtures in shape (parameter list, scenario construction, `purge_modules=("lambda_demo",)` argument). - Convert `test_unsupported_default_falls_back_to_plain_text` to consume the fixture parameter and call `read_output` directly; the two assertions are unchanged so the test still pins the plain-text-fallback contract for unparseable lambda defaults.
…ources why: gp-sphinx PR git-pull/gp-sphinx#36 ships curated parameter and data-attribute default rendering. Pinning the gp-sphinx-family deps to that branch via `[tool.uv.sources]` lets this repo's docs preview the user-visible win before the workspace release bump propagates the changes via PyPI. `RetryMiddleware.__init__(retry_exceptions=(<class 'libtmux.exc. LibTmuxException'>,))` now renders as `retry_exceptions=(libtmux_exc.LibTmuxException,)` with `LibTmuxException` linked to its documented exception class in the same `<a class="reference internal"><code class="xref py py-class">…</code></a>` shape that inline `:py:class:` roles produce in body prose. what: - Add `[tool.uv.sources]` overrides for gp-sphinx, sphinx-autodoc-typehints-gp, sphinx-autodoc-api-style, and sphinx-autodoc-fastmcp, all pointing at the `improved-defaults-reprs` branch with the appropriate monorepo subdirectory. - Regenerate `uv.lock`; uv resolves all transitive workspace siblings from the same commit (8c3a1418). - Revert this commit when gp-sphinx>=0.0.1a18 lands and the per-package version pins move forward in the usual workspace bump.
…ources why: gp-sphinx PR git-pull/gp-sphinx#36 ships curated parameter and data-attribute default rendering (source-text reprs, dataclass factory resolution, long-value truncation, and `:py:class:`-styled cross-reference links inside default values). Pinning the gp-sphinx-family deps to that branch via `[tool.uv.sources]` lets this repo's docs preview the user-visible win before the workspace release bump propagates the changes via PyPI. After the audit, libtmux's `<libtmux.constants._DefaultOptionScope object>` defaults across `Pane._show_option`, `Window._show_option`, `Session._show_option`, hook dataclasses, etc. drop from 171 ugly sig-params to 0; `scope=` renders as `DEFAULT_OPTION_SCOPE` linked to the documented constant. what: - Add `[tool.uv.sources]` overrides for gp-sphinx, sphinx-autodoc-typehints-gp, sphinx-autodoc-api-style, and sphinx-autodoc-pytest-fixtures, all pointing at the `improved-defaults-reprs` branch with the appropriate monorepo subdirectory. - Regenerate `uv.lock`; uv resolves all transitive workspace siblings (sphinx-fonts, sphinx-gp-opengraph, sphinx-ux-autodoc-layout, etc.) from the same commit. - Revert this commit when gp-sphinx>=0.0.1a18 lands and the per-package version pins move forward in the usual workspace bump.
bae0d5b to
80aa08c
Compare
why: PR #36 review caught a function-scoped Sphinx build inside `test_unsupported_default_falls_back_to_plain_text` — CLAUDE.md requires every integration test's Sphinx build to live in a module- or session-scoped fixture so the build cost is shared across runs. The three sibling tests in the file already follow the rule; this one was the outlier because it was the last test added and used a one-off scenario. what: - Hoist the lambda fixture project's module source into a module-level `_LAMBDA_MODULE_SOURCE` constant alongside the existing `_DATA_ATTRIBUTE_MODULE_SOURCE` and `_CROSS_MODULE_*` constants. - Add `lambda_default_html_result` as a `@pytest.fixture(scope="module")` wrapping `build_shared_sphinx_result`, mirroring the existing three fixtures in shape (parameter list, scenario construction, `purge_modules=("lambda_demo",)` argument). - Convert `test_unsupported_default_falls_back_to_plain_text` to consume the fixture parameter and call `read_output` directly; the two assertions are unchanged so the test still pins the plain-text-fallback contract for unparseable lambda defaults.
9418f46 to
6025465
Compare
why: The improved-defaults-reprs branch needs empirical evidence about which parameter and data-attribute defaults render badly across workspace consumers before any framework lands. An earlier audit using `class="default_value"` as the match pattern silently missed every truly ugly case — when a default's repr contains `<`, `ast.parse` of the arglist fails inside `_parse_arglist` and Sphinx falls back to `_pseudo_parse_arglist`, which emits the whole `name=value` as one `desc_sig_name` text run with no `default_value` span at all. The corrected probe matches `<em class="sig-param">… </em>` instead. what: - Add packages/sphinx-autodoc-typehints-gp/scripts/audit_defaults.py with classify_param() recognising factory_sentinel, instance_sentinel, missing_sentinel, other, and long_data_value buckets. Doctests cover each bucket; pathlib-only IO; ruff/mypy clean. - Add notes/defaults-discovery-d1.md recording the aggregate inventory across libtmux/vcspull/gp-libs/gp-sphinx, the per-tree breakdown, sample defaults per ugliness class, and the resolver- catalog implications (DataclassFactoryRepr, SentinelInstanceRepr, BoundMethodRepr, TruncateLongRepr). - Document the architectural consequence: a docutils post-transform on default_value spans cannot reach the cases that need fixing, because those cases produce no default_value spans. Only an upstream string-level fix via autodoc-before-process-signature + DefaultValue shim applies.
why: Across libtmux, libvcs, vcspull, gp-libs, and gp-sphinx, parameter defaults that hold non-primitive Python objects render as `<libtmux.constants._DefaultOptionScope object>` and similar unreadable repr text. Sphinx already ships a fix — `autodoc_preserve_defaults=True` invokes `update_default_value` which parses each function's source via AST and substitutes a `DefaultValue` shim whose `__repr__` returns the literal source text. The flag is off by default upstream and `gp-sphinx.defaults` never overrode it, so the workspace shipped with the ugly repr behavior. D2 evidence (notes/defaults-discovery-d2.md): the flip clears 81/171 (47%) of libtmux's ugly parameter defaults and is a prerequisite for the forthcoming Stage C cross-reference work that depends on having a parseable arglist. what: - Add `DEFAULT_AUTODOC_PRESERVE_DEFAULTS: bool = True` in `gp_sphinx.defaults` with a docstring documenting the limitation on synthetic dataclass / attrs / NamedTuple `__init__` (covered by D3 / Stage B2 follow-on). - Wire `autodoc_preserve_defaults` into the conf dict produced by `merge_sphinx_config()`. - Add `notes/defaults-discovery-d2.md` with reproducible before/after counts: libtmux's `_show_option(scope=...)` renders `DEFAULT_OPTION_SCOPE` instead of `<libtmux.constants._Default OptionScope object>`; libvcs's `DEFAULT_RULES` line under the combined preserve+no-value flags shrinks from 688 chars to 45. - Document why `autodoc_default_options['no-value']=True` was rejected as a workspace default (suppresses useful short values like `'/'`, `'HEAD'`, `OptionScope.Pane`); per-attribute opt-in via `:no-value:` directive option remains the right tool for individual long values until D4's curated resolver lands.
why: After defaulting `autodoc_preserve_defaults=True` workspace-wide
in the previous commit, the only remaining cluster of ugly
parameter defaults is dataclass synthetic `__init__` — Sphinx's own
`update_default_value` bails out at
`_dynamic/_preserve_defaults.py:107-110` because synthetic init has
no source code for `inspect.getsource()`. The empirical inventory
in notes/defaults-discovery-d1.md has libtmux's 90 `<factory>`
occurrences in this bucket. This commit fills the gap so workspace
consumers see clean source-text rendering for `field(default_factory=…)`.
what:
- Add `_param_defaults.py` with `ResolveContext`, `Resolver`
Protocol, `DataclassFactoryRepr` resolver, and
`update_synthetic_defvalues` listener. The listener walks
`dataclasses.fields(parent)`, runs the resolver chain on each
field's `default_factory`, and replaces matching
`Parameter.default` with `sphinx.util.inspect.DefaultValue` shims
so all downstream stringifiers emit the chosen text verbatim.
- `DataclassFactoryRepr` covers stdlib container constructors
(list/dict/set/frozenset/tuple → `[]`, `{}`, `set()`, etc.) and
named callable types (`Foo` → `Foo()`). Lambdas and unrecognised
factories defer (return `None`) — Sphinx's stock `<factory>`
rendering remains for those.
- Connect the listener via `app.connect("autodoc-before-process-
signature", update_synthetic_defvalues)` in extension.setup().
- Add config flag `gp_typehints_curate_param_defaults` (default
`True`) as a kill-switch for downstream debugging.
- Tests: 18 unit tests (Style A NamedTuple parametrization for
each factory shape; mock-app for listener semantics) plus 2
integration tests via `tests/_sphinx_scenarios.py`. Verify the
rendered HTML's `default_value` spans contain the resolver-chosen
text and that `<factory>` no longer appears.
- gp-sphinx own docs audit: 1 ugly factory_sentinel → 0 after this
change.
why: For module-level constants like libvcs's DEFAULT_RULES (688 chars) and class data like GitURL.rule_map (5 738 chars in the original audit), Sphinx emits a `:value: <objrepr>` line that explodes the signature block. The built-in `:no-value:` baseline (D2) suppresses every value indiscriminately — including useful short ones like `'/'` and `OptionScope.Pane` — so it can't ship as a workspace default. This commit adds the curated alternative: a resolver chain that truncates only long values and leaves short ones untouched. what: - Add `_data_defaults.py` with `TruncateLongRepr` resolver, `_curate_value_line` helper, and `GpDataDocumenter` / `GpAttributeDocumenter` subclasses. The subclasses override `add_line` to route `:value:` lines through the curator; everything else passes through. Reuses `ResolveContext` and `_run_chain` from D3's `_param_defaults` module so the resolver catalog grows uniformly across Site A and Site B. - Register the documenters via `app.add_autodocumenter(..., override=True)` and add the kill-switch config flag `gp_typehints_curate_data_defaults` (default `True`). - Tests: 9 unit tests parametrised in Style A NamedTuple form (TruncateLongRepr threshold edges, kill-switch, kind-filtering, pass-through) plus 2 integration tests via `tests/_sphinx_scenarios.py` that build fixture projects with a short and a long module constant and assert the HTML contains the truncated marker only on the long one. - Resolver scope is intentionally conservative for the prototype (truncation only). Richer resolvers (list-of-dataclasses summary, compiled-regex repr) are deferred to D5 where the shared catalog factoring decision lands.
cdd6a58 to
6dd623e
Compare
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18.dev0 ships the curated autodoc rendering work from PR git-pull/gp-sphinx#36 — default-value source text, identifier cross-references in parameter signatures, canonical field-list xrefs, responsive autodoc layout, and a workspace-wide linkcode_resolve factory. what: - bump every gp-sphinx workspace pin in pyproject.toml + uv.lock - add [tool.uv].prerelease = "allow" so uv resolves the *.dev0 pins
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
why: gp-sphinx 0.0.1a18 is now live on PyPI with the curated autodoc rendering work from PR git-pull/gp-sphinx#36 (merged to main). The dev0 scaffolding can come out. what: - bump every gp-sphinx workspace pin from 0.0.1a18.dev0 to 0.0.1a18 - remove [tool.uv].prerelease = "allow" — no longer needed now that the dev marker is gone
Summary
autodoc_preserve_defaults=Trueis now the workspace default, and a new listener fills the synthetic-__init__gap so dataclass parameters render as=[]instead of=<factory>and sentinel constants render as their source name (scope=DEFAULT_OPTION_SCOPErather than<...object at 0x...>). Long:value:text on module-level constants collapses with a truncation marker so multi-KB defaults no longer dominate API pages.:py:class:-shaped xrefs to their documented class, and type expressions in autodoc field lists route through the same xref pipeline as the signature — links resolve consistently and nested types render with chip styling matching the rest of the page.<dt>; below 52rem the header row stacks instead of squeezing, with the type badge pinned beside the signature and the source link right-anchored in the toolbar. Below 30rem long signatures scroll horizontally instead of wrapping mid-identifier, and the permalink reveals on tap for touch users.make_workspace_linkcode_resolve()— a drop-inlinkcode_resolvefactory for uv/pnpm monorepos. One registration covers every package underpackages/by computing GitHub URLs relative to arepo_root, always pointing at one configurable branch (defaultmain) since workspace packages carry independent versions while the docs site tracks live tip.gp-sphinxcascade layer makes workspace overrides win over Furo declaratively; parameter and field-list rows share a unified metadata-sized typography band; code chips sit flush against generic-type brackets; root-level scaling follows aclamp()ramp instead of stepping at a single breakpoint.sphinx-gp-themeTOC font-size override. The override was inert becausegp-furo-tokensemits the default onbody, not:root— moving the override tobodyrestores the configured larger size.sphinx-vite-builderCI hint. The recipe printed byPnpmMissingError(and matching README/AGENTS samples) used to includecache: pnpm, which fails on consumer CI when the consumer repo has no rootpnpm-lock.yaml. The hint now omits that line with a note explaining when it is safe to add back.Changes by area
gp-sphinxconfig.py: newmake_workspace_linkcode_resolve()factory; merge-config now wiresautodoc_preserve_defaults.defaults.py: newDEFAULT_AUTODOC_PRESERVE_DEFAULTS = True.sphinx-autodoc-typehints-gpFive new private modules implement the default-value pipeline:
_param_defaults.py— autodoc listener that fills synthetic__init__defaults (dataclass / attrs / NamedTuple) by walking the real class body, bypassing the upstream listener'sinspect.getsource()limitation._data_defaults.py—:value:truncation for long module-level constants._default_xref_transform.py— post-transform that AST-parses default-value text and replaces identifier nodes withpending_xref(reftype='class')wrapped to match an inline:py:class:role's HTML shape._field_xref_transform.py— same canonicalization for type expressions inside autodoc field lists._resolvers.py— shared resolver catalog consumed by both default-value pipelines; private API (no publicadd_default_resolveruntil external demand surfaces).sphinx-ux-autodoc-layout_transforms.py: dual-variant header emission._parse_signature_inputspartitions the originaldesc_signaturechildren into signature row / badges / source link / parameters;_build_signature_columnproduces one fresh, independently-parented subtree per variant._static/css/layout.css: container-query-driven@mediarules at 52rem and 30rem breakpoints.Theme / tokens / cross-package CSS
gp-furo-theme: declares agp-sphinxcascade layer after Furo'scomponentslayer; scaffold uses aclamp()ramp.gp-furo-tokens: four--gp-sphinx-type-*role aliases over Furo's font-size scale.sphinx-autodoc-api-style,sphinx-autodoc-pytest-fixtures,sphinx-ux-badges: field-list and icon rules relabelled as standalone fallbacks and re-layered intogp-sphinx. Single authoritative grid lives insphinx-ux-autodoc-layout.Bug fixes
sphinx-gp-theme/.../custom.css: TOC font-size override moved from:roottobody.sphinx-vite-builder/_internal/vite.py+ README + AGENTS: dropcache: pnpmfrom thePnpmMissingErrorhint and matching CI recipes.Design decisions
autodoc_preserve_defaultsships flipped on for every consumer rather than as a named option, because the off-state produces unreadable repr text universally. Consumers can still override per-project.linkcode_resolvefactory points at one branch, not version tags. Workspace packages carry independent version strings but the docs site tracks live tip. A per-package version tag would require dispatching by__name__and miss the live behaviour readers expect when following a "Source" link.add_default_resolveris not exported; the catalog inside_resolvers.pyships seeded but downstream registration waits for evidence of external demand.Test plan
uv run ruff check .— cleanuv run ruff format --check .— no diffuv run mypy— passes across all packagesuv run pytest -q— full suite passes, including new unit + integration coverage for_param_defaults,_data_defaults,_default_xref_transform,_field_xref_transform, andmake_workspace_linkcode_resolverm -rf docs/_build && just build-docs— builds without new warnings or errorsconfval,rst:directive,mcp:tool, and large-signature shapesgp-sphinx-api-layout--desktopandgp-sphinx-api-layout--mobilesiblings under each managed header