Skip to content

Commit 7992901

Browse files
author
Lidiya Murakhovs'ka
authored
Merge pull request #86 from Shopify/class_def_check
Check for blank line after a class definition
2 parents c96b4de + 7cc1536 commit 7992901

4 files changed

Lines changed: 71 additions & 0 deletions

File tree

shopify_python/google_styleguide.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ class GoogleStyleGuideChecker(checkers.BaseChecker):
8787
'lambda-func',
8888
"For common operations like multiplication, use the functions from the operator module"
8989
"instead of lambda functions. For example, prefer operator.mul to lambda x, y: x * y."),
90+
'C6015': ('No blank line after a class definition',
91+
'blank-line-after-class-required',
92+
'Missing a blank line after a class definition'),
9093
}
9194

9295
options = (
@@ -173,6 +176,9 @@ def visit_raise(self, node): # type: (astroid.Raise) -> None
173176
def visit_if(self, node):
174177
self.__use_cond_expr(node) # type: (astroid.If) -> None
175178

179+
def visit_classdef(self, node): # type: (astroid.ClassDef) -> None
180+
self.__class_def_check(node)
181+
176182
@staticmethod
177183
def __get_module_names(node): # type: (astroid.ImportFrom) -> typing.Generator[str, None, None]
178184
for name in node.names:
@@ -322,3 +328,17 @@ def __lambda_func(self, node): # type: (astroid.Lambda) -> None
322328
lambda_fun = "lambda " + left + ', ' + right + ": " + " ".join([left, node.ops[0][0], right])
323329
op_fun = "operator." + operator
324330
self.add_message('lambda-func', node=node, args={'op': op_fun, 'lambda_fun': lambda_fun})
331+
332+
def __class_def_check(self, node): # type: (astroid.ClassDef) -> None
333+
"""Enforce a blank line after a class definition line."""
334+
prev_line = node.lineno
335+
336+
for element in node.body:
337+
curr_line = element.lineno
338+
blank_lines = curr_line - prev_line - 1
339+
if isinstance(element, astroid.FunctionDef) and blank_lines < 1:
340+
self.add_message('blank-line-after-class-required', node=node)
341+
break
342+
elif isinstance(element, astroid.FunctionDef):
343+
break
344+
prev_line = curr_line
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# pylint:disable=missing-docstring,invalid-name,too-few-public-methods
2+
class SomeClass(object): # [blank-line-after-class-required]
3+
def apply(self):
4+
pass
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
blank-line-after-class-required:2:SomeClass:No blank line after a class definition

tests/shopify_python/test_google_styleguide.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,49 @@ def binaryfnc():
332332
""".format(expression))
333333
with self.assertNoMessages():
334334
self.walk(binary_root)
335+
336+
def test_class_def_blank_line(self):
337+
root = astroid.builder.parse("""
338+
class SomePipeline(object):
339+
def apply(content):
340+
return content.withColumn('zero', F.lit(0.0))
341+
342+
class Fact(object):
343+
INPUTS = {}
344+
def apply(self):
345+
pass
346+
347+
class Stage(object):
348+
INPUTS = {}
349+
350+
OUTPUTS = {}
351+
def apply(self):
352+
pass
353+
""")
354+
with self.assertAddsMessages(
355+
*[pylint.testutils.Message('blank-line-after-class-required', node=root.body[0]),
356+
pylint.testutils.Message('blank-line-after-class-required', node=root.body[1]),
357+
pylint.testutils.Message('blank-line-after-class-required', node=root.body[2])]
358+
):
359+
self.walk(root)
360+
361+
with self.assertNoMessages():
362+
self.walk(astroid.builder.parse("""
363+
class SomePipeline(object):
364+
365+
def apply(content):
366+
return content.withColumn('zero', F.lit(0.0))
367+
368+
class FirstStage(object):
369+
pass
370+
371+
class SecondStage(object):
372+
INPUTS = {}
373+
OUTPUTS = {}
374+
375+
def check():
376+
pass
377+
378+
def apply():
379+
pass
380+
"""))

0 commit comments

Comments
 (0)