Skip to content

Commit 0fa5c42

Browse files
dbiebercopybara-github
authored andcommitted
robustify docstring parser to blank lines after section starts. Resolves #183
PiperOrigin-RevId: 260593779 Change-Id: Iab9052b9d56b24d65df9beb3ed8add68e2288926
1 parent a54ef58 commit 0fa5c42

File tree

2 files changed

+32
-22
lines changed

2 files changed

+32
-22
lines changed

fire/docstrings.py

+7-10
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,9 @@ def parse(docstring):
188188
yields = _join_lines(state.yields.lines)
189189
raises = _join_lines(state.raises.lines)
190190

191-
args = [
192-
ArgInfo(
193-
name=arg.name,
194-
type=_cast_to_known_type(_join_lines(arg.type.lines)),
195-
description=_join_lines(arg.description.lines),
196-
)
197-
for arg in state.args
198-
]
191+
args = [ArgInfo(
192+
name=arg.name, type=_cast_to_known_type(_join_lines(arg.type.lines)),
193+
description=_join_lines(arg.description.lines)) for arg in state.args]
199194

200195
return DocstringInfo(
201196
summary=summary,
@@ -503,10 +498,12 @@ def _create_line_info(line, next_line):
503498
line_info.remaining_raw = line_info.line
504499
line_info.remaining = line_info.stripped
505500
line_info.indentation = len(line) - len(line.lstrip())
501+
# TODO(dbieber): If next_line is blank, use the next non-blank line.
506502
line_info.next.line = next_line
507-
line_info.next.stripped = next_line.strip() if next_line else None
503+
next_line_exists = next_line is not None
504+
line_info.next.stripped = next_line.strip() if next_line_exists else None
508505
line_info.next.indentation = (
509-
len(next_line) - len(next_line.lstrip()) if next_line else None)
506+
len(next_line) - len(next_line.lstrip()) if next_line_exists else None)
510507
# Note: This counts all whitespace equally.
511508
return line_info
512509

fire/docstrings_test.py

+25-12
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_one_line_simple(self):
3434
expected_docstring_info = DocstringInfo(
3535
summary='A simple one line docstring.',
3636
)
37-
self.assertEqual(docstring_info, expected_docstring_info)
37+
self.assertEqual(expected_docstring_info, docstring_info)
3838

3939
def test_one_line_simple_whitespace(self):
4040
docstring = """
@@ -44,7 +44,7 @@ def test_one_line_simple_whitespace(self):
4444
expected_docstring_info = DocstringInfo(
4545
summary='A simple one line docstring.',
4646
)
47-
self.assertEqual(docstring_info, expected_docstring_info)
47+
self.assertEqual(expected_docstring_info, docstring_info)
4848

4949
def test_one_line_too_long(self):
5050
# pylint: disable=line-too-long
@@ -57,7 +57,7 @@ def test_one_line_too_long(self):
5757
'a little too long so it keeps going well beyond a reasonable length '
5858
'for a one-liner.',
5959
)
60-
self.assertEqual(docstring_info, expected_docstring_info)
60+
self.assertEqual(expected_docstring_info, docstring_info)
6161

6262
def test_one_line_runs_over(self):
6363
# pylint: disable=line-too-long
@@ -70,7 +70,7 @@ def test_one_line_runs_over(self):
7070
summary='A one line docstring thats both a little too verbose and '
7171
'a little too long so it runs onto a second line.',
7272
)
73-
self.assertEqual(docstring_info, expected_docstring_info)
73+
self.assertEqual(expected_docstring_info, docstring_info)
7474

7575
def test_one_line_runs_over_whitespace(self):
7676
docstring = """
@@ -82,7 +82,7 @@ def test_one_line_runs_over_whitespace(self):
8282
summary='A one line docstring thats both a little too verbose and '
8383
'a little too long so it runs onto a second line.',
8484
)
85-
self.assertEqual(docstring_info, expected_docstring_info)
85+
self.assertEqual(expected_docstring_info, docstring_info)
8686

8787
def test_google_format_args_only(self):
8888
docstring = """One line description.
@@ -99,7 +99,7 @@ def test_google_format_args_only(self):
9999
ArgInfo(name='arg2', description='arg2_description'),
100100
]
101101
)
102-
self.assertEqual(docstring_info, expected_docstring_info)
102+
self.assertEqual(expected_docstring_info, docstring_info)
103103

104104
def test_google_format_arg_named_args(self):
105105
docstring = """
@@ -112,7 +112,7 @@ def test_google_format_arg_named_args(self):
112112
ArgInfo(name='args', description='arg_description'),
113113
]
114114
)
115-
self.assertEqual(docstring_info, expected_docstring_info)
115+
self.assertEqual(expected_docstring_info, docstring_info)
116116

117117
def test_google_format_typed_args_and_returns(self):
118118
docstring = """Docstring summary.
@@ -140,7 +140,7 @@ def test_google_format_typed_args_and_returns(self):
140140
],
141141
returns='bool: The return value. True for success, False otherwise.'
142142
)
143-
self.assertEqual(docstring_info, expected_docstring_info)
143+
self.assertEqual(expected_docstring_info, docstring_info)
144144

145145
def test_rst_format_typed_args_and_returns(self):
146146
docstring = """Docstring summary.
@@ -169,7 +169,7 @@ def test_rst_format_typed_args_and_returns(self):
169169
returns='int -- description of the return value.',
170170
raises='AttributeError, KeyError',
171171
)
172-
self.assertEqual(docstring_info, expected_docstring_info)
172+
self.assertEqual(expected_docstring_info, docstring_info)
173173

174174
def test_numpy_format_typed_args_and_returns(self):
175175
docstring = """Docstring summary.
@@ -203,7 +203,7 @@ def test_numpy_format_typed_args_and_returns(self):
203203
# TODO(dbieber): Support return type.
204204
returns='bool True if successful, False otherwise.',
205205
)
206-
self.assertEqual(docstring_info, expected_docstring_info)
206+
self.assertEqual(expected_docstring_info, docstring_info)
207207

208208
def test_multisection_docstring(self):
209209
docstring = """Docstring summary.
@@ -222,7 +222,20 @@ def test_multisection_docstring(self):
222222
'\n'
223223
'description has just two sections.',
224224
)
225-
self.assertEqual(docstring_info, expected_docstring_info)
225+
self.assertEqual(expected_docstring_info, docstring_info)
226+
227+
def test_google_section_with_blank_first_line(self):
228+
docstring = """Inspired by requests HTTPAdapter docstring.
229+
230+
:param x: Simple param.
231+
232+
Usage:
233+
234+
>>> import requests
235+
"""
236+
docstring_info = docstrings.parse(docstring)
237+
self.assertEqual('Inspired by requests HTTPAdapter docstring.',
238+
docstring_info.summary)
226239

227240
def test_ill_formed_docstring(self):
228241
docstring = """Docstring summary.
@@ -238,7 +251,7 @@ def test_strip_blank_lines(self):
238251
lines = [' ', ' foo ', ' ']
239252
expected_output = [' foo ']
240253

241-
self.assertEqual(docstrings._strip_blank_lines(lines), expected_output) # pylint: disable=protected-access
254+
self.assertEqual(expected_output, docstrings._strip_blank_lines(lines)) # pylint: disable=protected-access
242255

243256

244257
if __name__ == '__main__':

0 commit comments

Comments
 (0)