mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '4.x' into 8597_metadata_only_docstring
This commit is contained in:
commit
30efa3d947
4
CHANGES
4
CHANGES
@ -15,6 +15,9 @@ Deprecated
|
|||||||
Features added
|
Features added
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
* #8107: autodoc: Add ``class-doc-from`` option to :rst:dir:`autoclass`
|
||||||
|
directive to control the content of the specific class like
|
||||||
|
:confval:`autoclass_content`
|
||||||
* #9129: html search: Show search summaries when html_copy_source = False
|
* #9129: html search: Show search summaries when html_copy_source = False
|
||||||
* #9120: html theme: Eliminate prompt characters of code-block from copyable
|
* #9120: html theme: Eliminate prompt characters of code-block from copyable
|
||||||
text
|
text
|
||||||
@ -24,6 +27,7 @@ Features added
|
|||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* #8872: autodoc: stacked singledispatches are wrongly rendered
|
||||||
* #8597: autodoc: a docsting having metadata only should be treated as
|
* #8597: autodoc: a docsting having metadata only should be treated as
|
||||||
undocumented
|
undocumented
|
||||||
|
|
||||||
|
@ -343,6 +343,10 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
|
|||||||
|
|
||||||
.. autoclass:: module.name::Noodle
|
.. autoclass:: module.name::Noodle
|
||||||
|
|
||||||
|
* :rst:dir:`autoclass` also recognizes the ``class-doc-from`` option that
|
||||||
|
can be used to override the global value of :confval:`autoclass_content`.
|
||||||
|
|
||||||
|
.. versionadded:: 4.1
|
||||||
|
|
||||||
.. rst:directive:: autofunction
|
.. rst:directive:: autofunction
|
||||||
autodecorator
|
autodecorator
|
||||||
@ -507,7 +511,7 @@ There are also config values that you can set:
|
|||||||
The supported options are ``'members'``, ``'member-order'``,
|
The supported options are ``'members'``, ``'member-order'``,
|
||||||
``'undoc-members'``, ``'private-members'``, ``'special-members'``,
|
``'undoc-members'``, ``'private-members'``, ``'special-members'``,
|
||||||
``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'``,
|
``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'``,
|
||||||
``'imported-members'`` and ``'exclude-members'``.
|
``'imported-members'``, ``'exclude-members'`` and ``'class-doc-from'``.
|
||||||
|
|
||||||
.. versionadded:: 1.8
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
@ -517,6 +521,9 @@ There are also config values that you can set:
|
|||||||
.. versionchanged:: 2.1
|
.. versionchanged:: 2.1
|
||||||
Added ``'imported-members'``.
|
Added ``'imported-members'``.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.1
|
||||||
|
Added ``'class-doc-from'``.
|
||||||
|
|
||||||
.. confval:: autodoc_docstring_signature
|
.. confval:: autodoc_docstring_signature
|
||||||
|
|
||||||
Functions imported from C modules cannot be introspected, and therefore the
|
Functions imported from C modules cannot be introspected, and therefore the
|
||||||
|
3
setup.py
3
setup.py
@ -21,7 +21,8 @@ install_requires = [
|
|||||||
'sphinxcontrib-htmlhelp',
|
'sphinxcontrib-htmlhelp',
|
||||||
'sphinxcontrib-serializinghtml',
|
'sphinxcontrib-serializinghtml',
|
||||||
'sphinxcontrib-qthelp',
|
'sphinxcontrib-qthelp',
|
||||||
'Jinja2>=2.3',
|
'Jinja2>=2.3,<3.0',
|
||||||
|
'MarkupSafe<2.0',
|
||||||
'Pygments>=2.0',
|
'Pygments>=2.0',
|
||||||
'docutils>=0.14,<0.18',
|
'docutils>=0.14,<0.18',
|
||||||
'snowballstemmer>=1.1',
|
'snowballstemmer>=1.1',
|
||||||
|
@ -129,6 +129,14 @@ def member_order_option(arg: Any) -> Optional[str]:
|
|||||||
raise ValueError(__('invalid value for member-order option: %s') % arg)
|
raise ValueError(__('invalid value for member-order option: %s') % arg)
|
||||||
|
|
||||||
|
|
||||||
|
def class_doc_from_option(arg: Any) -> Optional[str]:
|
||||||
|
"""Used to convert the :class-doc-from: option to autoclass directives."""
|
||||||
|
if arg in ('both', 'class', 'init'):
|
||||||
|
return arg
|
||||||
|
else:
|
||||||
|
raise ValueError(__('invalid value for class-doc-from option: %s') % arg)
|
||||||
|
|
||||||
|
|
||||||
SUPPRESS = object()
|
SUPPRESS = object()
|
||||||
|
|
||||||
|
|
||||||
@ -1320,12 +1328,12 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
if typ is object:
|
if typ is object:
|
||||||
pass # default implementation. skipped.
|
pass # default implementation. skipped.
|
||||||
else:
|
else:
|
||||||
self.annotate_to_first_argument(func, typ)
|
dispatchfunc = self.annotate_to_first_argument(func, typ)
|
||||||
|
if dispatchfunc:
|
||||||
documenter = FunctionDocumenter(self.directive, '')
|
documenter = FunctionDocumenter(self.directive, '')
|
||||||
documenter.object = func
|
documenter.object = dispatchfunc
|
||||||
documenter.objpath = [None]
|
documenter.objpath = [None]
|
||||||
sigs.append(documenter.format_signature())
|
sigs.append(documenter.format_signature())
|
||||||
if overloaded:
|
if overloaded:
|
||||||
actual = inspect.signature(self.object,
|
actual = inspect.signature(self.object,
|
||||||
type_aliases=self.config.autodoc_type_aliases)
|
type_aliases=self.config.autodoc_type_aliases)
|
||||||
@ -1350,28 +1358,34 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
|
|
||||||
return overload.replace(parameters=parameters)
|
return overload.replace(parameters=parameters)
|
||||||
|
|
||||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
def annotate_to_first_argument(self, func: Callable, typ: Type) -> Optional[Callable]:
|
||||||
"""Annotate type hint to the first argument of function if needed."""
|
"""Annotate type hint to the first argument of function if needed."""
|
||||||
try:
|
try:
|
||||||
sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
|
sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
|
||||||
except TypeError as exc:
|
except TypeError as exc:
|
||||||
logger.warning(__("Failed to get a function signature for %s: %s"),
|
logger.warning(__("Failed to get a function signature for %s: %s"),
|
||||||
self.fullname, exc)
|
self.fullname, exc)
|
||||||
return
|
return None
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return None
|
||||||
|
|
||||||
if len(sig.parameters) == 0:
|
if len(sig.parameters) == 0:
|
||||||
return
|
return None
|
||||||
|
|
||||||
|
def dummy():
|
||||||
|
pass
|
||||||
|
|
||||||
params = list(sig.parameters.values())
|
params = list(sig.parameters.values())
|
||||||
if params[0].annotation is Parameter.empty:
|
if params[0].annotation is Parameter.empty:
|
||||||
params[0] = params[0].replace(annotation=typ)
|
params[0] = params[0].replace(annotation=typ)
|
||||||
try:
|
try:
|
||||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
dummy.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||||
|
return dummy
|
||||||
except (AttributeError, TypeError):
|
except (AttributeError, TypeError):
|
||||||
# failed to update signature (ex. built-in or extension types)
|
# failed to update signature (ex. built-in or extension types)
|
||||||
return
|
return None
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DecoratorDocumenter(FunctionDocumenter):
|
class DecoratorDocumenter(FunctionDocumenter):
|
||||||
@ -1417,6 +1431,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
'show-inheritance': bool_option, 'member-order': member_order_option,
|
'show-inheritance': bool_option, 'member-order': member_order_option,
|
||||||
'exclude-members': exclude_members_option,
|
'exclude-members': exclude_members_option,
|
||||||
'private-members': members_option, 'special-members': members_option,
|
'private-members': members_option, 'special-members': members_option,
|
||||||
|
'class-doc-from': class_doc_from_option,
|
||||||
}
|
}
|
||||||
|
|
||||||
_signature_class: Any = None
|
_signature_class: Any = None
|
||||||
@ -1651,7 +1666,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
if lines is not None:
|
if lines is not None:
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
content = self.config.autoclass_content
|
classdoc_from = self.options.get('class-doc-from', self.config.autoclass_content)
|
||||||
|
|
||||||
docstrings = []
|
docstrings = []
|
||||||
attrdocstring = self.get_attr(self.object, '__doc__', None)
|
attrdocstring = self.get_attr(self.object, '__doc__', None)
|
||||||
@ -1660,7 +1675,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
|
|
||||||
# for classes, what the "docstring" is can be controlled via a
|
# for classes, what the "docstring" is can be controlled via a
|
||||||
# config value; the default is only the class docstring
|
# config value; the default is only the class docstring
|
||||||
if content in ('both', 'init'):
|
if classdoc_from in ('both', 'init'):
|
||||||
__init__ = self.get_attr(self.object, '__init__', None)
|
__init__ = self.get_attr(self.object, '__init__', None)
|
||||||
initdocstring = getdoc(__init__, self.get_attr,
|
initdocstring = getdoc(__init__, self.get_attr,
|
||||||
self.config.autodoc_inherit_docstrings,
|
self.config.autodoc_inherit_docstrings,
|
||||||
@ -1682,7 +1697,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
initdocstring.strip() == object.__new__.__doc__)): # for !pypy
|
initdocstring.strip() == object.__new__.__doc__)): # for !pypy
|
||||||
initdocstring = None
|
initdocstring = None
|
||||||
if initdocstring:
|
if initdocstring:
|
||||||
if content == 'init':
|
if classdoc_from == 'init':
|
||||||
docstrings = [initdocstring]
|
docstrings = [initdocstring]
|
||||||
else:
|
else:
|
||||||
docstrings.append(initdocstring)
|
docstrings.append(initdocstring)
|
||||||
@ -2109,13 +2124,13 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
if typ is object:
|
if typ is object:
|
||||||
pass # default implementation. skipped.
|
pass # default implementation. skipped.
|
||||||
else:
|
else:
|
||||||
self.annotate_to_first_argument(func, typ)
|
dispatchmeth = self.annotate_to_first_argument(func, typ)
|
||||||
|
if dispatchmeth:
|
||||||
documenter = MethodDocumenter(self.directive, '')
|
documenter = MethodDocumenter(self.directive, '')
|
||||||
documenter.parent = self.parent
|
documenter.parent = self.parent
|
||||||
documenter.object = func
|
documenter.object = dispatchmeth
|
||||||
documenter.objpath = [None]
|
documenter.objpath = [None]
|
||||||
sigs.append(documenter.format_signature())
|
sigs.append(documenter.format_signature())
|
||||||
if overloaded:
|
if overloaded:
|
||||||
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
||||||
actual = inspect.signature(self.object, bound_method=False,
|
actual = inspect.signature(self.object, bound_method=False,
|
||||||
@ -2149,27 +2164,34 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
|
|
||||||
return overload.replace(parameters=parameters)
|
return overload.replace(parameters=parameters)
|
||||||
|
|
||||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
def annotate_to_first_argument(self, func: Callable, typ: Type) -> Optional[Callable]:
|
||||||
"""Annotate type hint to the first argument of function if needed."""
|
"""Annotate type hint to the first argument of function if needed."""
|
||||||
try:
|
try:
|
||||||
sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
|
sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
|
||||||
except TypeError as exc:
|
except TypeError as exc:
|
||||||
logger.warning(__("Failed to get a method signature for %s: %s"),
|
logger.warning(__("Failed to get a method signature for %s: %s"),
|
||||||
self.fullname, exc)
|
self.fullname, exc)
|
||||||
return
|
return None
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return None
|
||||||
|
|
||||||
if len(sig.parameters) == 1:
|
if len(sig.parameters) == 1:
|
||||||
return
|
return None
|
||||||
|
|
||||||
|
def dummy():
|
||||||
|
pass
|
||||||
|
|
||||||
params = list(sig.parameters.values())
|
params = list(sig.parameters.values())
|
||||||
if params[1].annotation is Parameter.empty:
|
if params[1].annotation is Parameter.empty:
|
||||||
params[1] = params[1].replace(annotation=typ)
|
params[1] = params[1].replace(annotation=typ)
|
||||||
try:
|
try:
|
||||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
dummy.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||||
|
return dummy
|
||||||
except (AttributeError, TypeError):
|
except (AttributeError, TypeError):
|
||||||
# failed to update signature (ex. built-in or extension types)
|
# failed to update signature (ex. built-in or extension types)
|
||||||
return
|
return None
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class NonDataDescriptorMixin(DataDocumenterMixinBase):
|
class NonDataDescriptorMixin(DataDocumenterMixinBase):
|
||||||
|
@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
|
|||||||
AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
|
AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
|
||||||
'show-inheritance', 'private-members', 'special-members',
|
'show-inheritance', 'private-members', 'special-members',
|
||||||
'ignore-module-all', 'exclude-members', 'member-order',
|
'ignore-module-all', 'exclude-members', 'member-order',
|
||||||
'imported-members']
|
'imported-members', 'class-doc-from']
|
||||||
|
|
||||||
AUTODOC_EXTENDABLE_OPTIONS = ['members', 'private-members', 'special-members',
|
AUTODOC_EXTENDABLE_OPTIONS = ['members', 'private-members', 'special-members',
|
||||||
'exclude-members']
|
'exclude-members']
|
||||||
|
@ -15,6 +15,7 @@ def func(arg, kwarg=None):
|
|||||||
|
|
||||||
|
|
||||||
@func.register(int)
|
@func.register(int)
|
||||||
|
@func.register(float)
|
||||||
def _func_int(arg, kwarg=None):
|
def _func_int(arg, kwarg=None):
|
||||||
"""A function for int."""
|
"""A function for int."""
|
||||||
pass
|
pass
|
||||||
|
@ -10,6 +10,7 @@ class Foo:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@meth.register(int)
|
@meth.register(int)
|
||||||
|
@meth.register(float)
|
||||||
def _meth_int(self, arg, kwarg=None):
|
def _meth_int(self, arg, kwarg=None):
|
||||||
"""A method for int."""
|
"""A method for int."""
|
||||||
pass
|
pass
|
||||||
|
@ -2108,6 +2108,7 @@ def test_singledispatch(app):
|
|||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:function:: func(arg, kwarg=None)',
|
'.. py:function:: func(arg, kwarg=None)',
|
||||||
|
' func(arg: float, kwarg=None)',
|
||||||
' func(arg: int, kwarg=None)',
|
' func(arg: int, kwarg=None)',
|
||||||
' func(arg: str, kwarg=None)',
|
' func(arg: str, kwarg=None)',
|
||||||
' :module: target.singledispatch',
|
' :module: target.singledispatch',
|
||||||
@ -2135,6 +2136,7 @@ def test_singledispatchmethod(app):
|
|||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
' .. py:method:: Foo.meth(arg, kwarg=None)',
|
' .. py:method:: Foo.meth(arg, kwarg=None)',
|
||||||
|
' Foo.meth(arg: float, kwarg=None)',
|
||||||
' Foo.meth(arg: int, kwarg=None)',
|
' Foo.meth(arg: int, kwarg=None)',
|
||||||
' Foo.meth(arg: str, kwarg=None)',
|
' Foo.meth(arg: str, kwarg=None)',
|
||||||
' :module: target.singledispatchmethod',
|
' :module: target.singledispatchmethod',
|
||||||
@ -2153,6 +2155,7 @@ def test_singledispatchmethod_automethod(app):
|
|||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:method:: Foo.meth(arg, kwarg=None)',
|
'.. py:method:: Foo.meth(arg, kwarg=None)',
|
||||||
|
' Foo.meth(arg: float, kwarg=None)',
|
||||||
' Foo.meth(arg: int, kwarg=None)',
|
' Foo.meth(arg: int, kwarg=None)',
|
||||||
' Foo.meth(arg: str, kwarg=None)',
|
' Foo.meth(arg: str, kwarg=None)',
|
||||||
' :module: target.singledispatchmethod',
|
' :module: target.singledispatchmethod',
|
||||||
|
@ -264,6 +264,53 @@ def test_show_inheritance_for_subclass_of_generic_type(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_class_doc_from_class(app):
|
||||||
|
options = {"members": None,
|
||||||
|
"class-doc-from": "class"}
|
||||||
|
actual = do_autodoc(app, 'class', 'target.autoclass_content.C', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:class:: C()',
|
||||||
|
' :module: target.autoclass_content',
|
||||||
|
'',
|
||||||
|
' A class having __init__, no __new__',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_class_doc_from_init(app):
|
||||||
|
options = {"members": None,
|
||||||
|
"class-doc-from": "init"}
|
||||||
|
actual = do_autodoc(app, 'class', 'target.autoclass_content.C', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:class:: C()',
|
||||||
|
' :module: target.autoclass_content',
|
||||||
|
'',
|
||||||
|
' __init__ docstring',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_class_doc_from_both(app):
|
||||||
|
options = {"members": None,
|
||||||
|
"class-doc-from": "both"}
|
||||||
|
actual = do_autodoc(app, 'class', 'target.autoclass_content.C', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:class:: C()',
|
||||||
|
' :module: target.autoclass_content',
|
||||||
|
'',
|
||||||
|
' A class having __init__, no __new__',
|
||||||
|
'',
|
||||||
|
' __init__ docstring',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_class_alias(app):
|
def test_class_alias(app):
|
||||||
def autodoc_process_docstring(*args):
|
def autodoc_process_docstring(*args):
|
||||||
"""A handler always raises an error.
|
"""A handler always raises an error.
|
||||||
|
@ -119,6 +119,7 @@ def test_singledispatch(app):
|
|||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:function:: func(arg, kwarg=None)',
|
'.. py:function:: func(arg, kwarg=None)',
|
||||||
|
' func(arg: float, kwarg=None)',
|
||||||
' func(arg: int, kwarg=None)',
|
' func(arg: int, kwarg=None)',
|
||||||
' func(arg: str, kwarg=None)',
|
' func(arg: str, kwarg=None)',
|
||||||
' :module: target.singledispatch',
|
' :module: target.singledispatch',
|
||||||
|
Loading…
Reference in New Issue
Block a user