Skip to content

Commit d79e497

Browse files
committed
DocBaseClass: Add DocBaseClass
Add DocBaseClass Merge DocumentationExtraction with DocBaseClass Closes #2659
1 parent 86f0544 commit d79e497

3 files changed

Lines changed: 147 additions & 62 deletions

File tree

coalib/bearlib/languages/documentation/DocumentationExtraction.py renamed to coalib/bearlib/languages/documentation/DocBaseClass.py

Lines changed: 78 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
DocstyleDefinition)
55
from coalib.bearlib.languages.documentation.DocumentationComment import (
66
DocumentationComment)
7+
from coalib.results.Diff import Diff
8+
from coalib.results.TextRange import TextRange
79
from coalib.results.TextPosition import TextPosition
810

911

@@ -254,31 +256,81 @@ def extract_documentation_with_markers(content, docstyle_definition):
254256
yield doc
255257

256258

257-
def extract_documentation(content, language, docstyle):
259+
class DocBaseClass:
258260
"""
259-
Extracts all documentation texts inside the given source-code-string using
260-
the coala docstyle definition files.
261-
262-
The documentation texts are sorted by their order appearing in ``content``.
263-
264-
For more information about how documentation comments are identified and
265-
extracted, see DocstyleDefinition.doctypes enumeration.
266-
267-
:param content: The source-code-string where to extract
268-
documentation from. Needs to be a list or tuple
269-
where each string item is a single line
270-
(including ending whitespaces like ``\\n``).
271-
:param language: The programming language used.
272-
:param docstyle: The documentation style/tool used
273-
(e.g. doxygen).
274-
:raises FileNotFoundError: Raised when the docstyle definition file was not
275-
found.
276-
:raises KeyError: Raised when the given language is not defined in
277-
given docstyle.
278-
:raises ValueError: Raised when a docstyle definition setting has an
279-
invalid format.
280-
:return: An iterator returning each DocumentationComment
281-
found in the content.
261+
DocBaseClass holds important functions which will extract, parse
262+
and generates diffs for documentation. All bears that processes
263+
documentation should inherit from this.
282264
"""
283-
docstyle_definition = DocstyleDefinition.load(language, docstyle)
284-
return extract_documentation_with_markers(content, docstyle_definition)
265+
266+
@staticmethod
267+
def extract(content, language, docstyle):
268+
"""
269+
Extracts all documentation texts inside the given source-code-string
270+
using the coala docstyle definition files.
271+
272+
The documentation texts are sorted by their order appearing in
273+
``content``.
274+
275+
For more information about how documentation comments are
276+
identified and extracted, see DocstyleDefinition.doctypes enumeration.
277+
278+
:param content: The source-code-string where to extract
279+
documentation from. Needs to be a list
280+
or tuple where each string item is a
281+
single line(including ending whitespaces
282+
like ``\\n``).
283+
:param language: The programming language used.
284+
:param docstyle: The documentation style/tool used
285+
(e.g. doxygen).
286+
:raises FileNotFoundError: Raised when the docstyle definition file
287+
was not found.
288+
:raises KeyError: Raised when the given language is not
289+
defined in given docstyle.
290+
:raises ValueError: Raised when a docstyle definition setting
291+
has an invalid format.
292+
:return: An iterator returning each
293+
DocumentationComment found in the content.
294+
"""
295+
docstyle_definition = DocstyleDefinition.load(language, docstyle)
296+
return extract_documentation_with_markers(
297+
content, docstyle_definition)
298+
299+
@staticmethod
300+
def generate_diff(file, doc_comment, new_comment):
301+
"""
302+
Generates diff between the original doc_comment and its fix
303+
new_comment which are instances of DocumentationComment.
304+
305+
:param doc_comment:
306+
Original instance of DocumentationComment.
307+
:param new_comment:
308+
Fixed instance of DocumentationComment.
309+
:return:
310+
Diff instance.
311+
"""
312+
diff = Diff(file)
313+
314+
# We need to update old comment positions, as `assemble()`
315+
# prepends indentation for first line.
316+
old_range = TextRange.from_values(
317+
doc_comment.range.start.line,
318+
1,
319+
doc_comment.range.end.line,
320+
doc_comment.range.end.column)
321+
322+
# Clearing cached assemble() so a fresh one is fetched.
323+
new_comment.assemble.cache_clear()
324+
325+
diff.replace(old_range, new_comment.assemble())
326+
return diff
327+
328+
def process_documentation(self, *args, **kwargs):
329+
"""
330+
Checks and handles the fixing part of documentation.
331+
332+
:return:
333+
A tuple of processed documentation and warning_desc.
334+
"""
335+
raise NotImplementedError('This function has to be implemented for a '
336+
'documentation bear.')

tests/bearlib/languages/documentation/DocumentationExtractionTest.py renamed to tests/bearlib/languages/documentation/DocBaseClassTest.py

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,28 @@
44
DocstyleDefinition)
55
from coalib.bearlib.languages.documentation.DocumentationComment import (
66
DocumentationComment)
7-
from coalib.bearlib.languages.documentation.DocumentationExtraction import (
8-
extract_documentation)
7+
from coalib.bearlib.languages.documentation.DocBaseClass import (
8+
DocBaseClass)
99
from tests.bearlib.languages.documentation.TestUtils import (
1010
load_testdata)
1111
from coalib.results.TextPosition import TextPosition
12+
from coalib.results.TextRange import TextRange
13+
from coalib.results.Diff import Diff
1214

1315

14-
class DocumentationExtractionTest(unittest.TestCase):
16+
class DocBaseClassTest(unittest.TestCase):
17+
18+
def test_DocBaseClass_extraction_invalid_input(self):
1519

16-
def test_extract_documentation_invalid_input(self):
1720
with self.assertRaises(FileNotFoundError):
18-
tuple(extract_documentation('', 'PYTHON', 'INVALID'))
21+
tuple(DocBaseClass.extract('', 'PYTHON', 'INVALID'))
1922

20-
def test_extract_documentation_C(self):
23+
def test_DocBaseClass_extraction_C(self):
2124
data = load_testdata('data.c')
2225

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

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

@@ -59,31 +62,31 @@ def test_extract_documentation_C(self):
5962
TextPosition(28, 1)))
6063

6164
self.assertEqual(tuple(
62-
extract_documentation(data, 'C', 'doxygen')),
65+
DocBaseClass.extract(data, 'C', 'doxygen')),
6366
expected_results)
6467

65-
def test_extract_documentation_C_2(self):
68+
def test_DocBaseClass_extraction_C_2(self):
6669
data = ['/** my main description\n', ' * continues here */']
6770

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

7073
self.assertEqual(
71-
list(extract_documentation(data, 'C', 'doxygen')),
74+
list(DocBaseClass.extract(data, 'C', 'doxygen')),
7275
[DocumentationComment(' my main description\n continues here',
7376
docstyle_C_doxygen, '',
7477
docstyle_C_doxygen.markers[0],
7578
TextPosition(1, 1))])
7679

77-
def test_extract_documentation_CPP(self):
80+
def test_DocBaseClass_extraction_CPP(self):
7881
data = load_testdata('data.cpp')
7982

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

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

86-
self.assertEqual(tuple(extract_documentation(data, 'CPP', 'doxygen')),
89+
self.assertEqual(tuple(DocBaseClass.extract(data, 'CPP', 'doxygen')),
8790
(DocumentationComment(
8891
('\n'
8992
' This is the main function.\n'
@@ -118,20 +121,20 @@ def test_extract_documentation_CPP(self):
118121
docstyle_CPP_doxygen.markers[4],
119122
TextPosition(32, 1))))
120123

121-
def test_extract_documentation_CPP_2(self):
124+
def test_DocBaseClass_CPP_2(self):
122125
data = load_testdata('data2.cpp')
123126

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

126-
self.assertEqual(tuple(extract_documentation(data, 'CPP', 'doxygen')),
129+
self.assertEqual(tuple(DocBaseClass.extract(data, 'CPP', 'doxygen')),
127130
(DocumentationComment(
128131
('module comment\n'
129132
' hello world\n'),
130133
docstyle_CPP_doxygen, '',
131134
docstyle_CPP_doxygen.markers[0],
132135
TextPosition(1, 1)),))
133136

134-
def test_extract_documentation_PYTHON3(self):
137+
def test_DocBaseClass_PYTHON3(self):
135138
data = load_testdata('data.py')
136139
docstyle_PYTHON3_default = DocstyleDefinition.load('PYTHON3',
137140
'default')
@@ -192,9 +195,8 @@ def test_extract_documentation_PYTHON3(self):
192195
)
193196

194197
self.assertEqual(
195-
tuple(extract_documentation(data, 'PYTHON3', 'default')),
198+
tuple(DocBaseClass.extract(data, 'PYTHON3', 'default')),
196199
expected)
197-
198200
# Change only the docstyle in expected results.
199201
expected = list(DocumentationComment(r.documentation,
200202
docstyle_PYTHON3_doxygen,
@@ -214,37 +216,37 @@ def test_extract_documentation_PYTHON3(self):
214216
TextPosition(30, 1)))
215217

216218
self.assertEqual(
217-
list(extract_documentation(data, 'PYTHON3', 'doxygen')),
219+
list(DocBaseClass.extract(data, 'PYTHON3', 'doxygen')),
218220
expected)
219221

220-
def test_extract_documentation_PYTHON3_2(self):
222+
def test_DocBaseClass_extraction_PYTHON3_2(self):
221223
data = ['\n', '""" documentation in single line """\n', 'print(1)\n']
222224

223225
docstyle_PYTHON3_default = DocstyleDefinition.load('PYTHON3',
224226
'default')
225227

226228
self.assertEqual(
227-
list(extract_documentation(data, 'PYTHON3', 'default')),
229+
list(DocBaseClass.extract(data, 'PYTHON3', 'default')),
228230
[DocumentationComment(' documentation in single line ',
229231
docstyle_PYTHON3_default, '',
230232
docstyle_PYTHON3_default.markers[0],
231233
TextPosition(2, 1))])
232234

233-
def test_extract_documentation_PYTHON3_3(self):
235+
def test_DocBaseClass_extraction_PYTHON3_3(self):
234236
data = ['## documentation in single line without return at end.']
235237

236238
docstyle_PYTHON3_doxygen = DocstyleDefinition.load('PYTHON3',
237239
'doxygen')
238240

239241
self.assertEqual(
240-
list(extract_documentation(data, 'PYTHON3', 'doxygen')),
242+
list(DocBaseClass.extract(data, 'PYTHON3', 'doxygen')),
241243
[DocumentationComment(' documentation in single line without '
242244
'return at end.',
243245
docstyle_PYTHON3_doxygen, '',
244246
docstyle_PYTHON3_doxygen.markers[1],
245247
TextPosition(1, 1))])
246248

247-
def test_extract_documentation_PYTHON3_4(self):
249+
def test_DocBaseClass_extraction_PYTHON3_4(self):
248250
data = ['\n', 'triple_quote_string_test = """\n',
249251
'This is not a docstring\n', '"""\n']
250252

@@ -254,5 +256,35 @@ def test_extract_documentation_PYTHON3_4(self):
254256
# Nothing is yielded as triple quote string literals are being
255257
# ignored.
256258
self.assertEqual(
257-
list(extract_documentation(data, 'PYTHON3', 'default')),
259+
list(DocBaseClass.extract(data, 'PYTHON3', 'default')),
258260
[])
261+
262+
def test_generate_diff(self):
263+
data_old = ['\n', '""" documentation in single line """\n']
264+
for doc_comment in DocBaseClass.extract(
265+
data_old, 'PYTHON3', 'default'):
266+
old_doc_comment = doc_comment
267+
268+
old_range = TextRange.from_values(
269+
old_doc_comment.range.start.line,
270+
1,
271+
old_doc_comment.range.end.line,
272+
old_doc_comment.range.end.column)
273+
274+
data_new = ['\n', '"""\n documentation in single line\n"""\n']
275+
for doc_comment in DocBaseClass.extract(
276+
data_new, 'PYTHON3', 'default'):
277+
new_doc_comment = doc_comment
278+
279+
diff = DocBaseClass.generate_diff(
280+
data_old, old_doc_comment, new_doc_comment)
281+
282+
diff_expected = Diff(data_old)
283+
diff_expected.replace(old_range, new_doc_comment.assemble())
284+
285+
self.assertEqual(diff, diff_expected)
286+
287+
def test_DocBaseClass_process_documentation_not_implemented(self):
288+
test_object = DocBaseClass()
289+
self.assertRaises(NotImplementedError,
290+
test_object.process_documentation)

tests/bearlib/languages/documentation/DocumentationCommentTest.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
DocstyleDefinition)
55
from coalib.bearlib.languages.documentation.DocumentationComment import (
66
DocumentationComment)
7-
from coalib.bearlib.languages.documentation.DocumentationExtraction import (
8-
extract_documentation)
7+
from coalib.bearlib.languages.documentation.DocBaseClass import (
8+
DocBaseClass)
99
from tests.bearlib.languages.documentation.TestUtils import (
1010
load_testdata)
1111
from coalib.results.TextPosition import TextPosition
@@ -72,7 +72,7 @@ def test_not_implemented(self):
7272
def test_from_metadata(self):
7373
data = load_testdata('default.py')
7474

75-
original = list(extract_documentation(data, 'python', 'default'))
75+
original = list(DocBaseClass.extract(data, 'python', 'default'))
7676

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

110110
def test_description(self):
111111
doc = ' description only '
112-
self.check_docstring(doc, [self.Description(desc=' description only ')])
112+
self.check_docstring(
113+
doc, [self.Description(desc=' description only ')])
113114

114115
def test_params_default(self):
115116
self.maxDiff = None
@@ -139,7 +140,7 @@ def test_python_default(self):
139140
data = load_testdata('default.py')
140141

141142
parsed_docs = [doc.parse() for doc in
142-
extract_documentation(data, 'python', 'default')]
143+
DocBaseClass.extract(data, 'python', 'default')]
143144

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

190191
parsed_docs = [doc.parse() for doc in
191-
extract_documentation(data, 'python', 'doxygen')]
192+
DocBaseClass.extract(data, 'python', 'doxygen')]
192193

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

234235
parsed_docs = [doc.parse() for doc in
235-
extract_documentation(data, 'java', 'default')]
236+
DocBaseClass.extract(data, 'java', 'default')]
236237

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

255256
parsed_docs = [doc.parse() for doc in
256-
extract_documentation(data, 'golang', 'golang')]
257+
DocBaseClass.extract(data, 'golang', 'golang')]
257258

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

275-
for doc in extract_documentation(data, 'python', 'default'):
276+
for doc in DocBaseClass.extract(data, 'python', 'default'):
276277
self.assertIn(doc.assemble(), docs)
277278

278279
def test_doxygen_assembly(self):
279280
data = load_testdata('doxygen.py')
280281
docs = ''.join(data)
281282

282-
for doc in extract_documentation(data, 'python', 'doxygen'):
283+
for doc in DocBaseClass.extract(data, 'python', 'doxygen'):
283284
self.assertIn(doc.assemble(), docs)
284285

285286
def test_c_assembly(self):
286287
data = load_testdata('default.c')
287288
docs = ''.join(data)
288289

289-
for doc in extract_documentation(data, 'c', 'doxygen'):
290+
for doc in DocBaseClass.extract(data, 'c', 'doxygen'):
290291
self.assertIn(doc.assemble(), docs)

0 commit comments

Comments
 (0)