Exemple #1
0
    def generate_diff(file, doc_comment, new_comment):
        """
        Generates diff between the original doc_comment and its fix
        new_comment which are instances of DocumentationComment.

        :param doc_comment:
            Original instance of DocumentationComment.
        :param new_comment:
            Fixed instance of DocumentationComment.
        :return:
            Diff instance.
        """
        diff = Diff(file)

        # We need to update old comment positions, as `assemble()`
        # prepends indentation for first line.
        old_range = TextRange.from_values(doc_comment.range.start.line, 1,
                                          doc_comment.range.end.line,
                                          doc_comment.range.end.column)

        # Clearing cached assemble() so a fresh one is fetched.
        new_comment.assemble.cache_clear()

        diff.replace(old_range, new_comment.assemble())
        return diff
Exemple #2
0
    def test_generate_diff(self):
        data_old = ['\n', '""" documentation in single line  """\n']
        for doc_comment in DocBaseClass.extract(data_old, 'PYTHON3',
                                                'default'):
            old_doc_comment = doc_comment

        old_range = TextRange.from_values(old_doc_comment.range.start.line, 1,
                                          old_doc_comment.range.end.line,
                                          old_doc_comment.range.end.column)

        data_new = ['\n', '"""\n documentation in single line\n"""\n']
        for doc_comment in DocBaseClass.extract(data_new, 'PYTHON3',
                                                'default'):
            new_doc_comment = doc_comment

        diff = DocBaseClass.generate_diff(data_old, old_doc_comment,
                                          new_doc_comment)

        diff_expected = Diff(data_old)
        diff_expected.replace(old_range, new_doc_comment.assemble())

        self.assertEqual(diff, diff_expected)
    def test_generate_diff(self):
        data_old = ['\n', '""" documentation in single line  """\n']
        for doc_comment in DocBaseClass.extract(
                                data_old, 'PYTHON3', 'default'):
            old_doc_comment = doc_comment

        old_range = TextRange.from_values(
            old_doc_comment.range.start.line,
            1,
            old_doc_comment.range.end.line,
            old_doc_comment.range.end.column)

        data_new = ['\n', '"""\n documentation in single line\n"""\n']
        for doc_comment in DocBaseClass.extract(
                                data_new, 'PYTHON3', 'default'):
            new_doc_comment = doc_comment

        diff = DocBaseClass.generate_diff(
                        data_old, old_doc_comment, new_doc_comment)

        diff_expected = Diff(data_old)
        diff_expected.replace(old_range, new_doc_comment.assemble())

        self.assertEqual(diff, diff_expected)
Exemple #4
0
    def run(self,
            filename,
            file,
            language: str,
            docstyle: str = 'default',
            allow_missing_func_desc: str = False,
            indent_size: int = 4):
        """
        Checks for certain in-code documentation styles.

        It currently checks for the following style: ::

            The first line needs to have no indentation.
                - Following lines can have indentation

            :param x:
                4 space indent
            :return:
                also 4 space indent
                following lines are also 4 space indented

        :param language: The programming language of the file(s).
        :param docstyle: The docstyle to use. For example ``default`` or
                         ``doxygen``. Docstyles are language dependent, meaning
                         not every language is supported by a certain docstyle.
        :param allow_missing_func_desc: When set ``True`` this will allow
                         functions with missing descriptions, allowing
                         functions to start with params.
        :param indent_size: Number of spaces per indentation level.
        """
        for doc_comment in extract_documentation(file, language, docstyle):
            parsed = doc_comment.parse()
            metadata = iter(parsed)

            # Assuming that the first element is always the only main
            # description.
            main_description = next(metadata)

            if main_description.desc == '\n' and not allow_missing_func_desc:
                warning_desc = """
Missing function description.
Please set allow_missing_func_desc = True to ignore this warning.
"""
            else:
                warning_desc = 'Documentation does not have correct style.'

            # one empty line shall follow main description (except it's empty
            # or no annotations follow).
            if main_description.desc.strip() != '':
                main_description = main_description._replace(
                    desc='\n' + main_description.desc.strip() + '\n' *
                    (1 if len(parsed) == 1 else 2))

            new_metadata = [main_description]
            for m in metadata:
                # Split newlines and remove leading and trailing whitespaces.
                stripped_desc = list(map(str.strip, m.desc.splitlines()))

                if len(stripped_desc) == 0:
                    # Annotations should be on their own line, though no
                    # further description follows.
                    stripped_desc.append('')
                else:
                    # Wrap parameter description onto next line if it follows
                    # annotation directly.
                    if stripped_desc[0] != '':
                        stripped_desc.insert(0, '')

                # Indent with 4 spaces.
                stripped_desc = ('' if line == '' else ' ' * indent_size + line
                                 for line in stripped_desc)

                new_desc = '\n'.join(stripped_desc)

                # Strip away trailing whitespaces and obsolete newlines (except
                # one newline which is mandatory).
                new_desc = new_desc.rstrip() + '\n'

                new_metadata.append(m._replace(desc=new_desc.lstrip(' ')))

            new_comment = DocumentationComment.from_metadata(
                new_metadata, doc_comment.docstyle_definition,
                doc_comment.marker, doc_comment.indent, doc_comment.position)

            if new_comment != doc_comment:
                # Something changed, let's apply a result.
                diff = Diff(file)

                # We need to update old comment positions, as `assemble()`
                # prepends indentation for first line.
                old_range = TextRange.from_values(doc_comment.range.start.line,
                                                  1,
                                                  doc_comment.range.end.line,
                                                  doc_comment.range.end.column)
                diff.replace(old_range, new_comment.assemble())

                yield Result(origin=self,
                             message=warning_desc,
                             affected_code=(diff.range(filename), ),
                             diffs={filename: diff})