Skip to content

Commit ad3f3cc

Browse files
mmatousAA-Turnerjbms
authored
Add initial support for PEP 695 type aliases (#13508)
Signed-off-by: Martin Matous <m@matous.dev> Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com> Co-authored-by: Jeremy Maitin-Shepard <jbms@google.com>
1 parent 22c5356 commit ad3f3cc

File tree

17 files changed

+325
-41
lines changed

17 files changed

+325
-41
lines changed

.ruff.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ extend-exclude = [
99
"tests/roots/test-pycode/cp_1251_coded.py", # Not UTF-8
1010
]
1111

12+
[per-file-target-version]
13+
"tests/roots/test-ext-autodoc/target/pep695.py" = "py312"
14+
1215
[format]
1316
preview = true
1417
quote-style = "single"

AUTHORS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ Contributors
8989
* Martin Larralde -- additional napoleon admonitions
9090
* Martin Liška -- option directive and role improvements
9191
* Martin Mahner -- nature theme
92+
* Martin Matouš -- initial support for PEP 695
9293
* Matthew Fernandez -- todo extension fix
9394
* Matthew Woodcraft -- text output improvements
9495
* Matthias Geier -- style improvements

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ Features added
6666
Patch by Adam Turner.
6767
* #13805: LaTeX: add support for ``fontawesome7`` package.
6868
Patch by Jean-François B.
69+
* #13508: Initial support for :pep:`695` type aliases.
70+
Patch by Martin Matouš, Jeremy Maitin-Shepard, and Adam Turner.
6971

7072
Bugs fixed
7173
----------

doc/usage/extensions/autodoc.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,38 @@ Automatically document attributes or data
966966
``:no-value:`` has no effect.
967967
968968
969+
Automatically document type aliases
970+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
971+
972+
.. rst:directive:: autotype
973+
974+
.. versionadded:: 8.3
975+
976+
Document a :pep:`695` type alias (the :keyword:`type` statement).
977+
By default, the directive only inserts the docstring of the alias itself:
978+
979+
The directive can also contain content of its own,
980+
which will be inserted into the resulting non-auto directive source
981+
after the docstring (but before any automatic member documentation).
982+
983+
Therefore, you can also mix automatic and non-automatic member documentation.
984+
985+
.. rubric:: Options
986+
987+
.. rst:directive:option:: no-index
988+
:type:
989+
990+
Do not generate an index entry for the documented class
991+
or any auto-documented members.
992+
993+
.. rst:directive:option:: no-index-entry
994+
:type:
995+
996+
Do not generate an index entry for the documented class
997+
or any auto-documented members.
998+
Unlike ``:no-index:``, cross-references are still created.
999+
1000+
9691001
Configuration
9701002
-------------
9711003

sphinx/domains/python/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ class PythonDomain(Domain):
742742
'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
743743
'attribute': ObjType(_('attribute'), 'attr', 'obj'),
744744
'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
745-
'type': ObjType(_('type alias'), 'type', 'obj'),
745+
'type': ObjType(_('type alias'), 'type', 'class', 'obj'),
746746
'module': ObjType(_('module'), 'mod', 'obj'),
747747
}
748748

sphinx/ext/autodoc/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
ModuleDocumenter,
3838
ModuleLevelDocumenter,
3939
PropertyDocumenter,
40+
TypeAliasDocumenter,
4041
autodoc_attrgetter,
4142
py_ext_sig_re,
4243
)
@@ -114,6 +115,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
114115
app.add_autodocumenter(MethodDocumenter)
115116
app.add_autodocumenter(AttributeDocumenter)
116117
app.add_autodocumenter(PropertyDocumenter)
118+
app.add_autodocumenter(TypeAliasDocumenter)
117119

118120
app.add_config_value(
119121
'autoclass_content',

sphinx/ext/autodoc/_documenters.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
safe_getattr,
4242
stringify_signature,
4343
)
44-
from sphinx.util.typing import restify, stringify_annotation
44+
from sphinx.util.typing import AnyTypeAliasType, restify, stringify_annotation
4545

4646
if TYPE_CHECKING:
4747
from collections.abc import Callable, Iterator, Sequence
@@ -58,6 +58,7 @@
5858
_FunctionDefProperties,
5959
_ItemProperties,
6060
_ModuleProperties,
61+
_TypeStatementProperties,
6162
)
6263
from sphinx.ext.autodoc.directive import DocumenterBridge
6364
from sphinx.registry import SphinxComponentRegistry
@@ -1800,6 +1801,27 @@ def _get_property_getter(self) -> Callable[..., Any] | None:
18001801
return None
18011802

18021803

1804+
class TypeAliasDocumenter(Documenter):
1805+
"""Specialized Documenter subclass for type aliases."""
1806+
1807+
props: _TypeStatementProperties
1808+
1809+
objtype = 'type'
1810+
member_order = 70
1811+
option_spec: ClassVar[OptionSpec] = {
1812+
'no-index': bool_option,
1813+
'no-index-entry': bool_option,
1814+
'annotation': annotation_option,
1815+
'no-value': bool_option,
1816+
}
1817+
1818+
@classmethod
1819+
def can_document_member(
1820+
cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
1821+
) -> bool:
1822+
return isinstance(member, AnyTypeAliasType)
1823+
1824+
18031825
class DocstringSignatureMixin:
18041826
"""Retained for compatibility."""
18051827

sphinx/ext/autodoc/_property_types.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
'property',
2727
'attribute',
2828
'data',
29+
'type',
2930
]
3031
_AutodocFuncProperty: TypeAlias = Literal[
3132
'abstractmethod',
@@ -189,3 +190,12 @@ class _AssignStatementProperties(_ItemProperties):
189190
)
190191
_obj_repr_rst: str
191192
_obj_type_annotation: str | None
193+
194+
195+
@dataclasses.dataclass(frozen=False, kw_only=True, slots=True)
196+
class _TypeStatementProperties(_ItemProperties):
197+
obj_type: Literal['type']
198+
199+
_obj___name__: str | None
200+
_obj___qualname__: str | None
201+
_obj___value__: str # The aliased annotation

sphinx/ext/autodoc/_renderer.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
_AssignStatementProperties,
77
_ClassDefProperties,
88
_FunctionDefProperties,
9+
_TypeStatementProperties,
910
)
1011
from sphinx.ext.autodoc._sentinels import SUPPRESS
1112
from sphinx.locale import _
@@ -166,6 +167,12 @@ def _directive_header_lines(
166167
):
167168
yield f' :value: {props._obj_repr_rst}'
168169

170+
if props.obj_type == 'type':
171+
assert isinstance(props, _TypeStatementProperties)
172+
173+
if not options.no_value and not props._docstrings_has_hide_value:
174+
yield f' :canonical: {props._obj___value__}'
175+
169176

170177
def _add_content(content: StringList, *, result: StringList, indent: str) -> None:
171178
for line, src in zip(content.data, content.items, strict=True):

sphinx/ext/autodoc/importer.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
_FunctionDefProperties,
2727
_ItemProperties,
2828
_ModuleProperties,
29+
_TypeStatementProperties,
2930
)
3031
from sphinx.ext.autodoc._sentinels import (
3132
RUNTIME_INSTANCE_ATTRIBUTE,
@@ -774,6 +775,34 @@ def _load_object_by_name(
774775
_obj_repr_rst=inspect.object_description(obj),
775776
_obj_type_annotation=type_annotation,
776777
)
778+
elif objtype == 'type':
779+
obj_module_name = getattr(obj, '__module__', module_name)
780+
if obj_module_name != module_name and module_name.startswith(obj_module_name):
781+
bases = module_name[len(obj_module_name) :].strip('.').split('.')
782+
parts = tuple(bases) + parts
783+
module_name = obj_module_name
784+
785+
if config.autodoc_typehints_format == 'short':
786+
mode = 'smart'
787+
else:
788+
mode = 'fully-qualified-except-typing'
789+
short_literals = config.python_display_short_literal_types
790+
ann = stringify_annotation(
791+
obj.__value__,
792+
mode, # type: ignore[arg-type]
793+
short_literals=short_literals,
794+
)
795+
props = _TypeStatementProperties(
796+
obj_type=objtype,
797+
module_name=module_name,
798+
parts=parts,
799+
docstring_lines=(),
800+
_obj=obj,
801+
_obj___module__=get_attr(obj, '__module__', None),
802+
_obj___name__=getattr(obj, '__name__', None),
803+
_obj___qualname__=getattr(obj, '__qualname__', None),
804+
_obj___value__=ann,
805+
)
777806
else:
778807
props = _ItemProperties(
779808
obj_type=objtype,
@@ -912,7 +941,7 @@ def _resolve_name(
912941
)
913942
return (path or '') + base, ()
914943

915-
if objtype in {'class', 'exception', 'function', 'decorator', 'data'}:
944+
if objtype in {'class', 'exception', 'function', 'decorator', 'data', 'type'}:
916945
if module_name is not None:
917946
return module_name, (*parents, base)
918947
if path:

0 commit comments

Comments
 (0)