Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions numpydoc/docscrape_sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

IMPORT_MATPLOTLIB_RE = r"\b(import +matplotlib|from +matplotlib +import)\b"

# Used to help identify the first sentence of an object's description. These
# values are based on sphinx.ext.autosummary.
WELL_KNOWN_ABBREVIATIONS = ("et al.", "e.g.", "i.e.", "vs.")


class SphinxDocString(NumpyDocString):
def __init__(self, docstring, config=None):
Expand Down Expand Up @@ -159,16 +163,22 @@ def _process_param(self, param, desc, fake_autosummary):
# Referenced object has a docstring
display_param = f":obj:`{param} <{link_prefix}{param}>`"
if obj_doc:
# Overwrite desc. Take summary logic of autosummary
desc = re.split(r"\n\s*\n", obj_doc.strip(), maxsplit=1)[0]
# XXX: Should this have DOTALL?
# It does not in autosummary
m = re.search(r"^([A-Z].*?\.)(?:\s|$)", " ".join(desc.split()))
if m:
desc = m.group(1).strip()
# Overwrite desc with the first sentence. Based on
# sphinx.ext.autosummary's `extract_summary()`.
stanza = re.split(r"\n\s*\n", obj_doc.strip(), maxsplit=1)[0]
sentences = re.split(r"\.(?:\s+|$)", " ".join(stanza.split()))
if len(sentences) == 1:
# If there are no periods, use only the first line. This
# differs from autosummary, which takes the whole stanza.
desc = stanza.partition("\n")[0]
else:
desc = desc.partition("\n")[0]
desc = desc.split("\n")
desc = ""
for i in range(len(sentences)):
desc = ". ".join(sentences[: i + 1]).rstrip(".") + "."
if not desc.endswith(WELL_KNOWN_ABBREVIATIONS):
break
desc = [desc]

return display_param, desc

def _str_param_list(self, name, fake_autosummary=False):
Expand Down
58 changes: 58 additions & 0 deletions numpydoc/tests/test_docscrape.py
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,7 @@ def test_duplicate_signature():
But a description
no_docstring2 : str
multiline_sentence
multiline_single_sentence
midword_period
no_period

Expand Down Expand Up @@ -1253,6 +1254,7 @@ def test_class_members_doc():
But a description
no_docstring2 : str
multiline_sentence
multiline_single_sentence
midword_period
no_period

Expand Down Expand Up @@ -1300,6 +1302,12 @@ def multiline_sentence(self):
sentence. It spans multiple lines."""
return

@property
def multiline_single_sentence(self):
"""This is a
sentence."""
return

Comment on lines +1305 to +1310
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this example because the sentence-splitting regex used in sphinx.ext.autosummary was a little different, and caused some subtle issues here. This example was helpful in differentiating multiline behavior with and without a single period and multiple periods.

@property
def midword_period(self):
"""The sentence for numpy.org."""
Expand Down Expand Up @@ -1351,6 +1359,9 @@ def no_period(self):
:obj:`multiline_sentence <multiline_sentence>`
This is a sentence.

:obj:`multiline_single_sentence <multiline_single_sentence>`
This is a sentence.

:obj:`midword_period <midword_period>`
The sentence for numpy.org.

Expand Down Expand Up @@ -1419,6 +1430,53 @@ def an_attribute(self):
assert "Another description" not in str(SphinxClassDoc(Foo, config=cfg))


def test_class_properties_with_abbreviations():
class Foo:
"""
Class docstring.

Attributes
----------
using_ie
using_ie_in_parens
using_others
"""

@property
def using_ie(self):
"""
Test property, i.e. a method that works like an attribute. More.
"""
return

@property
def using_ie_in_parens(self):
"""
Test property (i.e. a method that works like an attribute). More.
"""
return

@property
def using_others(self):
"""
Test others et al. and e.g. and vs. are ok. More.
"""
return

attr_doc = """:Attributes:

:obj:`using_ie <using_ie>`
Test property, i.e. a method that works like an attribute.

:obj:`using_ie_in_parens <using_ie_in_parens>`
Test property (i.e. a method that works like an attribute).

:obj:`using_others <using_others>`
Test others et al. and e.g. and vs. are ok."""

assert attr_doc in str(SphinxClassDoc(Foo))


def test_templated_sections():
doc = SphinxClassDoc(
None,
Expand Down