Пример #1
0
def extract_enum_doc(state: State, path: List[str], enum_):
    out = Empty()
    out.name = path[-1]
    out.values = []
    out.has_details = False
    out.has_value_details = False

    # The happy case
    if issubclass(enum_, enum.Enum):
        # Enum doc is by default set to a generic value. That's useless as well.
        if enum_.__doc__ == 'An enumeration.':
            out.summary = ''
        else:
            # TODO: external summary for enums
            out.summary = extract_summary(state, {}, [], enum_.__doc__)

        out.base = extract_type(enum_.__base__)

        for i in enum_:
            value = Empty()
            value.name = i.name
            value.value = html.escape(repr(i.value))

            # Value doc gets by default inherited from the enum, that's useless
            if i.__doc__ == enum_.__doc__:
                value.summary = ''
            else:
                # TODO: external summary for enum values
                value.summary = extract_summary(state, {}, [], i.__doc__)

            if value.summary:
                out.has_details = True
                out.has_value_details = True
            out.values += [value]

    # Pybind11 enums are ... different
    elif state.config['PYBIND11_COMPATIBILITY']:
        assert hasattr(enum_, '__members__')

        # TODO: external summary for enums
        out.summary = extract_summary(state, {}, [], enum_.__doc__)
        out.base = None

        for name, v in enum_.__members__.items():
            value = Empty()
            value. name = name
            value.value = int(v)
            # TODO: once https://github.com/pybind/pybind11/pull/1160 is
            #       released, extract from class docs (until then the class
            #       docstring is duplicated here, which is useless)
            value.summary = ''
            out.values += [value]

    return out
Пример #2
0
def extract_data_doc(state: State, parent, path: List[str], data):
    assert not inspect.ismodule(data) and not inspect.isclass(data) and not inspect.isroutine(data) and not inspect.isframe(data) and not inspect.istraceback(data) and not inspect.iscode(data)

    out = Empty()
    out.name = path[-1]
    # Welp. https://stackoverflow.com/questions/8820276/docstring-for-variable
    out.summary = ''
    out.has_details = False
    if hasattr(parent, '__annotations__') and out.name in parent.__annotations__:
        out.type = extract_annotation(state, parent.__annotations__[out.name])
    else:
        out.type = None
    # The autogenerated <foo.bar at 0xbadbeef> is useless, so provide the value
    # only if __repr__ is implemented for given type
    if '__repr__' in type(data).__dict__:
        out.value = html.escape(repr(data))
    else:
        out.value = None

    # External data summary, if provided
    path_str = '.'.join(path)
    if path_str in state.data_docs:
        # TODO: use also the contents
        out.summary = render_inline_rst(state, state.data_docs[path_str]['summary'])
        del state.data_docs[path_str]

    return out
Пример #3
0
def extract_property_doc(state: State, path: List[str], property):
    assert inspect.isdatadescriptor(property)

    out = Empty()
    out.name = path[-1]
    # TODO: external summary for properties
    out.summary = extract_summary(state, {}, [], property.__doc__)
    out.is_settable = property.fset is not None
    out.is_deletable = property.fdel is not None
    out.has_details = False

    try:
        signature = inspect.signature(property.fget)
        out.type = extract_annotation(state, signature.return_annotation)
    except ValueError:
        # pybind11 properties have the type in the docstring
        if state.config['PYBIND11_COMPATIBILITY']:
            out.type = parse_pybind_signature(state, property.fget.__doc__)[3]
        else:
            out.type = None

    return out
Пример #4
0
def extract_function_doc(state: State, parent, path: List[str], function) -> List[Any]:
    assert inspect.isfunction(function) or inspect.ismethod(function) or inspect.isroutine(function)

    # Extract the signature from the docstring for pybind11, since it can't
    # expose it to the metadata: https://github.com/pybind/pybind11/issues/990
    # What's not solvable with metadata, however, are function overloads ---
    # one function in Python may equal more than one function on the C++ side.
    # To make the docs usable, list all overloads separately.
    if state.config['PYBIND11_COMPATIBILITY'] and function.__doc__.startswith(path[-1]):
        funcs = parse_pybind_docstring(state, path[-1], function.__doc__)
        overloads = []
        for name, summary, args, type in funcs:
            out = Empty()
            out.name = path[-1]
            out.params = []
            out.has_complex_params = False
            out.has_details = False
            # TODO: external summary for functions
            out.summary = summary

            # Don't show None return type for void functions
            out.type = None if type == 'None' else type

            # There's no other way to check staticmethods than to check for
            # self being the name of first parameter :( No support for
            # classmethods, as C++11 doesn't have that
            out.is_classmethod = False
            if inspect.isclass(parent) and args and args[0][0] == 'self':
                out.is_staticmethod = False
            else:
                out.is_staticmethod = True

            # Guesstimate whether the arguments are positional-only or
            # position-or-keyword. It's either all or none. This is a brown
            # magic, sorry.

            # For instance methods positional-only argument names are either
            # self (for the first argument) or arg(I-1) (for second
            # argument and further). Also, the `self` argument is
            # positional-or-keyword only if there are positional-or-keyword
            # arguments afgter it, otherwise it's positional-only.
            if inspect.isclass(parent) and not out.is_staticmethod:
                assert args and args[0][0] == 'self'

                positional_only = True
                for i, arg in enumerate(args[1:]):
                    name, type, default = arg
                    if name != 'arg{}'.format(i):
                        positional_only = False
                        break

            # For static methods or free functions positional-only arguments
            # are argI.
            else:
                positional_only = True
                for i, arg in enumerate(args):
                    name, type, default = arg
                    if name != 'arg{}'.format(i):
                        positional_only = False
                        break

            for i, arg in enumerate(args):
                name, type, default = arg
                param = Empty()
                param.name = name
                # Don't include redundant type for the self argument
                if name == 'self': param.type = None
                else: param.type = type
                param.default = html.escape(default or '')
                if type or default: out.has_complex_params = True

                # *args / **kwargs can still appear in the parsed signatures if
                # the function accepts py::args / py::kwargs directly
                if name == '*args':
                    param.name = 'args'
                    param.kind = 'VAR_POSITIONAL'
                elif name == '**kwargs':
                    param.name = 'kwargs'
                    param.kind = 'VAR_KEYWORD'
                else:
                    param.kind = 'POSITIONAL_ONLY' if positional_only else 'POSITIONAL_OR_KEYWORD'

                out.params += [param]

            overloads += [out]

        return overloads

    # Sane introspection path for non-pybind11 code
    else:
        out = Empty()
        out.name = path[-1]
        out.params = []
        out.has_complex_params = False
        out.has_details = False
        # TODO: external summary for functions
        out.summary = extract_summary(state, {}, [], function.__doc__)

        # Decide if classmethod or staticmethod in case this is a method
        if inspect.isclass(parent):
            out.is_classmethod = inspect.ismethod(function)
            out.is_staticmethod = out.name in parent.__dict__ and isinstance(parent.__dict__[out.name], staticmethod)

        try:
            signature = inspect.signature(function)
            out.type = extract_annotation(state, signature.return_annotation)
            for i in signature.parameters.values():
                param = Empty()
                param.name = i.name
                param.type = extract_annotation(state, i.annotation)
                if param.type:
                    out.has_complex_params = True
                if i.default is inspect.Signature.empty:
                    param.default = None
                else:
                    param.default = repr(i.default)
                    out.has_complex_params = True
                param.kind = str(i.kind)
                out.params += [param]

        # In CPython, some builtin functions (such as math.log) do not provide
        # metadata about their arguments. Source:
        # https://docs.python.org/3/library/inspect.html#inspect.signature
        except ValueError:
            param = Empty()
            param.name = '...'
            param.name_type = param.name
            out.params = [param]
            out.type = None

        return [out]