summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rw-r--r--doc/extdev/deprecated.rst15
-rw-r--r--doc/usage/restructuredtext/roles.rst4
-rw-r--r--sphinx/application.py10
-rw-r--r--sphinx/builders/gettext.py4
-rw-r--r--sphinx/builders/html.py10
-rw-r--r--sphinx/builders/singlehtml.py8
-rw-r--r--sphinx/domains/std.py20
-rw-r--r--sphinx/environment/adapters/toctree.py14
-rw-r--r--sphinx/ext/autodoc/__init__.py25
-rw-r--r--sphinx/ext/autodoc/mock.py2
-rw-r--r--sphinx/ext/imgconverter.py1
-rw-r--r--sphinx/ext/napoleon/docstring.py4
-rw-r--r--sphinx/testing/util.py8
-rw-r--r--sphinx/transforms/post_transforms/images.py1
-rw-r--r--sphinx/util/images.py1
-rw-r--r--sphinx/util/inspect.py115
-rw-r--r--sphinx/util/jsonimpl.py20
-rw-r--r--tests/test_autodoc.py9
-rw-r--r--tests/test_ext_napoleon_docstring.py3
-rw-r--r--tests/test_util_inspect.py170
21 files changed, 273 insertions, 177 deletions
diff --git a/CHANGES b/CHANGES
index 95a2e1906..bdfcccea6 100644
--- a/CHANGES
+++ b/CHANGES
@@ -61,9 +61,7 @@ Deprecated
* ``sphinx.roles.Index``
* ``sphinx.util.detect_encoding()``
* ``sphinx.util.get_module_source()``
-* ``sphinx.util.inspect.Signature.format_annotation()``
-* ``sphinx.util.inspect.Signature.format_annotation_new()``
-* ``sphinx.util.inspect.Signature.format_annotation_old()``
+* ``sphinx.util.inspect.Signature``
Features added
--------------
@@ -75,6 +73,7 @@ Features added
* #6966: graphviz: Support ``:class:`` option
* #6696: html: ``:scale:`` option of image/figure directive not working for SVG
images (imagesize-1.2.0 or above is required)
+* #6994: imgconverter: Support illustrator file (.ai) to .png conversion
Bugs fixed
----------
@@ -84,6 +83,7 @@ Bugs fixed
* #6961: latex: warning for babel shown twice
* #6559: Wrong node-ids are generated in glossary directive
* #6986: apidoc: misdetects module name for .so file inside module
+* #6999: napoleon: fails to parse tilde in :exc: role
Testing
--------
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index d91ea9308..f6ebc3916 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -86,20 +86,11 @@ The following is a list of deprecated interfaces.
- 4.0
- N/A
- * - ``sphinx.util.inspect.Signature.format_annotation()``
+ * - ``sphinx.util.inspect.Signature``
- 2.4
- 4.0
- - ``sphinx.util.typing.stringify()``
-
- * - ``sphinx.util.inspect.Signature.format_annotation_new()``
- - 2.4
- - 4.0
- - ``sphinx.util.typing.stringify()``
-
- * - ``sphinx.util.inspect.Signature.format_annotation_old()``
- - 2.4
- - 4.0
- - ``sphinx.util.typing.stringify()``
+ - ``sphinx.util.inspect.signature`` and
+ ``sphinx.util.inspect.stringify_signature()``
* - ``sphinx.builders.gettext.POHEADER``
- 2.3
diff --git a/doc/usage/restructuredtext/roles.rst b/doc/usage/restructuredtext/roles.rst
index 637df711b..de12a41b5 100644
--- a/doc/usage/restructuredtext/roles.rst
+++ b/doc/usage/restructuredtext/roles.rst
@@ -189,8 +189,8 @@ Referencing downloadable files
When you use this role, the referenced file is automatically marked for
inclusion in the output when building (obviously, for HTML output only).
- All downloadable files are put into the ``_downloads`` subdirectory of the
- output directory; duplicate filenames are handled.
+ All downloadable files are put into a ``_downloads/<unique hash>/``
+ subdirectory of the output directory; duplicate filenames are handled.
An example::
diff --git a/sphinx/application.py b/sphinx/application.py
index 0fd95ba91..515d962dc 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -509,7 +509,7 @@ class Sphinx:
self.registry.add_translator(name, translator_class, override=override)
def add_node(self, node: "Type[Element]", override: bool = False,
- **kwds: Tuple[Callable, Callable]) -> None:
+ **kwargs: Tuple[Callable, Callable]) -> None:
"""Register a Docutils node class.
This is necessary for Docutils internals. It may also be used in the
@@ -539,17 +539,17 @@ class Sphinx:
.. versionchanged:: 0.5
Added the support for keyword arguments giving visit functions.
"""
- logger.debug('[app] adding node: %r', (node, kwds))
+ logger.debug('[app] adding node: %r', (node, kwargs))
if not override and docutils.is_node_registered(node):
logger.warning(__('node class %r is already registered, '
'its visitors will be overridden'),
node.__name__, type='app', subtype='add_node')
docutils.register_node(node)
- self.registry.add_translation_handlers(node, **kwds)
+ self.registry.add_translation_handlers(node, **kwargs)
def add_enumerable_node(self, node: "Type[Element]", figtype: str,
title_getter: TitleGetter = None, override: bool = False,
- **kwds: Tuple[Callable, Callable]) -> None:
+ **kwargs: Tuple[Callable, Callable]) -> None:
"""Register a Docutils node class as a numfig target.
Sphinx numbers the node automatically. And then the users can refer it
@@ -574,7 +574,7 @@ class Sphinx:
.. versionadded:: 1.4
"""
self.registry.add_enumerable_node(node, figtype, title_getter, override=override)
- self.add_node(node, override=override, **kwds)
+ self.add_node(node, override=override, **kwargs)
def add_directive(self, name: str, cls: "Type[Directive]", override: bool = False) -> None:
"""Register a Docutils directive.
diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py
index f5264837c..638408503 100644
--- a/sphinx/builders/gettext.py
+++ b/sphinx/builders/gettext.py
@@ -198,8 +198,8 @@ if source_date_epoch is not None:
class LocalTimeZone(tzinfo):
- def __init__(self, *args: Any, **kw: Any) -> None:
- super().__init__(*args, **kw) # type: ignore
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
+ super().__init__(*args, **kwargs) # type: ignore
self.tzdelta = tzdelta
def utcoffset(self, dt: datetime) -> timedelta:
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index f76cf5ce5..80c99d3b8 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -858,11 +858,11 @@ class StandaloneHTMLBuilder(Builder):
indexer_name, indexer_name),
RemovedInSphinx40Warning)
- def _get_local_toctree(self, docname: str, collapse: bool = True, **kwds: Any) -> str:
- if 'includehidden' not in kwds:
- kwds['includehidden'] = False
+ def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
+ if 'includehidden' not in kwargs:
+ kwargs['includehidden'] = False
return self.render_partial(TocTree(self.env).get_toctree_for(
- docname, self, collapse, **kwds))['fragment']
+ docname, self, collapse, **kwargs))['fragment']
def get_outfilename(self, pagename: str) -> str:
return path.join(self.outdir, os_path(pagename) + self.out_suffix)
@@ -971,7 +971,7 @@ class StandaloneHTMLBuilder(Builder):
return False
ctx['hasdoc'] = hasdoc
- ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw)
+ ctx['toctree'] = lambda **kwargs: self._get_local_toctree(pagename, **kwargs)
self.add_sidebars(pagename, ctx)
ctx.update(addctx)
diff --git a/sphinx/builders/singlehtml.py b/sphinx/builders/singlehtml.py
index 1c47596b8..b145109a6 100644
--- a/sphinx/builders/singlehtml.py
+++ b/sphinx/builders/singlehtml.py
@@ -67,10 +67,10 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
if hashindex >= 0:
refnode['refuri'] = fname + refuri[hashindex:]
- def _get_local_toctree(self, docname: str, collapse: bool = True, **kwds: Any) -> str:
- if 'includehidden' not in kwds:
- kwds['includehidden'] = False
- toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwds)
+ def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
+ if 'includehidden' not in kwargs:
+ kwargs['includehidden'] = False
+ toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwargs)
if toctree is not None:
self.fix_refuris(toctree)
return self.render_partial(toctree)['fragment']
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 9e7dd2353..65ebf070e 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -128,7 +128,7 @@ class Target(SphinxDirective):
targetname = '%s-%s' % (self.name, fullname)
node = nodes.target('', '', ids=[targetname])
self.state.document.note_explicit_target(node)
- ret = [node] # type: List[nodes.Node]
+ ret = [node] # type: List[Node]
if self.indextemplate:
indexentry = self.indextemplate % (fullname,)
indextype = 'single'
@@ -254,7 +254,7 @@ def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index
if node_id:
# node_id is given from outside (mainly i18n module), use it forcedly
- pass
+ term['ids'].append(node_id)
elif document:
node_id = make_id(env, document, 'term', termtext)
term['ids'].append(node_id)
@@ -313,7 +313,7 @@ class Glossary(SphinxDirective):
in_definition = True
in_comment = False
was_empty = True
- messages = [] # type: List[nodes.Node]
+ messages = [] # type: List[Node]
for line, (source, lineno) in zip(self.content, self.content.items):
# empty line -> add to last definition
if not line:
@@ -369,8 +369,8 @@ class Glossary(SphinxDirective):
items = []
for terms, definition in entries:
termtexts = [] # type: List[str]
- termnodes = [] # type: List[nodes.Node]
- system_messages = [] # type: List[nodes.Node]
+ termnodes = [] # type: List[Node]
+ system_messages = [] # type: List[Node]
for line, source, lineno in terms:
parts = split_term_classifiers(line)
# parse the term with inline markup
@@ -407,7 +407,7 @@ class Glossary(SphinxDirective):
def token_xrefs(text: str) -> List[Node]:
- retnodes = [] # type: List[nodes.Node]
+ retnodes = [] # type: List[Node]
pos = 0
for m in token_re.finditer(text):
if m.start() > pos:
@@ -436,7 +436,7 @@ class ProductionList(SphinxDirective):
def run(self) -> List[Node]:
domain = cast(StandardDomain, self.env.get_domain('std'))
- node = addnodes.productionlist() # type: nodes.Element
+ node = addnodes.productionlist() # type: Element
i = 0
for rule in self.arguments[0].split('\n'):
@@ -538,7 +538,7 @@ class StandardDomain(Domain):
nodes.figure: ('figure', None),
nodes.table: ('table', None),
nodes.container: ('code-block', None),
- } # type: Dict[Type[nodes.Node], Tuple[str, Callable]]
+ } # type: Dict[Type[Node], Tuple[str, Callable]]
def __init__(self, env: "BuildEnvironment") -> None:
super().__init__(env)
@@ -847,7 +847,7 @@ class StandardDomain(Domain):
def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str,
builder: "Builder", target: str, node: pending_xref,
contnode: Element) -> List[Tuple[str, Element]]:
- results = [] # type: List[Tuple[str, nodes.Element]]
+ results = [] # type: List[Tuple[str, Element]]
ltarget = target.lower() # :ref: lowercases its target automatically
for role in ('ref', 'option'): # do not try "keyword"
res = self.resolve_xref(env, fromdocname, builder, role,
@@ -898,7 +898,7 @@ class StandardDomain(Domain):
def get_numfig_title(self, node: Node) -> str:
"""Get the title of enumerable nodes to refer them using its title"""
if self.is_enumerable_node(node):
- elem = cast(nodes.Element, node)
+ elem = cast(Element, node)
_, title_getter = self.enumerable_nodes.get(elem.__class__, (None, None))
if title_getter:
return title_getter(elem)
diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py
index fe8f43656..bd3abd9ed 100644
--- a/sphinx/environment/adapters/toctree.py
+++ b/sphinx/environment/adapters/toctree.py
@@ -315,17 +315,17 @@ class TocTree:
return toc
def get_toctree_for(self, docname: str, builder: "Builder", collapse: bool,
- **kwds: Any) -> Element:
+ **kwargs: Any) -> Element:
"""Return the global TOC nodetree."""
doctree = self.env.get_doctree(self.env.config.master_doc)
toctrees = [] # type: List[Element]
- if 'includehidden' not in kwds:
- kwds['includehidden'] = True
- if 'maxdepth' not in kwds:
- kwds['maxdepth'] = 0
- kwds['collapse'] = collapse
+ if 'includehidden' not in kwargs:
+ kwargs['includehidden'] = True
+ if 'maxdepth' not in kwargs:
+ kwargs['maxdepth'] = 0
+ kwargs['collapse'] = collapse
for toctreenode in doctree.traverse(addnodes.toctree):
- toctree = self.resolve(docname, builder, toctreenode, prune=True, **kwds)
+ toctree = self.resolve(docname, builder, toctreenode, prune=True, **kwargs)
if toctree:
toctrees.append(toctree)
if not toctrees:
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index ea6a235c9..1a00fb43f 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -31,7 +31,7 @@ from sphinx.util import logging
from sphinx.util import rpartition
from sphinx.util.docstrings import prepare_docstring
from sphinx.util.inspect import (
- Signature, getdoc, object_description, safe_getattr, safe_getmembers
+ getdoc, object_description, safe_getattr, safe_getmembers, stringify_signature
)
if False:
@@ -1006,9 +1006,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
not inspect.isbuiltin(self.object) and
not inspect.isclass(self.object) and
hasattr(self.object, '__call__')):
- args = Signature(self.object.__call__).format_args(**kwargs)
+ sig = inspect.signature(self.object.__call__)
else:
- args = Signature(self.object).format_args(**kwargs)
+ sig = inspect.signature(self.object)
+ args = stringify_signature(sig, **kwargs)
except TypeError:
if (inspect.is_builtin_class_method(self.object, '__new__') and
inspect.is_builtin_class_method(self.object, '__init__')):
@@ -1018,11 +1019,11 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
# typing) we try to use the constructor signature as function
# signature without the first argument.
try:
- sig = Signature(self.object.__new__, bound_method=True, has_retval=False)
- args = sig.format_args(**kwargs)
+ sig = inspect.signature(self.object.__new__, bound_method=True)
+ args = stringify_signature(sig, show_return_annotation=False, **kwargs)
except TypeError:
- sig = Signature(self.object.__init__, bound_method=True, has_retval=False)
- args = sig.format_args(**kwargs)
+ sig = inspect.signature(self.object.__init__, bound_method=True)
+ args = stringify_signature(sig, show_return_annotation=False, **kwargs)
# escape backslashes for reST
args = args.replace('\\', '\\\\')
@@ -1103,8 +1104,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
return None
try:
- sig = Signature(initmeth, bound_method=True, has_retval=False)
- return sig.format_args(**kwargs)
+ sig = inspect.signature(initmeth, bound_method=True)
+ return stringify_signature(sig, show_return_annotation=False, **kwargs)
except TypeError:
# still not possible: happens e.g. for old-style classes
# with __init__ in C
@@ -1306,9 +1307,11 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
# can never get arguments of a C function or method
return None
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
- args = Signature(self.object, bound_method=False).format_args(**kwargs)
+ sig = inspect.signature(self.object, bound_method=False)
else:
- args = Signature(self.object, bound_method=True).format_args(**kwargs)
+ sig = inspect.signature(self.object, bound_method=True)
+ args = stringify_signature(sig, **kwargs)
+
# escape backslashes for reST
args = args.replace('\\', '\\\\')
return args
diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py
index 169034664..25f50d27e 100644
--- a/sphinx/ext/autodoc/mock.py
+++ b/sphinx/ext/autodoc/mock.py
@@ -57,7 +57,7 @@ class _MockObject:
def __getattr__(self, key: str) -> "_MockObject":
return _make_subclass(key, self.__display_name__, self.__class__)()
- def __call__(self, *args: Any, **kw: Any) -> Any:
+ def __call__(self, *args: Any, **kwargs: Any) -> Any:
if args and type(args[0]) in [type, FunctionType, MethodType]:
# Appears to be a decorator, pass through unchanged
return args[0]
diff --git a/sphinx/ext/imgconverter.py b/sphinx/ext/imgconverter.py
index b95ef2588..95d3fe65a 100644
--- a/sphinx/ext/imgconverter.py
+++ b/sphinx/ext/imgconverter.py
@@ -27,6 +27,7 @@ class ImagemagickConverter(ImageConverter):
('image/svg+xml', 'image/png'),
('image/gif', 'image/png'),
('application/pdf', 'image/png'),
+ ('application/illustrator', 'image/png'),
]
def is_available(self) -> bool:
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
index a06a79cea..81f2496cb 100644
--- a/sphinx/ext/napoleon/docstring.py
+++ b/sphinx/ext/napoleon/docstring.py
@@ -101,8 +101,8 @@ class GoogleDocstring:
"""
- _name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>[a-zA-Z0-9_.-]+)`|"
- r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X)
+ _name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>~?[a-zA-Z0-9_.-]+)`|"
+ r" (?P<name2>~?[a-zA-Z0-9_.-]+))\s*", re.X)
def __init__(self, docstring: Union[str, List[str]], config: SphinxConfig = None,
app: Sphinx = None, what: str = '', name: str = '',
diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py
index a031e2523..f4ef35d61 100644
--- a/sphinx/testing/util.py
+++ b/sphinx/testing/util.py
@@ -92,8 +92,8 @@ def etree_parse(path: str) -> Any:
class Struct:
- def __init__(self, **kwds: Any) -> None:
- self.__dict__.update(kwds)
+ def __init__(self, **kwargs: Any) -> None:
+ self.__dict__.update(kwargs)
class SphinxTestApp(application.Sphinx):
@@ -165,10 +165,10 @@ class SphinxTestAppWrapperForSkipBuilding:
def __getattr__(self, name: str) -> Any:
return getattr(self.app, name)
- def build(self, *args: Any, **kw: Any) -> None:
+ def build(self, *args: Any, **kwargs: Any) -> None:
if not self.app.outdir.listdir(): # type: ignore
# if listdir is empty, do build.
- self.app.build(*args, **kw)
+ self.app.build(*args, **kwargs)
# otherwise, we can use built cache
diff --git a/sphinx/transforms/post_transforms/images.py b/sphinx/transforms/post_transforms/images.py
index 6f51bc8e0..758e92f0d 100644
--- a/sphinx/transforms/post_transforms/images.py
+++ b/sphinx/transforms/post_transforms/images.py
@@ -222,7 +222,6 @@ class ImageConverter(BaseImageConverter):
if '?' in node['candidates']:
return []
elif '*' in node['candidates']:
- from sphinx.util.images import guess_mimetype
return [guess_mimetype(node['uri'])]
else:
return node['candidates'].keys()
diff --git a/sphinx/util/images.py b/sphinx/util/images.py
index b9065838b..17bd95685 100644
--- a/sphinx/util/images.py
+++ b/sphinx/util/images.py
@@ -28,6 +28,7 @@ mime_suffixes = OrderedDict([
('.pdf', 'application/pdf'),
('.svg', 'image/svg+xml'),
('.svgz', 'image/svg+xml'),
+ ('.ai', 'application/illustrator'),
])
DataURI = NamedTuple('DataURI', [('mimetype', str),
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index f199e6748..60745be61 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -315,6 +315,112 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls
+def signature(subject: Callable, bound_method: bool = False) -> inspect.Signature:
+ """Return a Signature object for the given *subject*.
+
+ :param bound_method: Specify *subject* is a bound method or not
+ """
+ # check subject is not a built-in class (ex. int, str)
+ if (isinstance(subject, type) and
+ is_builtin_class_method(subject, "__new__") and
+ is_builtin_class_method(subject, "__init__")):
+ raise TypeError("can't compute signature for built-in type {}".format(subject))
+
+ try:
+ signature = inspect.signature(subject)
+ parameters = list(signature.parameters.values())
+ return_annotation = signature.return_annotation
+ except IndexError:
+ # Until python 3.6.4, cpython has been crashed on inspection for
+ # partialmethods not having any arguments.
+ # https://bugs.python.org/issue33009
+ if hasattr(subject, '_partialmethod'):
+ parameters = []
+ return_annotation = inspect.Parameter.empty
+ else:
+ raise
+
+ try:
+ # Update unresolved annotations using ``get_type_hints()``.
+ annotations = typing.get_type_hints(subject)
+ for i, param in enumerate(parameters):
+ if isinstance(param.annotation, str) and param.name in annotations:
+ parameters[i] = param.replace(annotation=annotations[param.name])
+ if 'return' in annotations:
+ return_annotation = annotations['return']
+ except Exception:
+ # ``get_type_hints()`` does not support some kind of objects like partial,
+ # ForwardRef and so on.
+ pass
+
+ if bound_method:
+ if inspect.ismethod(subject):
+ # ``inspect.signature()`` considers the subject is a bound method and removes
+ # first argument from signature. Therefore no skips are needed here.
+ pass
+ else:
+ if len(parameters) > 0:
+ parameters.pop(0)
+
+ return inspect.Signature(parameters, return_annotation=return_annotation)
+
+
+def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
+ show_return_annotation: bool = True) -> str:
+ """Stringify a Signature object.
+
+ :param show_annotation: Show annotation in result
+ """
+ args = []
+ last_kind = None
+ for param in sig.parameters.values():
+ # insert '*' between POSITIONAL args and KEYWORD_ONLY args::
+ # func(a, b, *, c, d):
+ if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
+ param.POSITIONAL_ONLY,
+ None):
+ args.append('*')
+
+ arg = StringIO()
+ if param.kind in (param.POSITIONAL_ONLY,
+ param.POSITIONAL_OR_KEYWORD,
+ param.KEYWORD_ONLY):
+ arg.write(param.name)
+ if show_annotation and param.annotation is not param.empty:
+ arg.write(': ')
+ arg.write(stringify_annotation(param.annotation))
+ if param.default is not param.empty:
+ if show_annotation and param.annotation is not param.empty:
+ arg.write(' = ')
+ arg.write(object_description(param.default))
+ else:
+ arg.write('=')
+ arg.write(object_description(param.default))
+ elif param.kind == param.VAR_POSITIONAL:
+ arg.write('*')
+ arg.write(param.name)
+ if show_annotation and param.annotation is not param.empty:
+ arg.write(': ')
+ arg.write(stringify_annotation(param.annotation))
+ elif param.kind == param.VAR_KEYWORD:
+ arg.write('**')
+ arg.write(param.name)
+ if show_annotation and param.annotation is not param.empty:
+ arg.write(': ')
+ arg.write(stringify_annotation(param.annotation))
+
+ args.append(arg.getvalue())
+ last_kind = param.kind
+
+ if (sig.return_annotation is inspect.Parameter.empty or
+ show_annotation is False or
+ show_return_annotation is False):
+ return '(%s)' % ', '.join(args)
+ else:
+ annotation = stringify_annotation(sig.return_annotation)
+ return '(%s) -> %s' % (', '.join(args), annotation)
+
+
class Signature:
"""The Signature object represents the call signature of a callable object and
its return annotation.
@@ -322,6 +428,9 @@ class Signature:
def __init__(self, subject: Callable, bound_method: bool = False,
has_retval: bool = True) -> None:
+ warnings.warn('sphinx.util.inspect.Signature() is deprecated',
+ RemovedInSphinx40Warning)
+
# check subject is not a built-in class (ex. int, str)
if (isinstance(subject, type) and
is_builtin_class_method(subject, "__new__") and
@@ -447,20 +556,14 @@ class Signature:
def format_annotation(self, annotation: Any) -> str:
"""Return formatted representation of a type annotation."""
- warnings.warn('format_annotation() is deprecated',
- RemovedInSphinx40Warning)
return stringify_annotation(annotation)
def format_annotation_new(self, annotation: Any) -> str:
"""format_annotation() for py37+"""
- warnings.warn('format_annotation_new() is deprecated',
- RemovedInSphinx40Warning)
return stringify_annotation(annotation)
def format_annotation_old(self, annotation: Any) -> str:
"""format_annotation() for py36 or below"""
- warnings.warn('format_annotation_old() is deprecated',
- RemovedInSphinx40Warning)
return stringify_annotation(annotation)
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
index 52b4f2d3e..35501f03a 100644
--- a/sphinx/util/jsonimpl.py
+++ b/sphinx/util/jsonimpl.py
@@ -28,19 +28,19 @@ class SphinxJSONEncoder(json.JSONEncoder):
return super().default(obj)
-def dump(obj: Any, fp: IO, *args: Any, **kwds: Any) -> None:
- kwds['cls'] = SphinxJSONEncoder
- json.dump(obj, fp, *args, **kwds)
+def dump(obj: Any, fp: IO, *args: Any, **kwargs: Any) -> None:
+ kwargs['cls'] = SphinxJSONEncoder
+ json.dump(obj, fp, *args, **kwargs)
-def dumps(obj: Any, *args: Any, **kwds: Any) -> str:
- kwds['cls'] = SphinxJSONEncoder
- return json.dumps(obj, *args, **kwds)
+def dumps(obj: Any, *args: Any, **kwargs: Any) -> str:
+ kwargs['cls'] = SphinxJSONEncoder
+ return json.dumps(obj, *args, **kwargs)
-def load(*args: Any, **kwds: Any) -> Any:
- return json.load(*args, **kwds)
+def load(*args: Any, **kwargs: Any) -> Any:
+ return json.load(*args, **kwargs)
-def loads(*args: Any, **kwds: Any) -> Any:
- return json.loads(*args, **kwds)
+def loads(*args: Any, **kwargs: Any) -> Any:
+ return json.loads(*args, **kwargs)
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index c5bdc801b..819cbdcde 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -1342,13 +1342,13 @@ def test_partialmethod(app):
' refs: https://docs.python.jp/3/library/functools.html#functools.partialmethod',
' ',
' ',
- ' .. py:method:: Cell.set_alive() -> None',
+ ' .. py:method:: Cell.set_alive()',
' :module: target.partialmethod',
' ',
' Make a cell alive.',
' ',
' ',
- ' .. py:method:: Cell.set_dead() -> None',
+ ' .. py:method:: Cell.set_dead()',
' :module: target.partialmethod',
' ',
' Make a cell dead.',
@@ -1360,11 +1360,6 @@ def test_partialmethod(app):
' Update state of cell to *state*.',
' ',
]
- if (sys.version_info < (3, 5, 4) or
- (3, 6, 5) <= sys.version_info < (3, 7) or
- (3, 7, 0, 'beta', 3) <= sys.version_info):
- # TODO: this condition should be updated after 3.7-final release.
- expected = '\n'.join(expected).replace(' -> None', '').split('\n')
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.partialmethod.Cell', options)
diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py
index 9547a9d2b..2ce754eff 100644
--- a/tests/test_ext_napoleon_docstring.py
+++ b/tests/test_ext_napoleon_docstring.py
@@ -479,6 +479,8 @@ Raises:
If the dimensions couldn't be parsed.
`InvalidArgumentsError`
If the arguments are invalid.
+ :exc:`~ValueError`
+ If the arguments are wrong.
""", """
Example Function
@@ -488,6 +490,7 @@ Example Function
:raises AttributeError: errors for missing attributes.
:raises ~InvalidDimensionsError: If the dimensions couldn't be parsed.
:raises InvalidArgumentsError: If the arguments are invalid.
+:raises ~ValueError: If the arguments are wrong.
"""),
################################
("""
diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py
index 2f4631965..68d1ac604 100644
--- a/tests/test_util_inspect.py
+++ b/tests/test_util_inspect.py
@@ -17,6 +17,7 @@ import types
import pytest
from sphinx.util import inspect
+from sphinx.util.inspect import stringify_signature
def test_getargspec():
@@ -89,39 +90,39 @@ def test_getargspec_bound_methods():
assert expected_bound == inspect.getargspec(wrapped_bound_method)
-def test_Signature():
+def test_signature():
# literals
with pytest.raises(TypeError):
- inspect.Signature(1)
+ inspect.signature(1)
with pytest.raises(TypeError):
- inspect.Signature('')
+ inspect.signature('')
# builitin classes
with pytest.raises(TypeError):
- inspect.Signature(int)
+ inspect.signature(int)
with pytest.raises(TypeError):
- inspect.Signature(str)
+ inspect.signature(str)
# normal function
def func(a, b, c=1, d=2, *e, **f):
pass
- sig = inspect.Signature(func).format_args()
+ sig = inspect.stringify_signature(inspect.signature(func))
assert sig == '(a, b, c=1, d=2, *e, **f)'
-def test_Signature_partial():
+def test_signature_partial():
def fun(a, b, c=1, d=2):
pass
p = functools.partial(fun, 10, c=11)
- sig = inspect.Signature(p).format_args()
- assert sig == '(b, *, c=11, d=2)'
+ sig = inspect.signature(p)
+ assert stringify_signature(sig) == '(b, *, c=11, d=2)'
-def test_Signature_methods():
+def test_signature_methods():
class Foo:
def meth1(self, arg1, **kwargs):
pass
@@ -139,36 +140,36 @@ def test_Signature_methods():
pass
# unbound method
- sig = inspect.Signature(Foo.meth1).format_args()
- assert sig == '(self, arg1, **kwargs)'
+ sig = inspect.signature(Foo.meth1)
+ assert stringify_signature(sig) == '(self, arg1, **kwargs)'
- sig = inspect.Signature(Foo.meth1, bound_method=True).format_args()
- assert sig == '(arg1, **kwargs)'
+ sig = inspect.signature(Foo.meth1, bound_method=True)
+ assert stringify_signature(sig) == '(arg1, **kwargs)'
# bound method
- sig = inspect.Signature(Foo().meth1).format_args()
- assert sig == '(arg1, **kwargs)'
+ sig = inspect.signature(Foo().meth1)
+ assert stringify_signature(sig) == '(arg1, **kwargs)'
# class method
- sig = inspect.Signature(Foo.meth2).format_args()
- assert sig == '(arg1, *args, **kwargs)'
+ sig = inspect.signature(Foo.meth2)
+ assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
- sig = inspect.Signature(Foo().meth2).format_args()
- assert sig == '(arg1, *args, **kwargs)'
+ sig = inspect.signature(Foo().meth2)
+ assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
# static method
- sig = inspect.Signature(Foo.meth3).format_args()
- assert sig == '(arg1, *args, **kwargs)'
+ sig = inspect.signature(Foo.meth3)
+ assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
- sig = inspect.Signature(Foo().meth3).format_args()
- assert sig == '(arg1, *args, **kwargs)'
+ sig = inspect.signature(Foo().meth3)
+ assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
# wrapped bound method
- sig = inspect.Signature(wrapped_bound_method).format_args()
- assert sig == '(arg1, **kwargs)'
+ sig = inspect.signature(wrapped_bound_method)
+ assert stringify_signature(sig) == '(arg1, **kwargs)'
-def test_Signature_partialmethod():
+def test_signature_partialmethod():
from functools import partialmethod
class Foo:
@@ -183,116 +184,115 @@ def test_Signature_partialmethod():
baz = partialmethod(meth2, 1, 2)
subject = Foo()
- sig = inspect.Signature(subject.foo).format_args()
- assert sig == '(arg3=None, arg4=None)'
+ sig = inspect.signature(subject.foo)
+ assert stringify_signature(sig) == '(arg3=None, arg4=None)'
- sig = inspect.Signature(subject.bar).format_args()
- assert sig == '(arg2, *, arg3=3, arg4=None)'
+ sig = inspect.signature(subject.bar)
+ assert stringify_signature(sig) == '(arg2, *, arg3=3, arg4=None)'
- sig = inspect.Signature(subject.baz).format_args()
- assert sig == '()'
+ sig = inspect.signature(subject.baz)
+ assert stringify_signature(sig) == '()'
-def test_Signature_annotations():
+def test_signature_annotations():
from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10,
f11, f12, f13, f14, f15, f16, f17, f18, f19, Node)
# Class annotations
- sig = inspect.Signature(f0).format_args()
- assert sig == '(x: int, y: numbers.Integral) -> None'
+ sig = inspect.signature(f0)
+ assert stringify_signature(sig) == '(x: int, y: numbers.Integral) -> None'
# Generic types with concrete parameters
- sig = inspect.Signature(f1).format_args()
- assert sig == '(x: List[int]) -> List[int]'
+ sig = inspect.signature(f1)
+ assert stringify_signature(sig) == '(x: List[int]) -> List[int]'
# TypeVars and generic types with TypeVars
- sig = inspect.Signature(f2).format_args()
- assert sig == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]'
+ sig = inspect.signature(f2)
+ assert stringify_signature(sig) == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]'
# Union types
- sig = inspect.Signature(f3).format_args()
- assert sig == '(x: Union[str, numbers.Integral]) -> None'
+ sig = inspect.signature(f3)
+ assert stringify_signature(sig) == '(x: Union[str, numbers.Integral]) -> None'
# Quoted annotations
- sig = inspect.Signature(f4).format_args()
- assert sig == '(x: str, y: str) -> None'
+ sig = inspect.signature(f4)
+ assert stringify_signature(sig) == '(x: str, y: str) -> None'
# Keyword-only arguments
- sig = inspect.Signature(f5).format_args()
- assert sig == '(x: int, *, y: str, z: str) -> None'
+ sig = inspect.signature(f5)
+ assert stringify_signature(sig) == '(x: int, *, y: str, z: str) -> None'
# Keyword-only arguments with varargs
- sig = inspect.Signature(f6).format_args()
- assert sig == '(x: int, *args, y: str, z: str) -> None'
+ sig = inspect.signature(f6)
+ assert stringify_signature(sig) == '(x: int, *args, y: str, z: str) -> None'
# Space around '=' for defaults
- sig = inspect.Signature(f7).format_args()
- assert sig == '(x: int = None, y: dict = {}) -> None'
+ sig = inspect.signature(f7)
+ assert stringify_signature(sig) == '(x: int = None, y: dict = {}) -> None'
# Callable types
- sig = inspect.Signature(f8).format_args()
- assert sig == '(x: Callable[[int, str], int]) -> None'
+ sig = inspect.signature(f8)
+ assert stringify_signature(sig) == '(x: Callable[[int, str], int]) -> None'
- sig = inspect.Signature(f9).format_args()
- assert sig == '(x: Callable) -> None'
+ sig = inspect.signature(f9)
+ assert stringify_signature(sig) == '(x: Callable) -> None'
# Tuple types
- sig = inspect.Signature(f10).format_args()
- assert sig == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None'
+ sig = inspect.signature(f10)
+ assert stringify_signature(sig) == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None'
# Instance annotations
- sig = inspect.Signature(f11).format_args()
- assert sig == '(x: CustomAnnotation, y: 123) -> None'
-
- # has_retval=False
- sig = inspect.Signature(f11, has_retval=False).format_args()
- assert sig == '(x: CustomAnnotation, y: 123)'
+ sig = inspect.signature(f11)
+ assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None'
# tuple with more than two items
- sig = inspect.Signature(f12).format_args()
- assert sig == '() -> Tuple[int, str, int]'
+ sig = inspect.signature(f12)
+ assert stringify_signature(sig) == '() -> Tuple[int, str, int]'
# optional
- sig = inspect.Signature(f13).format_args()
- assert sig == '() -> Optional[str]'
+ sig = inspect.signature(f13)
+ assert stringify_signature(sig) == '() -> Optional[str]'
# Any
- sig = inspect.Signature(f14).format_args()
- assert sig == '() -> Any'
+ sig = inspect.signature(f14)
+ assert stringify_signature(sig) == '() -> Any'
# ForwardRef
- sig = inspect.Signature(f15).format_args()
- assert sig == '(x: Unknown, y: int) -> Any'
+ sig = inspect.signature(f15)
+ assert stringify_signature(sig) == '(x: Unknown, y: int) -> Any'
# keyword only arguments (1)
- sig = inspect.Signature(f16).format_args()
- assert sig == '(arg1, arg2, *, arg3=None, arg4=None)'
+ sig = inspect.signature(f16)
+ assert stringify_signature(sig) == '(arg1, arg2, *, arg3=None, arg4=None)'
# keyword only arguments (2)
- sig = inspect.Signature(f17).format_args()
- assert sig == '(*, arg3, arg4)'
+ sig = inspect.signature(f17)
+ assert stringify_signature(sig) == '(*, arg3, arg4)'
- sig = inspect.Signature(f18).format_args()
- assert sig == '(self, arg1: Union[int, Tuple] = 10) -> List[Dict]'
+ sig = inspect.signature(f18)
+ assert stringify_signature(sig) == '(self, arg1: Union[int, Tuple] = 10) -> List[Dict]'
# annotations for variadic and keyword parameters
- sig = inspect.Signature(f19).format_args()
- assert sig == '(*args: int, **kwargs: str)'
+ sig = inspect.signature(f19)
+ assert stringify_signature(sig) == '(*args: int, **kwargs: str)'
# type hints by string
- sig = inspect.Signature(Node.children).format_args()
+ sig = inspect.signature(Node.children)
if (3, 5, 0) <= sys.version_info < (3, 5, 3):
- assert sig == '(self) -> List[Node]'
+ assert stringify_signature(sig) == '(self) -> List[Node]'
else:
- assert sig == '(self) -> List[typing_test_data.Node]'
+ assert stringify_signature(sig) == '(self) -> List[typing_test_data.Node]'
- sig = inspect.Signature(Node.__init__).format_args()
- assert sig == '(self, parent: Optional[Node]) -> None'
+ sig = inspect.signature(Node.__init__)
+ assert stringify_signature(sig) == '(self, parent: Optional[Node]) -> None'
# show_annotation is False
- sig = inspect.Signature(f7).format_args(show_annotation=False)
- assert sig == '(x=None, y={})'
+ sig = inspect.signature(f7)
+ assert stringify_signature(sig, show_annotation=False) == '(x=None, y={})'
+ # show_return_annotation is False
+ sig = inspect.signature(f7)
+ assert stringify_signature(sig, show_return_annotation=False) == '(x: int = None, y: dict = {})'
def test_safe_getattr_with_default():
class Foo: