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

Commit 53ff113

Browse files
committed
Added optional error codes D212 and D213.
1 parent e22aaca commit 53ff113

File tree

7 files changed

+194
-5
lines changed

7 files changed

+194
-5
lines changed

docs/error_codes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ Not all error codes are checked for by default. The default behavior is to
1414
check only error codes that are part of the `PEP257
1515
<http://www.python.org/dev/peps/pep-0257/>`_ official convention.
1616

17-
All of the above error codes are checked for by default except for D203.
17+
All of the above error codes are checked for by default except for D203,
18+
D212 and D213.

docs/release_notes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Release Notes
77
Current Development Version
88
---------------------------
99

10+
* Added the optional error codes D212 and D213, for checking whether
11+
the summary of a multi-line docstring starts at the first line,
12+
respectively at the second line (#174).
13+
1014
* The error code D300 is now also being reported if a docstring has
1115
uppercase literals (``R`` or ``U``) as prefix (#176).
1216

src/pydocstyle.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,10 @@ def to_rst(cls):
683683
'docstring text')
684684
D211 = D2xx.create_error('D211', 'No blank lines allowed before class '
685685
'docstring', 'found %s')
686+
D212 = D2xx.create_error('D212', 'Multi-line docstring summary should start '
687+
'at the first line')
688+
D213 = D2xx.create_error('D213', 'Multi-line docstring summary should start '
689+
'at the second line')
686690

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

708712

709713
conventions = AttrDict({
710-
'pep257': set(ErrorRegistry.get_error_codes()) - set(['D203']),
714+
'pep257': set(ErrorRegistry.get_error_codes()) - set(['D203', 'D212',
715+
'D213'])
711716
})
712717

713718

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

1549+
@check_for(Definition)
1550+
def check_multi_line_summary_start(self, definition, docstring):
1551+
"""D21{2,3}: Multi-line docstring summary style check.
1552+
1553+
A multi-line docstring summary should start either at the first,
1554+
or separately at the second line of a docstring.
1555+
1556+
"""
1557+
if docstring:
1558+
start_triple = [
1559+
'"""', "'''",
1560+
'u"""', "u'''",
1561+
'r"""', "r'''",
1562+
'ur"""', "ur'''"
1563+
]
1564+
1565+
lines = ast.literal_eval(docstring).split('\n')
1566+
if len(lines) > 1:
1567+
first = docstring.split("\n")[0].strip().lower()
1568+
if first in start_triple:
1569+
return D212()
1570+
else:
1571+
return D213()
1572+
15441573
@check_for(Definition)
15451574
def check_triple_double_quotes(self, definition, docstring):
15461575
r'''D300: Use """triple double quotes""".
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
"""Module to check different multi-line docstring flavors."""
2+
3+
from .expected import Expectation
4+
5+
expectation = Expectation()
6+
expect = expectation.expect
7+
8+
_D212 = 'D212: Multi-line docstring summary should start at the first line'
9+
_D213 = 'D213: Multi-line docstring summary should start at the second line'
10+
11+
_D300 = 'D300: Use """triple double quotes""" (found \'\'\'-quotes)'
12+
_D301 = 'D301: Use r""" if any backslashes in a docstring'
13+
14+
15+
@expect(_D212)
16+
def multi_line_starts_second_line():
17+
"""
18+
Summary.
19+
20+
Description.
21+
22+
"""
23+
24+
25+
@expect(_D212)
26+
@expect(_D300)
27+
def multi_line_starts_second_line_single_quote():
28+
'''
29+
Summary.
30+
31+
Description.
32+
33+
'''
34+
35+
36+
@expect(_D212)
37+
def multi_line_starts_second_line_raw():
38+
r"""
39+
Summary.
40+
41+
Description with \backslash\.
42+
43+
"""
44+
45+
46+
@expect(_D212)
47+
@expect(_D301)
48+
def multi_line_starts_second_line_upper_raw():
49+
R"""
50+
Summary.
51+
52+
Description with \backslash\.
53+
54+
"""
55+
56+
57+
@expect(_D212)
58+
@expect(_D300)
59+
def multi_line_starts_second_line_raw_single_quote():
60+
r'''
61+
Summary.
62+
63+
Description with \backslash\.
64+
65+
'''
66+
67+
68+
@expect(_D212)
69+
@expect(_D300)
70+
@expect(_D301)
71+
def multi_line_starts_second_line_upper_raw_single_quote():
72+
R'''
73+
Summary.
74+
75+
Description with \backslash\.
76+
77+
'''
78+
79+
80+
@expect(_D213)
81+
def multi_line_starts_first_line():
82+
"""Summary.
83+
84+
Description.
85+
86+
"""
87+
88+
89+
@expect(_D213)
90+
@expect(_D300)
91+
def multi_line_starts_first_line_single_quote():
92+
'''Summary.
93+
94+
Description.
95+
96+
'''
97+
98+
99+
@expect(_D213)
100+
def multi_line_starts_first_line_raw():
101+
r"""Summary.
102+
103+
Description with \backslash\.
104+
105+
"""
106+
107+
108+
@expect(_D213)
109+
@expect(_D301)
110+
def multi_line_starts_first_line_upper_raw():
111+
R"""Summary.
112+
113+
Description with \backslash\.
114+
115+
"""
116+
117+
118+
@expect(_D213)
119+
@expect(_D300)
120+
def multi_line_starts_first_line_raw_single_quote():
121+
r'''Summary.
122+
123+
Description with \backslash\.
124+
125+
'''
126+
127+
128+
@expect(_D213)
129+
@expect(_D300)
130+
@expect(_D301)
131+
def multi_line_starts_first_line_upper_raw_single_quote():
132+
R'''Summary.
133+
134+
Description with \backslash\.
135+
136+
'''

src/tests/test_cases/test.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def nested():
5050

5151
@expect('D200: One-line docstring should fit on one line with quotes '
5252
'(found 3)')
53+
@expect('D212: Multi-line docstring summary should start at the first line')
5354
def asdlkfasd():
5455
"""
5556
Wrong.
@@ -120,6 +121,7 @@ class LeadingAndTrailingSpaceMissing:
120121

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

130132
@expect('D205: 1 blank line required between summary line and description '
131133
'(found 2)')
134+
@expect('D213: Multi-line docstring summary should start at the second line')
132135
def multi_line_two_separating_blanks():
133136
"""Summary.
134137
@@ -138,6 +141,7 @@ def multi_line_two_separating_blanks():
138141
"""
139142

140143

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

148152

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

157162

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

166172

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

175182

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

184192

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

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

211221

212222
@expect('D210: No whitespaces allowed surrounding docstring text')
223+
@expect('D213: Multi-line docstring summary should start at the second line')
213224
def multiline():
214225
""" Whitespace at the begining.
215226
@@ -275,6 +286,7 @@ def foobar():
275286
"""Signature: foobar()."""
276287

277288

289+
@expect('D213: Multi-line docstring summary should start at the second line')
278290
def new_209():
279291
"""First line.
280292
@@ -283,6 +295,7 @@ def new_209():
283295
pass
284296

285297

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

301314

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

312326

313-
def a_following_valid_function(x):
327+
@expect('D213: Multi-line docstring summary should start at the second line')
328+
def a_following_valid_function(x=None):
314329
"""Check for a bug where the previous function caused an assertion.
315330
316331
The assertion was caused in the next function, so this one is necessary.

src/tests/test_definitions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ def test_pep257():
261261
'nested_class',
262262
'capitalization',
263263
'comment_after_def_bug',
264+
'multi_line_summary_start'
264265
)
265266
for test_case in test_cases:
266267
case_module = __import__('test_cases.{0}'.format(test_case),

src/tests/test_integration.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ def function_with_bad_docstring(foo):
166166
mock_open = mock.mock_open(read_data=function_to_check)
167167
with mock.patch.object(
168168
pydocstyle, 'tokenize_open', mock_open, create=True):
169-
errors = tuple(pydocstyle.check(['filepath'], ignore=['D100', 'D202']))
169+
ignored = set(('D100', 'D202', 'D213'))
170+
errors = tuple(pydocstyle.check(['filepath'], ignore=ignored))
170171
error_codes = set(error.code for error in errors)
171-
assert error_codes == expected_error_codes - set(('D100', 'D202'))
172+
assert error_codes == expected_error_codes - ignored
172173

173174

174175
def test_config_file(env):
@@ -418,6 +419,8 @@ def foo():
418419
assert 'D100' in err
419420
assert 'D211' in err
420421
assert 'D203' not in err
422+
assert 'D212' not in err
423+
assert 'D213' not in err
421424

422425

423426
def test_config_file_inheritance(env):

0 commit comments

Comments
 (0)