def test_ModuleAnalyzer_find_tags():
    code = (
        'class Foo(object):\n'  # line: 1
        '    """class Foo!"""\n'
        '    def __init__(self):\n'
        '       pass\n'
        '\n'
        '    def bar(self, arg1, arg2=True, *args, **kwargs):\n'
        '       """method Foo.bar"""\n'
        '       pass\n'
        '\n'
        '    class Baz(object):\n'
        '       def __init__(self):\n'  # line: 11
        '           pass\n'
        '\n'
        'def qux():\n'
        '   """function baz"""\n'
        '   pass\n'
        '\n'
        '@decorator\n'
        'def quux():\n'
        '   pass\n')
    analyzer = ModuleAnalyzer.for_string(code, 'module')
    tags = analyzer.find_tags()
    assert set(tags.keys()) == {
        'Foo', 'Foo.__init__', 'Foo.bar', 'Foo.Baz', 'Foo.Baz.__init__', 'qux',
        'quux'
    }
    assert tags['Foo'] == ('class', 1, 13)  # type, start, end
    assert tags['Foo.__init__'] == ('def', 3, 5)
    assert tags['Foo.bar'] == ('def', 6, 9)
    assert tags['Foo.Baz'] == ('class', 10, 13)
    assert tags['Foo.Baz.__init__'] == ('def', 11, 13)
    assert tags['qux'] == ('def', 14, 17)
    assert tags['quux'] == ('def', 18, 21)  # decorator
Example #2
0
def test_ModuleAnalyzer_find_tags():
    code = ('class Foo(object):\n'  # line: 1
            '    """class Foo!"""\n'
            '    def __init__(self):\n'
            '       pass\n'
            '\n'
            '    def bar(self, arg1, arg2=True, *args, **kwargs):\n'
            '       """method Foo.bar"""\n'
            '       pass\n'
            '\n'
            '    class Baz(object):\n'
            '       def __init__(self):\n'  # line: 11
            '           pass\n'
            '\n'
            'def qux():\n'
            '   """function baz"""\n'
            '   pass\n'
            '\n'
            '@decorator\n'
            'def quux():\n'
            '   pass\n')
    analyzer = ModuleAnalyzer.for_string(code, 'module')
    tags = analyzer.find_tags()
    assert set(tags.keys()) == {'Foo', 'Foo.__init__', 'Foo.bar',
                                'Foo.Baz', 'Foo.Baz.__init__', 'qux', 'quux'}
    assert tags['Foo'] == ('class', 1, 13)  # type, start, end
    assert tags['Foo.__init__'] == ('def', 3, 5)
    assert tags['Foo.bar'] == ('def', 6, 9)
    assert tags['Foo.Baz'] == ('class', 10, 13)
    assert tags['Foo.Baz.__init__'] == ('def', 11, 13)
    assert tags['qux'] == ('def', 14, 17)
    assert tags['quux'] == ('def', 18, 21)  # decorator
Example #3
0
def test_ModuleAnalyzer_for_file():
    analyzer = ModuleAnalyzer.for_string(SPHINX_MODULE_PATH, 'sphinx')
    assert analyzer.modname == 'sphinx'
    assert analyzer.srcname == '<string>'
    if PY2:
        assert analyzer.encoding == 'ascii'
    else:
        assert analyzer.encoding is None
Example #4
0
def test_ModuleAnalyzer_for_string():
    analyzer = ModuleAnalyzer.for_string('print("Hello world")', 'module_name')
    assert analyzer.modname == 'module_name'
    assert analyzer.srcname == '<string>'
    if PY2:
        assert analyzer.encoding == 'ascii'
    else:
        assert analyzer.encoding is None
Example #5
0
def test_ModuleAnalyzer_for_file():
    analyzer = ModuleAnalyzer.for_string(SPHINX_MODULE_PATH, 'sphinx')
    assert analyzer.modname == 'sphinx'
    assert analyzer.srcname == '<string>'
    if PY2:
        assert analyzer.encoding == 'ascii'
    else:
        assert analyzer.encoding is None
Example #6
0
def test_ModuleAnalyzer_for_string():
    analyzer = ModuleAnalyzer.for_string('print("Hello world")', 'module_name')
    assert analyzer.modname == 'module_name'
    assert analyzer.srcname == '<string>'
    if PY2:
        assert analyzer.encoding == 'ascii'
    else:
        assert analyzer.encoding is None
Example #7
0
def test_ModuleAnalyzer_find_attr_docs_for_posonlyargs_method():
    code = ('class Foo(object):\n'
            '    def __init__(self, /):\n'
            '       self.attr = None  #: attribute comment\n')
    analyzer = ModuleAnalyzer.for_string(code, 'module')
    docs = analyzer.find_attr_docs()
    assert set(docs) == {('Foo', 'attr')}
    assert docs[('Foo', 'attr')] == ['attribute comment', '']
    assert analyzer.tagorder == {'Foo': 0, 'Foo.__init__': 1, 'Foo.attr': 2}
Example #8
0
def test_ModuleAnalyzer_pep3132():
    code = ('class Foo(object):\n'
            '    """class Foo!"""\n'
            '    #: comment before attr1\n'
            '    attr1 = None\n'
            '\n'
            '    def bar(self, *args, **kwargs):\n'
            '       """method Foo.bar"""\n'
            '       head, *tail = kwargs\n')
    analyzer = ModuleAnalyzer.for_string(code, 'module')
    docs = analyzer.find_attr_docs()
    assert set(docs) == {('Foo', 'attr1')}
Example #9
0
def test_ModuleAnalyzer_find_attr_docs():
    code = ('class Foo(object):\n'
            '    """class Foo!"""\n'
            '    #: comment before attr1\n'
            '    attr1 = None\n'
            '    attr2 = None  # attribute comment for attr2 (without colon)\n'
            '    attr3 = None  #: attribute comment for attr3\n'
            '    attr4 = None  #: long attribute comment\n'
            '                  #: for attr4\n'
            '    #: comment before attr5\n'
            '    attr5 = None  #: attribute comment for attr5\n'
            '    attr6, attr7 = 1, 2  #: this comment is ignored\n'
            '\n'
            '    def __init__(self):\n'
            '       self.attr8 = None  #: first attribute comment (ignored)\n'
            '       self.attr8 = None  #: attribute comment for attr8\n'
            '       #: comment before attr9\n'
            '       self.attr9 = None  #: comment after attr9\n'
            '       "string after attr9"\n'
            '\n'
            '    def bar(self, arg1, arg2=True, *args, **kwargs):\n'
            '       """method Foo.bar"""\n'
            '       pass\n'
            '\n'
            'def baz():\n'
            '   """function baz"""\n'
            '   pass\n')
    analyzer = ModuleAnalyzer.for_string(code, 'module')
    docs = analyzer.find_attr_docs()
    assert set(docs) == {('Foo', 'attr1'),
                         ('Foo', 'attr3'),
                         ('Foo', 'attr4'),
                         ('Foo', 'attr5'),
                         ('Foo', 'attr8'),
                         ('Foo', 'attr9')}
    assert docs[('Foo', 'attr1')] == ['comment before attr1', '']
    assert docs[('Foo', 'attr3')] == ['attribute comment for attr3', '']
    assert docs[('Foo', 'attr4')] == ['long attribute comment', '']
    assert docs[('Foo', 'attr4')] == ['long attribute comment', '']
    assert docs[('Foo', 'attr5')] == ['attribute comment for attr5', '']
    assert docs[('Foo', 'attr8')] == ['attribute comment for attr8', '']
    assert docs[('Foo', 'attr9')] == ['string after attr9', '']
def test_ModuleAnalyzer_find_attr_docs():
    code = ('class Foo(object):\n'
            '    """class Foo!"""\n'
            '    #: comment before attr1\n'
            '    attr1 = None\n'
            '    attr2 = None  # attribute comment for attr2 (without colon)\n'
            '    attr3 = None  #: attribute comment for attr3\n'
            '    attr4 = None  #: long attribute comment\n'
            '                  #: for attr4\n'
            '    #: comment before attr5\n'
            '    attr5 = None  #: attribute comment for attr5\n'
            '    attr6, attr7 = 1, 2  #: this comment is ignored\n'
            '\n'
            '    def __init__(self):\n'
            '       self.attr8 = None  #: first attribute comment (ignored)\n'
            '       self.attr8 = None  #: attribute comment for attr8\n'
            '       #: comment before attr9\n'
            '       self.attr9 = None  #: comment after attr9\n'
            '       "string after attr9"\n'
            '\n'
            '    def bar(self, arg1, arg2=True, *args, **kwargs):\n'
            '       """method Foo.bar"""\n'
            '       pass\n'
            '\n'
            'def baz():\n'
            '   """function baz"""\n'
            '   pass\n')
    analyzer = ModuleAnalyzer.for_string(code, 'module')
    docs = analyzer.find_attr_docs()
    assert set(docs) == {('Foo', 'attr1'), ('Foo', 'attr3'), ('Foo', 'attr4'),
                         ('Foo', 'attr5'), ('Foo', 'attr8'), ('Foo', 'attr9')}
    assert docs[('Foo', 'attr1')] == ['comment before attr1', '']
    assert docs[('Foo', 'attr3')] == ['attribute comment for attr3', '']
    assert docs[('Foo', 'attr4')] == ['long attribute comment', '']
    assert docs[('Foo', 'attr4')] == ['long attribute comment', '']
    assert docs[('Foo', 'attr5')] == ['attribute comment for attr5', '']
    assert docs[('Foo', 'attr8')] == ['attribute comment for attr8', '']
    assert docs[('Foo', 'attr9')] == ['string after attr9', '']
Example #11
0
def test_ModuleAnalyzer_find_attr_docs():
    code = ('class Foo(object):\n'
            '    """class Foo!"""\n'
            '    #: comment before attr1\n'
            '    attr1 = None\n'
            '    attr2 = None  # attribute comment for attr2 (without colon)\n'
            '    attr3 = None  #: attribute comment for attr3\n'
            '    attr4 = None  #: long attribute comment\n'
            '                  #: for attr4\n'
            '    #: comment before attr5\n'
            '    attr5 = None  #: attribute comment for attr5\n'
            '    attr6, attr7 = 1, 2  #: this comment is ignored\n'
            '\n'
            '    def __init__(self):\n'
            '       self.attr8 = None  #: first attribute comment (ignored)\n'
            '       self.attr8 = None  #: attribute comment for attr8\n'
            '       #: comment before attr9\n'
            '       self.attr9 = None  #: comment after attr9\n'
            '       "string after attr9"\n'
            '\n'
            '    def bar(self, arg1, arg2=True, *args, **kwargs):\n'
            '       """method Foo.bar"""\n'
            '       pass\n'
            '\n'
            'def baz():\n'
            '   """function baz"""\n'
            '   pass\n'
            '\n'
            'class Qux: attr1 = 1; attr2 = 2')
    analyzer = ModuleAnalyzer.for_string(code, 'module')
    docs = analyzer.find_attr_docs()
    assert set(docs) == {('Foo', 'attr1'), ('Foo', 'attr3'), ('Foo', 'attr4'),
                         ('Foo', 'attr5'), ('Foo', 'attr6'), ('Foo', 'attr7'),
                         ('Foo', 'attr8'), ('Foo', 'attr9')}
    assert docs[('Foo', 'attr1')] == ['comment before attr1', '']
    assert docs[('Foo', 'attr3')] == ['attribute comment for attr3', '']
    assert docs[('Foo', 'attr4')] == ['long attribute comment', '']
    assert docs[('Foo', 'attr4')] == ['long attribute comment', '']
    assert docs[('Foo', 'attr5')] == ['attribute comment for attr5', '']
    assert docs[('Foo', 'attr6')] == ['this comment is ignored', '']
    assert docs[('Foo', 'attr7')] == ['this comment is ignored', '']
    assert docs[('Foo', 'attr8')] == ['attribute comment for attr8', '']
    assert docs[('Foo', 'attr9')] == ['string after attr9', '']
    assert analyzer.tagorder == {
        'Foo': 0,
        'Foo.__init__': 8,
        'Foo.attr1': 1,
        'Foo.attr2': 2,
        'Foo.attr3': 3,
        'Foo.attr4': 4,
        'Foo.attr5': 5,
        'Foo.attr6': 6,
        'Foo.attr7': 7,
        'Foo.attr8': 10,
        'Foo.attr9': 12,
        'Foo.bar': 13,
        'baz': 14,
        'Qux': 15,
        'Qux.attr1': 16,
        'Qux.attr2': 17
    }
Example #12
0
def test_ModuleAnalyzer_for_file():
    analyzer = ModuleAnalyzer.for_string(SPHINX_MODULE_PATH, 'sphinx')
    assert analyzer.modname == 'sphinx'
    assert analyzer.srcname == '<string>'
Example #13
0
def test_ModuleAnalyzer_for_string():
    analyzer = ModuleAnalyzer.for_string('print("Hello world")', 'module_name')
    assert analyzer.modname == 'module_name'
    assert analyzer.srcname == '<string>'
    def sort_members(
        self,
        documenters: List[Tuple[Documenter, bool]],
        order: str,
    ) -> List[Tuple[Documenter, bool]]:
        r"""
		Sort the :class:`typing.NamedTuple`\'s members.

		:param documenters:
		:param order:
		"""

        # The documenters for the fields and methods, in the desired order
        # The fields will be in bysource order regardless of the order option
        documenters = super().sort_members(documenters, order)

        # Size varies depending on docutils config
        a_tab = ' ' * self.env.app.config.docutils_tab_width

        # Mapping of member names to docstrings (as list of strings)
        member_docstrings: Dict[str, List[str]]

        try:
            namedtuple_source = textwrap.dedent(inspect.getsource(self.object))

            # Mapping of member names to docstrings (as list of strings)
            member_docstrings = {
                k[1]: v
                for k, v in ModuleAnalyzer.for_string(
                    namedtuple_source,
                    self.object.__module__).find_attr_docs().items()
            }

        except (TypeError, OSError):
            member_docstrings = {}

        # set sourcename and add content from attribute documentation
        sourcename = self.get_sourcename()

        params, pre_output, post_output = self._get_docstring()

        self.add_line('', sourcename)

        self.add_line(":Fields:", sourcename)
        # TODO: Add xref targets for each field as an attribute
        # TODO: support for default_values
        self.add_line('', sourcename)

        fields = self.object._fields

        for pos, field in enumerate(fields):
            doc: List[str] = ['']
            arg_type: str = ''

            # Prefer doc from class docstring
            if field in params:
                doc, arg_type = params.pop(field).values()  # type: ignore

            # Otherwise use attribute docstring
            if not ''.join(doc).strip() and field in member_docstrings:
                doc = member_docstrings[field]

            # Fallback to namedtuple's default docstring
            if not ''.join(doc).strip():
                doc = [getattr(self.object, field).__doc__]

            # Prefer annotations over docstring types
            type_hints = get_type_hints(self.object)
            if type_hints:
                if field in type_hints:
                    arg_type = format_annotation(type_hints[field])

            field_entry = [f"{a_tab}{pos})", "|nbsp|", f"**{field}**"]
            if arg_type:
                field_entry.append(f"({arg_type}\\)")
            field_entry.append("--")
            field_entry.extend(doc)

            if field_alias_re.match(getattr(self.object, field).__doc__ or ''):
                getattr(self.object, field).__doc__ = ' '.join(doc)

            self.add_line(' '.join(field_entry), sourcename)

        self.add_line('', sourcename)

        for line in post_output:
            self.add_line(line, sourcename)

        self.add_line('', sourcename)

        # Remove documenters corresponding to fields and return the rest
        return [
            d for d in documenters if d[0].name.split('.')[-1] not in fields
        ]
Example #15
0
def test_ModuleAnalyzer_find_attr_docs():
    code = ('class Foo(object):\n'
            '    """class Foo!"""\n'
            '    #: comment before attr1\n'
            '    attr1 = None\n'
            '    attr2 = None  # attribute comment for attr2 (without colon)\n'
            '    attr3 = None  #: attribute comment for attr3\n'
            '    attr4 = None  #: long attribute comment\n'
            '                  #: for attr4\n'
            '    #: comment before attr5\n'
            '    attr5 = None  #: attribute comment for attr5\n'
            '    attr6, attr7 = 1, 2  #: this comment is ignored\n'
            '\n'
            '    def __init__(self):\n'
            '       self.attr8 = None  #: first attribute comment (ignored)\n'
            '       self.attr8 = None  #: attribute comment for attr8\n'
            '       #: comment before attr9\n'
            '       self.attr9 = None  #: comment after attr9\n'
            '       "string after attr9"\n'
            '\n'
            '    def bar(self, arg1, arg2=True, *args, **kwargs):\n'
            '       """method Foo.bar"""\n'
            '       pass\n'
            '\n'
            'def baz():\n'
            '   """function baz"""\n'
            '   pass\n'
            '\n'
            'class Qux: attr1 = 1; attr2 = 2')
    analyzer = ModuleAnalyzer.for_string(code, 'module')
    docs = analyzer.find_attr_docs()
    assert set(docs) == {('Foo', 'attr1'),
                         ('Foo', 'attr3'),
                         ('Foo', 'attr4'),
                         ('Foo', 'attr5'),
                         ('Foo', 'attr6'),
                         ('Foo', 'attr7'),
                         ('Foo', 'attr8'),
                         ('Foo', 'attr9')}
    assert docs[('Foo', 'attr1')] == ['comment before attr1', '']
    assert docs[('Foo', 'attr3')] == ['attribute comment for attr3', '']
    assert docs[('Foo', 'attr4')] == ['long attribute comment', '']
    assert docs[('Foo', 'attr4')] == ['long attribute comment', '']
    assert docs[('Foo', 'attr5')] == ['attribute comment for attr5', '']
    assert docs[('Foo', 'attr6')] == ['this comment is ignored', '']
    assert docs[('Foo', 'attr7')] == ['this comment is ignored', '']
    assert docs[('Foo', 'attr8')] == ['attribute comment for attr8', '']
    assert docs[('Foo', 'attr9')] == ['string after attr9', '']
    assert analyzer.tagorder == {'Foo': 0,
                                 'Foo.__init__': 8,
                                 'Foo.attr1': 1,
                                 'Foo.attr2': 2,
                                 'Foo.attr3': 3,
                                 'Foo.attr4': 4,
                                 'Foo.attr5': 5,
                                 'Foo.attr6': 6,
                                 'Foo.attr7': 7,
                                 'Foo.attr8': 10,
                                 'Foo.attr9': 12,
                                 'Foo.bar': 13,
                                 'baz': 14,
                                 'Qux': 15,
                                 'Qux.attr1': 16,
                                 'Qux.attr2': 17}