def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None: """Update defvalue info of *obj* using type_comments.""" if not app.config.autodoc_preserve_defaults: return try: function = get_function_def(obj) if function.args.defaults or function.args.kw_defaults: sig = inspect.signature(obj) defaults = list(function.args.defaults) kw_defaults = list(function.args.kw_defaults) parameters = list(sig.parameters.values()) for i, param in enumerate(parameters): if param.default is not param.empty: if param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD): value = DefaultValue(ast_unparse( defaults.pop(0))) # type: ignore parameters[i] = param.replace(default=value) else: value = DefaultValue(ast_unparse( kw_defaults.pop(0))) # type: ignore parameters[i] = param.replace(default=value) sig = sig.replace(parameters=parameters) obj.__signature__ = sig except (AttributeError, TypeError): # failed to update signature (ex. built-in or extension types) pass except NotImplementedError as exc: # failed to ast.unparse() logger.warning( __("Failed to parse a default argument value for %r: %s"), obj, exc)
def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None: """Update defvalue info of *obj* using type_comments.""" if not app.config.autodoc_preserve_defaults: return try: lines = inspect.getsource(obj).splitlines() if lines[0].startswith((' ', r'\t')): lines.insert(0, '') # insert a dummy line to follow what get_function_def() does. except (OSError, TypeError): lines = [] try: function = get_function_def(obj) if function.args.defaults or function.args.kw_defaults: sig = inspect.signature(obj) defaults = list(function.args.defaults) kw_defaults = list(function.args.kw_defaults) parameters = list(sig.parameters.values()) for i, param in enumerate(parameters): if param.default is param.empty: if param.kind == param.KEYWORD_ONLY: # Consume kw_defaults for kwonly args kw_defaults.pop(0) else: if param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD): default = defaults.pop(0) value = get_default_value(lines, default) if value is None: value = ast_unparse(default) # type: ignore parameters[i] = param.replace(default=DefaultValue(value)) else: default = kw_defaults.pop(0) value = get_default_value(lines, default) if value is None: value = ast_unparse(default) # type: ignore parameters[i] = param.replace(default=DefaultValue(value)) if bound_method and inspect.ismethod(obj): # classmethods cls = inspect.Parameter('cls', Parameter.POSITIONAL_OR_KEYWORD) parameters.insert(0, cls) sig = sig.replace(parameters=parameters) if bound_method and inspect.ismethod(obj): # classmethods can't be assigned __signature__ attribute. obj.__dict__['__signature__'] = sig else: obj.__signature__ = sig except (AttributeError, TypeError): # failed to update signature (ex. built-in or extension types) pass except NotImplementedError as exc: # failed to ast.unparse() logger.warning(__("Failed to parse a default argument value for %r: %s"), obj, exc)
def signature_from_str(signature: str) -> inspect.Signature: """Create a Signature object from string.""" module = ast.parse('def func' + signature + ': pass') definition = cast(ast.FunctionDef, module.body[0]) # type: ignore # parameters args = definition.args defaults = list(args.defaults) params = [] if hasattr(args, "posonlyargs"): posonlyargs = len(args.posonlyargs) # type: ignore positionals = posonlyargs + len(args.args) else: posonlyargs = 0 positionals = len(args.args) for _ in range(len(defaults), positionals): defaults.insert(0, Parameter.empty) if hasattr(args, "posonlyargs"): for i, arg in enumerate(args.posonlyargs): # type: ignore if defaults[i] is Parameter.empty: default = Parameter.empty else: default = ast_unparse(defaults[i]) annotation = ast_unparse(arg.annotation) or Parameter.empty params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY, default=default, annotation=annotation)) for i, arg in enumerate(args.args): if defaults[i + posonlyargs] is Parameter.empty: default = Parameter.empty else: default = ast_unparse(defaults[i + posonlyargs]) annotation = ast_unparse(arg.annotation) or Parameter.empty params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD, default=default, annotation=annotation)) if args.vararg: annotation = ast_unparse(args.vararg.annotation) or Parameter.empty params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL, annotation=annotation)) for i, arg in enumerate(args.kwonlyargs): default = ast_unparse(args.kw_defaults[i]) or Parameter.empty annotation = ast_unparse(arg.annotation) or Parameter.empty params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default, annotation=annotation)) if args.kwarg: annotation = ast_unparse(args.kwarg.annotation) or Parameter.empty params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD, annotation=annotation)) return_annotation = ast_unparse(definition.returns) or Parameter.empty return inspect.Signature(params, return_annotation=return_annotation)
def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signature: """Create a Signature object from AST *node*.""" args = node.args defaults = list(args.defaults) params = [] if hasattr(args, "posonlyargs"): posonlyargs = len(args.posonlyargs) # type: ignore positionals = posonlyargs + len(args.args) else: posonlyargs = 0 positionals = len(args.args) for _ in range(len(defaults), positionals): defaults.insert(0, Parameter.empty) # type: ignore if hasattr(args, "posonlyargs"): for i, arg in enumerate(args.posonlyargs): # type: ignore if defaults[i] is Parameter.empty: default = Parameter.empty else: default = DefaultValue(ast_unparse(defaults[i], code)) # type: ignore annotation = ast_unparse(arg.annotation, code) or Parameter.empty params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY, default=default, annotation=annotation)) for i, arg in enumerate(args.args): if defaults[i + posonlyargs] is Parameter.empty: default = Parameter.empty else: default = DefaultValue(ast_unparse(defaults[i + posonlyargs], code)) # type: ignore # NOQA annotation = ast_unparse(arg.annotation, code) or Parameter.empty params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD, default=default, annotation=annotation)) if args.vararg: annotation = ast_unparse(args.vararg.annotation, code) or Parameter.empty params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL, annotation=annotation)) for i, arg in enumerate(args.kwonlyargs): if args.kw_defaults[i] is None: default = Parameter.empty else: default = DefaultValue(ast_unparse(args.kw_defaults[i], code)) # type: ignore # NOQA annotation = ast_unparse(arg.annotation, code) or Parameter.empty params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default, annotation=annotation)) if args.kwarg: annotation = ast_unparse(args.kwarg.annotation, code) or Parameter.empty params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD, annotation=annotation)) return_annotation = ast_unparse(node.returns, code) or Parameter.empty return inspect.Signature(params, return_annotation=return_annotation)
def update_annotations_using_type_comments(app: Sphinx, obj: Any, bound_method: bool) -> None: """Update annotations info of *obj* using type_comments.""" try: function = get_type_comment(obj) if function and hasattr(function, 'argtypes'): if function.argtypes != [ast.Ellipsis]: # type: ignore sig = inspect.signature(obj, bound_method) for i, param in enumerate(sig.parameters.values()): if param.name not in obj.__annotations__: annotation = ast_unparse(function.argtypes[i]) # type: ignore obj.__annotations__[param.name] = annotation if 'return' not in obj.__annotations__: obj.__annotations__['return'] = ast_unparse(function.returns) # type: ignore except NotImplementedError as exc: # failed to ast.unparse() logger.warning("Failed to parse type_comment for %r: %s", obj, exc)
def signature_from_str(signature: str) -> inspect.Signature: """Create a Signature object from string.""" module = ast.parse('def func' + signature + ': pass') definition = cast(ast.FunctionDef, module.body[0]) # type: ignore # parameters args = definition.args params = [] if hasattr(args, "posonlyargs"): for arg in args.posonlyargs: # type: ignore annotation = ast_unparse(arg.annotation) or Parameter.empty params.append( Parameter(arg.arg, Parameter.POSITIONAL_ONLY, annotation=annotation)) for i, arg in enumerate(args.args): if len(args.args) - i <= len(args.defaults): default = ast_unparse(args.defaults[-len(args.args) + i]) else: default = Parameter.empty annotation = ast_unparse(arg.annotation) or Parameter.empty params.append( Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD, default=default, annotation=annotation)) if args.vararg: annotation = ast_unparse(args.vararg.annotation) or Parameter.empty params.append( Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL, annotation=annotation)) for i, arg in enumerate(args.kwonlyargs): default = ast_unparse(args.kw_defaults[i]) or Parameter.empty annotation = ast_unparse(arg.annotation) or Parameter.empty params.append( Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default, annotation=annotation)) if args.kwarg: annotation = ast_unparse(args.kwarg.annotation) or Parameter.empty params.append( Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD, annotation=annotation)) return_annotation = ast_unparse(definition.returns) or Parameter.empty return inspect.Signature(params, return_annotation=return_annotation)
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 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 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 NotImplementedError as exc: # failed to ast.unparse() logger.warning("Failed to parse type_comment for %r: %s", obj, exc)