Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions pep8.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import re
import time
import inspect
from itertools import takewhile
import keyword
import tokenize
from optparse import OptionParser
Expand Down Expand Up @@ -233,7 +234,7 @@ def maximum_line_length(physical_line, max_line_length):


def blank_lines(logical_line, blank_lines, indent_level, line_number,
previous_logical, previous_indent_level):
previous_logical, previous_indent_level, inside_function):
r"""
Separate top-level function and class definitions with two blank lines.

Expand Down Expand Up @@ -261,13 +262,16 @@ def blank_lines(logical_line, blank_lines, indent_level, line_number,
yield 0, "E304 blank lines found after function decorator"
elif blank_lines > 2 or (indent_level and blank_lines == 2):
yield 0, "E303 too many blank lines (%d)" % blank_lines
elif logical_line.startswith(('def ', 'class ', '@')):
if indent_level:
if not (blank_lines or previous_indent_level < indent_level or
DOCSTRING_REGEX.match(previous_logical)):
yield 0, "E301 expected 1 blank line, found 0"
elif blank_lines != 2:
yield 0, "E302 expected 2 blank lines, found %d" % blank_lines
elif (logical_line.startswith(('def ', 'class ', '@')) and
not indent_level and blank_lines != 2):
yield 0, "E302 expected 2 blank lines, found %d" % blank_lines
elif (logical_line.startswith(('def ', '@')) and
0 < indent_level <= 4 and
not inside_function and
not blank_lines and
previous_indent_level >= indent_level and
not DOCSTRING_REGEX.match(previous_logical)):
yield 0, "E301 expected 1 blank line, found 0"


def extraneous_whitespace(logical_line):
Expand Down Expand Up @@ -1319,6 +1323,14 @@ def check_logical(self):
indent = first_line[:self.mapping[0][1][2][1]]
self.previous_indent_level = self.indent_level
self.indent_level = expand_indent(indent)

# Is this line inside a global (non-indented) function?
def is_not_global_class(line):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't actually perfect. Take for example:

# pkg/compat.py
"""Compat module for <library>."""
import collections

try:
    from collections import OrderedDict
except ImportError:
    class OrderedDict(collections.MutableMapping):
        # ...

try:
    from urlparse import urlparse
except ImportError:
    from urllib.parse import urlparse
# pkg/something.py
from .compat import OrderedDict
#...

That's a global class and that line does not start with class instead it starts with class.

return not line.startswith('class ')
lines = reversed(self.lines[:self.line_number])
lines = takewhile(is_not_global_class, lines)
self.inside_function = any(line.startswith('def ') for line in lines)

if self.verbose >= 2:
print(self.logical_line[:80].rstrip())
for name, check, argument_names in self._logical_checks:
Expand Down
2 changes: 0 additions & 2 deletions testsuite/E30.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
#: E301
class X:

def a():
pass
def b():
pass
#: E301
class X:

def a():
pass
# comment
Expand Down
47 changes: 47 additions & 0 deletions testsuite/E30not.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,50 @@ def a():
def b():

pass


# Nested functions
def c():
def d():
pass
def f():
pass
@decorated
def g():
pass
def h():
pass


# Function nested in a method
class Foo(object):
def foo(self):
def bar():
pass
def bar2():
pass
@decorated
def bar3():
pass
def bar4():
pass

def bar(self):
pass


# Class nested in a function
def foo():
class A(object):
def a(self):
pass
def b(self):
pass
@decorated
def c(self):
pass
def d(self):
pass
class B(object):
def c(self):
pass