You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/sphinx/ext/autodoc/type_comment.py

140 lines
5.5 KiB

"""
sphinx.ext.autodoc.type_comment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Update annotations info of living objects using type_comments.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from inspect import Parameter, Signature, getsource
from typing import Any, Dict, List, cast
import sphinx
from sphinx.application import Sphinx
from sphinx.locale import __
from sphinx.pycode.ast import ast
from sphinx.pycode.ast import parse as ast_parse
from sphinx.pycode.ast import unparse as ast_unparse
from sphinx.util import inspect, logging
logger = logging.getLogger(__name__)
def not_suppressed(argtypes: List[ast.AST] = []) -> bool:
"""Check given *argtypes* is suppressed type_comment or not."""
if len(argtypes) == 0: # no argtypees
return False
elif len(argtypes) == 1 and ast_unparse(argtypes[0]) == "...": # suppressed
# Note: To support multiple versions of python, this uses ``ast_unparse()`` for
# comparison with Ellipsis. Since 3.8, ast.Constant has been used to represent
# Ellipsis node instead of ast.Ellipsis.
return False
else: # not suppressed
return True
def signature_from_ast(node: ast.FunctionDef, bound_method: bool,
type_comment: ast.FunctionDef) -> Signature:
"""Return a Signature object for the given *node*.
:param bound_method: Specify *node* is a bound method or not
"""
params = []
if hasattr(node.args, "posonlyargs"): # for py38+
for arg in node.args.posonlyargs: # type: ignore
param = Parameter(arg.arg, Parameter.POSITIONAL_ONLY, annotation=arg.type_comment)
params.append(param)
for arg in node.args.args:
param = Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD,
annotation=arg.type_comment or Parameter.empty)
params.append(param)
if node.args.vararg:
param = Parameter(node.args.vararg.arg, Parameter.VAR_POSITIONAL,
annotation=node.args.vararg.type_comment or Parameter.empty)
params.append(param)
for arg in node.args.kwonlyargs:
param = Parameter(arg.arg, Parameter.KEYWORD_ONLY,
annotation=arg.type_comment or Parameter.empty)
params.append(param)
if node.args.kwarg:
param = Parameter(node.args.kwarg.arg, Parameter.VAR_KEYWORD,
annotation=node.args.kwarg.type_comment or Parameter.empty)
params.append(param)
# Remove first parameter when *obj* is bound_method
if bound_method and params:
params.pop(0)
# merge type_comment into signature
if not_suppressed(type_comment.argtypes): # type: ignore
for i, param in enumerate(params):
params[i] = param.replace(annotation=type_comment.argtypes[i]) # type: ignore
if node.returns:
return Signature(params, return_annotation=node.returns)
elif type_comment.returns:
return Signature(params, return_annotation=ast_unparse(type_comment.returns))
else:
return Signature(params)
def get_type_comment(obj: Any, bound_method: bool = False) -> Signature:
"""Get type_comment'ed FunctionDef object from living object.
This tries to parse original code for living object and returns
Signature for given *obj*. It requires py38+ or typed_ast module.
"""
try:
source = getsource(obj)
if source.startswith((' ', r'\t')):
# subject is placed inside class or block. To read its docstring,
# this adds if-block before the declaration.
module = ast_parse('if True:\n' + source)
subject = cast(ast.FunctionDef, module.body[0].body[0]) # type: ignore
else:
module = ast_parse(source)
subject = cast(ast.FunctionDef, module.body[0]) # type: ignore
if getattr(subject, "type_comment", None):
function = ast_parse(subject.type_comment, mode='func_type')
return signature_from_ast(subject, bound_method, function) # type: ignore
else:
return None
except (OSError, TypeError): # failed to load source code
return None
except SyntaxError: # failed to parse type_comments
return None
def update_annotations_using_type_comments(app: Sphinx, obj: Any, bound_method: bool) -> None:
"""Update annotations info of *obj* using type_comments."""
try:
type_sig = get_type_comment(obj, bound_method)
if type_sig:
sig = inspect.signature(obj, bound_method)
for param in sig.parameters.values():
if param.name not in obj.__annotations__:
annotation = type_sig.parameters[param.name].annotation
if annotation is not Parameter.empty:
obj.__annotations__[param.name] = ast_unparse(annotation)
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)
def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('autodoc-before-process-signature', update_annotations_using_type_comments)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}