-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-30181: parse docstring using pydoc #1505
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
Changes from 4 commits
e77fa7a
538ab19
cec5371
d1315d4
5c71fd0
0094fdc
b4fe113
e3dbe3e
669b46d
0caec7e
1720114
30cec86
ea60b7b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
import difflib | ||
import logging | ||
import pprint | ||
import pydoc | ||
import re | ||
import warnings | ||
import collections | ||
|
@@ -338,6 +339,25 @@ def __exit__(self, exc_type, exc_value, tb): | |
.format(logging.getLevelName(self.level), self.logger.name)) | ||
|
||
|
||
def short_description_from_docstring(text): | ||
""" | ||
Return the test case short description from a docstring. | ||
|
||
Ths docstring is parsed by the standard `pydoc.splitdoc` function | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an implementation detail. Both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There appears to be different ideas at play here surrounding the use docstrings. With one view being docstrings should only document the public API, so users of the API know how to use the package. The other view being docstrings are equally applicable for internal functions, which should have a well defined API for internal use, so internal programmers can tell what the function does without peering into the source. Any external references to help justify one or the other side of this argument appreciated (I have no opinions on this yet myself). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have also had the out of band comment from Ben saying just because it isn't a public API doesn't make the docstring any less useful. Since a docstring will be available to anyone introspecting the object whether private or public There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docstrings of internal functions are not forbidden, they are just not necessary. Comments would be enough (see PEP 8). In any case neither comment, nor docstring shouldn't just repeat the code. They should describe what function does, but not how it does this. Otherwise any change of the code will make a docstring or a comment incorrect. Note also that the function name itself serves the documenting function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about: def short_description_from_docstring(text):
# Return the summary line from a docstring.
# If there is no summary line, return None.
... The use of the word "summary line" comes straight from PEP-257. |
||
into (`synopsis`, `long_description`). | ||
|
||
If there is no `synopsis`, return None. | ||
""" | ||
if text: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be cleaner to use a guard clause and just write (and with no comment):
|
||
(synopsis, long_description) = pydoc.splitdoc(text) | ||
synopsis = synopsis.strip() | ||
else: | ||
# The text is either an empty string, or some other false value. | ||
synopsis = None | ||
|
||
return synopsis | ||
|
||
|
||
class TestCase(object): | ||
"""A class whose instances are single test cases. | ||
|
||
|
@@ -463,15 +483,8 @@ def defaultTestResult(self): | |
return result.TestResult() | ||
|
||
def shortDescription(self): | ||
"""Returns a one-line description of the test, or None if no | ||
description has been provided. | ||
|
||
The default implementation of this method returns the first line of | ||
the specified test method's docstring. | ||
""" | ||
doc = self._testMethodDoc | ||
return doc and doc.split("\n")[0].strip() or None | ||
|
||
""" Return a one-line description of the test, if any; otherwise None. """ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure that the previous description is entirely relevant. Also does saying "The default implementation" add any useful information? How about:
|
||
return short_description_from_docstring(self._testMethodDoc) | ||
|
||
def id(self): | ||
return "%s.%s" % (strclass(self.__class__), self._testMethodName) | ||
|
@@ -1395,8 +1408,7 @@ def __repr__(self): | |
def shortDescription(self): | ||
if self._description is not None: | ||
return self._description | ||
doc = self._testFunc.__doc__ | ||
return doc and doc.split("\n")[0].strip() or None | ||
return short_description_from_docstring(self._testFunc.__doc__) | ||
|
||
|
||
class _SubTest(TestCase): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -596,6 +596,48 @@ def testShortDescriptionWithMultiLineDocstring(self): | |
'Tests shortDescription() for a method with a longer ' | ||
'docstring.') | ||
|
||
@unittest.skipIf( | ||
sys.flags.optimize >= 2, | ||
"Docstrings are omitted with -O2 and above") | ||
def testShortDescriptionWithSurroundingSpaceOneLineDocstring(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is enough to just add spaces or newlines in the two preceding tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, not sure I understand. You appear to be saying that we should use the same docstring as for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean that adding spaces at the start and end of the docstring of testShortDescriptionWithOneLineDocstring and adding a newline and indentation at the start of the docstring of testShortD 9E7A escriptionWithMultiLineDocstring will cover cases tested by new testes. No new tests will be needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, OK, I think I understand now. So to clarify, you feel there is no need to have a separate test for the no-leading/trailing whitespace case? |
||
""" Surrounding space should be stripped to get the shortDescription. """ | ||
expected_description = ( | ||
"Surrounding space should be stripped" | ||
" to get the shortDescription.") | ||
self.assertEqual(self.shortDescription(), expected_description) | ||
|
||
@unittest.skipIf( | ||
sys.flags.optimize >= 2, | ||
"Docstrings are omitted with -O2 and above") | ||
def testShortDescriptionWithSurroundingNewlineOneLineDocstring(self): | ||
""" | ||
Surrounding newlines should be stripped to get the shortDescription. | ||
""" | ||
expected_description = ( | ||
"Surrounding newlines should be stripped" | ||
" to get the shortDescription.") | ||
self.assertEqual(self.shortDescription(), expected_description) | ||
|
||
@unittest.skipIf( | ||
sys.flags.optimize >= 2, | ||
"Docstrings are omitted with -O2 and above") | ||
def testShortDescriptionWithSurroundingNewlineMultiLineDocstring(self): | ||
""" | ||
Surrounding newlines should be stripped to get the shortDescription. | ||
|
||
The specification of how docstring space should be parsed is at | ||
https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation | ||
which requires that “Blank lines should be removed from the | ||
beginning and end of the docstring.” | ||
|
||
The PEP 257 algorithm is implemented by `pydoc.splitdoc`. | ||
|
||
""" | ||
expected_description = ( | ||
"Surrounding newlines should be stripped" | ||
" to get the shortDescription.") | ||
self.assertEqual(self.shortDescription(), expected_description) | ||
|
||
def testAddTypeEqualityFunc(self): | ||
class SadSnake(object): | ||
"""Dummy class for test_addTypeEqualityFunc.""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function name should start with an underscore since it's not meant to be part of the public API. Also, I think a shorter name should be used like
_make_short_description(docstring)
or_get_short_description(docstring)
. Information about the arguments can be gotten from the argument names or function docstring.