Skip to content

Commit 50f848e

Browse files
committed
Implement warning block, rework follower (back to previous state),
improve test coverage
1 parent c43708f commit 50f848e

File tree

3 files changed

+52
-33
lines changed

3 files changed

+52
-33
lines changed

docstring_to_markdown/rst.py

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -110,26 +110,31 @@ class IParser(ABC):
110110

111111
@abstractmethod
112112
def can_parse(self, line: str) -> bool:
113-
pass
113+
"""Whether the line looks like a valid beginning of parsed block."""
114114

115115
@abstractmethod
116116
def initiate_parsing(self, line: str, current_language: str) -> IBlockBeginning:
117-
pass
117+
"""Initiate parsing of given line.
118+
119+
Arguments:
120+
line: first line to be parsed (that passed `can_parse()` test)
121+
current_language: language to use if highlighting code and no other language is specified in `line`
122+
"""
118123

119124
@abstractmethod
120125
def can_consume(self, line: str) -> bool:
121-
pass
126+
"""Whether the line can be parsed, or does it look like an end of parsable area?"""
122127

123128
@abstractmethod
124129
def consume(self, line: str) -> None:
125-
pass
130+
"""Parse given line."""
126131

127132
@abstractmethod
128133
def finish_consumption(self, final: bool) -> str:
129-
pass
134+
"""Finish parsing and return the converted part of the docstring."""
130135

131-
def get_follower(self, line: str) -> Union['IParser', None]:
132-
return None
136+
"""Is there another parser that should follow after this parser finished?"""
137+
follower: Union['IParser', None] = None
133138

134139

135140
class BlockParser(IParser):
@@ -144,27 +149,12 @@ def __init__(self):
144149

145150
@abstractmethod
146151
def can_parse(self, line: str) -> bool:
147-
"""
148-
All children should call _start_block in initiate_parsing() implementation.
149-
"""
150-
pass
151-
152-
@abstractmethod
153-
def initiate_parsing(
154-
self,
155-
line: str,
156-
current_language: str
157-
) -> IBlockBeginning:
158-
pass
152+
"""All children should call _start_block in initiate_parsing() implementation."""
159153

160154
def _start_block(self, language: str):
161155
self._buffer.append(self.enclosure + language)
162156
self._block_started = True
163157

164-
@abstractmethod
165-
def can_consume(self, line: str) -> bool:
166-
pass
167-
168158
def consume(self, line: str):
169159
if not self._block_started:
170160
raise ValueError('Block has not started')
@@ -222,8 +212,7 @@ def can_consume(self, line: str) -> bool:
222212
return line.strip() != '' and not line.startswith('>>>')
223213

224214
def can_parse(self, line: str) -> bool:
225-
# cannot be initiated directly
226-
return False
215+
return line.strip() != ''
227216

228217
def initiate_parsing(self, line: str, current_language: str) -> IBlockBeginning:
229218
self._start_block('')
@@ -250,9 +239,7 @@ def _strip_prompt(self, line: str) -> str:
250239
start = 4 if line.startswith('>>> ') or line.startswith('... ') else 3
251240
return line[start:]
252241

253-
def get_follower(self, line: str) -> Union['IParser', None]:
254-
if line:
255-
return PythonOutputBlockParser()
242+
follower = PythonOutputBlockParser()
256243

257244

258245
class DoubleColonBlockParser(IndentedBlockParser):
@@ -286,12 +273,13 @@ def initiate_parsing(self, line: str, current_language: str):
286273

287274
class NoteBlockParser(IndentedBlockParser):
288275
enclosure = '\n---'
276+
directives = {'.. note::', '.. warning::'}
289277

290278
def can_parse(self, line: str):
291-
return line.strip() == '.. note::'
279+
return line.strip() in self.directives
292280

293281
def initiate_parsing(self, line: str, current_language: str):
294-
self._start_block('\n**Note**\n')
282+
self._start_block('\n**Note**\n' if 'note' in line else '\n**Warning**\n')
295283
return IBlockBeginning(remainder='')
296284

297285

@@ -378,8 +366,8 @@ def flush_buffer():
378366
else:
379367
markdown += flush_buffer()
380368
markdown += active_parser.finish_consumption(False)
381-
follower = active_parser.get_follower(line)
382-
if follower:
369+
follower = active_parser.follower
370+
if follower and follower.can_parse(line):
383371
active_parser = follower
384372
active_parser.initiate_parsing(line, language)
385373
else:

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ python_requires = >=3.6
2727
addopts =
2828
--pyargs tests
2929
--cov docstring_to_markdown
30-
--cov-fail-under=92
30+
--cov-fail-under=97
3131
--cov-report term-missing:skip-covered
3232
-p no:warnings
3333
--flake8

tests/test_rst.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,32 @@ def func(): pass
350350
351351
"""
352352

353+
WARNING_BLOCK = """
354+
Load pickled object from file.
355+
356+
.. warning::
357+
Loading pickled data received from untrusted sources can be
358+
unsafe.
359+
360+
Parameters
361+
"""
362+
363+
364+
WARNING_BLOCK_MARKDOWN = """
365+
Load pickled object from file.
366+
367+
368+
---
369+
**Warning**
370+
371+
Loading pickled data received from untrusted sources can be
372+
unsafe.
373+
374+
---
375+
376+
Parameters
377+
"""
378+
353379

354380
INTEGRATION = """
355381
Return a fixed frequency DatetimeIndex.
@@ -431,6 +457,10 @@ def func(): pass
431457
'separates following paragraph after a code blocks without output': {
432458
'rst': CODE_BLOCK_BUT_NOT_OUTPUT,
433459
'md': CODE_BLOCK_BUT_NOT_OUTPUT_MD
460+
},
461+
'converts warnings': {
462+
'rst': WARNING_BLOCK,
463+
'md': WARNING_BLOCK_MARKDOWN
434464
}
435465
}
436466

@@ -440,6 +470,7 @@ def test_looks_like_rst_recognises_rst():
440470
assert looks_like_rst('the following code ::\n\n\tcode')
441471
assert looks_like_rst('the following code::\n\n\tcode')
442472
assert looks_like_rst('See Also\n--------\n')
473+
assert looks_like_rst('.. versionadded:: 0.1')
443474

444475

445476
def test_looks_like_rst_ignores_plain_text():

0 commit comments

Comments
 (0)