def format_args(self): # type: () -> str # We use the original signature from the wrapped _function has_retval= isinstance(self.object, FontBakeryCondition) sig = Signature(self.object._func, bound_method=False, has_retval=has_retval) args = sig.format_args() # escape backslashes for reST args = args.replace('\\', '\\\\') return args
def process_signature(app, what: str, name: str, obj, options, signature, return_annotation): if not callable(obj): return if what in ('class', 'exception'): obj = getattr(obj, '__init__', getattr(obj, '__new__', None)) if not getattr(obj, '__annotations__', None): return obj = inspect.unwrap(obj) signature = Signature(obj) parameters = [ param.replace(annotation=inspect.Parameter.empty) for param in signature.signature.parameters.values() ] if '<locals>' in obj.__qualname__: logger.warning( 'Cannot treat a function defined as a local function: "%s" (use @functools.wraps)', name) return if parameters: if what in ('class', 'exception'): del parameters[0] elif what == 'method': outer = inspect.getmodule(obj) for clsname in obj.__qualname__.split('.')[:-1]: outer = getattr(outer, clsname) method_name = obj.__name__ if method_name.startswith("__") and not method_name.endswith("__"): # If the method starts with double underscore (dunder) # Python applies mangling so we need to prepend the class name. # This doesn't happen if it always ends with double underscore. class_name = obj.__qualname__.split('.')[-2] method_name = "_{c}{m}".format(c=class_name, m=method_name) method_object = outer.__dict__[method_name] if outer else obj if not isinstance(method_object, (classmethod, staticmethod)): del parameters[0] signature.signature = signature.signature.replace( parameters=parameters, return_annotation=inspect.Signature.empty) return signature.format_args().replace('\\', '\\\\'), None
def preprocess_function_defaults( obj: Callable ) -> Tuple[Optional[inspect.Signature], List[inspect.Parameter]]: """ Pre-processes the default values for the arguments of a function. .. versionadded:: 0.8.0 :param obj: The function. :return: The function signature and a list of arguments/parameters. """ try: signature = Signature(inspect.unwrap(obj)) except ValueError: # pragma: no cover return None, [] parameters = [] for param in signature.parameters.values(): default = param.default if default is not inspect.Parameter.empty: for check, preprocessor in default_preprocessors: if check(default): default = preprocessor(default) break parameters.append( param.replace(annotation=inspect.Parameter.empty, default=default)) return signature, parameters
def strip_annotations(app, what: str, name: str, obj, options, signature, return_annotation): if what not in {'function', 'method', 'class'}: return new_signature = Signature(obj) new_signature.signature = new_signature.signature.replace( return_annotation=inspect.Signature.empty, parameters=[ param.replace(annotation=inspect.Parameter.empty) for param in new_signature.signature.parameters.values() if param.name != 'self' ], ) return new_signature.format_args(), None
def format_args(self): # type: () -> str # for instances, the relevant signature is the __call__ method's callmeth = self.get_attr(self.object, '__call__', None) if callmeth: return Signature(callmeth, bound_method=True, has_retval=True).format_args() return None
def get_args(func: Callable, for_sphinx: bool = True) -> List[str]: signature = Signature(unwrap_all(func)) result = [] for name, param in signature.parameters.items(): if param.kind == inspect.Parameter.VAR_POSITIONAL: name = (r'\*' if for_sphinx else '*') + name elif param.kind == inspect.Parameter.VAR_KEYWORD: name = (r'\*\*' if for_sphinx else '**') + name result.append(name[:-1] + r'\_' if for_sphinx and name.endswith('_') else name) return result
def get_arguments(obj: Callable) -> Mapping[str, inspect.Parameter]: """ Returns a dictionary mapping argument names to parameters/arguments for a function. :param obj: A function (can be the ``__init__`` method of a class). """ try: signature = Signature(inspect.unwrap(obj)) except ValueError: # pragma: no cover return {} return signature.parameters
def process_signature(app, what: str, name: str, obj, options, signature, return_annotation): if not callable(obj): return if what in ('class', 'exception'): obj = getattr(obj, '__init__') if not getattr(obj, '__annotations__', None): return obj = unwrap(obj) return Signature(obj).format_args(), None
def preprocess_class_defaults( obj: Callable ) -> Tuple[Optional[Callable], Optional[inspect.Signature], List[inspect.Parameter]]: """ Pre-processes the default values for the arguments of a class. .. versionadded:: 0.8.0 :param obj: The class. :return: The class signature and a list of arguments/parameters. """ init: Optional[Callable[..., Any]] = getattr(obj, "__init__", getattr(obj, "__new__", None)) if is_namedtuple(obj): init = getattr(obj, "__new__") try: signature = Signature(inspect.unwrap(init)) # type: ignore[arg-type] except ValueError: # pragma: no cover return init, None, [] parameters = [] for argname, param in signature.parameters.items(): default = param.default if default is not inspect.Parameter.empty: for check, preprocessor in default_preprocessors: if check(default): default = preprocessor(default) break else: if hasattr(obj, "__attrs_attrs__") and default is attr.NOTHING: # Special casing for attrs classes for value in obj.__attrs_attrs__: # type: ignore if value.name == argname and isinstance( value.default, attr.Factory): # type: ignore default = value.default.factory() parameters.append( param.replace(annotation=inspect.Parameter.empty, default=default)) return init, signature, parameters
def get_default_args(func: Callable, for_sphinx: bool = True) -> OrderedDictType[str, Any]: signature = Signature(func) # Backward Compatibility # The built-in Parameter object is guaranteed # an ordered mapping in >= 3.5. default_args = OrderedDict() for k, v in signature.parameters.items(): # assume *args, **kwargs does not have default if v.default is not inspect.Parameter.empty: if for_sphinx and k.endswith('_'): k = '{}\\_'.format(k[:-1]) default_args[k] = (v.default, v.kind == inspect.Parameter.KEYWORD_ONLY) return default_args
def process_signature( app, what: str, name: str, obj, # pylint: disable=too-many-arguments,unused-argument options, signature, return_annotation, ): # pylint: disable=unused-argument if callable(obj): if what in ("class", "exception"): obj = getattr(obj, "__init__") obj = unwrap(obj) try: signature = Signature(obj) except: signature = Signature(lambda: None) parameters = [ param.replace(annotation=inspect.Parameter.empty) for param in signature.signature.parameters.values() ] if parameters: if what in ("class", "exception"): del parameters[0] elif what == "method": outer = inspect.getmodule(obj) if outer is None: return for clsname in obj.__qualname__.split(".")[:-1]: outer = getattr(outer, clsname, outer) method_name = obj.__name__ if method_name.startswith("__") and not method_name.endswith("__"): # If the method starts with double underscore (dunder) # Python applies mangling so we need to prepend the class name. # This doesn't happen if it always ends with double underscore. class_name = obj.__qualname__.split(".")[-2] method_name = "_{c}{m}".format(c=class_name, m=method_name) method_object = getattr(outer,'__dict__',{}).get(method_name, None) if not isinstance(method_object, (classmethod, staticmethod)): del parameters[0] signature.signature = signature.signature.replace( parameters=parameters, return_annotation=inspect.Signature.empty ) return signature.format_args().replace("\\", "\\\\"), None
def process_signature(app, what: str, name: str, obj, options, signature, return_annotation): if not callable(obj): return original_obj = obj if inspect.isclass(obj): obj = getattr(obj, '__init__', getattr(obj, '__new__', None)) if not getattr(obj, '__annotations__', None): return obj = inspect.unwrap(obj) signature = Signature(obj) parameters = [ param.replace(annotation=inspect.Parameter.empty) for param in signature.parameters.values() ] # The generated dataclass __init__() and class are weird and need extra checks # This helper function operates on the generated class and methods # of a dataclass, not an instantiated dataclass object. As such, # it cannot be replaced by a call to `dataclasses.is_dataclass()`. def _is_dataclass(name: str, what: str, qualname: str) -> bool: if what == 'method' and name.endswith('.__init__'): # generated __init__() return True if what == 'class' and qualname.endswith('.__init__'): # generated class return True return False if '<locals>' in obj.__qualname__ and not _is_dataclass(name, what, obj.__qualname__): logger.warning( 'Cannot treat a function defined as a local function: "%s" (use @functools.wraps)', name) return if parameters: if inspect.isclass(original_obj) or (what == 'method' and name.endswith('.__init__')): del parameters[0] elif what == 'method': outer = inspect.getmodule(obj) for clsname in obj.__qualname__.split('.')[:-1]: outer = getattr(outer, clsname) method_name = obj.__name__ if method_name.startswith("__") and not method_name.endswith("__"): # If the method starts with double underscore (dunder) # Python applies mangling so we need to prepend the class name. # This doesn't happen if it always ends with double underscore. class_name = obj.__qualname__.split('.')[-2] method_name = "_{c}{m}".format(c=class_name, m=method_name) method_object = outer.__dict__[method_name] if outer else obj if not isinstance(method_object, (classmethod, staticmethod)): del parameters[0] signature = signature.replace( parameters=parameters, return_annotation=inspect.Signature.empty) return stringify_signature(signature).replace('\\', '\\\\'), None
def get_signature_end(function): signature_end = Signature(function).format_args() if ismethod(function): signature_end = signature_end.replace("(self, ", "(") signature_end = signature_end.replace("(self)", "()") return signature_end
def get_signature_end(function): signature_end = Signature(function).format_args() if utils.ismethod(function): signature_end = signature_end.replace('(self, ', '(') signature_end = signature_end.replace('(self)', '()') return signature_end
def process_docstring(app: Sphinx, what: str, name: str, obj: Any, options: Any, lines: List[str]) -> None: """Process docstring after Sphinx. See `autodoc-process-docstring <https://www.sphinx-doc.org/en/master/ usage/extensions/autodoc.html#event-autodoc-process-docstring>`_ """ # original_obj = obj if isinstance(obj, property): obj = obj.fget if not callable(obj): return if inspect.isclass(obj): obj = getattr(obj, '__init__', getattr(obj, '__new__', None)) # obj = getattr(obj, '__init__') obj = inspect.unwrap(obj) rm_first_arg = what in ['method', 'property', 'class' ] and not isstaticmethod(obj) first_argname = next(iter(Signature( unwrap_all(obj)).parameters)) if rm_first_arg else None if first_argname and first_argname.endswith('_'): first_argname = '{}\\_'.format(first_argname[:-1]) default_args = get_default_args(obj) for argname, (default, is_keyword_only) in default_args.items(): # what if default has \ default = ':code:`{}`'.format(object_description(default)) # TODO # should be arguments strip = app.config.docstring_default_arg_strip_matching docstring_default_arg_parenthesis = False # Search for parameters # TODO Test case: empty param searchfor = [ ':{} {}:'.format(field, argname) for field in param_fields ] param_found, param_start, param_end, param_matched, param_text = ( match_field(lines, searchfor, include_blank=app.config. docstring_default_arg_after_directives)) if param_found: if app.config.docstring_default_arg_substitution not in ' '.join( param_text): # Extracts all the flags for head, tail in app.config.docstring_default_arg_flags: tail_found, is_end, t_start = rfind_substring_in_paragraph( param_text, tail, strip, app.config. docstring_default_arg_flags_multiline_matching)[:3] if tail_found and is_end: head_found, _, h_start, h_end = ( rfind_substring_in_paragraph( param_text, head, strip, app.config. docstring_default_arg_flags_multiline_matching) ) if head_found: # what if default has \ if h_end[0] == t_start[0]: default = param_text[ h_end[0]][h_end[1]:t_start[1]] else: default = ' '.join( [param_text[h_end[0]][h_end[1]:]] + param_text[h_end[0] + 1:t_start[0]] + [param_text[t_start[0]][:t_start[1]]]) if strip: default = default.strip() lines[param_start + h_start[0]] = ( lines[param_start + h_start[0]][:len(param_matched) + 1 + h_start[1]]) del lines[param_start + h_start[0] + 1:param_end] param_end = param_start + h_start[0] + 1 break if strip: lines[param_end - 1] = rstrip_min(lines[param_end - 1], len(param_matched) + 1) if docstring_default_arg_parenthesis: raise NotImplementedError else: # To prevent insertion into Note directives or so lines.insert( param_end, ' ' * len(param_matched) + ' {} {}'.format( app.config.docstring_default_arg_substitution, default)) elif app.config.always_document_default_args and ( not rm_first_arg or argname != first_argname): # Since ``kwargs`` (no default args) might come # after ``argname``, it will not be in ``default_args``. # Need to generate the full args list. next_start, next_type = find_next_arg(lines, get_args(obj), argname) if docstring_default_arg_parenthesis: raise NotImplementedError else: lines.insert( next_start, ':{} {}: {} {}'.format( 'keyword' if is_keyword_only and (next_type is None or next_type in kw_fields) else 'param', argname, app.config.docstring_default_arg_substitution, default)) # Search for type type_found, type_start, type_end, type_matched, type_text = ( match_field( lines, [':{} {}:'.format(field, argname) for field in type_fields], include_blank=False)) if type_found: type_text = ' '.join(type_text) if strip: type_text = type_text.rstrip() lines[type_end - 1] = rstrip_min(lines[type_end - 1], len(type_matched) + 1) if not type_text.endswith('optional'): if not type_text.strip(): lines[type_start] = '{} optional'.format(type_matched) elif '`' in type_text: # TODO check \` escape lines[type_end - 1] += ', *optional*' else: # Do not insert newline to prevent whitespace before ',' lines[type_end - 1] += ', optional' elif param_found or app.config.always_document_default_args and ( not rm_first_arg or argname != first_argname): # insert type before param param_start, param_type = find_curr_arg(lines, get_args(obj), argname) assert any(lines[param_start].startswith(search) for search in searchfor) lines.insert( param_start, ':{}type {}: optional'.format( 'kw' if param_type in kw_fields else '', argname))