Skip to content

Commit 07d04bd

Browse files
MarkDaoustcopybara-github
authored andcommitted
More flexible arg lists and better "brief" docstrings.
Arg lists now allow both: `Name: description` and `Name (type): description` Brief docstrings will now try to take the first sentence if the first line doesn't look like one. PiperOrigin-RevId: 525602573
1 parent f3745bc commit 07d04bd

File tree

3 files changed

+97
-15
lines changed

3 files changed

+97
-15
lines changed

tools/tensorflow_docs/api_generator/parser.py

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -379,11 +379,12 @@ def __str__(self) -> str:
379379

380380
ITEM_RE = re.compile(
381381
r"""
382-
^(\*?\*?'?"? # Capture optional *s to allow *args, **kwargs and quotes
383-
\w[\w.'"]*? # Capture a word character followed by word characters
384-
# or "."s or ending quotes.
382+
^(\*?\*?'?"? # Optional * to allow *args, **kwargs and quotes
383+
\w[\w.'"]*? # words, dots and closing quotes
384+
(?:[ ]?[\(\]][\(\[\w.\)\]]*?)? # maybe a space and some words in parens.
385385
)\s*:\s # Allow any whitespace around the colon.""",
386-
re.MULTILINE | re.VERBOSE)
386+
re.MULTILINE | re.VERBOSE,
387+
)
387388

388389
@classmethod
389390
def split_string(cls, docstring: str):
@@ -561,23 +562,70 @@ def parse_md_docstring(
561562
else:
562563
raw_docstring = _get_raw_docstring(py_object)
563564

564-
raw_docstring = parser_config.reference_resolver.replace_references(
565-
raw_docstring, full_name)
566-
567565
atat_re = re.compile(r' *@@[a-zA-Z_.0-9]+ *$')
568566
raw_docstring = '\n'.join(
569567
line for line in raw_docstring.split('\n') if not atat_re.match(line))
570568

571569
docstring, compatibility = _handle_compatibility(raw_docstring)
570+
compatibility = {
571+
key: parser_config.reference_resolver.replace_references(value, full_name)
572+
for key, value in compatibility.items()
573+
}
572574

573575
if 'Generated by: tensorflow/tools/api/generator' in docstring:
574576
docstring = ''
575577

576-
# Remove the first-line "brief" docstring.
577578
lines = docstring.split('\n')
578-
brief = lines.pop(0)
579-
580-
docstring = '\n'.join(lines)
579+
first_line = lines[0].strip()
580+
581+
good_first_line = (
582+
first_line.endswith(('.', '!', '?', ')')) or first_line.isupper()
583+
)
584+
585+
def escape(match):
586+
return (
587+
match.group(0)
588+
.replace('.', '.<skip>')
589+
.replace('!', '!<skip>')
590+
.replace('?', '?<skip>')
591+
)
592+
593+
def unescape(s):
594+
return (
595+
s.replace('.<skip>', '.')
596+
.replace('!<skip>', '!')
597+
.replace('?<skip>', '?')
598+
)
599+
600+
escaped = re.sub('`(.|\n)*?`', escape, docstring)
601+
match = re.match(
602+
r"""
603+
(?P<first_sentence>
604+
.*? # Take as little as possible
605+
(
606+
[.!?](?!<skip>)($|(?=\s))\n?| # stop at the end of a sentence
607+
(?=\n\n) # or before a blank line
608+
)
609+
)
610+
(?P<remainder>.*) # collect the rest of the docstring
611+
""",
612+
escaped,
613+
re.VERBOSE | re.DOTALL,
614+
)
615+
if not good_first_line and match:
616+
groupdict = match.groupdict()
617+
brief = unescape(re.sub('\s+', ' ', groupdict['first_sentence']))
618+
docstring = unescape(groupdict['remainder'])
619+
else:
620+
# Use the first line
621+
brief = lines.pop(0)
622+
docstring = '\n'.join(lines)
623+
624+
brief = brief.strip()
625+
brief = parser_config.reference_resolver.replace_references(brief, full_name)
626+
docstring = parser_config.reference_resolver.replace_references(
627+
docstring, full_name
628+
)
581629

582630
docstring_parts = TitleBlock.split_string(docstring)
583631

tools/tensorflow_docs/api_generator/parser_test.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,8 +526,10 @@ def foo(self):
526526
self.assertCountEqual(doc_info.compatibility.keys(),
527527
{'numpy', 'two words!'})
528528

529-
self.assertEqual(doc_info.compatibility['numpy'],
530-
'NumPy has nothing as awesome as this function.\n')
529+
self.assertEqual(
530+
doc_info.compatibility['numpy'],
531+
'NumPy has nothing as awesome as this function.',
532+
)
531533

532534
def test_downgrade_h1_docstrings(self):
533535
h1_docstring = textwrap.dedent("""\
@@ -859,6 +861,32 @@ def test_split_title_blocks(self):
859861
'\nSome tensors, with the same type as the input.\n')
860862
self.assertLen(returns.items, 2)
861863

864+
def test_title_block(self):
865+
docstring = textwrap.dedent("""\
866+
hello
867+
868+
Attributes:
869+
extra paragraph?
870+
item: description
871+
describe describe
872+
item2 (int): is a number
873+
this is not an item: really not
874+
this either: nope
875+
876+
goodbye
877+
""")
878+
docstring_parts = parser.TitleBlock.split_string(docstring)
879+
print(docstring_parts)
880+
self.assertEqual('hello', docstring_parts[0])
881+
self.assertIsInstance(docstring_parts[1], parser.TitleBlock)
882+
self.assertEqual('\ngoodbye\n', docstring_parts[2])
883+
884+
block = docstring_parts[1]
885+
self.assertEqual('\nextra paragraph?\n', block.text)
886+
self.assertEqual('item', block.items[0][0])
887+
self.assertEqual('item2 (int)', block.items[1][0])
888+
self.assertLen(block.items, 2)
889+
862890
def test_strip_todos(self):
863891
input_str = ("""# TODO(blah) blah
864892

tools/tensorflow_docs/api_generator/pretty_docs/class_page.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,17 +400,18 @@ def _augment_attributes(
400400
Returns:
401401
Augmented "Attr" block.
402402
"""
403-
404403
attribute_block = None
405404

406405
for attr_block_index, part in enumerate(docstring_parts):
407406
if isinstance(part, parser.TitleBlock) and part.title.startswith('Attr'):
408407
raw_attrs = collections.OrderedDict(part.items)
408+
old_block = part
409409
break
410410
else:
411411
# Didn't find the attributes block, there may still be attributes so
412412
# add a placeholder for them at the end.
413413
raw_attrs = collections.OrderedDict()
414+
old_block = None
414415
attr_block_index = len(docstring_parts)
415416
docstring_parts.append(None)
416417

@@ -436,8 +437,13 @@ def _augment_attributes(
436437
attrs.setdefault(name, desc)
437438

438439
if attrs:
440+
if old_block is not None:
441+
text = old_block.text
442+
else:
443+
text = ''
439444
attribute_block = parser.TitleBlock(
440-
title='Attributes', text='', items=list(attrs.items()))
445+
title='Attributes', text=text, items=list(attrs.items())
446+
)
441447

442448
# Delete the Attrs block if it exists or delete the placeholder.
443449
del docstring_parts[attr_block_index]

0 commit comments

Comments
 (0)