Commit be2b28dd authored by Mikhail Golubev's avatar Mikhail Golubev
Browse files

PY-19705 Add blanks lines around methods of a class as required by PEP 8

parent 81442902
Showing with 183 additions and 17 deletions
+183 -17
......@@ -719,6 +719,8 @@ public class PyBlock implements ASTBlock {
@Override
@Nullable
public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) {
final CommonCodeStyleSettings settings = myContext.getSettings();
final PyCodeStyleSettings pySettings = myContext.getPySettings();
if (child1 instanceof ASTBlock && child2 instanceof ASTBlock) {
final ASTNode node1 = ((ASTBlock)child1).getNode();
ASTNode node2 = ((ASTBlock)child2).getNode();
......@@ -727,6 +729,22 @@ public class PyBlock implements ASTBlock {
PsiElement psi2 = node2.getPsi();
if (psi2 instanceof PyStatementList) {
// Quite odd getSpacing() doesn't get called with child1=null for the first statement
// in the statement list of a class, yet it does get called for the preceding colon and
// the statement list itself. Hence we have to handle blank lines around methods here in
// addition to SpacingBuilder.
if (myNode.getElementType() == PyElementTypes.CLASS_DECLARATION) {
final PyStatement[] statements = ((PyStatementList)psi2).getStatements();
if (statements.length > 0 && statements[0] instanceof PyFunction) {
return getBlankLinesForOption(settings.BLANK_LINES_AROUND_METHOD);
}
}
if (childType1 == PyTokenTypes.COLON && needLineBreakInStatement()) {
return Spacing.createSpacing(0, 0, 1, true, settings.KEEP_BLANK_LINES_IN_CODE);
}
}
// pycodestyle.py enforces at most 2 blank lines only between comments directly
// at the top-level of a file, not inside if, try/except, etc.
if (psi1 instanceof PsiComment && myNode.getPsi() instanceof PsiFile) {
......@@ -744,8 +762,6 @@ public class PyBlock implements ASTBlock {
final IElementType childType2 = psi2.getNode().getElementType();
//noinspection ConstantConditions
child2 = getSubBlockByNode(node2);
final CommonCodeStyleSettings settings = myContext.getSettings();
final PyCodeStyleSettings pySettings = myContext.getPySettings();
if ((childType1 == PyTokenTypes.EQ || childType2 == PyTokenTypes.EQ)) {
final PyNamedParameter namedParameter = as(myNode.getPsi(), PyNamedParameter.class);
......@@ -754,19 +770,13 @@ public class PyBlock implements ASTBlock {
}
}
if (childType1 == PyTokenTypes.COLON && psi2 instanceof PyStatementList) {
if (needLineBreakInStatement()) {
return Spacing.createSpacing(0, 0, 1, true, settings.KEEP_BLANK_LINES_IN_CODE);
}
}
if (psi1 instanceof PyImportStatementBase) {
if (psi2 instanceof PyImportStatementBase) {
final Boolean leftImportIsGroupStart = psi1.getCopyableUserData(IMPORT_GROUP_BEGIN);
final Boolean rightImportIsGroupStart = psi2.getCopyableUserData(IMPORT_GROUP_BEGIN);
// Cleanup user data, it's no longer needed
psi1.putCopyableUserData(IMPORT_GROUP_BEGIN, null);
// Don't remove IMPORT_GROUP_BEGIN from the element psi2 yet, because spacing is constructed pairwise:
// Don't remove IMPORT_GROUP_BEGIN from the element psi2 yet, because spacing is constructed pairwise:
// it might be needed on the next iteration.
//psi2.putCopyableUserData(IMPORT_GROUP_BEGIN, null);
if (rightImportIsGroupStart != null) {
......@@ -962,7 +972,7 @@ public class PyBlock implements ASTBlock {
}
}
else if (lastChild != null && PyElementTypes.LIST_LIKE_EXPRESSIONS.contains(lastChild.getElementType())) {
// handle pressing enter at the end of a list literal when there's no closing paren or bracket
// handle pressing enter at the end of a list literal when there's no closing paren or bracket
final ASTNode lastLastChild = lastChild.getLastChildNode();
if (lastLastChild != null && lastLastChild.getPsi() instanceof PsiErrorElement) {
// we're at a place like this: [foo, ... bar, <caret>
......
......@@ -79,13 +79,10 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, C
return new SpacingBuilder(commonSettings)
.before(END_OF_LINE_COMMENT).spacing(2, 0, 0, commonSettings.KEEP_LINE_BREAKS, commonSettings.KEEP_BLANK_LINES_IN_CODE)
.after(END_OF_LINE_COMMENT).spacing(0, 0, 1, commonSettings.KEEP_LINE_BREAKS, commonSettings.KEEP_BLANK_LINES_IN_CODE)
.between(CLASS_DECLARATION, STATEMENT_OR_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_CLASS)
.between(STATEMENT_OR_DECLARATION, CLASS_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_CLASS)
.between(FUNCTION_DECLARATION, STATEMENT_OR_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_METHOD)
.between(STATEMENT_OR_DECLARATION, FUNCTION_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_METHOD)
.after(FUNCTION_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_METHOD)
.after(CLASS_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_CLASS)
// Remove excess blank lines between imports (at most one is allowed).
// Top-level definitions are supposed to be handled in PyBlock#getSpacing
.around(CLASS_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_CLASS)
.around(FUNCTION_DECLARATION).blankLines(commonSettings.BLANK_LINES_AROUND_METHOD)
// Remove excess blank lines between imports (at most one is allowed).
// Note that ImportOptimizer gets rid of them anyway.
// Empty lines between import groups are handles in PyBlock#getSpacing
.between(IMPORT_STATEMENTS, IMPORT_STATEMENTS).spacing(0, Integer.MAX_VALUE, 1, false, 1)
......
......@@ -2,6 +2,7 @@ from unittest import TestCase
class Spam(TestCase):
def eggs(self):
self.fail()
......
class Checkpoints(webapp2.RequestHandler):
def get(self):
self.response.write(json.dumps({"meta": {"code": 400,
"errorType": "paramError",
......
......@@ -2,5 +2,6 @@ from unittest import TestCase
class MyTest(TestCase):
def test_pass(self):
self.assertEqual(1 + 1, 2)
class Adjunct:
def apply(self, right, arg):
pass
......
class C:
def foo(self):
pass
......
class C1:
# comment 1
# comment 2
def __init__(self):
pass
class C2:
# comment 2
def __init__(self):
pass
class C3:
def __init__(self):
pass
class C4:
"""Docstring."""
# comment 1
# comment 2
def __init__(self):
pass
class C5:
"""Docstring."""
# comment 2
def __init__(self):
pass
class C6:
"""Docstring."""
def __init__(self):
pass
class C7:
attr = 42
# comment 1
# comment 2
def __init__(self):
pass
class C8:
attr = 42
# comment 2
def __init__(self):
pass
class C9:
attr = 42
def __init__(self):
pass
class C10: # comment before statement list
def __init__(self):
pass
class C1:
# comment 1
# comment 2
def __init__(self):
pass
class C2:
# comment 2
def __init__(self):
pass
class C3:
def __init__(self):
pass
class C4:
"""Docstring."""
# comment 1
# comment 2
def __init__(self):
pass
class C5:
"""Docstring."""
# comment 2
def __init__(self):
pass
class C6:
"""Docstring."""
def __init__(self):
pass
class C7:
attr = 42
# comment 1
# comment 2
def __init__(self):
pass
class C8:
attr = 42
# comment 2
def __init__(self):
pass
class C9:
attr = 42
def __init__(self):
pass
class C10: # comment before statement list
def __init__(self):
pass
class T1(object):
def m1(self):
pass
......@@ -6,5 +7,6 @@ class T1(object):
# comment about T2
class T2(object):
def m2(self):
pass
class Foo(object):
def wrong_blank_lines(self):
pass
......
class A(object):
def foo(self):
pass
......
......@@ -2,5 +2,6 @@ class Dialog:
def validate(self): pass
class B(Dialog):
def validate(self):
<selection>Dialog.validate(self)</selection>
\ No newline at end of file
class A:
def y(self):
pass
......
......@@ -6,6 +6,7 @@ def f(x):
class Child(Base):
def __init__(self):
super(Child, self).__init__()
class Ancestor(object):
def __init__(self, a, b):
self.a = a
self.b = b
......@@ -8,5 +9,6 @@ class Ancestor(object):
class Basic(Ancestor):
def func2(self):
return self.func1()
\ No newline at end of file
......@@ -3,6 +3,7 @@ from shared_module import module_function as my_function, ModuleClass
class NewParent(object):
def do_useful_stuff(self):
i = shared_module.MODULE_CONTANT
my_function()
......
class Parent(object):
def __init__(self):
self.eggs = 12
......
......@@ -7,6 +7,7 @@ abstractmethod()
class NewParent(metaclass=ABCMeta):
@classmethod
@abstractmethod
def foo_method(cls):
......
......@@ -3,5 +3,6 @@ A = 1
class Suppa:
def foo(self):
print "bar"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment