summaryrefslogtreecommitdiff
path: root/sphinx/ext/autodoc
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/ext/autodoc')
-rw-r--r--sphinx/ext/autodoc/__init__.py86
-rw-r--r--sphinx/ext/autodoc/directive.py2
2 files changed, 55 insertions, 33 deletions
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index c92709deb..ff6475c94 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -30,7 +30,7 @@ from sphinx.ext.autodoc.mock import ismock, mock, undecorate
from sphinx.locale import _, __
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import inspect, logging
-from sphinx.util.docstrings import extract_metadata, prepare_docstring
+from sphinx.util.docstrings import prepare_docstring, separate_metadata
from sphinx.util.inspect import (evaluate_signature, getdoc, object_description, safe_getattr,
stringify_signature)
from sphinx.util.typing import OptionSpec, get_type_hints, restify
@@ -129,6 +129,14 @@ def member_order_option(arg: Any) -> Optional[str]:
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()
@@ -722,9 +730,9 @@ class Documenter:
# hack for ClassDocumenter to inject docstring via ObjectMember
doc = obj.docstring
+ doc, metadata = separate_metadata(doc)
has_doc = bool(doc)
- metadata = extract_metadata(doc)
if 'private' in metadata:
# consider a member private if docstring has "private" metadata
isprivate = True
@@ -1320,12 +1328,12 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
if typ is object:
pass # default implementation. skipped.
else:
- self.annotate_to_first_argument(func, typ)
-
- documenter = FunctionDocumenter(self.directive, '')
- documenter.object = func
- documenter.objpath = [None]
- sigs.append(documenter.format_signature())
+ dispatchfunc = self.annotate_to_first_argument(func, typ)
+ if dispatchfunc:
+ documenter = FunctionDocumenter(self.directive, '')
+ documenter.object = dispatchfunc
+ documenter.objpath = [None]
+ sigs.append(documenter.format_signature())
if overloaded:
actual = inspect.signature(self.object,
type_aliases=self.config.autodoc_type_aliases)
@@ -1350,28 +1358,34 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
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."""
try:
sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
except TypeError as exc:
logger.warning(__("Failed to get a function signature for %s: %s"),
self.fullname, exc)
- return
+ return None
except ValueError:
- return
+ return None
if len(sig.parameters) == 0:
- return
+ return None
+
+ def dummy():
+ pass
params = list(sig.parameters.values())
if params[0].annotation is Parameter.empty:
params[0] = params[0].replace(annotation=typ)
try:
- func.__signature__ = sig.replace(parameters=params) # type: ignore
+ dummy.__signature__ = sig.replace(parameters=params) # type: ignore
+ return dummy
except (AttributeError, TypeError):
# failed to update signature (ex. built-in or extension types)
- return
+ return None
+ else:
+ return None
class DecoratorDocumenter(FunctionDocumenter):
@@ -1417,6 +1431,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
'show-inheritance': bool_option, 'member-order': member_order_option,
'exclude-members': exclude_members_option,
'private-members': members_option, 'special-members': members_option,
+ 'class-doc-from': class_doc_from_option,
}
_signature_class: Any = None
@@ -1651,7 +1666,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if lines is not None:
return lines
- content = self.config.autoclass_content
+ classdoc_from = self.options.get('class-doc-from', self.config.autoclass_content)
docstrings = []
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
# 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)
initdocstring = getdoc(__init__, self.get_attr,
self.config.autodoc_inherit_docstrings,
@@ -1682,7 +1697,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
initdocstring.strip() == object.__new__.__doc__)): # for !pypy
initdocstring = None
if initdocstring:
- if content == 'init':
+ if classdoc_from == 'init':
docstrings = [initdocstring]
else:
docstrings.append(initdocstring)
@@ -1918,7 +1933,7 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin,
return True
else:
doc = self.get_doc()
- metadata = extract_metadata('\n'.join(sum(doc, [])))
+ docstring, metadata = separate_metadata('\n'.join(sum(doc, [])))
if 'hide-value' in metadata:
return True
@@ -2109,13 +2124,13 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
if typ is object:
pass # default implementation. skipped.
else:
- self.annotate_to_first_argument(func, typ)
-
- documenter = MethodDocumenter(self.directive, '')
- documenter.parent = self.parent
- documenter.object = func
- documenter.objpath = [None]
- sigs.append(documenter.format_signature())
+ dispatchmeth = self.annotate_to_first_argument(func, typ)
+ if dispatchmeth:
+ documenter = MethodDocumenter(self.directive, '')
+ documenter.parent = self.parent
+ documenter.object = dispatchmeth
+ documenter.objpath = [None]
+ sigs.append(documenter.format_signature())
if overloaded:
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
actual = inspect.signature(self.object, bound_method=False,
@@ -2149,27 +2164,34 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
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."""
try:
sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
except TypeError as exc:
logger.warning(__("Failed to get a method signature for %s: %s"),
self.fullname, exc)
- return
+ return None
except ValueError:
- return
+ return None
+
if len(sig.parameters) == 1:
- return
+ return None
+
+ def dummy():
+ pass
params = list(sig.parameters.values())
if params[1].annotation is Parameter.empty:
params[1] = params[1].replace(annotation=typ)
try:
- func.__signature__ = sig.replace(parameters=params) # type: ignore
+ dummy.__signature__ = sig.replace(parameters=params) # type: ignore
+ return dummy
except (AttributeError, TypeError):
# failed to update signature (ex. built-in or extension types)
- return
+ return None
+ else:
+ return None
class NonDataDescriptorMixin(DataDocumenterMixinBase):
@@ -2456,7 +2478,7 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
else:
doc = self.get_doc()
if doc:
- metadata = extract_metadata('\n'.join(sum(doc, [])))
+ docstring, metadata = separate_metadata('\n'.join(sum(doc, [])))
if 'hide-value' in metadata:
return True
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index c58d0c411..a554adf68 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
'show-inheritance', 'private-members', 'special-members',
'ignore-module-all', 'exclude-members', 'member-order',
- 'imported-members']
+ 'imported-members', 'class-doc-from']
AUTODOC_EXTENDABLE_OPTIONS = ['members', 'private-members', 'special-members',
'exclude-members']