Пример #1
0
def _get_source_definition(obj):
    """Get a source representation of the function definition."""
    try:
        obj = _unwrap(obj)

        if dir2.safe_hasattr(
                obj, '__call__') and not oinspect.is_simple_callable(obj):
            obj = obj.__call__

        lines, lnum = inspect.findsource(obj)
        block = inspect.getblock(lines[lnum:])

        # Trim leading whitespace for all lines.
        prefix = re.match('^([ \t]*)', block[0]).group()
        trimmed = []
        for line in block:
            if line.startswith(prefix):
                line = line[len(prefix):]
            trimmed.append(line)

        # Override the default join to avoid wrapping.
        def join_lines(source):
            return ''.join(source)

        module = ast.parse('\n'.join(trimmed), mode='exec')
        function = module.body[0]
        # Remove 'self' if it's the first arg.
        if function.args.args and function.args.args[0].arg == 'self':
            function.args.args.pop(0)

        function.body = []
        decl = astor.to_source(function,
                               indent_with='',
                               pretty_source=join_lines).strip()
        # Strip the trailing `:`
        if decl.endswith(':'):
            decl = decl[:-1]
        return decl
    except Exception:  # pylint: disable=broad-except
        return None
Пример #2
0
    def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
        """Compute a dict with detailed information about an object.

    This overrides the superclass method for two main purposes:
     * avoid calling str() or repr() on the object
     * use our custom repr

    Args:
      obj: object to inspect.
      oname: (optional) string reference to this object
      formatter: (optional) custom docstring formatter
      info: (optional) previously computed information about obj
      detail_level: (optional) 0 or 1; 1 means "include more detail"

    Returns:
      A dict with information about obj.
    """

        # We want to include the following list of keys for all objects:
        # * name
        # * found
        # * isclass
        # * string_form
        # * type_name
        #
        # For callables, we want to add a subset of:
        # * argspec
        # * call_docstring
        # * definition
        # * docstring
        # * file
        # * init_definition
        # * init_docstring
        # * source_end_line
        # * source_start_line
        # * source_definition
        #
        # For detail_level 1, we include:
        # * file
        # This can be expensive, as the stdlib mechanisms for looking up the file
        # containing obj may call repr(obj).
        #
        # NOTE: These keys should stay in sync with the corresponding list in our
        # frontend code.
        #
        # We want non-None values for:
        # * isalias
        # * ismagic
        # * namespace
        #
        # TODO(b/138128444): Handle class_docstring and call_def, or determine that
        # we're safe ignoring them.

        obj_type = type(obj)
        out = {
            'name': oname,
            'found': True,
            'is_class': inspect.isclass(obj),
            'string_form': None,
            # Fill in empty values.
            'docstring': None,
            'file': None,
            'isalias': False,
            'ismagic': info.ismagic if info else False,
            'namespace': info.namespace if info else '',
        }
        if detail_level >= self.str_detail_level:
            out['string_form'] = _safe_repr(obj)

        if getattr(info, 'ismagic', False):
            out['type_name'] = 'Magic function'
        else:
            out['type_name'] = obj_type.__name__

        # If the object is callable, we want to compute a docstring and related
        # information. We could exit early, but all the code below is in conditional
        # blocks, so there's no need.
        #
        # We can't simply call into the superclass method, as we need to avoid
        # (transitively) calling inspect.getfile(): this function will end up
        # calling repr() on our object.

        # We want to include a docstring if we don't have source, which happens
        # when:
        # * detail_level == 0, or
        # * detail_level == 1 but we can't find source
        # So we first try dealing with detail_level == 1, and then set
        # the docstring if no source is set.
        if detail_level == 1:
            # This should only ever happen if the user has asked for source (eg via
            # `obj??`), so we're OK with potentially calling repr for now.
            # TODO(b/138128444): Ensure we don't call str() or repr().
            source = _getsource(obj)
            if source is None and hasattr(obj, '__class__'):
                source = _getsource(obj.__class__)
            if source is not None:
                out['source'] = source
        if 'source' not in out:
            formatter = formatter or (lambda x: x)
            docstring = formatter(_getdoc(obj) or '<no docstring>')
            if docstring:
                out['docstring'] = docstring

        if _iscallable(obj):
            filename = oinspect.find_file(obj)
            if filename and (filename.endswith(
                ('.py', '.py3', '.pyc')) or '<ipython-input' in filename):
                out['file'] = filename

            line = oinspect.find_source_lines(obj)
            out['source_start_line'] = line
            # inspect.getsourcelines exposes the length of the source as well, which
            # can be used to highlight the entire code block, but find_source_lines
            # currently does not expose this. For now just highlight the first line.
            out['source_end_line'] = line

        # The remaining attributes only apply to classes or callables.
        if inspect.isclass(obj):
            # For classes with an __init__, we set init_definition and init_docstring.
            init = getattr(obj, '__init__', None)
            if init:
                init_docstring = _getdoc(init)
                if init_docstring and init_docstring != _BASE_INIT_DOC:
                    out['init_docstring'] = init_docstring
                init_def = self._getdef(init, oname)
                if init_def:
                    out['init_definition'] = init_def
                src_def = _get_source_definition(init)
                if src_def:
                    out['source_definition'] = src_def
            # For classes, the __init__ method is the method invoked on call, but
            # old-style classes may not have an __init__ method.
            if init:
                argspec = _getargspec_dict(init)
                if argspec:
                    out['argspec'] = argspec
        elif callable(obj):
            definition = self._getdef(obj, oname)
            if definition:
                out['definition'] = definition
            src_def = _get_source_definition(obj)
            if src_def:
                out['source_definition'] = src_def

            if not oinspect.is_simple_callable(obj):
                call_docstring = _getdoc(obj.__call__)
                if call_docstring and call_docstring != _BASE_CALL_DOC:
                    out['call_docstring'] = call_docstring

            out['argspec'] = _getargspec_dict(obj)

        return oinspect.object_info(**out)