Skip to content

Commit

Permalink
DocBaseClass: Add DocBaseClass
Browse files Browse the repository at this point in the history
Add DocBaseClass
Merge DocumentationExtraction with DocBaseClass
Closes #2659
  • Loading branch information
damngamerz committed Jul 22, 2017
1 parent 86f0544 commit d79e497
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
DocstyleDefinition)
from coalib.bearlib.languages.documentation.DocumentationComment import (
DocumentationComment)
from coalib.results.Diff import Diff
from coalib.results.TextRange import TextRange
from coalib.results.TextPosition import TextPosition


Expand Down Expand Up @@ -254,31 +256,81 @@ def extract_documentation_with_markers(content, docstyle_definition):
yield doc


def extract_documentation(content, language, docstyle):
class DocBaseClass:
"""
Extracts all documentation texts inside the given source-code-string using
the coala docstyle definition files.
The documentation texts are sorted by their order appearing in ``content``.
For more information about how documentation comments are identified and
extracted, see DocstyleDefinition.doctypes enumeration.
:param content: The source-code-string where to extract
documentation from. Needs to be a list or tuple
where each string item is a single line
(including ending whitespaces like ``\\n``).
:param language: The programming language used.
:param docstyle: The documentation style/tool used
(e.g. doxygen).
:raises FileNotFoundError: Raised when the docstyle definition file was not
found.
:raises KeyError: Raised when the given language is not defined in
given docstyle.
:raises ValueError: Raised when a docstyle definition setting has an
invalid format.
:return: An iterator returning each DocumentationComment
found in the content.
DocBaseClass holds important functions which will extract, parse
and generates diffs for documentation. All bears that processes
documentation should inherit from this.
"""
docstyle_definition = DocstyleDefinition.load(language, docstyle)
return extract_documentation_with_markers(content, docstyle_definition)

@staticmethod
def extract(content, language, docstyle):
"""
Extracts all documentation texts inside the given source-code-string
using the coala docstyle definition files.
The documentation texts are sorted by their order appearing in
``content``.
For more information about how documentation comments are
identified and extracted, see DocstyleDefinition.doctypes enumeration.
:param content: The source-code-string where to extract
documentation from. Needs to be a list
or tuple where each string item is a
single line(including ending whitespaces
like ``\\n``).
:param language: The programming language used.
:param docstyle: The documentation style/tool used
(e.g. doxygen).
:raises FileNotFoundError: Raised when the docstyle definition file
was not found.
:raises KeyError: Raised when the given language is not
defined in given docstyle.
:raises ValueError: Raised when a docstyle definition setting
has an invalid format.
:return: An iterator returning each
DocumentationComment found in the content.
"""
docstyle_definition = DocstyleDefinition.load(language, docstyle)
return extract_documentation_with_markers(
content, docstyle_definition)

@staticmethod
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

def process_documentation(self, *args, **kwargs):
"""
Checks and handles the fixing part of documentation.
:return:
A tuple of processed documentation and warning_desc.
"""
raise NotImplementedError('This function has to be implemented for a '
'documentation bear.')
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,28 @@
DocstyleDefinition)
from coalib.bearlib.languages.documentation.DocumentationComment import (
DocumentationComment)
from coalib.bearlib.languages.documentation.DocumentationExtraction import (
extract_documentation)
from coalib.bearlib.languages.documentation.DocBaseClass import (
DocBaseClass)
from tests.bearlib.languages.documentation.TestUtils import (
load_testdata)
from coalib.results.TextPosition import TextPosition
from coalib.results.TextRange import TextRange
from coalib.results.Diff import Diff


class DocumentationExtractionTest(unittest.TestCase):
class DocBaseClassTest(unittest.TestCase):

def test_DocBaseClass_extraction_invalid_input(self):

def test_extract_documentation_invalid_input(self):
with self.assertRaises(FileNotFoundError):
tuple(extract_documentation('', 'PYTHON', 'INVALID'))
tuple(DocBaseClass.extract('', 'PYTHON', 'INVALID'))

def test_extract_documentation_C(self):
def test_DocBaseClass_extraction_C(self):
data = load_testdata('data.c')

# No built-in documentation for C.
with self.assertRaises(KeyError):
tuple(extract_documentation(data, 'C', 'default'))
tuple(DocBaseClass.extract(data, 'C', 'default'))

docstyle_C_doxygen = DocstyleDefinition.load('C', 'doxygen')

Expand Down Expand Up @@ -59,31 +62,31 @@ def test_extract_documentation_C(self):
TextPosition(28, 1)))

self.assertEqual(tuple(
extract_documentation(data, 'C', 'doxygen')),
DocBaseClass.extract(data, 'C', 'doxygen')),
expected_results)

def test_extract_documentation_C_2(self):
def test_DocBaseClass_extraction_C_2(self):
data = ['/** my main description\n', ' * continues here */']

docstyle_C_doxygen = DocstyleDefinition.load('C', 'doxygen')

self.assertEqual(
list(extract_documentation(data, 'C', 'doxygen')),
list(DocBaseClass.extract(data, 'C', 'doxygen')),
[DocumentationComment(' my main description\n continues here',
docstyle_C_doxygen, '',
docstyle_C_doxygen.markers[0],
TextPosition(1, 1))])

def test_extract_documentation_CPP(self):
def test_DocBaseClass_extraction_CPP(self):
data = load_testdata('data.cpp')

# No built-in documentation for C++.
with self.assertRaises(KeyError):
tuple(extract_documentation(data, 'CPP', 'default'))
tuple(DocBaseClass.extract(data, 'CPP', 'default'))

docstyle_CPP_doxygen = DocstyleDefinition.load('CPP', 'doxygen')

self.assertEqual(tuple(extract_documentation(data, 'CPP', 'doxygen')),
self.assertEqual(tuple(DocBaseClass.extract(data, 'CPP', 'doxygen')),
(DocumentationComment(
('\n'
' This is the main function.\n'
Expand Down Expand Up @@ -118,20 +121,20 @@ def test_extract_documentation_CPP(self):
docstyle_CPP_doxygen.markers[4],
TextPosition(32, 1))))

def test_extract_documentation_CPP_2(self):
def test_DocBaseClass_CPP_2(self):
data = load_testdata('data2.cpp')

docstyle_CPP_doxygen = DocstyleDefinition.load('CPP', 'doxygen')

self.assertEqual(tuple(extract_documentation(data, 'CPP', 'doxygen')),
self.assertEqual(tuple(DocBaseClass.extract(data, 'CPP', 'doxygen')),
(DocumentationComment(
('module comment\n'
' hello world\n'),
docstyle_CPP_doxygen, '',
docstyle_CPP_doxygen.markers[0],
TextPosition(1, 1)),))

def test_extract_documentation_PYTHON3(self):
def test_DocBaseClass_PYTHON3(self):
data = load_testdata('data.py')
docstyle_PYTHON3_default = DocstyleDefinition.load('PYTHON3',
'default')
Expand Down Expand Up @@ -192,9 +195,8 @@ def test_extract_documentation_PYTHON3(self):
)

self.assertEqual(
tuple(extract_documentation(data, 'PYTHON3', 'default')),
tuple(DocBaseClass.extract(data, 'PYTHON3', 'default')),
expected)

# Change only the docstyle in expected results.
expected = list(DocumentationComment(r.documentation,
docstyle_PYTHON3_doxygen,
Expand All @@ -214,37 +216,37 @@ def test_extract_documentation_PYTHON3(self):
TextPosition(30, 1)))

self.assertEqual(
list(extract_documentation(data, 'PYTHON3', 'doxygen')),
list(DocBaseClass.extract(data, 'PYTHON3', 'doxygen')),
expected)

def test_extract_documentation_PYTHON3_2(self):
def test_DocBaseClass_extraction_PYTHON3_2(self):
data = ['\n', '""" documentation in single line """\n', 'print(1)\n']

docstyle_PYTHON3_default = DocstyleDefinition.load('PYTHON3',
'default')

self.assertEqual(
list(extract_documentation(data, 'PYTHON3', 'default')),
list(DocBaseClass.extract(data, 'PYTHON3', 'default')),
[DocumentationComment(' documentation in single line ',
docstyle_PYTHON3_default, '',
docstyle_PYTHON3_default.markers[0],
TextPosition(2, 1))])

def test_extract_documentation_PYTHON3_3(self):
def test_DocBaseClass_extraction_PYTHON3_3(self):
data = ['## documentation in single line without return at end.']

docstyle_PYTHON3_doxygen = DocstyleDefinition.load('PYTHON3',
'doxygen')

self.assertEqual(
list(extract_documentation(data, 'PYTHON3', 'doxygen')),
list(DocBaseClass.extract(data, 'PYTHON3', 'doxygen')),
[DocumentationComment(' documentation in single line without '
'return at end.',
docstyle_PYTHON3_doxygen, '',
docstyle_PYTHON3_doxygen.markers[1],
TextPosition(1, 1))])

def test_extract_documentation_PYTHON3_4(self):
def test_DocBaseClass_extraction_PYTHON3_4(self):
data = ['\n', 'triple_quote_string_test = """\n',
'This is not a docstring\n', '"""\n']

Expand All @@ -254,5 +256,35 @@ def test_extract_documentation_PYTHON3_4(self):
# Nothing is yielded as triple quote string literals are being
# ignored.
self.assertEqual(
list(extract_documentation(data, 'PYTHON3', 'default')),
list(DocBaseClass.extract(data, 'PYTHON3', 'default')),
[])

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_DocBaseClass_process_documentation_not_implemented(self):
test_object = DocBaseClass()
self.assertRaises(NotImplementedError,
test_object.process_documentation)
23 changes: 12 additions & 11 deletions tests/bearlib/languages/documentation/DocumentationCommentTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
DocstyleDefinition)
from coalib.bearlib.languages.documentation.DocumentationComment import (
DocumentationComment)
from coalib.bearlib.languages.documentation.DocumentationExtraction import (
extract_documentation)
from coalib.bearlib.languages.documentation.DocBaseClass import (
DocBaseClass)
from tests.bearlib.languages.documentation.TestUtils import (
load_testdata)
from coalib.results.TextPosition import TextPosition
Expand Down Expand Up @@ -72,7 +72,7 @@ def test_not_implemented(self):
def test_from_metadata(self):
data = load_testdata('default.py')

original = list(extract_documentation(data, 'python', 'default'))
original = list(DocBaseClass.extract(data, 'python', 'default'))

parsed_docs = [(doc.parse(), doc.marker, doc.indent, doc.position)
for doc in original]
Expand Down Expand Up @@ -109,7 +109,8 @@ def test_empty_docstring(self):

def test_description(self):
doc = ' description only '
self.check_docstring(doc, [self.Description(desc=' description only ')])
self.check_docstring(
doc, [self.Description(desc=' description only ')])

def test_params_default(self):
self.maxDiff = None
Expand Down Expand Up @@ -139,7 +140,7 @@ def test_python_default(self):
data = load_testdata('default.py')

parsed_docs = [doc.parse() for doc in
extract_documentation(data, 'python', 'default')]
DocBaseClass.extract(data, 'python', 'default')]

expected = [
[self.Description(desc='\nModule description.\n\n'
Expand Down Expand Up @@ -188,7 +189,7 @@ def test_python_doxygen(self):
data = load_testdata('doxygen.py')

parsed_docs = [doc.parse() for doc in
extract_documentation(data, 'python', 'doxygen')]
DocBaseClass.extract(data, 'python', 'doxygen')]

expected = [
[self.Description(desc=' @package pyexample\n Documentation for'
Expand Down Expand Up @@ -232,7 +233,7 @@ def test_java_default(self):
data = load_testdata('default.java')

parsed_docs = [doc.parse() for doc in
extract_documentation(data, 'java', 'default')]
DocBaseClass.extract(data, 'java', 'default')]

expected = [[self.Description(
desc='\n Returns an String that says Hello with the name'
Expand All @@ -253,7 +254,7 @@ def test_go_default(self):
data = load_testdata('default.go')

parsed_docs = [doc.parse() for doc in
extract_documentation(data, 'golang', 'golang')]
DocBaseClass.extract(data, 'golang', 'golang')]

expected = [['\n',
'Comments may span\n',
Expand All @@ -272,19 +273,19 @@ def test_python_assembly(self):
data = load_testdata('default.py')
docs = ''.join(data)

for doc in extract_documentation(data, 'python', 'default'):
for doc in DocBaseClass.extract(data, 'python', 'default'):
self.assertIn(doc.assemble(), docs)

def test_doxygen_assembly(self):
data = load_testdata('doxygen.py')
docs = ''.join(data)

for doc in extract_documentation(data, 'python', 'doxygen'):
for doc in DocBaseClass.extract(data, 'python', 'doxygen'):
self.assertIn(doc.assemble(), docs)

def test_c_assembly(self):
data = load_testdata('default.c')
docs = ''.join(data)

for doc in extract_documentation(data, 'c', 'doxygen'):
for doc in DocBaseClass.extract(data, 'c', 'doxygen'):
self.assertIn(doc.assemble(), docs)

0 comments on commit d79e497

Please sign in to comment.