Ejemplo n.º 1
0
def test_init():
    """Test DocSection.__init__."""
    with pytest.raises(TypeError):
        DocSection(1, '')
    with pytest.raises(TypeError):
        DocSection(['1'], '')
    with pytest.raises(TypeError):
        DocSection('1', 1)
    with pytest.raises(TypeError):
        DocSection('1', {'1'})
    with pytest.raises(TypeError):
        DocSection('1', ['1', DocDescription('test')])
    with pytest.raises(TypeError):
        DocSection('1', [DocDescription('test'), '1'])

    test = DocSection('header name', 'hello')
    assert test.header == 'header name'
    assert test.contents == ['hello']
    test = DocSection('header name', ['hello', 'i am'])
    assert test.header == 'header name'
    assert test.contents == ['hello', 'i am']
    doc1 = DocDescription('hello')
    doc2 = DocDescription('i am')
    test = DocSection('header name', doc1)
    assert test.header == 'header name'
    assert test.contents == [doc1]
    test = DocSection('header name', [doc1, doc2])
    assert test.header == 'header name'
    assert test.contents == [doc1, doc2]
Ejemplo n.º 2
0
def test_wrapper_docstring_on_func():
    """Test docinstance.wrapper.docstring on a function."""
    # no _docinstance
    @docstring
    def test():  # pragma: no cover
        """Test docstring.

        Parameters
        ----------
        x : int
            Something.

        """
        pass

    # NOTE: the indentation can be removed with inspect.cleandoc
    assert test.__doc__ == ('Test docstring.\n'
                            '\n'
                            '        Parameters\n'
                            '        ----------\n'
                            '        x : int\n'
                            '            Something.\n\n'
                            '        ')
    assert not hasattr(test, '_docinstance')

    # _docinstance
    docinstance = Docstring([
        'Test docstring.',
        DocSection('parameters',
                   DocDescription('x', types=int, descs='Something.'))
    ])

    def test():  # pragma: no cover
        """Test function."""
        pass

    # FIXME: this is terrible because contents inside function is ignored when declaring a function
    # i.e. we cannot set the attribute of a function within a function definition. Docstrings are
    # special so they are not ignored. Using docstring that get parsed seems to be only option for
    # standalone functions
    test._docinstance = docinstance
    docstring(test)

    assert test.__doc__ == ('Test docstring.\n'
                            '\n'
                            'Parameters\n'
                            '----------\n'
                            'x : int\n'
                            '    Something.\n\n')
    assert test._docinstance == docinstance

    docstring(test, indent_level=2)
    assert test.__doc__ == ('Test docstring.\n'
                            '\n'
                            '        Parameters\n'
                            '        ----------\n'
                            '        x : int\n'
                            '            Something.\n\n'
                            '        ')
    assert test._docinstance == docinstance
Ejemplo n.º 3
0
def test_docstring_current_module():
    """Test docinstance.wrapper.docstring_current_module on the current module."""
    global test_docstring_current_module
    test_docstring_current_module._docinstance = Docstring([
        'Test docinstance.wrapper.docstring_current_module on the current module.',
        'Did it work?'
    ])

    def supplementary_docstring_current_module():
        """To be used to test docstring_current_module in test_docstring_current_module."""
        pass

    supplementary_docstring_current_module._docinstance = Docstring([
        'Some docstring.',
        DocSection('parameters',
                   DocDescription('x', types=int, descs='Example.'))
    ])
    test_docstring_current_module.f = supplementary_docstring_current_module

    docstring_current_module()
    assert (
        test_docstring_current_module.__doc__ ==
        'Test docinstance.wrapper.docstring_current_module on the current module.\n\n'
        '    Did it work?\n\n'
        '    ')

    assert (test_docstring_current_module.f.__doc__ == 'Some docstring.\n\n'
            '        Parameters\n'
            '        ----------\n'
            '        x : int\n'
            '            Example.\n\n'
            '        ')
Ejemplo n.º 4
0
def test_init():
    """Test Docstring.__init__."""
    with pytest.raises(TypeError):
        Docstring(1)
    with pytest.raises(TypeError):
        Docstring({'1'})
    with pytest.raises(TypeError):
        Docstring([1])
    with pytest.raises(TypeError):
        Docstring(['1', 1])
    with pytest.raises(ValueError):
        Docstring([])
    with pytest.raises(ValueError):
        Docstring('1', 'nothing')
    with pytest.raises(ValueError):
        Docstring('1', None)

    test = Docstring('some text')
    assert isinstance(test.sections, list)
    assert len(test.sections) == 1
    assert isinstance(test.sections[0], DocSection)
    assert test.sections[0].header == ''
    assert test.sections[0].contents == ['some text']

    test = Docstring(DocSection('some header', 'Hello World'))
    assert isinstance(test.sections, list)
    assert len(test.sections) == 1
    assert isinstance(test.sections[0], DocSection)
    assert test.sections[0].header == 'some header'
    assert test.sections[0].contents == ['Hello World']

    test = Docstring([DocSection('some header', 'Hello World'), 'some text'])
    assert isinstance(test.sections, list)
    assert len(test.sections) == 2
    assert isinstance(test.sections[0], DocSection)
    assert test.sections[0].header == 'some header'
    assert test.sections[0].contents == ['Hello World']
    assert isinstance(test.sections[1], DocSection)
    assert test.sections[1].header == ''
    assert test.sections[1].contents == ['some text']

    test = Docstring('some text', 'numpy')
    assert test.default_style == 'numpy'
    test = Docstring('some text', 'rst')
    assert test.default_style == 'rst'
Ejemplo n.º 5
0
    def __init__(self, sections, default_style='numpy'):
        """Initialize.

        Parameters
        ----------
        sections : {str, list/tuple of str, list/tuple of DocSection}
            Sections of the docstring.
        default_style : {'numpy with signature', 'google', 'rst', 'numpy'}
            Style of the docstring.

        Raises
        ------
        TypeError
            If sections is not a string, list/tuple of strings, or list/tuple of DocSection
            instances.
        ValueError
            If there are no sections.
            If style is not one of 'numpy', 'numpy with signature', 'google', 'rst'.

        """
        if isinstance(sections, (str, DocContent)):
            sections = [sections]
        elif not (isinstance(sections, (list, tuple))
                  and all(isinstance(i, (str, DocContent)) for i in sections)):
            raise TypeError(
                'Sections of the docstring must be provided as a string, list/tuple of '
                'strings, or list/tuple of DocContent instances.')
        # NOTE: should the empty sections be allowed?
        elif not sections:
            raise ValueError('At least one section must be provided.')
        self.sections = [
            section if isinstance(section, DocSection) else DocSection(
                '', section) for section in sections
        ]

        if default_style not in [
                'numpy', 'numpy with signature', 'google', 'rst'
        ]:
            raise ValueError(
                "Default style must be one of 'numpy', 'numpy with signature', "
                "'google', 'rst'.")
        self.default_style = default_style
Ejemplo n.º 6
0
def test_make_docstring():
    """Test Docstring.make_docstring."""
    test = Docstring(['summary', 'extended summary', DocSection('parameters', '')])
    # standard input check
    with pytest.raises(TypeError):
        test.make_docstring(width=100.0)
    with pytest.raises(ValueError):
        test.make_docstring(width=-2)
    with pytest.raises(ValueError):
        test.make_docstring(width=0)
    with pytest.raises(TypeError):
        test.make_docstring(indent_level=2.0)
    with pytest.raises(ValueError):
        test.make_docstring(indent_level=-1)
    with pytest.raises(TypeError):
        test.make_docstring(tabsize=2.0)
    with pytest.raises(ValueError):
        test.make_docstring(tabsize=-2)
    with pytest.raises(ValueError):
        test.make_docstring(tabsize=0)
    with pytest.raises(ValueError):
        test.make_docstring(style='random style')
    # bad ordering
    test = Docstring(['summary', DocSection('parameters', ''), 'extended summary'])
    with pytest.raises(ValueError):
        test.make_docstring(style='numpy')
    # check summary errors
    test = Docstring([DocSection('parameters', DocDescription('something'))])
    with pytest.raises(ValueError):
        test.make_docstring()
    # one line summary
    test = Docstring('very very long summary')
    assert test.make_docstring(width=25) == 'very very long summary\n\n'
    assert test.make_docstring(width=24) == '\nvery very long summary\n\n'
    # multiple line summary
    test = Docstring(['very very long summary', 'extended summary'])
    assert test.make_docstring(width=25) == 'very very long summary\n\nextended summary\n\n'
    assert test.make_docstring(width=24) == '\nvery very long summary\n\nextended summary\n\n'
    test = Docstring(DocSection('', ['very very long summary', 'extended summary']))
    assert test.make_docstring(width=25) == 'very very long summary\n\nextended summary\n\n'
    assert test.make_docstring(width=24) == '\nvery very long summary\n\nextended summary\n\n'
    # other sections
    test = Docstring([DocSection('', ['very very long summary', 'extended summary']),
                      DocSection('parameters',
                                 DocDescription('var1', types=str, descs='Example.'))])
    assert (test.make_docstring(width=25) ==
            'very very long summary\n\nextended summary\n\n'
            'Parameters\n----------\nvar1 : str\n    Example.\n\n')
    assert (test.make_docstring(width=24) ==
            '\nvery very long summary\n\nextended summary\n\n'
            'Parameters\n----------\nvar1 : str\n    Example.\n\n')
    # numpy with signature
    test = Docstring(['summary',
                      DocSection('methods',
                                 DocDescription('func1', signature='(a, b)', types=str,
                                                descs='Example.'))])
    assert (test.make_docstring(width=25, style='numpy with signature') ==
            'summary\n\nMethods\n-------\nfunc1(a, b) : str\n    Example.\n\n')
    # google
    assert test.make_docstring(width=25, style='google') == ('summary\n\n'
                                                             'Methods:\n'
                                                             '    func1 (:obj:`str`):\n'
                                                             '        Example.\n\n')
    # rst
    assert test.make_docstring(width=25, style='rst') == ('summary\n\n'
                                                          ':Methods:\n\n'
                                                          ':param func1: Example.\n'
                                                          ':type func1: :obj:`str`\n\n')
Ejemplo n.º 7
0
def test_check_section_order():
    """Test Docstring.check_section_order."""
    test = Docstring(['summary', 'extended', DocSection('parameters', ''), DocSection('warns', '')])
    assert test.check_section_order('numpy') is True
    test = Docstring(['summary', DocSection('parameters', ''), 'extended', DocSection('warns', '')])
    assert test.check_section_order('numpy') is False
    test = Docstring(['summary', 'extended', DocSection('warns', ''), DocSection('parameters', '')])
    assert test.check_section_order('numpy') is False
    # note that the unidentified seections are not permitted for numpy style
    test = Docstring(['summary', DocSection('asdfdsaf', ''), 'extended', DocSection('warns', ''),
                      DocSection('parameters', '')])
    with pytest.raises(ValueError):
        test.check_section_order('numpy')
    test = Docstring(['summary', 'extended', DocSection('warns', ''), DocSection('parameters', ''),
                      DocSection('asdfdsaf', '')])
    with pytest.raises(ValueError):
        test.check_section_order('numpy')

    # other styles do not enforce such ordering
    test = Docstring(['summary', DocSection('asdfdsaf', ''), 'extended', DocSection('warns', ''),
                      DocSection('parameters', '')])
    assert test.check_section_order('rst') is True
    assert test.check_section_order('random') is True
    # note that the unidentified sections are permitted for other styles
    # NOTE: following does not actually check for the effect of adding unidentified section for non
    # numpy style because all sections are unidentified for non numpy styles
    test = Docstring(['summary', 'extended', DocSection('warns', ''), DocSection('parameters', ''),
                      DocSection('asdfdsaf', '')])
    assert test.check_section_order('random') is True
Ejemplo n.º 8
0
def test_parse_numpy():
    """Tests docinstance.numpy.parse_numpy."""
    # monkeypatch equality for Docstring
    Docstring.__eq__ = lambda self, other: self.__dict__ == other.__dict__
    # summary
    docstring = 'summary'
    assert parse_numpy(docstring) == Docstring(Summary('summary'))
    docstring = 'summary\n'
    assert parse_numpy(docstring) == Docstring(Summary('summary'))
    docstring = '\nsummary\n'
    assert parse_numpy(docstring) == Docstring(Summary('summary'))
    docstring = '    """summary\n    """'
    assert parse_numpy(docstring,
                       contains_quotes=True) == Docstring(Summary('summary'))
    # FIXME: this should raise an error
    docstring = '\n\nsummary\n'
    assert parse_numpy(docstring), Docstring([Summary('summary')])
    docstring = '"""\n\nsummary\n"""'
    with pytest.raises(ValueError):
        parse_numpy(docstring, contains_quotes=True)
    docstring = '    """\n\n    summary\n    """'
    with pytest.raises(ValueError):
        parse_numpy(docstring, contains_quotes=True)
    docstring = 'summary\na'
    with pytest.raises(ValueError):
        parse_numpy(docstring)

    # extended
    docstring = 'summary\n\nblock1\n\nblock2'
    assert (parse_numpy(docstring) == Docstring(
        [Summary('summary'),
         ExtendedSummary(['block1', 'block2'])]))
    docstring = '\nsummary\n\nblock1\n\nblock2'
    assert (parse_numpy(docstring) == Docstring(
        [Summary('summary'),
         ExtendedSummary(['block1', 'block2'])]))
    docstring = '\nsummary\n\n\n\nblock2\n\n'
    assert (parse_numpy(docstring) == Docstring(
        [Summary('summary'), ExtendedSummary(['block2'])]))
    # FIXME: is this a bug?
    docstring = '\n\nsummary\n\nblock1\n\nblock2'
    assert (parse_numpy(docstring) == Docstring(
        [Summary('summary'),
         ExtendedSummary(['block1', 'block2'])]))
    # extended + headers
    docstring = 'summary\n\nblock1\n\nblock2\n\nheader\n------\nstuff'
    assert (parse_numpy(docstring) == Docstring([
        Summary('summary'),
        ExtendedSummary(['block1', 'block2']),
        DocSection('header', 'stuff')
    ]))

    # header with bad divider (-----)
    docstring = 'summary\n\nblock1\n\nblock2\n\nheader1\n--\nstuff\n\n'
    with pytest.raises(ValueError):
        parse_numpy(docstring)

    for header in [
            'parameters', 'attributes', 'methods', 'returns', 'yields',
            'raises', 'other parameters', 'see also'
    ]:
        # name + multiple descriptions
        docstring = (
            'summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc\n    description1.\n'
            '    description2.'.format(header.title(), '-' * len(header)))
        assert (parse_numpy(docstring) == Docstring([
            Summary('summary'),
            ExtendedSummary(['block1', 'block2']),
            DocSection(
                header,
                DocDescription('abc', descs=['description1.',
                                             'description2.']))
        ]))
        # name + types + multiple descriptions
        docstring = (
            'summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc : str\n    description1.\n'
            '    description2.'.format(header.title(), '-' * len(header)))
        assert (parse_numpy(docstring) == Docstring([
            Summary('summary'),
            ExtendedSummary(['block1', 'block2']),
            DocSection(
                header,
                DocDescription('abc',
                               types='str',
                               descs=['description1.', 'description2.']))
        ]))
        docstring = (
            'summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc : {{str, int}}\n'
            '    description1.\n'
            '    description2.'.format(header.title(), '-' * len(header)))
        assert (parse_numpy(docstring) == Docstring([
            Summary('summary'),
            ExtendedSummary(['block1', 'block2']),
            DocSection(
                header,
                DocDescription('abc',
                               types=['str', 'int'],
                               descs=['description1.', 'description2.']))
        ]))
        # name + signature + multiple descriptions
        docstring = (
            'summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc(x, y)\n    description1.\n'
            '    description2.'.format(header.title(), '-' * len(header)))
        assert (parse_numpy(docstring) == Docstring([
            Summary('summary'),
            ExtendedSummary(['block1', 'block2']),
            DocSection(
                header,
                DocDescription('abc',
                               signature='(x, y)',
                               descs=['description1.', 'description2.']))
        ]))
        # name + types + signature + multiple descriptions
        docstring = (
            'summary\n\nblock1\n\nblock2\n\n{0}\n{1}\nabc(x, y): str\n    description1.\n'
            '    description2.'.format(header.title(), '-' * len(header)))
        assert (parse_numpy(docstring) == Docstring([
            Summary('summary'),
            ExtendedSummary(['block1', 'block2']),
            DocSection(
                header,
                DocDescription('abc',
                               types='str',
                               signature='(x, y)',
                               descs=['description1.', 'description2.']))
        ]))
        # name + types + signature + multiple descriptions - extended summary
        docstring = ('summary\n\n{0}\n{1}\nabc(x, y): str\n    description1.\n'
                     '    description2.'.format(header.title(),
                                                '-' * len(header)))
        assert (parse_numpy(docstring) == Docstring([
            Summary('summary'),
            DocSection(
                header,
                DocDescription('abc',
                               types='str',
                               signature='(x, y)',
                               descs=['description1.', 'description2.']))
        ]))
        # name + types
        docstring = ('summary\n\n{0}\n{1}\nabc: str\ndef: int'.format(
            header.title(), '-' * len(header)))
        assert (parse_numpy(docstring) == Docstring([
            Summary('summary'),
            DocSection(header, [
                DocDescription('abc', types='str'),
                DocDescription('def', types='int')
            ])
        ]))
Ejemplo n.º 9
0
def test_compare_docinstances():
    """Test docinstance.parser.test.test_numpy."""
    doc1 = Docstring([
        'summary',
        DocSection('section1', ['content1', 'content2']),
        DocSection('section2', [
            DocDescription('name1', '(a, b)', str, ['desc1', 'desc2']),
            DocDescription('name2', '(c, d)', int,
                           ['desc3', DocEquation('desc4')])
        ])
    ])
    doc2 = Docstring([
        'summary',
        DocSection('section1', ['content1', 'content2']),
        DocSection('section2', [
            DocDescription('name1', '(a, b)', str, ['desc1', 'desc2']),
            DocDescription('name2', '(c, d)', int,
                           ['desc3', DocEquation('desc4')])
        ])
    ])
    assert doc1 != 1
    assert Docstring(['a', 'b']) != Docstring(['a', 'b', 'c'])
    assert Docstring(['a', 'b']) != Docstring(['a', DocSection('x', 'b')])
    assert Docstring(DocSection('a', 'b')) != Docstring(
        DocSection('a', ['b', 'c']))
    assert Docstring(DocSection('a', 'b')) != Docstring(DocSection('a', 'c'))
    assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', 'k'))) !=
            Docstring(DocSection('a', DocDescription('1', 'y', 'z', 'k'))))
    assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', 'k'))) !=
            Docstring(DocSection('a', DocDescription('x', '1', 'z', 'k'))))
    assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', 'k'))) !=
            Docstring(DocSection('a', DocDescription('x', 'y', '1', 'k'))))
    assert (Docstring(DocSection('a', DocDescription('x', 'y', 'z', 'k'))) !=
            Docstring(DocSection('a', DocDescription('x', 'y', 'z', '1'))))
    assert (Docstring(
        DocSection('a', DocDescription('x', 'y', 'z', DocEquation('k')))) !=
            Docstring(
                DocSection('a', DocDescription('x', 'y', 'z',
                                               DocEquation('1')))))
    assert doc1.__dict__ == doc2.__dict__
Ejemplo n.º 10
0
def test_wrapper_docstring_recursive_on_class():
    """Test docinstance.wrapper.docstring_recursive on a class."""
    # no _docinstance
    @docstring_recursive
    class Test:  # pragma: no cover
        """Test docstring.

        Attributes
        ----------
        x : int
            Something.

        """
        def f(self):
            """Test function.

            Returns
            -------
            nothing

            """
            pass

    assert Test.__doc__ == ('Test docstring.\n\n'
                            '        Attributes\n'
                            '        ----------\n'
                            '        x : int\n'
                            '            Something.\n\n'
                            '        ')
    assert not hasattr(Test, '_docinstance')
    assert Test.f.__doc__ == ('Test function.\n\n'
                              '            Returns\n'
                              '            -------\n'
                              '            nothing\n\n'
                              '            ')
    assert not hasattr(Test.f, '_docinstance')

    # docinstance
    docinstance1 = Docstring([
        'Test docstring.',
        DocSection('attributes',
                   DocDescription('x', types=int, descs='Something.'))
    ])
    docinstance2 = Docstring(
        ['Test function.', DocSection('returns', 'nothing')])

    # w/o indentation
    @docstring_recursive
    class Test:  # pragma: no cover
        """Test class."""
        _docinstance = docinstance1

        def f(self):
            """Test function."""
            pass

        f._docinstance = docinstance2

    assert Test.__doc__ == ('Test docstring.\n\n'
                            'Attributes\n'
                            '----------\n'
                            'x : int\n'
                            '    Something.\n\n')
    assert Test._docinstance == docinstance1
    assert Test.f.__doc__ == ('Test function.\n\n'
                              '    Returns\n'
                              '    -------\n'
                              '    nothing\n\n'
                              '    ')
    assert Test.f._docinstance == docinstance2

    # w indentation
    @docstring_recursive(indent_level=2)
    class Test:  # pragma: no cover
        """Test class."""
        _docinstance = docinstance1

        def f(self):
            """Test function."""
            pass

        f._docinstance = docinstance2

    assert Test.__doc__ == ('Test docstring.\n\n'
                            '        Attributes\n'
                            '        ----------\n'
                            '        x : int\n'
                            '            Something.\n\n'
                            '        ')
    assert Test._docinstance == docinstance1
    assert Test.f.__doc__ == ('Test function.\n\n'
                              '            Returns\n'
                              '            -------\n'
                              '            nothing\n\n'
                              '            ')
    assert Test.f._docinstance == docinstance2
Ejemplo n.º 11
0
def parse_numpy(docstring, contains_quotes=False):
    r"""Parse a docstring in numpy format into a Docstring instance.

    Multiple descriptions of the indented information (e.g. parameters, attributes, methods,
    returns, yields, raises, see also) are distinguished from one another with a period.
    If the period is not present, then the description is assumed to be a multiline description.

    Parameters
    ----------
    docstring : str
        Numpy docstring.
    contains_quotes : bool
        True if docstring contains \"\"\" or \'\'\'.

    Returns
    -------
    docstring : Docstring
       Instance of Docstring that contains the necessary information.

    Raises
    ------
    ValueError
        If summary is not in the first or second line.
        If summary is now followed with a blank line.
        If number of '-' does not match the number of characters in the header.
        If given entry of the tabbed information (parameters, attributes, methods, returns, yields,
        raises, see also) had an unexpected pattern.
    NotImplementedError
        If quotes corresponds to a raw string, i.e. r\"\"\".

    Notes
    -----
    Copied from https://github.com/kimt33/pydocstring.

    """
    docstring = inspect.cleandoc('\n' * contains_quotes + docstring)

    # remove quotes from docstring
    if contains_quotes:
        quotes = r'[\'\"]{3}'
        if re.search(r'^r{0}'.format(quotes), docstring):
            raise NotImplementedError(
                'A raw string quotation, i.e. r""" cannot be given as a '
                'string, i.e. from reading a python file as a string, '
                'because the backslashes belonging to escape sequences '
                'cannot be distinguished from those of normal backslash.'
                'You either need to change existing raw string to normal '
                'i.e. convert all occurences of \\ to \\\\, or import the '
                'docstring from the instance through `__doc__` attribute.')
    else:
        quotes = r''
    docstring = re.sub(r'^{0}'.format(quotes), '', docstring)
    docstring = re.sub(r'{0}$'.format(quotes), '', docstring)

    sections = []
    # summary
    for regex in [r'^\n?(.+?)\n\n+', r'^\n?(.*?)\n*$']:
        re_summary = re.compile(regex)
        try:
            sections.append(Summary(re_summary.search(docstring).group(1)))
            break
        except AttributeError:
            pass
    else:
        raise ValueError(
            'The summary must be in the first or the second line with a blank line '
            'afterwards.')
    # remove summary from docstring
    docstring = re_summary.sub('', docstring)
    if docstring == '':
        return Docstring(sections)

    # if headers do not exist
    re_header = re.compile(r'\n*(.+)\n(-+)\n+')
    if re_header.search(docstring) is None:
        # split into blocks by math equations and multiple newlines
        extended = [[lines] if isinstance(lines, DocEquation) else re.split(
            r'\n\n+', lines) for lines in parse_equation(docstring)]
        extended = [line for lines in extended for line in lines]
        extended_contents = []
        for block in extended:
            # NOTE: all newlines at the end of the docstring will be removed by inspect.cleandoc. So
            # there is no empty blocks
            # if block == '':
            #     continue
            if not isinstance(block, DocEquation):
                # remove quotes
                block = re.sub(r'\n*{0}$'.format(quotes), '', block)
                # remove trailing newlines
                block = re.sub(r'\n+$', '', block)
                # replace newlines
                block = block.replace('\n', ' ')
            extended_contents.append(block)
        sections.append(ExtendedSummary(extended_contents))
        return Docstring(sections)

    # split docstring by the headers
    split_docstring = re_header.split(docstring)
    # 0th element is always the extended summary, 1st element is the header, 2nd element is the
    # ----- divider
    extended, *split_docstring = split_docstring
    # FIXME: repeated code
    # extract math and split blocks
    extended = [[lines] if isinstance(lines, DocEquation) else re.split(
        r'\n\n+', lines) for lines in parse_equation(extended)]
    extended = [line for lines in extended for line in lines]
    # process blocks
    processed_extended = []
    for block in extended:
        # NOTE: all newlines at the end of the docstring will be removed by inspect.cleandoc. So
        # there is no empty blocks
        # if block == '':
        #     continue
        if not isinstance(block, DocEquation):
            # remove quotes
            block = re.sub(r'\n*{0}$'.format(quotes), '', block)
            # remove trailing newlines
            block = re.sub(r'\n+$', '', block)
            # replace newlines
            block = block.replace('\n', ' ')
        processed_extended.append(block)

    if processed_extended != []:
        sections.append(ExtendedSummary(processed_extended))

    headers_sections = {
        'parameters': Parameters,
        'other parameters': OtherParameters,
        'attributes': Attributes,
        'methods': Methods,
        'returns': Returns,
        'yields': Yields,
        'raises': Raises,
        'see also': SeeAlso,
        'warns': Warns,
        'warnings': Warnings,
        'examples': Examples,
        'references': References,
        'notes': Notes,
        'properties': None,
        'abstract properties': None,
        'abstract methods': None
    }
    for header, lines, contents in zip(split_docstring[0::3],
                                       split_docstring[1::3],
                                       split_docstring[2::3]):
        contents = re.sub(r'\n+$', r'\n', contents)

        if len(header) != len(lines):
            raise ValueError('Need {0} of `-` underneath the header title, {1}'
                             ''.format(len(header), header))

        header = header.lower()
        header_contents = []
        # special headers (special format for each entry)
        if header in headers_sections:
            entries = (entry for entry in re.split(r'\n(?!\s+)', contents)
                       if entry != '')
            # FIXME: following regular expression would work only if docstring has spaces adjacent
            #        to ':'
            re_entry = re.compile(r'^(.+?)(\(.+?\))?(?: *: *(.+))?(?:\n|$)')
            for entry in entries:
                # keep only necessary pieces
                _, name, signature, types, descs = re_entry.split(entry)

                # process signature
                if signature is None:
                    signature = ''
                else:
                    signature = ', '.join(i.strip()
                                          for i in signature.split(','))

                # process types
                if types is None:
                    types = []
                elif re.search(r'\{.+\}', types):
                    types = re.search(r'^\{((?:(.+?),\s*)*(.+?))\}$',
                                      types).group(1)
                    types = re.split(r',\s*', types)
                else:
                    types = re.search(r'^((?:(.+?),\s*)*(.+?))$',
                                      types).group(1)
                    types = re.split(r',\s*', types)
                types = [i for i in types if i is not None]

                # process documentation
                descs = inspect.cleandoc('\n' + descs)
                # NOTE: period is used to terminate a description. i.e. one description is
                #       distinguished from another with a period and a newline.
                descs = re.split(r'\.\n+', descs)
                # add period (only the last line is not missing the period)
                descs = [line + '.' for line in descs[:-1]] + descs[-1:]
                # extract equations
                descs = [
                    line for lines in descs for line in parse_equation(lines)
                ]
                # non math blocks will replace newlines with spaces.
                # math blocks will add newline at the end
                descs = [
                    line if isinstance(line, DocEquation) else line.replace(
                        '\n', ' ') for line in descs
                ]

                # store
                header_contents.append(
                    DocDescription(name,
                                   signature=signature,
                                   types=types,
                                   descs=descs))
        else:
            header_contents = [
                i for i in re.split(r'\n\n+', contents) if i != ''
            ]
        try:
            sections.append(headers_sections[header](header_contents))
        except KeyError:
            sections.append(DocSection(header, header_contents))
    return Docstring(sections)
Ejemplo n.º 12
0
    def make_docstring(self, width=100, indent_level=0, tabsize=4, style=None):
        """Return the docstring in the given style.

        Parameters
        ----------
        width : {int, 100}
            Maximum number of characters allowed in a line.
            Default is 100 characters.
        indent_level : {int, 0}
            Number of indents (tabs) that are needed for the docstring.
            Default is 0.
        tabsize : {int, 4}
            Number of spaces that corresponds to a tab.
            Default is 4.
        style : {'numpy', 'google', 'rst', 'numpy with signature', None}
            Style of the docstring.
            Default is the `default_style`.

        Returns
        -------
        section_docstring : str
            Docstring that correspond to the given section.

        Raises
        ------
        TypeError
            If width is not an integer.
            If indent_level is not an integer.
            If tabsize is not an integer.
        ValueError
            If width is less than or equal to zero.
            If indent_level is less than zero.
            If tabsize is less than or equal to zero.
            If the given style is not 'numpy', 'numpy with signature', google', or 'rst'.
            If the sections are not ordered correctly according to the given style.
            If the first section of the docstring (summary) does not have an empty header.
            If the first section of the docstring (summary) does not consist of one string.
            If the first section of the docstring (summary) does not fit completely into the first
            line of the docstring (including the triple quotation) or the second line.

        """
        if style is None:
            style = self.default_style
        # check input
        if not isinstance(width, int):
            raise TypeError(
                'Maximum width of the line must be given as an integer.')
        elif width <= 0:
            raise ValueError(
                'Maximum width of the line must be greater than zero.')

        if not isinstance(indent_level, int):
            raise TypeError(
                'Level of indentation must be given as an integer.')
        elif indent_level < 0:
            raise ValueError(
                'Level of indentation must be greater than or equal to zero.')

        if not isinstance(tabsize, int):
            raise TypeError(
                'Number of spaces in a tab must be given as an integer.')
        elif tabsize <= 0:
            raise ValueError(
                'Number of spaces in a tab must be greater than zero.')

        if style == 'numpy':
            docstring_func = 'make_numpy_docstring'
        elif style == 'numpy with signature':
            docstring_func = 'make_numpy_docstring_signature'
        elif style == 'google':
            docstring_func = 'make_google_docstring'
        elif style == 'rst':
            docstring_func = 'make_rst_docstring'
        else:
            raise ValueError(
                "Given docstring style must be one of 'numpy', 'numpy with signature',"
                " 'google', 'rst'.")

        # FIXME: this may not be necessary and can be removed
        if not self.check_section_order(style):
            raise ValueError(
                'Sections must be ordered according to the guideline set by the given '
                'docstring style.')

        output = ''
        # check that first section does not have a header
        if self.sections[0].header != '':
            raise ValueError(
                'First section of the docstring (summary) must have an empty header.'
            )
        # add summary
        summary = Summary(self.sections[0].contents[0])
        output += summary.make_docstring(
            width,
            indent_level,
            tabsize,
            summary_only=(len(self.sections) == len(self.sections[0].contents)
                          == 1))
        # add remaining summary
        if len(self.sections[0].contents) > 1:
            summary = DocSection('', self.sections[0].contents[1:])
            output += getattr(summary, docstring_func)(width, indent_level,
                                                       tabsize)
        # add other sections
        if len(self.sections) > 1:
            for section in self.sections[1:]:
                output += getattr(section, docstring_func)(width, indent_level,
                                                           tabsize)
        # add whitespace to indent the triple quotation
        output += ' ' * indent_level * tabsize
        return output
Ejemplo n.º 13
0
def test_make_google_docstring():
    """Test DocSection.make_google_docstring."""
    with pytest.raises(ValueError):
        test = DocSection('quite long header name', '')
        test.make_google_docstring(10, 0, 4)
    # no header
    test = DocSection('', 'Some text.')
    assert test.make_google_docstring(10, 0, 4) == ('Some text.\n\n')
    # one docdescription
    test = DocSection('header name',
                      DocDescription('var_name', signature='(a, b)', types=str, descs='Example.'))
    assert test.make_google_docstring(35, 0, 4) == ('Header Name:\n'
                                                    '    var_name (:obj:`str`): Example.\n\n')
    # multiple docdescription
    test = DocSection('header name',
                      [DocDescription('var1', signature='(a, b)', types=str, descs='Example1.'),
                       DocDescription('var2', signature='(c)', types='int', descs='Example2.')])
    assert test.make_google_docstring(35, 0, 4) == ('Header Name:\n'
                                                    '    var1 (:obj:`str`): Example1.\n'
                                                    '    var2 (int): Example2.\n\n')
    # one string
    test = DocSection('header name', 'Some text.')
    assert test.make_google_docstring(14, 0, 4) == ('Header Name:\n'
                                                    '    Some text.\n\n')
    assert test.make_google_docstring(13, 0, 4) == ('Header Name:\n'
                                                    '    Some\n'
                                                    '    text.\n\n')
    # multiple string
    test = DocSection('header name', ['Some text.', 'Another text.'])
    assert test.make_google_docstring(17, 0, 4) == ('Header Name:\n'
                                                    '    Some text.\n\n'
                                                    '    Another text.\n\n')
    assert test.make_google_docstring(14, 0, 4) == ('Header Name:\n'
                                                    '    Some text.\n\n'
                                                    '    Another\n'
                                                    '    text.\n\n')
    assert test.make_google_docstring(13, 0, 4) == ('Header Name:\n'
                                                    '    Some\n'
                                                    '    text.\n\n'
                                                    '    Another\n'
                                                    '    text.\n\n')
Ejemplo n.º 14
0
def test_make_numpy_docstring_signature():
    """Test DocSection.make_numpy_docstring_signature."""
    test = DocSection('header name',
                      DocDescription('var_name', signature='(a, b)', types=str, descs='Example.'))
    assert (test.make_numpy_docstring_signature(20, 0, 4) ==
            'Header Name\n-----------\nvar_name(a, b) : str\n    Example.\n\n')
Ejemplo n.º 15
0
def test_make_numpy_docstring():
    """Test DocSection.make_numpy_docstring."""
    # string content
    test = DocSection('header name', 'hello')
    assert test.make_numpy_docstring(11, 0, 4) == 'Header Name\n-----------\nhello\n\n'
    assert test.make_numpy_docstring(11, 0, 4) == 'Header Name\n-----------\nhello\n\n'
    # multiple string contents
    test = DocSection('header name', ['hello', 'i am'])
    assert test.make_numpy_docstring(11, 0, 4) == 'Header Name\n-----------\nhello\n\ni am\n\n'
    # doc description
    test = DocSection('header name', DocDescription('var_name', types=str, descs='Example.'))
    assert (test.make_numpy_docstring(20, 0, 4) ==
            'Header Name\n-----------\nvar_name : str\n    Example.\n\n')
    # multiple doc descriptions
    test = DocSection('header name', [DocDescription('var_name1', types=str, descs='Example 1.'),
                                      DocDescription('var_name2', types=int, descs='Example 2.')])
    assert (test.make_numpy_docstring(20, 0, 4) ==
            'Header Name\n-----------\nvar_name1 : str\n    Example 1.\nvar_name2 : int\n'
            '    Example 2.\n\n')
    # signature does nothing
    test = DocSection('header name',
                      DocDescription('var_name', signature='(a, b)', types=str, descs='Example.'))
    assert (test.make_numpy_docstring(20, 0, 4) ==
            'Header Name\n-----------\nvar_name : str\n    Example.\n\n')
Ejemplo n.º 16
0
def test_make_rst_docstring():
    """Test DocSection.make_rst_docstring."""
    # no header
    test = DocSection('', 'Some text.')
    assert test.make_rst_docstring(10, 0, 4) == ('Some text.\n\n')
    # normal header, one docdescription
    test = DocSection('header name',
                      DocDescription('var_name', signature='(a, b)', types=str, descs='Example.'))
    assert test.make_rst_docstring(35, 0, 4) == (':Header Name:\n\n'
                                                 ':param var_name: Example.\n'
                                                 ':type var_name: :obj:`str`\n\n')
    # normal header, multiple docdescription
    test = DocSection('header name',
                      [DocDescription('var1', signature='(a, b)', types=str, descs='Example1.'),
                       DocDescription('var2', signature='(c)', types='int', descs='Example2.')])
    assert test.make_rst_docstring(35, 0, 4) == (':Header Name:\n\n'
                                                 ':param var1: Example1.\n'
                                                 ':type var1: :obj:`str`\n'
                                                 ':param var2: Example2.\n'
                                                 ':type var2: int\n\n')
    # normal header, one string
    test = DocSection('header name', 'Some text.')
    assert test.make_rst_docstring(13, 0, 4) == (':Header Name:\n\n'
                                                 'Some text.\n\n')
    # normal header, multiple string
    test = DocSection('header name', ['Some text.', 'Another text.'])
    assert test.make_rst_docstring(13, 0, 4) == (':Header Name:\n\n'
                                                 'Some text.\n\n'
                                                 'Another text.\n\n')

    # special header, doc description
    test = DocSection('see also',
                      [DocDescription('var1', signature='(a, b)', types=str, descs='Example1.'),
                       DocDescription('var2', signature='(c)', types='int', descs='Example2.')])
    assert test.make_rst_docstring(35, 0, 4) == ('.. seealso::\n'
                                                 '    :param var1: Example1.\n'
                                                 '    :type var1: :obj:`str`\n'
                                                 '    :param var2: Example2.\n'
                                                 '    :type var2: int\n\n')
    # special header, string
    test = DocSection('to do', ['Example 1, something.', 'Example 2.'])
    assert test.make_rst_docstring(20, 0, 4) == ('.. todo:: Example 1,\n'
                                                 '    something.\n'
                                                 '    Example 2.\n\n')