Skip to content

Commit afa6dd9

Browse files
authored
feat: Support more experimental annotations (names, deprecations, warnings, exceptions)
Issue #1: #1 PR #3: #3
1 parent c7a61c6 commit afa6dd9

File tree

11 files changed

+720
-92
lines changed

11 files changed

+720
-92
lines changed

docs/examples.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Examples
2+
3+
## Simple
4+
5+
/// details | `simple` Python module
6+
type: example
7+
8+
```python
9+
--8<-- "docs/examples/simple.py"
10+
```
11+
///
12+
13+
::: simple
14+
options:
15+
heading_level: 3
16+
17+
## Enhanced
18+
19+
/// details | `enhanced` Python module
20+
type: example
21+
22+
```python
23+
--8<-- "docs/examples/enhanced.py"
24+
```
25+
///
26+
27+
::: enhanced
28+
options:
29+
heading_level: 3

docs/examples/enhanced.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# This gist shows how we could get rid of docstrings micro-syntax
2+
# like Google-style and Numpydoc-style pseudo-standards,
3+
# using an enhanced version of PEP 727.
4+
5+
# The goal is to replace the following sections:
6+
7+
# Deprecated
8+
# Parameters
9+
# Other Parameters
10+
# Raises
11+
# Warns
12+
# Yields
13+
# Receives
14+
# Returns
15+
16+
from __future__ import annotations
17+
18+
from typing import (
19+
Annotated,
20+
Generator,
21+
Deprecated,
22+
Doc,
23+
Name,
24+
Raises,
25+
Warns,
26+
)
27+
28+
29+
# Documenting deprecations, replacing Deprecated sections:
30+
DEPRECATED: Annotated[
31+
int,
32+
Deprecated(
33+
"""Deprecated since v2.
34+
35+
Please stop using this deprecated attribute, thanks!
36+
"""
37+
),
38+
Doc("Showing off deprecated attributes."),
39+
]
40+
41+
42+
# For functions, maybe add the information to the return value annotation:
43+
def deprecated1() -> Annotated[None, Deprecated("Deprecated since v2.")]:
44+
"""Showing off deprecated functions."""
45+
46+
47+
# For parameters:
48+
def deprecated2(param1: Annotated[int, Deprecated("Deprecated since v2."), Doc("Description of param1.")] = 0):
49+
"""Showing off deprecated parameters."""
50+
51+
52+
# Documenting exceptions, replacing Raises sections,
53+
# maybe add the information to the return value annotation:
54+
def exceptions() -> (
55+
Annotated[
56+
None,
57+
Raises(ValueError, "When something goes wrong."),
58+
Raises(TypeError, "When something goes even wronger."),
59+
]
60+
):
61+
"""Showing off raised exceptions."""
62+
63+
64+
# Documenting warnings, replacing Warns sections,
65+
# maybe add the information to the return value annotation:
66+
def warnings() -> (
67+
Annotated[
68+
None,
69+
Warns(FutureWarning, "Hello users."),
70+
Warns(DeprecationWarning, "Hello developers."),
71+
]
72+
):
73+
"""Showing off emitted warnings."""
74+
75+
76+
# Advanced use-case: documenting multiple yielded/received/returned values:
77+
def return_tuple() -> (
78+
Generator[
79+
tuple[
80+
Annotated[int, Name("python"), Doc("First element of the yielded value.")],
81+
Annotated[float, Name("cobra"), Doc("Second element of the yielded value.")],
82+
],
83+
tuple[
84+
Annotated[int, Name("beep"), Doc("First element of the received value.")],
85+
Annotated[float, Name("boop"), Doc("Second element of the received value.")],
86+
],
87+
tuple[
88+
Annotated[int, Name("super"), Doc("First element of the returned value.")],
89+
Annotated[float, Name("hyper"), Doc("Second element of the returned value.")],
90+
],
91+
]
92+
):
93+
"""Showing off tuples as yield/receive/return values."""

docs/examples/simple.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from __future__ import annotations
2+
3+
from typing import (
4+
Annotated,
5+
Generator,
6+
Iterator,
7+
NotRequired,
8+
TypedDict,
9+
Unpack,
10+
Doc,
11+
)
12+
13+
# Documenting module/class attributes, replacing Attributes sections:
14+
ATTRIBUTE: Annotated[
15+
str,
16+
Doc(
17+
"""Showing off attributes.
18+
19+
Supporting multiple lines.
20+
"""
21+
)
22+
]
23+
24+
25+
# Documenting parameters, replacing Parameters sections:
26+
def parameters(param1: Annotated[str, Doc("Description of param1.")] = "default"):
27+
"""Showing off parameters."""
28+
29+
30+
# Documenting other parameters (keyword arguments), replacing Other Parameters sections:
31+
class OtherParameters(TypedDict, total=False):
32+
"""Keyword arguments of [`simple.other_parameters`][]."""
33+
param1: Annotated[NotRequired[str], Doc("Description of param1.")]
34+
param2: Annotated[NotRequired[str], Doc("Description of param2.")]
35+
36+
37+
def other_parameters(
38+
**kwargs: Annotated[Unpack[OtherParameters], Doc("See other parameters.")], # noqa: ARG001
39+
) -> None:
40+
"""Showing off other parameters."""
41+
42+
43+
# Documenting yielded and received values, replacing Yields and Receives sections:
44+
def generator() -> (
45+
Generator[
46+
Annotated[int, Doc("Yielded integers.")],
47+
Annotated[int, Doc("Received integers.")],
48+
Annotated[int, Doc("Final returned value.")],
49+
]
50+
):
51+
"""Showing off generators."""
52+
53+
54+
# Same thing with Iterator instead of Generator:
55+
def iterator() -> Iterator[Annotated[int, Doc("Yielded integers.")]]:
56+
"""Showing off iterators."""
57+
58+
59+
# Advanced use-case: documenting multiple yielded/received/returned values:
60+
def return_tuple() -> (
61+
Generator[
62+
tuple[
63+
Annotated[int, Doc("First element of the yielded value.")],
64+
Annotated[float, Doc("Second element of the yielded value.")],
65+
],
66+
tuple[
67+
Annotated[int, Doc("First element of the received value.")],
68+
Annotated[float, Doc("Second element of the received value.")],
69+
],
70+
tuple[
71+
Annotated[int, Doc("First element of the returned value.")],
72+
Annotated[float, Doc("Second element of the returned value.")],
73+
],
74+
]
75+
):
76+
"""Showing off tuples as yield/receive/return values."""

mkdocs.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ nav:
1919
- Changelog: changelog.md
2020
- Credits: credits.md
2121
- License: license.md
22+
- Examples: examples.md
2223
# defer to gen-files + literate-nav
2324
- API reference:
2425
- Griffe TypingDoc: reference/
@@ -78,6 +79,7 @@ markdown_extensions:
7879
- admonition
7980
- callouts
8081
- footnotes
82+
- pymdownx.blocks.details
8183
- pymdownx.emoji:
8284
emoji_index: !!python/name:materialx.emoji.twemoji
8385
emoji_generator: !!python/name:materialx.emoji.to_svg
@@ -110,6 +112,7 @@ plugins:
110112
import:
111113
- https://docs.python.org/3/objects.inv
112114
- https://mkdocstrings.github.io/griffe/objects.inv
115+
paths: [src, docs/examples]
113116
options:
114117
docstring_options:
115118
ignore_init_summary: true

src/griffe_typingdoc/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55

66
from __future__ import annotations
77

8-
from griffe_typingdoc.extension import TypingDocExtension as Extension
8+
from griffe_typingdoc._extension import TypingDocExtension
99

10-
__all__: list[str] = ["Extension"]
10+
__all__: list[str] = ["TypingDocExtension"]
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""Helpers to build docstring sections."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING, Any, Iterator
6+
7+
from griffe.docstrings.dataclasses import (
8+
DocstringParameter,
9+
DocstringRaise,
10+
DocstringReceive,
11+
DocstringReturn,
12+
DocstringSectionAdmonition,
13+
DocstringSectionOtherParameters,
14+
DocstringSectionParameters,
15+
DocstringSectionRaises,
16+
DocstringSectionReceives,
17+
DocstringSectionReturns,
18+
DocstringSectionWarns,
19+
DocstringSectionYields,
20+
DocstringWarn,
21+
DocstringYield,
22+
)
23+
24+
if TYPE_CHECKING:
25+
from griffe import Function
26+
from griffe.dataclasses import Parameter
27+
28+
29+
def _no_self_params(func: Function) -> list[Parameter]:
30+
if func.parent and func.parent.is_class and func.parameters[0].name in {"self", "cls"}:
31+
return list(func.parameters)[1:]
32+
return list(func.parameters)
33+
34+
35+
def _to_parameters_section(params_dict: dict[str, dict[str, Any]], func: Function) -> DocstringSectionParameters:
36+
return DocstringSectionParameters(
37+
[
38+
DocstringParameter(
39+
name=param_name,
40+
description=param_doc["description"],
41+
annotation=param_doc["annotation"],
42+
value=func.parameters[param_name].default, # type: ignore[arg-type]
43+
)
44+
for param_name, param_doc in params_dict.items()
45+
],
46+
)
47+
48+
49+
def _to_other_parameters_section(params_dict: dict[str, dict[str, Any]]) -> DocstringSectionOtherParameters:
50+
return DocstringSectionOtherParameters(
51+
[
52+
DocstringParameter(
53+
name=param_name,
54+
description=param_doc["description"],
55+
annotation=param_doc["annotation"],
56+
)
57+
for param_name, param_doc in params_dict.items()
58+
],
59+
)
60+
61+
62+
def _to_yields_section(yield_data: Iterator[dict[str, Any]]) -> DocstringSectionYields:
63+
return DocstringSectionYields(
64+
[
65+
DocstringYield(
66+
name=yield_dict.get("name", ""),
67+
description=yield_dict.get("doc", ""),
68+
annotation=yield_dict["annotation"],
69+
)
70+
for yield_dict in yield_data
71+
],
72+
)
73+
74+
75+
def _to_receives_section(receive_data: Iterator[dict[str, Any]]) -> DocstringSectionReceives:
76+
return DocstringSectionReceives(
77+
[
78+
DocstringReceive(
79+
name=receive_dict.get("name", ""),
80+
description=receive_dict.get("doc", ""),
81+
annotation=receive_dict["annotation"],
82+
)
83+
for receive_dict in receive_data
84+
],
85+
)
86+
87+
88+
def _to_returns_section(return_data: Iterator[dict[str, Any]]) -> DocstringSectionReturns:
89+
return DocstringSectionReturns(
90+
[
91+
DocstringReturn(
92+
name=return_dict.get("name", ""),
93+
description=return_dict.get("doc", ""),
94+
annotation=return_dict["annotation"],
95+
)
96+
for return_dict in return_data
97+
],
98+
)
99+
100+
101+
def _to_warns_section(warn_data: Iterator[dict[str, Any]]) -> DocstringSectionWarns:
102+
return DocstringSectionWarns(
103+
[
104+
DocstringWarn(
105+
annotation=warn_dict["annotation"],
106+
description=warn_dict.get("description", ""),
107+
)
108+
for warn_dict in warn_data
109+
],
110+
)
111+
112+
113+
def _to_raises_section(raise_data: Iterator[dict[str, Any]]) -> DocstringSectionRaises:
114+
return DocstringSectionRaises(
115+
[
116+
DocstringRaise(
117+
annotation=raise_dict["annotation"],
118+
description=raise_dict.get("description", ""),
119+
)
120+
for raise_dict in raise_data
121+
],
122+
)
123+
124+
125+
def _to_deprecated_section(deprecation_data: dict[str, Any]) -> DocstringSectionAdmonition:
126+
description = deprecation_data["description"]
127+
description_lines = deprecation_data["description"].split("\n")
128+
if len(description_lines) > 1:
129+
title = description_lines[0].strip()
130+
description = "\n".join(description_lines[1:]).strip()
131+
else:
132+
title = description
133+
description = ""
134+
return DocstringSectionAdmonition(kind="danger", title=title, text=description)

0 commit comments

Comments
 (0)