Skip to content

Commit dbc8022

Browse files
EvanJuVcopybara-github
authored andcommitted
Don't strip whitespace when collecting descriptions in docstring
PiperOrigin-RevId: 260030371 Change-Id: Id307fa5ca4c77e07290928daea6b3e4291e1c0e6
1 parent ed3cd74 commit dbc8022

File tree

3 files changed

+63
-6
lines changed

3 files changed

+63
-6
lines changed

fire/docstrings.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656

5757
import collections
5858
import re
59+
import textwrap
5960

6061
import enum
6162

@@ -179,7 +180,10 @@ def parse(docstring):
179180
_consume_line(line_info, state)
180181

181182
summary = ' '.join(state.summary.lines) if state.summary.lines else None
182-
description = _join_lines(state.description.lines)
183+
state.description.lines = _strip_blank_lines(state.description.lines)
184+
description = textwrap.dedent('\n'.join(state.description.lines))
185+
if not description:
186+
description = None
183187
returns = _join_lines(state.returns.lines)
184188
yields = _join_lines(state.yields.lines)
185189
raises = _join_lines(state.raises.lines)
@@ -203,6 +207,32 @@ def parse(docstring):
203207
)
204208

205209

210+
def _strip_blank_lines(lines):
211+
"""Removes lines containing only blank characters before and after the text.
212+
213+
Args:
214+
lines: A list of lines.
215+
Returns:
216+
A list of lines without trailing or leading blank lines.
217+
"""
218+
# Find the first non-blank line.
219+
start = 0
220+
while lines and _is_blank(lines[start]):
221+
start += 1
222+
223+
lines = lines[start:]
224+
225+
# Remove trailing blank lines.
226+
while lines and _is_blank(lines[-1]):
227+
lines.pop()
228+
229+
return lines
230+
231+
232+
def _is_blank(line):
233+
return not line or line.isspace()
234+
235+
206236
def _join_lines(lines):
207237
"""Joins lines with the appropriate connective whitespace.
208238
@@ -391,7 +421,7 @@ def _consume_line(line_info, state):
391421
else:
392422
# We're past the end of the summary.
393423
# Additions now contribute to the description.
394-
state.description.lines.append(line_info.remaining)
424+
state.description.lines.append(line_info.remaining_raw)
395425
else:
396426
state.summary.permitted = False
397427

@@ -470,6 +500,7 @@ def _create_line_info(line, next_line):
470500
line_info = Namespace() # TODO(dbieber): Switch to an explicit class.
471501
line_info.line = line
472502
line_info.stripped = line.strip()
503+
line_info.remaining_raw = line_info.line
473504
line_info.remaining = line_info.stripped
474505
line_info.indentation = len(line) - len(line.lstrip())
475506
line_info.next.line = next_line
@@ -497,20 +528,23 @@ def _update_section_state(line_info, state):
497528
state.section.format = Formats.GOOGLE
498529
state.section.title = google_section
499530
line_info.remaining = _get_after_google_header(line_info)
531+
line_info.remaining_raw = line_info.remaining
500532
section_updated = True
501533

502534
rst_section = _rst_section(line_info)
503535
if rst_section:
504536
state.section.format = Formats.RST
505537
state.section.title = rst_section
506538
line_info.remaining = _get_after_directive(line_info)
539+
line_info.remaining_raw = line_info.remaining
507540
section_updated = True
508541

509542
numpy_section = _numpy_section(line_info)
510543
if numpy_section:
511544
state.section.format = Formats.NUMPY
512545
state.section.title = numpy_section
513546
line_info.remaining = ''
547+
line_info.remaining_raw = line_info.remaining
514548
section_updated = True
515549

516550
if section_updated:

fire/docstrings_test.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def test_google_format_typed_args_and_returns(self):
131131
expected_docstring_info = DocstringInfo(
132132
summary='Docstring summary.',
133133
description='This is a longer description of the docstring. It spans '
134-
'multiple lines, as is allowed.',
134+
'multiple lines, as\nis allowed.',
135135
args=[
136136
ArgInfo(name='param1', type='int',
137137
description='The first parameter.'),
@@ -159,7 +159,7 @@ def test_rst_format_typed_args_and_returns(self):
159159
expected_docstring_info = DocstringInfo(
160160
summary='Docstring summary.',
161161
description='This is a longer description of the docstring. It spans '
162-
'across multiple lines.',
162+
'across multiple\nlines.',
163163
args=[
164164
ArgInfo(name='arg1', type='str',
165165
description='Description of arg1.'),
@@ -193,7 +193,7 @@ def test_numpy_format_typed_args_and_returns(self):
193193
expected_docstring_info = DocstringInfo(
194194
summary='Docstring summary.',
195195
description='This is a longer description of the docstring. It spans '
196-
'across multiple lines.',
196+
'across multiple\nlines.',
197197
args=[
198198
ArgInfo(name='param1', type='int',
199199
description='The first parameter.'),
@@ -217,7 +217,7 @@ def test_multisection_docstring(self):
217217
expected_docstring_info = DocstringInfo(
218218
summary='Docstring summary.',
219219
description='This is the first section of a docstring description.\n\n'
220-
'This is the second section of a docstring description. This docstring '
220+
'This is the second section of a docstring description. This docstring\n'
221221
'description has just two sections.',
222222
)
223223
self.assertEqual(docstring_info, expected_docstring_info)
@@ -232,6 +232,12 @@ def test_ill_formed_docstring(self):
232232
"""
233233
docstrings.parse(docstring)
234234

235+
def test_strip_blank_lines(self):
236+
lines = [' ', ' foo ', ' ']
237+
expected_output = [' foo ']
238+
239+
self.assertEqual(docstrings._strip_blank_lines(lines), expected_output)
240+
235241

236242
if __name__ == '__main__':
237243
testutils.main()

fire/test_components.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,20 @@ def function_with_varargs(arg1, arg2, arg3=1, *varargs): # pylint: disable=keyw
440440
def function_with_keyword_arguments(arg1, arg2=3, **kwargs):
441441
del arg2 # Unused.
442442
return arg1, kwargs
443+
444+
445+
def fn_with_code_in_docstring():
446+
"""This has code in the docstring.
447+
448+
449+
450+
Example:
451+
x = fn_with_code_in_docstring()
452+
indentation_matters = True
453+
454+
455+
456+
Returns:
457+
True.
458+
"""
459+
return True

0 commit comments

Comments
 (0)