def test_format_annotation(inv, annotation, expected_result):
    result = format_annotation(annotation)
    assert result == expected_result

    # Test with the "fully_qualified" flag turned on
    if 'typing' in expected_result or __name__ in expected_result:
        expected_result = expected_result.replace('~typing', 'typing')
        expected_result = expected_result.replace('~' + __name__, __name__)
        assert format_annotation(annotation,
                                 fully_qualified=True) == expected_result

    # Test for the correct role (class vs data) using the official Sphinx inventory
    if 'typing' in expected_result:
        m = re.match('^:py:(?P<role>class|data|func):`~(?P<name>[^`]+)`',
                     result)
        assert m, 'No match'
        name = m.group('name')
        role = next((o.role for o in inv.objects if o.name == name), None)
        if name in {'typing.Pattern', 'typing.Match', 'typing.NoReturn'}:
            if sys.version_info < (3, 6):
                assert role is None, 'No entry in Python 3.5’s objects.inv'
                return

        assert role is not None, 'Name {} not found'.format(name)
        assert m.group('role') == 'func' if role == 'function' else role
Exemplo n.º 2
0
def _get_lazy_property_doc(prop: LazyProperty,
                           config: Config) -> Tuple[str, str, str]:
    """Get a row documenting a LazyProperty"""
    rtype = format_annotation(prop.type, config)

    lazy_property_type = format_annotation(LazyProperty, config)
    return (f'**{prop.__name__}** ({lazy_property_type})', rtype, prop.__doc__
            or '')
Exemplo n.º 3
0
def _get_property_doc(prop: property, config: Config) -> Tuple[str, str, str]:
    """Get a row documenting a regular @property"""
    fget_rtype = get_type_hints(prop.fget).get('return', Any)
    rtype = format_annotation(fget_rtype, config)
    if TYPE_CHECKING:
        assert prop.fget is not None

    doc = (prop.fget.__doc__ or '').split('\n')[0]
    property_type = format_annotation(property, config)
    return (f'**{prop.fget.__name__}** ({property_type})', rtype, doc)
Exemplo n.º 4
0
def process_docstring(app, what, name, obj, options, lines):
    if what != 'attribute':
        return

    from importlib import import_module
    from typing import get_type_hints

    from sphinx_autodoc_typehints import format_annotation

    *parts, attrname = name.split('.')
    moduleparts = parts.copy()
    while moduleparts:
        try:
            cls = import_module('.'.join(moduleparts))
        except ImportError:
            del moduleparts[-1]
        else:
            break
    if not moduleparts:
        print('XXX failed to import module')
        return
    innerparts = parts[len(moduleparts):]
    while innerparts:
        cls = getattr(cls, innerparts.pop(0))
    annotations = get_type_hints(cls)
    if attrname in annotations:
        lines.extend([
            '', '**Type**: {}'.format(format_annotation(annotations[attrname]))
        ])
Exemplo n.º 5
0
def test_format_annotation(inv, annotation, expected_result):
    result = format_annotation(annotation)
    assert result == expected_result

    # Test with the "simplify_optional_unions" flag turned off:
    if re.match(r'^:py:data:`~typing\.Union`\\\[.*``None``.*\]',
                expected_result):
        # strip None - argument and copy string to avoid conflicts with
        # subsequent tests
        expected_result_not_simplified = expected_result.replace(
            ', ``None``', '')
        # encapsulate Union in typing.Optional
        expected_result_not_simplified = ':py:data:`~typing.Optional`\\[' + \
            expected_result_not_simplified
        expected_result_not_simplified += ']'
        assert format_annotation(annotation, simplify_optional_unions=False) == \
            expected_result_not_simplified

        # Test with the "fully_qualified" flag turned on
        if 'typing' in expected_result_not_simplified:
            expected_result_not_simplified = expected_result_not_simplified.replace(
                '~typing', 'typing')
            assert format_annotation(annotation,
                                     fully_qualified=True,
                                     simplify_optional_unions=False) == \
                expected_result_not_simplified

    # Test with the "fully_qualified" flag turned on
    if 'typing' in expected_result or __name__ in expected_result:
        expected_result = expected_result.replace('~typing', 'typing')
        expected_result = expected_result.replace('~' + __name__, __name__)
        assert format_annotation(annotation,
                                 fully_qualified=True) == expected_result

    # Test for the correct role (class vs data) using the official Sphinx inventory
    if 'typing' in expected_result:
        m = re.match('^:py:(?P<role>class|data|func):`~(?P<name>[^`]+)`',
                     result)
        assert m, 'No match'
        name = m.group('name')
        expected_role = next((o.role for o in inv.objects if o.name == name),
                             None)
        if expected_role:
            if expected_role == 'function':
                expected_role = 'func'

            assert m.group('role') == expected_role
Exemplo n.º 6
0
def test_format_annotation_both_libs(inv, library, annotation, params, expected_result):
    try:
        annotation_cls = getattr(library, annotation)
    except AttributeError:
        pytest.skip('{} not available in the {} module'.format(annotation, library.__name__))

    ann = annotation_cls if params is None else annotation_cls[params]
    result = format_annotation(ann)
    assert result == expected_result
Exemplo n.º 7
0
def _get_field_doc(field: Attribute, config: Config) -> Tuple[str, str, str]:
    """Get a row documenting an attrs Attribute"""
    rtype = format_annotation(field.type, config)
    doc = field.metadata.get('doc', '')
    options = field.metadata.get('options', [])
    if options:
        options = ', '.join([f'``{opt}``' for opt in options if opt])
        doc += f'\n\n**Options:** {options}'
    return (f'**{field.name}**', rtype, doc)
Exemplo n.º 8
0
def test_format_annotation(inv, annotation, expected_result):
    result = format_annotation(annotation)
    assert result == expected_result

    # Test with the "fully_qualified" flag turned on
    if 'typing' in expected_result or __name__ in expected_result:
        expected_result = expected_result.replace('~typing', 'typing')
        expected_result = expected_result.replace('~' + __name__, __name__)
        assert format_annotation(annotation, fully_qualified=True) == expected_result

    # Test for the correct role (class vs data) using the official Sphinx inventory
    if 'typing' in expected_result:
        m = re.match('^:py:(?P<role>class|data|func):`~(?P<name>[^`]+)`', result)
        assert m, 'No match'
        name = m.group('name')
        expected_role = next((o.role for o in inv.objects if o.name == name), None)
        if expected_role:
            if expected_role == 'function':
                expected_role = 'func'

            assert m.group('role') == expected_role
Exemplo n.º 9
0
    def process_docstring(self, app, what, name, obj, options, lines):
        if what == "class":
            self.hints = sphinx_autodoc_typehints.get_all_type_hints(obj, name)

        elif what == "attribute":
            name = name.split(".")[-1]
            hint = self.hints.get(name)
            if hint:

                typename = sphinx_autodoc_typehints.format_annotation(hint)
                lines.append(":type: " + typename)
                lines.append("")
def test_format_annotation_type(type_param, expected_result):
    annotation = Type[type_param] if type_param else Type
    result = format_annotation(annotation, {})
    assert result == expected_result
Exemplo n.º 11
0
def test_format_annotation_type(type_param, expected_result):
    annotation = Type[type_param] if type_param else Type
    result = format_annotation(annotation)
    assert result.startswith(expected_result)
Exemplo n.º 12
0
def test_format_annotation(annotation, expected_result):
    result = format_annotation(annotation)
    assert result == expected_result
def test_format_annotation_fully_qualified(annotation, expected_result):
    result = format_annotation(annotation, fully_qualified=True)
    assert result == expected_result
Exemplo n.º 14
0
    def run(self):
        of_class = self.arguments[0].split('.')
        class_type = getattr(
            __import__('.'.join(of_class[:-1]), fromlist=[of_class[-1]]),
            of_class[-1])

        def predicate(a):
            return isinstance(a, property)

        class_members = inspect.getmembers(class_type, predicate=predicate)
        return_types = [
            inspect.signature(p.fget).return_annotation
            for _, p in class_members
        ]
        docs = [inspect.getdoc(p) or '' for _, p in class_members]

        table = nodes.table()
        t_group = nodes.tgroup(cols=4)

        t_group += nodes.colspec(colwidth=1)
        t_group += nodes.colspec(colwidth=1)
        t_group += nodes.colspec(colwidth=1)
        t_group += nodes.colspec(colwidth=10)

        # header
        t_group += nodes.thead(
            '',
            nodes.row(
                '', *[
                    nodes.entry('', nodes.line(text=c))
                    for c in ["field", "type", "note", "description"]
                ]))

        t_body = nodes.tbody()
        for (name, _), return_type, doc in zip(class_members, return_types,
                                               docs):
            doc_l = doc.split('\n')
            location = next((i for i in doc_l if i.startswith(':note:')),
                            '')[len(':note:'):]
            doc_stripped = '\n'.join(i for i in doc_l
                                     if not i.startswith(':note:'))

            doc_stripped_node = self.render_content(doc_stripped)
            return_type_node = self.render_content(
                format_annotation(return_type))

            ref_key = '{}.{}'.format(self.arguments[1], name)
            name_node = nodes.reference('', name, refid=ref_key)
            ref = nodes.paragraph('', '', name_node)

            t_body += nodes.row(name,
                                nodes.entry('', ref),
                                nodes.entry('', return_type_node),
                                nodes.entry('', nodes.literal(text=location)),
                                nodes.entry('', doc_stripped_node),
                                ids=[ref_key])

        t_group += t_body
        table += t_group

        return [table]
def test_format_annotation_test():
    result = format_annotation(Tuple[str, ...], {})
    assert result
def test_format_annotation_alias(annotation, expected_result, alias):
    result = format_annotation(annotation, alias)
    assert expected_result == result
Exemplo n.º 17
0
    def generate(
        self,
        more_content: Any = None,
        real_modname: str = None,
        check_module: bool = False,
        all_members: bool = False,
    ) -> None:
        """Generate reST for the object given by *self.name*, and possibly for
        its members.

        If *more_content* is given, include that content. If *real_modname* is
        given, use that module name to find attribute docs. If *check_module* is
        True, only generate if the object is defined in the module name it is
        imported from. If *all_members* is True, document all members.
        """
        if not self.parse_name():
            # need a module to import
            logger.warning(
                __('don\'t know which module to import for autodocumenting '
                   '%r (try placing a "module" or "currentmodule" directive '
                   'in the document, or giving an explicit module name)') %
                self.name,
                type='autodoc',
            )
            return

        # now, import the module and get object to document
        if not self.import_object():
            return

        # Is this the right kind?  If not, delegate to the superclass?
        # TODO
        #   return super().generate(...)
        #

        if not isinstance(self.object, _XCType):
            super().generate(
                more_content=more_content,
                real_modname=real_modname,
                check_module=check_module,
                all_members=all_members,
            )
            return

        schema = self.object._model.schema()

        try:
            hints = get_type_hints(self.object._model)
        except Exception as e:
            print("Get type hints for %s: %s" % (self.fullname, e))
            hints = {}

        source = "description of %s" % (self.object_name)

        sig = "(%s)" % (", ".join(schema['properties'].keys()))

        self.add_directive_header(sig)

        self.add_line('', source)

        indent = self.indent
        try:
            self.indent += '   '

            for (i, line) in enumerate(schema['description'].splitlines()):
                self.add_line(line.strip(), source, i)

            # Always do 'show-inheritance'

            bases = self.get_base_description()

            if bases:
                self.add_line('', source)
                self.add_line("A subclass of %s" % (bases), source)

            self.add_line('', source)

            source = self.get_sourcename()

            has_params = False
            for (pname, ptype) in schema['properties'].items():
                has_params = True
                hint = hints.get(pname, '')
                if hint:
                    hint = format_annotation(hint)
                else:
                    hint = '(unspecified)'

                self.add_line(':param %s: %s' % (pname, ptype['title']),
                              source)
                self.add_line(':type %s: %s' % (pname, hint), source)

                self.add_line('', source)

            if has_params:
                self.add_line(
                    'Each parameter defines an attribute of the same name.',
                    source)
                self.add_line('', source)

            subclasses = self.object.__subclasses__()

            # Add the attributes

            # For a base class, the only inherited property that's of interest
            # is the status

            if subclasses:
                show_props = ('status', )
            else:
                show_props = ('typename', 'title', 'detail', 'status')

            self.add_line('Properties (read-only)', source)

            for name in show_props:
                self.add_line(
                    '  :py:attr:`%s` = %s' %
                    (name, repr(getattr(self.object, name, '(not set)'))),
                    source,
                )
                self.add_line('', source)

            self.add_line(
                ('For more information about the above properties'
                 ' please refer to the documentation for :py:mod:`rjgtoys.xc`.'
                 ),
                source,
            )

            if subclasses:
                self.add_line('', source)
                self.add_line('Subclasses:', source)
                self.add_line('', source)

                for sub in subclasses:
                    self.add_line(' - :exc:`%s`' % (sub.__name__), source)
                    self.add_line('', source)

        finally:
            self.indent = indent

        return

        # DEBUG: dump our output

        for line in self.directive.result:
            print("RESULT: %s" % (line))