Merge branch '2.0'

This commit is contained in:
Takeshi KOMIYA 2020-01-11 02:56:58 +09:00
commit cad1f86dd8
21 changed files with 273 additions and 177 deletions

View File

@ -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
--------

View File

@ -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

View File

@ -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::

View File

@ -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.

View File

@ -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:

View File

@ -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)

View File

@ -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']

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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]

View File

@ -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:

View File

@ -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 = '',

View File

@ -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

View File

@ -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()

View File

@ -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),

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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.
"""),
################################
("""

View File

@ -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: