Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docstring description multiline parsing #476

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
75 changes: 73 additions & 2 deletions fire/docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,22 @@ def parse(docstring):
state.returns.lines = []
state.yields.lines = []
state.raises.lines = []
state.line1 = None
thebadcoder96 marked this conversation as resolved.
Show resolved Hide resolved
state.line1_length = None
state.line2_first_word_length = None
state.line2_length = None
state.line3_first_word_length = None
state.max_line_length = 0

for index, line in enumerate(lines):
has_next = index + 1 < lines_len
previous_line = lines[index - 1] if index > 0 else None
next_line = lines[index + 1] if has_next else None
line_info = _create_line_info(line, next_line, previous_line)
_consume_line(line_info, state)

if state.line2_length:
thebadcoder96 marked this conversation as resolved.
Show resolved Hide resolved
_merge_if_long_arg(state)

summary = ' '.join(state.summary.lines) if state.summary.lines else None
state.description.lines = _strip_blank_lines(state.description.lines)
Expand Down Expand Up @@ -253,7 +262,7 @@ def _join_lines(lines):
# TODO(dbieber): Add parameters for variations in whitespace handling.
if not lines:
return None

started = False
group_texts = [] # Full text of each section.
group_lines = [] # Lines within the current section.
Expand All @@ -269,7 +278,7 @@ def _join_lines(lines):
group_lines = []

if group_lines: # Process the final group.
group_text = ' '.join(group_lines)
group_text = '\n'.join(group_lines)
group_texts.append(group_text)

return '\n\n'.join(group_texts)
Expand Down Expand Up @@ -391,6 +400,9 @@ def _consume_google_args_line(line_info, state):
"""Consume a single line from a Google args section."""
split_line = line_info.remaining.split(':', 1)
if len(split_line) > 1:
state.line1 = line_info.line
state.line1_length = len(line_info.line)
state.max_line_length = max(state.max_line_length, state.line1_length)
first, second = split_line # first is either the "arg" or "arg (type)"
if _is_arg_name(first.strip()):
arg = _get_or_create_arg_by_name(state, first.strip())
Expand All @@ -410,6 +422,65 @@ def _consume_google_args_line(line_info, state):
else:
if state.current_arg:
state.current_arg.description.lines.append(split_line[0])
state.max_line_length = max(state.max_line_length, len(line_info.line))
if line_info.previous.line == state.line1: # check for line2
thebadcoder96 marked this conversation as resolved.
Show resolved Hide resolved
state.line2_first_word_length = len(line_info.line.strip().split(' ')[0])
state.line2_length = len(line_info.line)
if line_info.next.line: #check for line3
state.line3_first_word_length = len(line_info.next.line.strip().split(' ')[0])


def _merge_if_long_arg(state):
"""Merges first two lines of the description if the arg name is too long.

Args:
state: The state of the docstring parser.
"""
apparent_max_line_length = roundup(state.max_line_length)
long_arg_name = roundup(len(state.current_arg.name), 5) >= 0.5 * apparent_max_line_length
if long_arg_name and state.line2_first_word_length and state.line3_first_word_length:
line1_intentionally_short = (state.line1_length + state.line2_first_word_length) <= apparent_max_line_length
line2_intentionally_short = (state.line2_length + state.line3_first_word_length) <= apparent_max_line_length
line1_intentionally_long = state.line1_length >= 1.05 * apparent_max_line_length
line2_intentionally_long = state.line2_length >= 1.05 * apparent_max_line_length
if not line1_intentionally_short and not line1_intentionally_long and not line2_intentionally_short and not line2_intentionally_long:
_merge_line1_line2(state.current_arg.description.lines)


def _merge_line1_line2(lines):
"""Merges the first two lines of a list of strings.

Example:
_merge_line1_line2(["oh","no","bro"]) == ["oh no","bro"]

Args:
lines: a list of strings representing each line.
Returns:
the same list but with the first two lines of the list now merged as one line.
"""
merged_line = lines[0] + " " + lines[1]
lines[0] = merged_line
lines.pop(1)
return lines


def roundup(number, multiple=10):
"""Rounds a number to the nearst multiple.

Example:
roundup(72) == 80

Args:
number: an interger type variable.
multiple: nearst multiple to round up to
Returns:
An interger value.
"""
remainder = number % multiple
if remainder == 0:
return number #already rounded
else:
return number + (multiple - remainder)


def _consume_line(line_info, state):
Expand Down
6 changes: 3 additions & 3 deletions fire/docstrings_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def test_google_format_multiline_arg_description(self):
description='The first parameter.'),
ArgInfo(name='param2', type='str',
description='The second parameter. This has a lot of text, '
'enough to cover two lines.'),
'enough to\ncover two lines.'),
],
)
self.assertEqual(expected_docstring_info, docstring_info)
Expand Down Expand Up @@ -229,7 +229,7 @@ def test_numpy_format_typed_args_and_returns(self):
description='The second parameter.'),
],
# TODO(dbieber): Support return type.
returns='bool True if successful, False otherwise.',
returns='bool\nTrue if successful, False otherwise.',
)
self.assertEqual(expected_docstring_info, docstring_info)

Expand Down Expand Up @@ -257,7 +257,7 @@ def test_numpy_format_multiline_arg_description(self):
description='The first parameter.'),
ArgInfo(name='param2', type='str',
description='The second parameter. This has a lot of text, '
'enough to cover two lines.'),
'enough to cover two\nlines.'),
],
)
thebadcoder96 marked this conversation as resolved.
Show resolved Hide resolved
self.assertEqual(expected_docstring_info, docstring_info)
Expand Down