Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

Commit

Permalink
Merge pull request #174 from toroettg/MultiLineStyleOption
Browse files Browse the repository at this point in the history
Optional checks for multi-line docstring summary style.
  • Loading branch information
Nurdok committed Mar 25, 2016
2 parents e22aaca + 53ff113 commit c3cd2dc
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 5 deletions.
3 changes: 2 additions & 1 deletion docs/error_codes.rst
Expand Up @@ -14,4 +14,5 @@ Not all error codes are checked for by default. The default behavior is to
check only error codes that are part of the `PEP257
<http://www.python.org/dev/peps/pep-0257/>`_ official convention.

All of the above error codes are checked for by default except for D203.
All of the above error codes are checked for by default except for D203,
D212 and D213.
4 changes: 4 additions & 0 deletions docs/release_notes.rst
Expand Up @@ -7,6 +7,10 @@ Release Notes
Current Development Version
---------------------------

* Added the optional error codes D212 and D213, for checking whether
the summary of a multi-line docstring starts at the first line,
respectively at the second line (#174).

* The error code D300 is now also being reported if a docstring has
uppercase literals (``R`` or ``U``) as prefix (#176).

Expand Down
31 changes: 30 additions & 1 deletion src/pydocstyle.py
Expand Up @@ -683,6 +683,10 @@ def to_rst(cls):
'docstring text')
D211 = D2xx.create_error('D211', 'No blank lines allowed before class '
'docstring', 'found %s')
D212 = D2xx.create_error('D212', 'Multi-line docstring summary should start '
'at the first line')
D213 = D2xx.create_error('D213', 'Multi-line docstring summary should start '
'at the second line')

D3xx = ErrorRegistry.create_group('D3', 'Quotes Issues')
D300 = D3xx.create_error('D300', 'Use """triple double quotes"""',
Expand All @@ -707,7 +711,8 @@ def __getattr__(self, item):


conventions = AttrDict({
'pep257': set(ErrorRegistry.get_error_codes()) - set(['D203']),
'pep257': set(ErrorRegistry.get_error_codes()) - set(['D203', 'D212',
'D213'])
})


Expand Down Expand Up @@ -1541,6 +1546,30 @@ def check_surrounding_whitespaces(self, definition, docstring):
len(lines) == 1 and lines[0].endswith(' '):
return D210()

@check_for(Definition)
def check_multi_line_summary_start(self, definition, docstring):
"""D21{2,3}: Multi-line docstring summary style check.
A multi-line docstring summary should start either at the first,
or separately at the second line of a docstring.
"""
if docstring:
start_triple = [
'"""', "'''",
'u"""', "u'''",
'r"""', "r'''",
'ur"""', "ur'''"
]

lines = ast.literal_eval(docstring).split('\n')
if len(lines) > 1:
first = docstring.split("\n")[0].strip().lower()
if first in start_triple:
return D212()
else:
return D213()

@check_for(Definition)
def check_triple_double_quotes(self, definition, docstring):
r'''D300: Use """triple double quotes""".
Expand Down
136 changes: 136 additions & 0 deletions src/tests/test_cases/multi_line_summary_start.py
@@ -0,0 +1,136 @@
"""Module to check different multi-line docstring flavors."""

from .expected import Expectation

expectation = Expectation()
expect = expectation.expect

_D212 = 'D212: Multi-line docstring summary should start at the first line'
_D213 = 'D213: Multi-line docstring summary should start at the second line'

_D300 = 'D300: Use """triple double quotes""" (found \'\'\'-quotes)'
_D301 = 'D301: Use r""" if any backslashes in a docstring'


@expect(_D212)
def multi_line_starts_second_line():
"""
Summary.
Description.
"""


@expect(_D212)
@expect(_D300)
def multi_line_starts_second_line_single_quote():
'''
Summary.
Description.
'''


@expect(_D212)
def multi_line_starts_second_line_raw():
r"""
Summary.
Description with \backslash\.
"""


@expect(_D212)
@expect(_D301)
def multi_line_starts_second_line_upper_raw():
R"""
Summary.
Description with \backslash\.
"""


@expect(_D212)
@expect(_D300)
def multi_line_starts_second_line_raw_single_quote():
r'''
Summary.
Description with \backslash\.
'''


@expect(_D212)
@expect(_D300)
@expect(_D301)
def multi_line_starts_second_line_upper_raw_single_quote():
R'''
Summary.
Description with \backslash\.
'''


@expect(_D213)
def multi_line_starts_first_line():
"""Summary.
Description.
"""


@expect(_D213)
@expect(_D300)
def multi_line_starts_first_line_single_quote():
'''Summary.
Description.
'''


@expect(_D213)
def multi_line_starts_first_line_raw():
r"""Summary.
Description with \backslash\.
"""


@expect(_D213)
@expect(_D301)
def multi_line_starts_first_line_upper_raw():
R"""Summary.
Description with \backslash\.
"""


@expect(_D213)
@expect(_D300)
def multi_line_starts_first_line_raw_single_quote():
r'''Summary.
Description with \backslash\.
'''


@expect(_D213)
@expect(_D300)
@expect(_D301)
def multi_line_starts_first_line_upper_raw_single_quote():
R'''Summary.
Description with \backslash\.
'''
17 changes: 16 additions & 1 deletion src/tests/test_cases/test.py
Expand Up @@ -50,6 +50,7 @@ def nested():

@expect('D200: One-line docstring should fit on one line with quotes '
'(found 3)')
@expect('D212: Multi-line docstring summary should start at the first line')
def asdlkfasd():
"""
Wrong.
Expand Down Expand Up @@ -120,6 +121,7 @@ class LeadingAndTrailingSpaceMissing:

@expect('D205: 1 blank line required between summary line and description '
'(found 0)')
@expect('D213: Multi-line docstring summary should start at the second line')
def multi_line_zero_separating_blanks():
"""Summary.
Description.
Expand All @@ -129,6 +131,7 @@ def multi_line_zero_separating_blanks():

@expect('D205: 1 blank line required between summary line and description '
'(found 2)')
@expect('D213: Multi-line docstring summary should start at the second line')
def multi_line_two_separating_blanks():
"""Summary.
Expand All @@ -138,6 +141,7 @@ def multi_line_two_separating_blanks():
"""


@expect('D213: Multi-line docstring summary should start at the second line')
def multi_line_one_separating_blanks():
"""Summary.
Expand All @@ -147,6 +151,7 @@ def multi_line_one_separating_blanks():


@expect('D207: Docstring is under-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfsdf():
"""Summary.
Expand All @@ -156,6 +161,7 @@ def asdfsdf():


@expect('D207: Docstring is under-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdsdfsdffsdf():
"""Summary.
Expand All @@ -165,6 +171,7 @@ def asdsdfsdffsdf():


@expect('D208: Docstring is over-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfsdsdf24():
"""Summary.
Expand All @@ -174,6 +181,7 @@ def asdfsdsdf24():


@expect('D208: Docstring is over-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfsdsdfsdf24():
"""Summary.
Expand All @@ -183,6 +191,7 @@ def asdfsdsdfsdf24():


@expect('D208: Docstring is over-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfsdfsdsdsdfsdf24():
"""Summary.
Expand All @@ -193,6 +202,7 @@ def asdfsdfsdsdsdfsdf24():

@expect('D209: Multi-line docstring closing quotes should be on a separate '
'line')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfljdf24():
"""Summary.
Expand All @@ -210,6 +220,7 @@ def around():


@expect('D210: No whitespaces allowed surrounding docstring text')
@expect('D213: Multi-line docstring summary should start at the second line')
def multiline():
""" Whitespace at the begining.
Expand Down Expand Up @@ -275,6 +286,7 @@ def foobar():
"""Signature: foobar()."""


@expect('D213: Multi-line docstring summary should start at the second line')
def new_209():
"""First line.
Expand All @@ -283,6 +295,7 @@ def new_209():
pass


@expect('D213: Multi-line docstring summary should start at the second line')
def old_209():
"""One liner.
Expand All @@ -300,6 +313,7 @@ def oneliner_withdoc(): """One liner"""


@expect("D207: Docstring is under-indented")
@expect('D213: Multi-line docstring summary should start at the second line')
def docstring_start_in_same_line(): """First Line.
Second Line
Expand All @@ -310,7 +324,8 @@ def function_with_lambda_arg(x=lambda y: y):
"""A valid docstring."""


def a_following_valid_function(x):
@expect('D213: Multi-line docstring summary should start at the second line')
def a_following_valid_function(x=None):
"""Check for a bug where the previous function caused an assertion.
The assertion was caused in the next function, so this one is necessary.
Expand Down
1 change: 1 addition & 0 deletions src/tests/test_definitions.py
Expand Up @@ -261,6 +261,7 @@ def test_pep257():
'nested_class',
'capitalization',
'comment_after_def_bug',
'multi_line_summary_start'
)
for test_case in test_cases:
case_module = __import__('test_cases.{0}'.format(test_case),
Expand Down
7 changes: 5 additions & 2 deletions src/tests/test_integration.py
Expand Up @@ -166,9 +166,10 @@ def function_with_bad_docstring(foo):
mock_open = mock.mock_open(read_data=function_to_check)
with mock.patch.object(
pydocstyle, 'tokenize_open', mock_open, create=True):
errors = tuple(pydocstyle.check(['filepath'], ignore=['D100', 'D202']))
ignored = set(('D100', 'D202', 'D213'))
errors = tuple(pydocstyle.check(['filepath'], ignore=ignored))
error_codes = set(error.code for error in errors)
assert error_codes == expected_error_codes - set(('D100', 'D202'))
assert error_codes == expected_error_codes - ignored


def test_config_file(env):
Expand Down Expand Up @@ -418,6 +419,8 @@ def foo():
assert 'D100' in err
assert 'D211' in err
assert 'D203' not in err
assert 'D212' not in err
assert 'D213' not in err


def test_config_file_inheritance(env):
Expand Down

0 comments on commit c3cd2dc

Please sign in to comment.