Skip to content

Commit fed43c5

Browse files
committed
Merge. Allow long lines in docstrings and comments.
Allow long lines in multiline strings and comments if they cannot be wrapped; issue #224 Optionally disable physical line checks inside multiline strings, using '# noqa'; issue #242
2 parents 5685c97 + fd5cc44 commit fed43c5

8 files changed

Lines changed: 148 additions & 22 deletions

File tree

CHANGES.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ Changes:
1515
* Report E713 and E714 when operators ``not in`` and ``is not`` are
1616
recommended. (Issue #236)
1717

18+
* Allow long lines in multiline strings and comments if they cannot
19+
be wrapped. (Issue #224).
20+
21+
* Optionally disable physical line checks inside multiline strings,
22+
using ``# noqa``. (Issue #242)
23+
1824
* Change text for E121 to report "continuation line under-indented
1925
for hanging indent" instead of indentation not being a
2026
multiple of 4.

pep8.py

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def missing_newline(physical_line):
201201
return len(physical_line), "W292 no newline at end of file"
202202

203203

204-
def maximum_line_length(physical_line, max_line_length):
204+
def maximum_line_length(physical_line, max_line_length, multiline):
205205
"""
206206
Limit all lines to a maximum of 79 characters.
207207
@@ -217,6 +217,13 @@ def maximum_line_length(physical_line, max_line_length):
217217
line = physical_line.rstrip()
218218
length = len(line)
219219
if length > max_line_length and not noqa(line):
220+
# Special case for long URLs in multi-line docstrings or comments,
221+
# but still report the error when the 72 first chars are whitespaces.
222+
chunks = line.split()
223+
if ((len(chunks) == 1 and multiline) or
224+
(len(chunks) == 2 and chunks[0] == '#')) and \
225+
len(line) - len(chunks[-1]) < max_line_length - 7:
226+
return
220227
if hasattr(line, 'decode'): # Python 2
221228
# The line could contain multi-byte characters
222229
try:
@@ -1251,6 +1258,7 @@ def __init__(self, filename=None, lines=None,
12511258
self._logical_checks = options.logical_checks
12521259
self._ast_checks = options.ast_checks
12531260
self.max_line_length = options.max_line_length
1261+
self.multiline = False # in a multiline string?
12541262
self.hang_closing = options.hang_closing
12551263
self.verbose = options.verbose
12561264
self.filename = filename
@@ -1299,16 +1307,9 @@ def readline(self):
12991307
self.line_number += 1
13001308
if self.line_number > len(self.lines):
13011309
return ''
1302-
return self.lines[self.line_number - 1]
1303-
1304-
def readline_check_physical(self):
1305-
"""
1306-
Check and return the next physical line. This method can be
1307-
used to feed tokenize.generate_tokens.
1308-
"""
1309-
line = self.readline()
1310-
if line:
1311-
self.check_physical(line)
1310+
line = self.lines[self.line_number - 1]
1311+
if self.indent_char is None and line[:1] in WHITESPACE:
1312+
self.indent_char = line[0]
13121313
return line
13131314

13141315
def run_check(self, check, argument_names):
@@ -1325,8 +1326,6 @@ def check_physical(self, line):
13251326
Run all physical checks on a raw input line.
13261327
"""
13271328
self.physical_line = line
1328-
if self.indent_char is None and line[:1] in WHITESPACE:
1329-
self.indent_char = line[0]
13301329
for name, check, argument_names in self._physical_checks:
13311330
result = self.run_check(check, argument_names)
13321331
if result is not None:
@@ -1421,13 +1420,46 @@ def check_ast(self):
14211420
def generate_tokens(self):
14221421
if self._io_error:
14231422
self.report_error(1, 0, 'E902 %s' % self._io_error, readlines)
1424-
tokengen = tokenize.generate_tokens(self.readline_check_physical)
1423+
tokengen = tokenize.generate_tokens(self.readline)
14251424
try:
14261425
for token in tokengen:
1426+
self.maybe_check_physical(token)
14271427
yield token
14281428
except (SyntaxError, tokenize.TokenError):
14291429
self.report_invalid_syntax()
14301430

1431+
def maybe_check_physical(self, token):
1432+
"""
1433+
If appropriate (based on token), check current physical line(s).
1434+
"""
1435+
# Called after every token, but act only on end of line.
1436+
if token[0] in (tokenize.NEWLINE, tokenize.NL):
1437+
# Obviously, a newline token ends a single physical line.
1438+
self.check_physical(token[4])
1439+
elif token[0] == tokenize.STRING and '\n' in token[1]:
1440+
# Less obviously, a string that contains newlines is a
1441+
# multiline string, either triple-quoted or with internal
1442+
# newlines backslash-escaped. Check every physical line in the
1443+
# string *except* for the last one: its newline is outside of
1444+
# the multiline string, so we consider it a regular physical
1445+
# line, and will check it like any other physical line.
1446+
#
1447+
# Subtleties:
1448+
# - we don't *completely* ignore the last line; if it contains
1449+
# the magical "# noqa" comment, we disable all physical
1450+
# checks for the entire multiline string
1451+
# - have to wind self.line_number back because initially it
1452+
# points to the last line of the string, and we want
1453+
# check_physical() to give accurate feedback
1454+
if noqa(token[4]):
1455+
return
1456+
self.multiline = True
1457+
self.line_number = token[2][0]
1458+
for line in token[1].split('\n')[:-1]:
1459+
self.check_physical(line + '\n')
1460+
self.line_number += 1
1461+
self.multiline = False
1462+
14311463
def check_all(self, expected=None, line_offset=0):
14321464
"""
14331465
Run all checks on the input file.

testsuite/E50.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,60 @@
4545
('''
4646
''' + ' \
4747
')
48+
#: E501 E225 E226
49+
very_long_identifiers=and_terrible_whitespace_habits(are_no_excuse+for_long_lines)
50+
#
51+
#: E501
52+
'''multiline string
53+
with a long long long long long long long long long long long long long long long long line
54+
'''
55+
#: E501
56+
'''same thing, but this time without a terminal newline in the string
57+
long long long long long long long long long long long long long long long long line'''
58+
#
59+
# issue 224 (unavoidable long lines in docstrings)
60+
#: Okay
61+
"""
62+
I'm some great documentation. Because I'm some great documentation, I'm
63+
going to give you a reference to some valuable information about some API
64+
that I'm calling:
65+
66+
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
67+
"""
68+
#: E501
69+
"""
70+
longnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaces"""
71+
#: Okay
72+
"""
73+
This
74+
almost_empty_line
75+
"""
76+
#: E501
77+
"""
78+
This
79+
almost_empty_line
80+
"""
81+
#: E501
82+
# A basic comment
83+
# with a long long long long long long long long long long long long long long long long line
84+
85+
#
86+
#: Okay
87+
# I'm some great comment. Because I'm so great, I'm going to give you a
88+
# reference to some valuable information about some API that I'm calling:
89+
#
90+
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
91+
92+
import this
93+
94+
# longnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaces
95+
96+
#
97+
#: Okay
98+
# This
99+
# almost_empty_line
100+
101+
#
102+
#: E501
103+
# This
104+
# almost_empty_line

testsuite/E90.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
}
33
#: E901
44
= [x
5-
#: E901 E101 W191 W191
5+
#: E901 E101 W191
66
while True:
77
try:
88
pass

testsuite/W19.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def long_function_name(
8686
b == """abc def ghi
8787
jkl mno"""):
8888
return True
89-
#: E101 W191 W191
89+
#: E101 W191
9090
if length > options.max_line_length:
9191
return options.max_line_length, \
9292
"E501 line too long (%d characters)" % length
@@ -97,6 +97,28 @@ def long_function_name(
9797
if os.path.exists(os.path.join(path, PEP8_BIN)):
9898
cmd = ([os.path.join(path, PEP8_BIN)] +
9999
self._pep8_options(targetfile))
100+
#: W191
101+
'''
102+
multiline string with tab in it'''
103+
#: E101 W191
104+
'''multiline string
105+
with tabs
106+
and spaces
107+
'''
108+
#: Okay
109+
'''sometimes, you just need to go nuts in a multiline string
110+
and allow all sorts of crap
111+
like mixed tabs and spaces
112+
113+
or trailing whitespace
114+
or long long long long long long long long long long long long long long long long long lines
115+
''' # nopep8
116+
#: Okay
117+
'''this one
118+
will get no warning
119+
even though the noqa comment is not immediately after the string
120+
''' + foo # noqa
121+
#
100122
#: E101 W191
101123
if foo is None and bar is "frop" and \
102124
blah == 'yeah':

testsuite/W29.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@
66
class Foo(object):
77

88
bang = 12
9+
#: W291
10+
'''multiline
11+
string with trailing whitespace'''
912
#: W292
1013
# This line doesn't have a linefeed

testsuite/W39.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
#: W391
22
# The next line is blank
33

4+
#: Okay
5+
'''there is nothing wrong
6+
with a multiline string at EOF
7+
8+
that happens to have a blank line in it
9+
'''

testsuite/utf-8.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ def __init__(self, width, height,
3131
# 78 narrow chars (Na) + 1 wide char (W)
3232
# 01 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8情
3333

34-
# 2 narrow chars (Na) + 40 wide chars (W)
35-
# 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情
34+
# 3 narrow chars (Na) + 40 wide chars (W)
35+
# 情 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情
3636

37-
# 2 narrow chars (Na) + 77 wide chars (W)
38-
# 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情
37+
# 3 narrow chars (Na) + 76 wide chars (W)
38+
# 情 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情
3939

4040
#
4141
#: E501
@@ -47,6 +47,6 @@ def __init__(self, width, height,
4747
# 01 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8情情
4848
#
4949
#: E501
50-
# 2 narrow chars (Na) + 78 wide chars (W)
51-
# 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情
50+
# 3 narrow chars (Na) + 77 wide chars (W)
51+
# 情 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情
5252
#

0 commit comments

Comments
 (0)