@@ -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.
0 commit comments