Skip to content

Commit e96bd89

Browse files
authored
feat(api-style): Add sphinx-autodoc-api-style extension package (#10)
New workspace package that adds type and modifier badges to standard Python domain autodoc entries. Hooks into `doctree-resolved` to inject badge groups and a toolbar container into `desc_signature` nodes — no special directives required; existing `autofunction`, `autoclass`, and related directives receive badges automatically. - **Type badges**: function, class, method, property, attribute, data, exception — each with a distinct color - **Modifier badges**: async, classmethod, staticmethod, abstract, final, deprecated — detected from signature annotations and `.. deprecated::` directives - **Toolbar layout**: badges and `[source]` links grouped in a flex container that stays right-aligned; permalink (¶) remains next to the signature name - **Card containers**: bordered cards with secondary-background headers, responsive field-list grids, full light/dark theming - **Accessibility**: keyboard-focusable badges with tooltip popups - **CI**: smoke test verifying clean install and import - **Docs**: live demo page exercising every badge combination
2 parents c36fbd6 + dee1c36 commit e96bd89

22 files changed

Lines changed: 2050 additions & 6 deletions

File tree

.github/workflows/tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ jobs:
130130
- sphinx-autodoc-docutils
131131
- sphinx-autodoc-sphinx
132132
- sphinx-autodoc-pytest-fixtures
133+
- sphinx-autodoc-api-style
133134
steps:
134135
- uses: actions/checkout@v6
135136

CHANGES

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ $ uv add gp-sphinx --prerelease allow
3535
Furo code blocks use 300, and intermediate weights inherit from
3636
surrounding context — previously only Sans had the full set)
3737
- Replace `font-weight: 650` with `700` in badge CSS across
38-
`sphinx-autodoc-pytest-fixtures`, `sphinx-gptheme`, and docs (650 is not
39-
a standard Fontsource weight, so browsers were synthesizing bold instead
40-
of using the real font file)
38+
`sphinx-autodoc-api-style`, `sphinx-autodoc-pytest-fixtures`,
39+
`sphinx-gptheme`, and docs (650 is not a standard Fontsource weight,
40+
so browsers were synthesizing bold instead of using the real font file)
4141

4242
### Workspace packages
4343

docs/_ext/gas_demo_api.py

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
"""Synthetic Python objects for the sphinx_autodoc_api_style badge demo page.
2+
3+
Each object exercises one badge combination so the demo page can show
4+
every type and modifier badge side-by-side:
5+
6+
Types: function | class | method | property | attribute | data | exception
7+
Modifiers: async | classmethod | staticmethod | abstract | final | deprecated
8+
9+
These definitions are purely for documentation; they are never used in
10+
production code.
11+
"""
12+
13+
from __future__ import annotations
14+
15+
import abc
16+
import typing as t
17+
18+
19+
def demo_function(name: str, count: int = 1) -> list[str]:
20+
"""Plain function. Shows ``function`` type badge.
21+
22+
Parameters
23+
----------
24+
name : str
25+
The name to repeat.
26+
count : int
27+
Number of repetitions.
28+
29+
Returns
30+
-------
31+
list[str]
32+
A list of repeated names.
33+
"""
34+
return [name] * count
35+
36+
37+
async def demo_async_function(url: str) -> bytes:
38+
"""Asynchronous function. Shows ``async`` + ``function`` badges.
39+
40+
Parameters
41+
----------
42+
url : str
43+
The URL to fetch.
44+
45+
Returns
46+
-------
47+
bytes
48+
The fetched content.
49+
"""
50+
return b""
51+
52+
53+
def demo_deprecated_function() -> None:
54+
"""Do nothing (deprecated placeholder).
55+
56+
Shows ``deprecated`` + ``function`` badges.
57+
58+
.. deprecated:: 2.0
59+
Use :func:`demo_function` instead.
60+
"""
61+
62+
63+
DEMO_CONSTANT: int = 42
64+
"""Module-level constant. Shows ``data`` type badge."""
65+
66+
67+
class DemoError(Exception):
68+
"""Custom exception class. Shows ``exception`` type badge.
69+
70+
Raised when a demo operation fails unexpectedly.
71+
"""
72+
73+
74+
class DemoClass:
75+
"""Demonstration class with various method types.
76+
77+
Shows ``class`` type badge on the class itself, and per-method
78+
badges for each method kind.
79+
80+
Parameters
81+
----------
82+
value : str
83+
Initial value for the demo instance.
84+
"""
85+
86+
demo_attr: str = "hello"
87+
"""Class attribute. Shows ``attribute`` type badge."""
88+
89+
def __init__(self, value: str) -> None:
90+
self.value = value
91+
92+
def regular_method(self, x: int) -> str:
93+
"""Regular instance method. Shows ``method`` type badge.
94+
95+
Parameters
96+
----------
97+
x : int
98+
Input value.
99+
100+
Returns
101+
-------
102+
str
103+
String representation.
104+
"""
105+
return f"{self.value}:{x}"
106+
107+
@classmethod
108+
def from_int(cls, n: int) -> DemoClass:
109+
"""Class method. Shows ``classmethod`` + ``method`` badges.
110+
111+
Parameters
112+
----------
113+
n : int
114+
Integer to convert.
115+
116+
Returns
117+
-------
118+
DemoClass
119+
A new instance.
120+
"""
121+
return cls(str(n))
122+
123+
@staticmethod
124+
def utility(a: int, b: int) -> int:
125+
"""Add two integers. Shows ``staticmethod`` + ``method`` badges.
126+
127+
Parameters
128+
----------
129+
a : int
130+
First operand.
131+
b : int
132+
Second operand.
133+
134+
Returns
135+
-------
136+
int
137+
Sum of operands.
138+
"""
139+
return a + b
140+
141+
@property
142+
def computed(self) -> str:
143+
"""Computed property. Shows ``property`` type badge.
144+
145+
Returns
146+
-------
147+
str
148+
The uppercased value.
149+
"""
150+
return self.value.upper()
151+
152+
async def async_method(self) -> None:
153+
"""Asynchronous method. Shows ``async`` + ``method`` badges."""
154+
155+
def deprecated_method(self) -> None:
156+
"""Do nothing (deprecated placeholder).
157+
158+
Shows ``deprecated`` + ``method`` badges.
159+
160+
.. deprecated:: 1.5
161+
Use :meth:`regular_method` instead.
162+
"""
163+
164+
165+
class DemoAbstractBase(abc.ABC):
166+
"""Abstract base class. Shows ``class`` type badge.
167+
168+
Subclass this to provide concrete implementations.
169+
"""
170+
171+
@abc.abstractmethod
172+
def must_implement(self) -> str:
173+
"""Abstract method. Shows ``abstract`` + ``method`` badges.
174+
175+
Returns
176+
-------
177+
str
178+
Implementation-specific value.
179+
"""
180+
181+
@abc.abstractmethod
182+
async def async_abstract(self) -> None:
183+
"""Async abstract method. Shows ``async`` + ``abstract`` + ``method`` badges."""
184+
185+
186+
if t.TYPE_CHECKING:
187+
DemoAlias = str | int

docs/conf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
)
1919
sys.path.insert(0, str(project_root / "packages" / "sphinx-autodoc-docutils" / "src"))
2020
sys.path.insert(0, str(project_root / "packages" / "sphinx-autodoc-sphinx" / "src"))
21+
sys.path.insert(0, str(project_root / "packages" / "sphinx-autodoc-api-style" / "src"))
2122
sys.path.insert(0, str(cwd / "_ext")) # docs demo modules
2223

2324
import gp_sphinx # noqa: E402
@@ -37,6 +38,7 @@
3738
source_branch="main",
3839
extra_extensions=[
3940
"package_reference",
41+
"sphinx_autodoc_api_style",
4042
"sphinx_autodoc_pytest_fixtures",
4143
"sphinx_autodoc_docutils",
4244
"sphinx_autodoc_sphinx",

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Install and get started in minutes.
1616
:::{grid-item-card} Packages
1717
:link: packages/index
1818
:link-type: doc
19-
Seven workspace packages — coordinator, extensions, and theme.
19+
Eight workspace packages — coordinator, extensions, and theme.
2020
:::
2121

2222
:::{grid-item-card} Configuration

docs/packages/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Packages
22

3-
Seven workspace packages, each independently installable.
3+
Eight workspace packages, each independently installable.
44

55
```{workspace-package-grid}
66
```
@@ -9,6 +9,7 @@ Seven workspace packages, each independently installable.
99
:hidden:
1010
1111
gp-sphinx
12+
sphinx-autodoc-api-style
1213
sphinx-autodoc-docutils
1314
sphinx-autodoc-sphinx
1415
sphinx-autodoc-pytest-fixtures
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
(sphinx-autodoc-api-style)=
2+
3+
# sphinx-autodoc-api-style
4+
5+
{bdg-warning-line}`Alpha` {bdg-link-secondary-line}`GitHub <https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-autodoc-api-style>` {bdg-link-secondary-line}`PyPI <https://pypi.org/project/sphinx-autodoc-api-style/>`
6+
7+
Sphinx extension that adds type and modifier badges to standard Python domain
8+
entries (functions, classes, methods, properties, attributes, data,
9+
exceptions). Mirrors the badge system from
10+
{doc}`sphinx-autodoc-pytest-fixtures` so API pages and fixture pages share a
11+
consistent visual language.
12+
13+
```console
14+
$ pip install sphinx-autodoc-api-style
15+
```
16+
17+
## Features
18+
19+
- **Type badges** (rightmost): `function`, `class`, `method`, `property`,
20+
`attribute`, `data`, `exception` — each with a distinct color
21+
- **Modifier badges** (left of type): `async`, `classmethod`, `staticmethod`,
22+
`abstract`, `final`, `deprecated`
23+
- **Card containers**: bordered cards with secondary-background headers
24+
- **Dark mode**: full light/dark theming via CSS custom properties
25+
- **Accessibility**: keyboard-focusable badges with tooltip popups
26+
- **Non-invasive**: hooks into `doctree-resolved` without replacing directives
27+
28+
## How it works
29+
30+
Add `sphinx_autodoc_api_style` to your Sphinx extensions. With `gp-sphinx`,
31+
use `extra_extensions`:
32+
33+
```python
34+
conf = merge_sphinx_config(
35+
project="my-project",
36+
version="1.0.0",
37+
copyright="2026, Your Name",
38+
source_repository="https://github.com/your-org/my-project/",
39+
extra_extensions=["sphinx_autodoc_api_style"],
40+
)
41+
```
42+
43+
Or without `merge_sphinx_config`:
44+
45+
```python
46+
extensions = ["sphinx_autodoc_api_style"]
47+
```
48+
49+
No special directives are needed — existing `.. autofunction::`,
50+
`.. autoclass::`, `.. automodule::` directives automatically receive badges.
51+
52+
## Live demo
53+
54+
```{py:module} gas_demo_api
55+
```
56+
57+
### Functions
58+
59+
```{eval-rst}
60+
.. autofunction:: gas_demo_api.demo_function
61+
```
62+
63+
```{eval-rst}
64+
.. autofunction:: gas_demo_api.demo_async_function
65+
```
66+
67+
```{eval-rst}
68+
.. autofunction:: gas_demo_api.demo_deprecated_function
69+
```
70+
71+
### Module data
72+
73+
```{eval-rst}
74+
.. autodata:: gas_demo_api.DEMO_CONSTANT
75+
```
76+
77+
### Exceptions
78+
79+
```{eval-rst}
80+
.. autoexception:: gas_demo_api.DemoError
81+
```
82+
83+
### Classes
84+
85+
```{eval-rst}
86+
.. autoclass:: gas_demo_api.DemoClass
87+
:members:
88+
:undoc-members:
89+
```
90+
91+
### Abstract base classes
92+
93+
```{eval-rst}
94+
.. autoclass:: gas_demo_api.DemoAbstractBase
95+
:members:
96+
```
97+
98+
## Badge reference
99+
100+
### Type badges
101+
102+
| Object type | CSS class | Color |
103+
|-------------|-----------|-------|
104+
| `function` | `gas-type-function` | Blue |
105+
| `class` | `gas-type-class` | Indigo |
106+
| `method` | `gas-type-method` | Cyan |
107+
| `property` | `gas-type-property` | Teal |
108+
| `attribute` | `gas-type-attribute` | Slate |
109+
| `data` | `gas-type-data` | Grey |
110+
| `exception` | `gas-type-exception` | Rose |
111+
112+
### Modifier badges
113+
114+
| Modifier | CSS class | Style |
115+
|----------|-----------|-------|
116+
| `async` | `gas-mod-async` | Purple outlined |
117+
| `classmethod` | `gas-mod-classmethod` | Amber outlined |
118+
| `staticmethod` | `gas-mod-staticmethod` | Grey outlined |
119+
| `abstract` | `gas-mod-abstract` | Indigo outlined |
120+
| `final` | `gas-mod-final` | Emerald outlined |
121+
| `deprecated` | `gas-deprecated` | Red/grey outlined |
122+
123+
## CSS prefix
124+
125+
All CSS classes use the `gas-` prefix (**g**p-sphinx **a**pi **s**tyle) to avoid
126+
collision with `spf-` (sphinx pytest fixtures) or other extensions.
127+
128+
```{package-reference} sphinx-autodoc-api-style
129+
```
130+
131+
[Source on GitHub](https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-autodoc-api-style) · [PyPI](https://pypi.org/project/sphinx-autodoc-api-style/)

docs/redirects.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ extensions/sphinx-argparse-neo packages/sphinx-argparse-neo
44
extensions/sphinx-autodoc-pytest-fixtures packages/sphinx-autodoc-pytest-fixtures
55
extensions/sphinx-autodoc-docutils packages/sphinx-autodoc-docutils
66
extensions/sphinx-autodoc-sphinx packages/sphinx-autodoc-sphinx
7+
extensions/sphinx-autodoc-api-style packages/sphinx-autodoc-api-style
78
extensions/sphinx-fonts packages/sphinx-fonts
89
extensions/sphinx-gptheme packages/sphinx-gptheme

0 commit comments

Comments
 (0)