def _extract_signature(obj_sig): try: signature = inspect.signature(obj_sig) parameters = signature.parameters except TypeError as e: mes = "[docfx] unable to get signature of '{0}' - {1}.".format( object_name, str(e).replace("\n", "\\n")) logger.warning(mes) signature = None parameters = None except ValueError as e: # Backup plan, no __text_signature__, this happen # when a function was created with pybind11. doc = obj_sig.__doc__ sigs = set(enumerate_cleaned_signature(doc)) if len(sigs) == 0: mes = "[docfx] unable to get signature of '{0}' - {1}.".format( object_name, str(e).replace("\n", "\\n")) logger.warning(mes) signature = None parameters = None elif len(sigs) > 1: mes = "[docfx] too many signatures for '{0}' - {1} - {2}.".format( object_name, str(e).replace("\n", "\\n"), " *** ".join(sigs)) logger.warning(mes) signature = None parameters = None else: try: signature = inspect._signature_fromstr(inspect.Signature, obj_sig, list(sigs)[0]) parameters = signature.parameters except TypeError as e: mes = "[docfx] unable to get signature of '{0}' - {1}.".format( object_name, str(e).replace("\n", "\\n")) logger.warning(mes) signature = None parameters = None return signature, parameters
def inspect_signature(obj): """ Custom signature inspection primarily for cython generated callables. Cython puts the signatures to the first line of the docstrings, which we can reuse to parse the python signature from, but some gymnastics are required, like removing the cython typehints. It converts the cython signature: array(obj, type=None, mask=None, size=None, from_pandas=None, bool safe=True, MemoryPool memory_pool=None) To: <Signature (obj, type=None, mask=None, size=None, from_pandas=None, safe=True, memory_pool=None)> """ cython_signature = obj.__doc__.splitlines()[0] cython_tokens = _tokenize_signature(cython_signature) python_tokens = _convert_typehint(cython_tokens) python_signature = tokenize.untokenize(python_tokens) return inspect._signature_fromstr(inspect.Signature, obj, python_signature)
def getfullargspec(func): if func in (_np.empty_like, _np.ones_like, _np.zeros_like): if func is _np.empty_like: sig = inspect._signature_fromstr( inspect.Signature, func, func.__doc__.strip().split('\n', maxsplit=1)[0]) else: sig = inspect.signature(func) return inspect.FullArgSpec( list(sig.parameters.keys()), # args None, # varargs None, # varkw tuple(v.default for v in sig.parameters.values() if v.default is not inspect._empty), # defaults [], # knwonlyargs {}, # kwonlydefaults {}, # annotations ) else: return _inspect_getfullargspec(func)
def _signature_from_callable( obj, follow_wrapper_chains=True, skip_bound_arg=True, sigcls=None ): """Private helper function to get signature for arbitrary callable objects. """ # We don't want to import pyobj, since that imports us if not xCallable(obj): raise TypeError("%r is not a callable object" % obj) if isinstance(obj, types.MethodType): # In this case we skip the first parameter of the underlying # function (usually `self` or `cls`). sig = _signature_from_callable( obj.__func__, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, sigcls=sigcls, ) if skip_bound_arg: return _signature_bound_method(sig) else: return sig # Was this function wrapped by a decorator? if follow_wrapper_chains: obj = unwrap(obj, stop=(lambda f: hasattr(f, "__signature__"))) if isinstance(obj, types.MethodType): # If the unwrapped object is a *method*, we might want to # skip its first parameter (self). # See test_signature_wrapped_bound_method for details. return _signature_from_callable( obj, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, sigcls=sigcls, ) try: sig = obj.__signature__ except AttributeError: pass else: if sig is not None: if not isinstance(sig, Signature): raise TypeError( "unexpected object %r in __signature__ " "attribute" % sig ) return sig try: partialmethod = obj._partialmethod except AttributeError: pass else: if isinstance(partialmethod, functools.partialmethod): # Unbound partialmethod (see functools.partialmethod) # This means, that we need to calculate the signature # as if it's a regular partial object, but taking into # account that the first positional argument # (usually `self`, or `cls`) will not be passed # automatically (as for boundmethods) wrapped_sig = _signature_from_callable( partialmethod.func, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, sigcls=sigcls, ) sig = _signature_get_partial(wrapped_sig, partialmethod, (None,)) first_wrapped_param = tuple(wrapped_sig.parameters.values())[0] if first_wrapped_param.kind is Parameter.VAR_POSITIONAL: # First argument of the wrapped callable is `*args`, as in # `partialmethod(lambda *args)`. return sig else: sig_params = tuple(sig.parameters.values()) assert not sig_params or first_wrapped_param is not sig_params[0] new_params = (first_wrapped_param,) + sig_params return sig.replace(parameters=new_params) if isfunction(obj) or _signature_is_functionlike(obj): # If it's a pure Python function, or an object that is duck type # of a Python function (Cython functions, for instance), then: return _signature_from_function(sigcls, obj) if _signature_is_builtin(obj): return _signature_from_builtin(sigcls, obj, skip_bound_arg=skip_bound_arg) if isinstance(obj, functools.partial): wrapped_sig = _signature_from_callable( obj.func, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, sigcls=sigcls, ) return _signature_get_partial(wrapped_sig, obj) sig = None if isinstance(obj, type): # obj is a class or a metaclass # First, let's see if it has an overloaded __call__ defined # in its metaclass call = _signature_get_user_defined_method(type(obj), "__call__") if call is not None: sig = _signature_from_callable( call, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, sigcls=sigcls, ) else: # Now we check if the 'obj' class has a '__new__' method new = _signature_get_user_defined_method(obj, "__new__") if new is not None: sig = _signature_from_callable( new, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, sigcls=sigcls, ) else: # Finally, we should have at least __init__ implemented init = _signature_get_user_defined_method(obj, "__init__") if init is not None: sig = _signature_from_callable( init, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, sigcls=sigcls, ) if sig is None: # At this point we know, that `obj` is a class, with no user- # defined '__init__', '__new__', or class-level '__call__' for base in obj.__mro__[:-1]: # Since '__text_signature__' is implemented as a # descriptor that extracts text signature from the # class docstring, if 'obj' is derived from a builtin # class, its own '__text_signature__' may be 'None'. # Therefore, we go through the MRO (except the last # class in there, which is 'object') to find the first # class with non-empty text signature. try: text_sig = base.__text_signature__ except AttributeError: pass else: if text_sig: # If 'obj' class has a __text_signature__ attribute: # return a signature based on it return _signature_fromstr(sigcls, obj, text_sig) # No '__text_signature__' was found for the 'obj' class. # Last option is to check if its '__init__' is # object.__init__ or type.__init__. if type not in obj.__mro__: # We have a class (not metaclass), but no user-defined # __init__ or __new__ for it if obj.__init__ is object.__init__ and obj.__new__ is object.__new__: # Return a signature of 'object' builtin. return signature(object) else: raise ValueError( "no signature found for builtin type %r" % obj ) elif not isinstance(obj, _NonUserDefinedCallables): # An object with __call__ # We also check that the 'obj' is not an instance of # _WrapperDescriptor or _MethodWrapper to avoid # infinite recursion (and even potential segfault) call = _signature_get_user_defined_method(type(obj), "__call__") if call is not None: try: sig = _signature_from_callable( call, follow_wrapper_chains=follow_wrapper_chains, skip_bound_arg=skip_bound_arg, sigcls=sigcls, ) except ValueError: msg = "no signature found for %r" % obj raise ValueError(msg) # from ex if sig is not None: # For classes and objects we skip the first parameter of their # __call__, __new__, or __init__ methods if skip_bound_arg: return _signature_bound_method(sig) else: return sig if isinstance(obj, types.BuiltinFunctionType): # Raise a nicer error message for builtins msg = "no signature found for builtin function %r" % obj raise ValueError(msg) raise ValueError("callable %r is not supported by signature" % obj)
def run(self): self.filename_set = set() # a set of dependent filenames self.reporter = self.state.document.reporter self.env = self.state.document.settings.env opt_summary = 'nosummary' not in self.options opt_annotation = 'annotation' in self.options opt_link = 'nolink' not in self.options opt_members = self.options.get('members', None) opt_debug = 'debug' in self.options if opt_members in (None, '') and 'members' in self.options: opt_members = "all" opt_path = self.options.get('path', 'import') opt_syspath = self.options.get('syspath', None) if opt_debug: keep_logged = [] def keep_logging(*els): keep_logged.append(" ".join(str(_) for _ in els)) logging_function = keep_logging else: logging_function = None try: source, lineno = self.reporter.get_source_and_line(self.lineno) except AttributeError: # pragma: no cover source = lineno = None # object name object_name = " ".join(_.strip("\n\r\t ") for _ in self.content) if opt_syspath: syslength = len(sys.path) sys.path.extend(opt_syspath.split(';')) try: obj, _, kind = import_any_object(object_name, use_init=False, fLOG=logging_function) except ImportError as e: mes = "[autosignature] unable to import '{0}' due to '{1}'".format( object_name, e) logger = logging.getLogger("autosignature") logger.warning(mes) if logging_function: logging_function(mes) # pragma: no cover if lineno is not None: logger.warning(' File "{0}", line {1}'.format( source, lineno)) obj = None kind = None if opt_syspath: del sys.path[syslength:] if opt_members is not None and kind != "class": # pragma: no cover logger = logging.getLogger("autosignature") logger.warning( "[autosignature] option members is specified but '{0}' " "is not a class (kind='{1}').".format(object_name, kind)) obj = None # build node node = self.__class__.autosignature_class(rawsource=object_name, source=source, lineno=lineno, objectname=object_name) if opt_path == 'import': if obj is None: logger = logging.getLogger("autosignature") logger.warning( "[autosignature] object '{0}' cannot be imported.".format( object_name)) anchor = object_name elif kind == "staticmethod": cl, fu = object_name.split(".")[-2:] pimp = import_path(obj, class_name=cl, fLOG=logging_function) anchor = '{0}.{1}.{2}'.format(pimp, cl, fu) else: pimp = import_path( obj, err_msg="object name: '{0}'".format(object_name)) anchor = '{0}.{1}'.format( pimp, object_name.rsplit('.', maxsplit=1)[-1]) elif opt_path == 'full': anchor = object_name elif opt_path == 'name': anchor = object_name.rsplit('.', maxsplit=1)[-1] else: # pragma: no cover logger = logging.getLogger("autosignature") logger.warning( "[autosignature] options path is '{0}', it should be in " "(import, name, full) for object '{1}'.".format( opt_path, object_name)) anchor = object_name if obj is None: if opt_link: text = "\n:py:func:`{0} <{1}>`\n\n".format(anchor, object_name) else: text = "\n``{0}``\n\n".format(anchor) # pragma: no cover else: obj_sig = obj.__init__ if kind == "class" else obj try: signature = inspect.signature(obj_sig) parameters = signature.parameters except TypeError as e: # pragma: no cover mes = "[autosignature](1) unable to get signature of '{0}' - {1}.".format( object_name, str(e).replace("\n", "\\n")) logger = logging.getLogger("autosignature") logger.warning(mes) if logging_function: logging_function(mes) signature = None parameters = None except ValueError as e: # pragma: no cover # Backup plan, no __text_signature__, this happen # when a function was created with pybind11. doc = obj_sig.__doc__ sigs = set(enumerate_cleaned_signature(doc)) if len(sigs) == 0: mes = "[autosignature](2) unable to get signature of '{0}' - {1}.".format( object_name, str(e).replace("\n", "\\n")) logger = logging.getLogger("autosignature") logger.warning(mes) if logging_function: logging_function(mes) signature = None parameters = None elif len(sigs) > 1: mes = "[autosignature](2) too many signatures for '{0}' - {1} - {2}.".format( object_name, str(e).replace("\n", "\\n"), " *** ".join(sigs)) logger = logging.getLogger("autosignature") logger.warning(mes) if logging_function: logging_function(mes) signature = None parameters = None else: try: signature = inspect._signature_fromstr( inspect.Signature, obj_sig, list(sigs)[0]) parameters = signature.parameters except TypeError as e: mes = "[autosignature](3) unable to get signature of '{0}' - {1}.".format( object_name, str(e).replace("\n", "\\n")) logger = logging.getLogger("autosignature") logger.warning(mes) if logging_function: logging_function(mes) signature = None parameters = None domkind = { 'meth': 'func', 'function': 'func', 'method': 'meth', 'class': 'class', 'staticmethod': 'meth', 'property': 'meth' }[kind] if signature is None: if opt_link: # pragma: no cover text = "\n:py:{2}:`{0} <{1}>`\n\n".format( anchor, object_name, domkind) else: # pragma: no cover text = "\n``{0} {1}``\n\n".format(kind, object_name) else: signature = self.build_parameters_list(parameters, opt_annotation) text = "\n:py:{3}:`{0} <{1}>` ({2})\n\n".format( anchor, object_name, signature, domkind) if obj is not None and opt_summary: # Documentation. doc = obj.__doc__ # if kind != "class" else obj.__class__.__doc__ if doc is None: # pragma: no cover mes = "[autosignature] docstring empty for '{0}'.".format( object_name) logger = logging.getLogger("autosignature") logger.warning(mes) if logging_function: logging_function(mes) else: if "type(object_or_name, bases, dict)" in doc: raise TypeError( # pragma: no cover "issue with {0}\n{1}".format(obj, doc)) docstring = self.build_summary(doc) text += docstring + "\n\n" if opt_members is not None and kind == "class": docstring = self.build_members(obj, opt_members, object_name, opt_annotation, opt_summary) docstring = "\n".join( map(lambda s: " " + s, docstring.split("\n"))) text += docstring + "\n\n" text_lines = text.split("\n") if logging_function: text_lines.extend([' ::', '', ' [debug]', '']) text_lines.extend(' ' + li for li in keep_logged) text_lines.append('') st = StringList(text_lines) nested_parse_with_titles(self.state, st, node) return [node]