mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
autodoc: Support type union operator (PEP-604) (refs: #8775)
Upgrade autodoc to support type union operator introduced in PEP-604. It's available only with python 3.10+.
This commit is contained in:
parent
be20f17892
commit
7318d1dfd4
1
CHANGES
1
CHANGES
@ -38,6 +38,7 @@ Features added
|
|||||||
info-field-list
|
info-field-list
|
||||||
* #8514: autodoc: Default values of overloaded functions are taken from actual
|
* #8514: autodoc: Default values of overloaded functions are taken from actual
|
||||||
implementation if they're ellipsis
|
implementation if they're ellipsis
|
||||||
|
* #8775: autodoc: Support type union operator (PEP-604) in Python 3.10 or above
|
||||||
* #8619: html: kbd role generates customizable HTML tags for compound keys
|
* #8619: html: kbd role generates customizable HTML tags for compound keys
|
||||||
* #8634: html: Allow to change the order of JS/CSS via ``priority`` parameter
|
* #8634: html: Allow to change the order of JS/CSS via ``priority`` parameter
|
||||||
for :meth:`Sphinx.add_js_file()` and :meth:`Sphinx.add_css_file()`
|
for :meth:`Sphinx.add_js_file()` and :meth:`Sphinx.add_css_file()`
|
||||||
|
@ -30,6 +30,11 @@ else:
|
|||||||
ref = _ForwardRef(self.arg)
|
ref = _ForwardRef(self.arg)
|
||||||
return ref._eval_type(globalns, localns)
|
return ref._eval_type(globalns, localns)
|
||||||
|
|
||||||
|
if sys.version_info > (3, 10):
|
||||||
|
from types import Union as types_Union
|
||||||
|
else:
|
||||||
|
types_Union = None
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
from typing import Type # NOQA # for python3.5.1
|
from typing import Type # NOQA # for python3.5.1
|
||||||
@ -100,6 +105,12 @@ def restify(cls: Optional["Type"]) -> str:
|
|||||||
return ':class:`struct.Struct`'
|
return ':class:`struct.Struct`'
|
||||||
elif inspect.isNewType(cls):
|
elif inspect.isNewType(cls):
|
||||||
return ':class:`%s`' % cls.__name__
|
return ':class:`%s`' % cls.__name__
|
||||||
|
elif types_Union and isinstance(cls, types_Union):
|
||||||
|
if len(cls.__args__) > 1 and None in cls.__args__:
|
||||||
|
args = ' | '.join(restify(a) for a in cls.__args__ if a)
|
||||||
|
return 'Optional[%s]' % args
|
||||||
|
else:
|
||||||
|
return ' | '.join(restify(a) for a in cls.__args__)
|
||||||
elif cls.__module__ in ('__builtin__', 'builtins'):
|
elif cls.__module__ in ('__builtin__', 'builtins'):
|
||||||
return ':class:`%s`' % cls.__name__
|
return ':class:`%s`' % cls.__name__
|
||||||
else:
|
else:
|
||||||
@ -336,6 +347,8 @@ def _stringify_py37(annotation: Any) -> str:
|
|||||||
elif hasattr(annotation, '__origin__'):
|
elif hasattr(annotation, '__origin__'):
|
||||||
# instantiated generic provided by a user
|
# instantiated generic provided by a user
|
||||||
qualname = stringify(annotation.__origin__)
|
qualname = stringify(annotation.__origin__)
|
||||||
|
elif types_Union and isinstance(annotation, types_Union): # types.Union (for py3.10+)
|
||||||
|
qualname = 'types.Union'
|
||||||
else:
|
else:
|
||||||
# we weren't able to extract the base type, appending arguments would
|
# we weren't able to extract the base type, appending arguments would
|
||||||
# only make them appear twice
|
# only make them appear twice
|
||||||
@ -355,6 +368,12 @@ def _stringify_py37(annotation: Any) -> str:
|
|||||||
else:
|
else:
|
||||||
args = ', '.join(stringify(a) for a in annotation.__args__)
|
args = ', '.join(stringify(a) for a in annotation.__args__)
|
||||||
return 'Union[%s]' % args
|
return 'Union[%s]' % args
|
||||||
|
elif qualname == 'types.Union':
|
||||||
|
if len(annotation.__args__) > 1 and None in annotation.__args__:
|
||||||
|
args = ' | '.join(stringify(a) for a in annotation.__args__ if a)
|
||||||
|
return 'Optional[%s]' % args
|
||||||
|
else:
|
||||||
|
return ' | '.join(stringify(a) for a in annotation.__args__)
|
||||||
elif qualname == 'Callable':
|
elif qualname == 'Callable':
|
||||||
args = ', '.join(stringify(a) for a in annotation.__args__[:-1])
|
args = ', '.join(stringify(a) for a in annotation.__args__[:-1])
|
||||||
returns = stringify(annotation.__args__[-1])
|
returns = stringify(annotation.__args__[-1])
|
||||||
|
16
tests/roots/test-ext-autodoc/target/pep604.py
Normal file
16
tests/roots/test-ext-autodoc/target/pep604.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
attr: int | str #: docstring
|
||||||
|
|
||||||
|
|
||||||
|
def sum(x: int | str, y: int | str) -> int | str:
|
||||||
|
"""docstring"""
|
||||||
|
|
||||||
|
|
||||||
|
class Foo:
|
||||||
|
"""docstring"""
|
||||||
|
|
||||||
|
attr: int | str #: docstring
|
||||||
|
|
||||||
|
def meth(self, x: int | str, y: int | str) -> int | str:
|
||||||
|
"""docstring"""
|
@ -2237,6 +2237,50 @@ def test_name_mangling(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_type_union_operator(app):
|
||||||
|
options = {'members': None}
|
||||||
|
actual = do_autodoc(app, 'module', 'target.pep604', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:module:: target.pep604',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:class:: Foo()',
|
||||||
|
' :module: target.pep604',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Foo.attr',
|
||||||
|
' :module: target.pep604',
|
||||||
|
' :type: int | str',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:method:: Foo.meth(x: int | str, y: int | str) -> int | str',
|
||||||
|
' :module: target.pep604',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:data:: attr',
|
||||||
|
' :module: target.pep604',
|
||||||
|
' :type: int | str',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:function:: sum(x: int | str, y: int | str) -> int | str',
|
||||||
|
' :module: target.pep604',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
|
@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_hide_value(app):
|
def test_hide_value(app):
|
||||||
|
@ -117,6 +117,13 @@ def test_restify_type_ForwardRef():
|
|||||||
assert restify(ForwardRef("myint")) == ":class:`myint`"
|
assert restify(ForwardRef("myint")) == ":class:`myint`"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
|
||||||
|
def test_restify_type_union_operator():
|
||||||
|
assert restify(int | None) == "Optional[:class:`int`]" # type: ignore
|
||||||
|
assert restify(int | str) == ":class:`int` | :class:`str`" # type: ignore
|
||||||
|
assert restify(int | str | None) == "Optional[:class:`int` | :class:`str`]" # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def test_restify_broken_type_hints():
|
def test_restify_broken_type_hints():
|
||||||
assert restify(BrokenType) == ':class:`tests.test_util_typing.BrokenType`'
|
assert restify(BrokenType) == ':class:`tests.test_util_typing.BrokenType`'
|
||||||
|
|
||||||
@ -206,5 +213,12 @@ def test_stringify_type_hints_alias():
|
|||||||
assert stringify(MyTuple) == "Tuple[str, str]" # type: ignore
|
assert stringify(MyTuple) == "Tuple[str, str]" # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
|
||||||
|
def test_stringify_type_union_operator():
|
||||||
|
assert stringify(int | None) == "Optional[int]" # type: ignore
|
||||||
|
assert stringify(int | str) == "int | str" # type: ignore
|
||||||
|
assert stringify(int | str | None) == "Optional[int | str]" # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def test_stringify_broken_type_hints():
|
def test_stringify_broken_type_hints():
|
||||||
assert stringify(BrokenType) == 'tests.test_util_typing.BrokenType'
|
assert stringify(BrokenType) == 'tests.test_util_typing.BrokenType'
|
||||||
|
Loading…
Reference in New Issue
Block a user