mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge tag 'v3.1.1'
This commit is contained in:
commit
fa9dc7c698
30
CHANGES
30
CHANGES
@ -36,6 +36,33 @@ Bugs fixed
|
||||
Testing
|
||||
--------
|
||||
|
||||
Release 3.1.1 (released Jun 14, 2020)
|
||||
=====================================
|
||||
|
||||
Incompatible changes
|
||||
--------------------
|
||||
|
||||
* #7808: napoleon: a type for attribute are represented as typed field
|
||||
|
||||
Features added
|
||||
--------------
|
||||
|
||||
* #7807: autodoc: Show detailed warning when type_comment is mismatched with its
|
||||
signature
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #7808: autodoc: Warnings raised on variable and attribute type annotations
|
||||
* #7802: autodoc: EOFError is raised on parallel build
|
||||
* #7821: autodoc: TypeError is raised for overloaded C-ext function
|
||||
* #7805: autodoc: an object which descriptors returns is unexpectedly documented
|
||||
* #7807: autodoc: wrong signature is shown for the function using contextmanager
|
||||
* #7812: autosummary: generates broken stub files if the target code contains
|
||||
an attribute and module that are same name
|
||||
* #7808: napoleon: Warnings raised on variable and attribute type annotations
|
||||
* #7811: sphinx.util.inspect causes circular import problem
|
||||
|
||||
Release 3.1.0 (released Jun 08, 2020)
|
||||
=====================================
|
||||
|
||||
@ -194,9 +221,6 @@ Bugs fixed
|
||||
* #7763: C and C++, don't crash during display stringification of unary
|
||||
expressions and fold expressions.
|
||||
|
||||
Release 3.0.5 (in development)
|
||||
==============================
|
||||
|
||||
Release 3.0.4 (released May 27, 2020)
|
||||
=====================================
|
||||
|
||||
|
@ -592,7 +592,8 @@ class PyVariable(PyObject):
|
||||
|
||||
typ = self.options.get('type')
|
||||
if typ:
|
||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), type_to_xref(typ))
|
||||
annotations = _parse_annotation(typ)
|
||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
|
||||
|
||||
value = self.options.get('value')
|
||||
if value:
|
||||
@ -752,7 +753,8 @@ class PyAttribute(PyObject):
|
||||
|
||||
typ = self.options.get('type')
|
||||
if typ:
|
||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), type_to_xref(typ))
|
||||
annotations = _parse_annotation(typ)
|
||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
|
||||
|
||||
value = self.options.get('value')
|
||||
if value:
|
||||
|
@ -421,9 +421,9 @@ class Documenter:
|
||||
if matched:
|
||||
args = matched.group(1)
|
||||
retann = matched.group(2)
|
||||
except Exception:
|
||||
logger.warning(__('error while formatting arguments for %s:') %
|
||||
self.fullname, type='autodoc', exc_info=True)
|
||||
except Exception as exc:
|
||||
logger.warning(__('error while formatting arguments for %s: %s'),
|
||||
self.fullname, exc, type='autodoc')
|
||||
args = None
|
||||
|
||||
result = self.env.events.emit_firstresult('autodoc-process-signature',
|
||||
@ -790,8 +790,8 @@ class Documenter:
|
||||
# parse right now, to get PycodeErrors on parsing (results will
|
||||
# be cached anyway)
|
||||
self.analyzer.find_attr_docs()
|
||||
except PycodeError:
|
||||
logger.debug('[autodoc] module analyzer failed:', exc_info=True)
|
||||
except PycodeError as exc:
|
||||
logger.debug('[autodoc] module analyzer failed: %s', exc)
|
||||
# no source file -- e.g. for builtin and C modules
|
||||
self.analyzer = None
|
||||
# at least add the module.__file__ as a dependency
|
||||
@ -1223,7 +1223,11 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
params = list(sig.parameters.values())
|
||||
if params[0].annotation is Parameter.empty:
|
||||
params[0] = params[0].replace(annotation=typ)
|
||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||
try:
|
||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||
except TypeError:
|
||||
# failed to update signature (ex. built-in or extension types)
|
||||
return
|
||||
|
||||
|
||||
class SingledispatchFunctionDocumenter(FunctionDocumenter):
|
||||
@ -1815,7 +1819,11 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
params = list(sig.parameters.values())
|
||||
if params[1].annotation is Parameter.empty:
|
||||
params[1] = params[1].replace(annotation=typ)
|
||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||
try:
|
||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||
except TypeError:
|
||||
# failed to update signature (ex. built-in or extension types)
|
||||
return
|
||||
|
||||
|
||||
class SingledispatchMethodDocumenter(MethodDocumenter):
|
||||
@ -1903,6 +1911,17 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
||||
else:
|
||||
self.add_line(' :annotation: %s' % self.options.annotation, sourcename)
|
||||
|
||||
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
|
||||
try:
|
||||
# Disable `autodoc_inherit_docstring` temporarily to avoid to obtain
|
||||
# a docstring from the value which descriptor returns unexpectedly.
|
||||
# ref: https://github.com/sphinx-doc/sphinx/issues/7805
|
||||
orig = self.env.config.autodoc_inherit_docstrings
|
||||
self.env.config.autodoc_inherit_docstrings = False # type: ignore
|
||||
return super().get_doc(encoding, ignore)
|
||||
finally:
|
||||
self.env.config.autodoc_inherit_docstrings = orig # type: ignore
|
||||
|
||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||
if not self._datadescriptor:
|
||||
# if it's not a data descriptor, its docstring is very probably the
|
||||
|
@ -128,6 +128,9 @@ def update_annotations_using_type_comments(app: Sphinx, obj: Any, bound_method:
|
||||
|
||||
if 'return' not in obj.__annotations__:
|
||||
obj.__annotations__['return'] = type_sig.return_annotation
|
||||
except KeyError as exc:
|
||||
logger.warning(__("Failed to update signature for %r: parameter not found: %s"),
|
||||
obj, exc)
|
||||
except NotImplementedError as exc: # failed to ast.unparse()
|
||||
logger.warning(__("Failed to parse type_comment for %r: %s"), obj, exc)
|
||||
|
||||
|
@ -168,10 +168,11 @@ class Config:
|
||||
**If False**::
|
||||
|
||||
.. attribute:: attr1
|
||||
:type: int
|
||||
|
||||
Description of `attr1`
|
||||
|
||||
:type: int
|
||||
|
||||
napoleon_use_param : :obj:`bool` (Defaults to True)
|
||||
True to use a ``:param:`` role for each function parameter. False to
|
||||
use a single ``:parameters:`` role for all the parameters.
|
||||
|
@ -584,12 +584,13 @@ class GoogleDocstring:
|
||||
lines.append('.. attribute:: ' + _name)
|
||||
if self._opt and 'noindex' in self._opt:
|
||||
lines.append(' :noindex:')
|
||||
if _type:
|
||||
lines.extend(self._indent([':type: %s' % _type], 3))
|
||||
lines.append('')
|
||||
|
||||
fields = self._format_field('', '', _desc)
|
||||
lines.extend(self._indent(fields, 3))
|
||||
if _type:
|
||||
lines.append('')
|
||||
lines.extend(self._indent([':type: %s' % _type], 3))
|
||||
lines.append('')
|
||||
if self._config.napoleon_use_ivar:
|
||||
lines.append('')
|
||||
|
@ -19,7 +19,6 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from sphinx.pycode.ast import ast # for py37 or older
|
||||
from sphinx.pycode.ast import parse, unparse
|
||||
from sphinx.util.inspect import signature_from_ast
|
||||
|
||||
|
||||
comment_re = re.compile('^\\s*#: ?(.*)\r?\n?$')
|
||||
@ -265,6 +264,8 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
self.finals.append(".".join(qualname))
|
||||
|
||||
def add_overload_entry(self, func: ast.FunctionDef) -> None:
|
||||
# avoid circular import problem
|
||||
from sphinx.util.inspect import signature_from_ast
|
||||
qualname = self.get_qualname_for(func.name)
|
||||
if qualname:
|
||||
overloads = self.overloads.setdefault(".".join(qualname), [])
|
||||
|
@ -425,13 +425,28 @@ def split_full_qualified_name(name: str) -> Tuple[str, str]:
|
||||
Therefore you need to mock 3rd party modules if needed before
|
||||
calling this function.
|
||||
"""
|
||||
from sphinx.util import inspect
|
||||
|
||||
parts = name.split('.')
|
||||
for i, part in enumerate(parts, 1):
|
||||
try:
|
||||
modname = ".".join(parts[:i])
|
||||
import_module(modname)
|
||||
module = import_module(modname)
|
||||
|
||||
# check the module has a member named as attrname
|
||||
#
|
||||
# Note: This is needed to detect the attribute having the same name
|
||||
# as the module.
|
||||
# ref: https://github.com/sphinx-doc/sphinx/issues/7812
|
||||
attrname = parts[i]
|
||||
if hasattr(module, attrname):
|
||||
value = inspect.safe_getattr(module, attrname)
|
||||
if not inspect.ismodule(value):
|
||||
return ".".join(parts[:i]), ".".join(parts[i:])
|
||||
except ImportError:
|
||||
return ".".join(parts[:i - 1]), ".".join(parts[i - 1:])
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return name, ""
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
"""
|
||||
|
||||
import builtins
|
||||
import contextlib
|
||||
import enum
|
||||
import inspect
|
||||
import re
|
||||
@ -18,7 +19,7 @@ import typing
|
||||
import warnings
|
||||
from functools import partial, partialmethod
|
||||
from inspect import ( # NOQA
|
||||
Parameter, isclass, ismethod, ismethoddescriptor
|
||||
Parameter, isclass, ismethod, ismethoddescriptor, ismodule
|
||||
)
|
||||
from io import StringIO
|
||||
from typing import Any, Callable
|
||||
@ -404,6 +405,17 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
|
||||
return getattr(builtins, name, None) is cls
|
||||
|
||||
|
||||
def _should_unwrap(subject: Callable) -> bool:
|
||||
"""Check the function should be unwrapped on getting signature."""
|
||||
if (safe_getattr(subject, '__globals__', None) and
|
||||
subject.__globals__.get('__name__') == 'contextlib' and # type: ignore
|
||||
subject.__globals__.get('__file__') == contextlib.__file__): # type: ignore
|
||||
# contextmanger should be unwrapped
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = False
|
||||
) -> inspect.Signature:
|
||||
"""Return a Signature object for the given *subject*.
|
||||
@ -414,7 +426,10 @@ def signature(subject: Callable, bound_method: bool = False, follow_wrapped: boo
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
signature = inspect.signature(subject, follow_wrapped=follow_wrapped)
|
||||
if _should_unwrap(subject):
|
||||
signature = inspect.signature(subject)
|
||||
else:
|
||||
signature = inspect.signature(subject, follow_wrapped=follow_wrapped)
|
||||
except ValueError:
|
||||
# follow built-in wrappers up (ex. functools.lru_cache)
|
||||
signature = inspect.signature(subject)
|
||||
|
@ -27,7 +27,7 @@ if TYPE_CHECKING:
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.domain import IndexEntry
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.utils.tags import Tags
|
||||
from sphinx.util.tags import Tags
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -1,8 +1,15 @@
|
||||
# for py32 or above
|
||||
from contextlib import contextmanager
|
||||
from functools import lru_cache
|
||||
from typing import Generator
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def slow_function(message, timeout):
|
||||
"""This function is slow."""
|
||||
print(message)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def feeling_good(x: int, y: int) -> Generator:
|
||||
"""You'll feel better in this context!"""
|
||||
yield
|
||||
|
@ -681,7 +681,7 @@ def test_pyattribute(app):
|
||||
text = (".. py:class:: Class\n"
|
||||
"\n"
|
||||
" .. py:attribute:: attr\n"
|
||||
" :type: str\n"
|
||||
" :type: Optional[str]\n"
|
||||
" :value: ''\n")
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
@ -694,7 +694,10 @@ def test_pyattribute(app):
|
||||
entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
|
||||
[desc_annotation, (": ",
|
||||
[pending_xref, "str"])],
|
||||
[pending_xref, "Optional"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "str"],
|
||||
[desc_sig_punctuation, "]"])],
|
||||
[desc_annotation, " = ''"])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.attr' in domain.objects
|
||||
|
@ -146,3 +146,16 @@ def test_wrapped_function(app):
|
||||
' This function is slow.',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_wrapped_function_contextmanager(app):
|
||||
actual = do_autodoc(app, 'function', 'target.wrappedfunction.feeling_good')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: feeling_good(x: int, y: int) -> Generator',
|
||||
' :module: target.wrappedfunction',
|
||||
'',
|
||||
" You'll feel better in this context!",
|
||||
'',
|
||||
]
|
||||
|
@ -53,19 +53,22 @@ class NamedtupleSubclassTest(BaseDocstringTest):
|
||||
Sample namedtuple subclass
|
||||
|
||||
.. attribute:: attr1
|
||||
:type: Arbitrary type
|
||||
|
||||
Quick description of attr1
|
||||
|
||||
:type: Arbitrary type
|
||||
|
||||
.. attribute:: attr2
|
||||
:type: Another arbitrary type
|
||||
|
||||
Quick description of attr2
|
||||
|
||||
:type: Another arbitrary type
|
||||
|
||||
.. attribute:: attr3
|
||||
:type: Type
|
||||
|
||||
Adds a newline after the type
|
||||
|
||||
:type: Type
|
||||
"""
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
@ -409,9 +412,10 @@ Attributes:
|
||||
actual = str(GoogleDocstring(docstring))
|
||||
expected = """\
|
||||
.. attribute:: in_attr
|
||||
:type: :class:`numpy.ndarray`
|
||||
|
||||
super-dooper attribute
|
||||
|
||||
:type: :class:`numpy.ndarray`
|
||||
"""
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
@ -423,9 +427,10 @@ Attributes:
|
||||
actual = str(GoogleDocstring(docstring))
|
||||
expected = """\
|
||||
.. attribute:: in_attr
|
||||
:type: numpy.ndarray
|
||||
|
||||
super-dooper attribute
|
||||
|
||||
:type: numpy.ndarray
|
||||
"""
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user