Report E266 instead of E265 when the block common starts with multiple #; issue #270
[third_party/pep8] / pep8.py
diff --git a/pep8.py b/pep8.py
index ac6bfedabfe0932c1f53c7dac8fd7a284d574163..b64dfdaf5897641e7a6749f060c34729e2a03f9c 100755 (executable)
--- a/pep8.py
+++ b/pep8.py
@@ -46,7 +46,7 @@ W warnings
 """
 from __future__ import with_statement
 
-__version__ = '1.5.7a0'
+__version__ = '1.6.0a0'
 
 import os
 import sys
@@ -353,20 +353,25 @@ def indentation(logical_line, previous_logical, indent_char,
     Okay: a = 1
     Okay: if a == 0:\n    a = 1
     E111:   a = 1
+    E114:   # a = 1
 
     Okay: for item in items:\n    pass
     E112: for item in items:\npass
+    E115: for item in items:\n# Hi\n    pass
 
     Okay: a = 1\nb = 2
     E113: a = 1\n    b = 2
+    E116: a = 1\n    # b = 2
     """
-    if indent_char == ' ' and indent_level % 4:
-        yield 0, "E111 indentation is not a multiple of four"
+    c = 0 if logical_line else 3
+    tmpl = "E11%d %s" if logical_line else "E11%d %s (comment)"
+    if indent_level % 4:
+        yield 0, tmpl % (1 + c, "indentation is not a multiple of four")
     indent_expect = previous_logical.endswith(':')
     if indent_expect and indent_level <= previous_indent_level:
-        yield 0, "E112 expected an indented block"
-    if indent_level > previous_indent_level and not indent_expect:
-        yield 0, "E113 unexpected indentation"
+        yield 0, tmpl % (2 + c, "expected an indented block")
+    elif not indent_expect and indent_level > previous_indent_level:
+        yield 0, tmpl % (3 + c, "unexpected indentation")
 
 
 def continued_indentation(logical_line, tokens, indent_level, hang_closing,
@@ -837,6 +842,9 @@ def compound_statements(logical_line):
     on the same line, never do this for multi-clause statements.
     Also avoid folding such long lines!
 
+    Always use a def statement instead of an assignment statement that
+    binds a lambda expression directly to a name.
+
     Okay: if foo == 'blah':\n    do_blah_thing()
     Okay: do_one()
     Okay: do_two()
@@ -850,20 +858,26 @@ def compound_statements(logical_line):
     E701: try: something()
     E701: finally: cleanup()
     E701: if foo == 'blah': one(); two(); three()
-
     E702: do_one(); do_two(); do_three()
     E703: do_four();  # useless semicolon
+    E704: def f(x): return 2*x
+    E731: f = lambda x: 2*x
     """
     line = logical_line
     last_char = len(line) - 1
     found = line.find(':')
     while -1 < found < last_char:
         before = line[:found]
-        if (before.count('{') <= before.count('}') and  # {'a': 1} (dict)
-            before.count('[') <= before.count(']') and  # [1:2] (slice)
-            before.count('(') <= before.count(')') and  # (Python 3 annotation)
-                not LAMBDA_REGEX.search(before)):       # lambda x: x
-            yield found, "E701 multiple statements on one line (colon)"
+        if ((before.count('{') <= before.count('}') and   # {'a': 1} (dict)
+             before.count('[') <= before.count(']') and   # [1:2] (slice)
+             before.count('(') <= before.count(')'))):    # (annotation)
+            if LAMBDA_REGEX.search(before):
+                yield 0, "E731 do not assign a lambda expression, use a def"
+                break
+            if before.startswith('def '):
+                yield 0, "E704 multiple statements on one line (def)"
+            else:
+                yield found, "E701 multiple statements on one line (colon)"
         found = line.find(':', found + 1)
     found = line.find(';')
     while -1 < found:
@@ -1040,7 +1054,7 @@ if '' == ''.encode():
     # Python 2: implicit encoding.
     def readlines(filename):
         """Read the source code."""
-        with open(filename) as f:
+        with open(filename, 'rU') as f:
             return f.readlines()
     isidentifier = re.compile(r'[a-zA-Z_]\w*').match
     stdin_get_value = sys.stdin.read
@@ -1370,6 +1384,8 @@ class Checker(object):
         tokengen = tokenize.generate_tokens(self.readline)
         try:
             for token in tokengen:
+                if token[2][0] > self.total_lines:
+                    return
                 self.maybe_check_physical(token)
                 yield token
         except (SyntaxError, tokenize.TokenError):
@@ -1452,10 +1468,8 @@ class Checker(object):
                         token[3] = (token[2][0], token[2][1] + len(token[1]))
                         self.tokens = [tuple(token)]
                         self.check_logical()
-        if len(self.tokens) > 1 and (token_type == tokenize.ENDMARKER and
-                                     self.tokens[-2][0] not in SKIP_TOKENS):
-            self.tokens.pop()
-            self.check_physical(self.tokens[-1][4])
+        if self.tokens:
+            self.check_physical(self.lines[-1])
             self.check_logical()
         return self.report.get_file_results()
 
@@ -1840,12 +1854,11 @@ def read_config(options, args, arglist, parser):
 
         # Second, parse the configuration
         for opt in config.options(pep8_section):
+            if opt.replace('_', '-') not in parser.config_options:
+                print("  unknown option '%s' ignored" % opt)
+                continue
             if options.verbose > 1:
                 print("  %s = %s" % (opt, config.get(pep8_section, opt)))
-            if opt.replace('_', '-') not in parser.config_options:
-                print("Unknown option: '%s'\n  not in [%s]" %
-                      (opt, ' '.join(parser.config_options)))
-                sys.exit(1)
             normalized_opt = opt.replace('-', '_')
             opt_type = option_list[normalized_opt]
             if opt_type in ('int', 'count'):
@@ -1921,7 +1934,7 @@ def _main():
     # Handle "Broken pipe" gracefully
     try:
         signal.signal(signal.SIGPIPE, lambda signum, frame: sys.exit(1))
-    except ValueError:
+    except AttributeError:
         pass    # not supported on Windows
 
     pep8style = StyleGuide(parse_argv=True, config_file=True)