From e6d8b1733d17a0137c2bf1c6317e321cc4d6faf1 Mon Sep 17 00:00:00 2001 From: Lidiya Murakhovs'ka Date: Fri, 13 Oct 2017 10:44:26 -0400 Subject: [PATCH 1/3] Add check for blank line after class def --- shopify_python/google_styleguide.py | 17 +++++++++++ .../shopify_python/test_google_styleguide.py | 28 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/shopify_python/google_styleguide.py b/shopify_python/google_styleguide.py index bc4af42..8a16218 100644 --- a/shopify_python/google_styleguide.py +++ b/shopify_python/google_styleguide.py @@ -87,6 +87,9 @@ class GoogleStyleGuideChecker(checkers.BaseChecker): 'lambda-func', "For common operations like multiplication, use the functions from the operator module" "instead of lambda functions. For example, prefer operator.mul to lambda x, y: x * y."), + 'C6015': ('No blank line after a class definition', + 'blank-line-after-class-required', + 'Missing a blank line after a class definition'), } options = ( @@ -173,6 +176,9 @@ def visit_raise(self, node): # type: (astroid.Raise) -> None def visit_if(self, node): self.__use_cond_expr(node) # type: (astroid.If) -> None + def visit_classdef(self, node): # type: (astroid.ClassDef) -> None + self.__class_def_check(node) + @staticmethod def __get_module_names(node): # type: (astroid.ImportFrom) -> typing.Generator[str, None, None] for name in node.names: @@ -322,3 +328,14 @@ def __lambda_func(self, node): # type: (astroid.Lambda) -> None lambda_fun = "lambda " + left + ', ' + right + ": " + " ".join([left, node.ops[0][0], right]) op_fun = "operator." + operator self.add_message('lambda-func', node=node, args={'op': op_fun, 'lambda_fun': lambda_fun}) + + def __class_def_check(self, node): # type: (astroid.ClassDef) -> None + """Enforce a blank line after a class definition line.""" + if isinstance(node, astroid.ClassDef): + class_def_line = node.lineno + for next_def in node.body: + if isinstance(next_def, astroid.FunctionDef): + next_line = next_def.lineno + if next_line - class_def_line < 2: + self.add_message('blank-line-after-class-required', node=node) + break diff --git a/tests/shopify_python/test_google_styleguide.py b/tests/shopify_python/test_google_styleguide.py index 2326264..633c99d 100644 --- a/tests/shopify_python/test_google_styleguide.py +++ b/tests/shopify_python/test_google_styleguide.py @@ -332,3 +332,31 @@ def binaryfnc(): """.format(expression)) with self.assertNoMessages(): self.walk(binary_root) + + def test_class_def_blank_line(self): + root = astroid.builder.parse(""" + class SomePipeline(object): + def apply(content): + return content.withColumn('zero', F.lit(0.0)) + """) + with self.assertAddsMessages( + *[pylint.testutils.Message('blank-line-after-class-required', node=root.body[0])] + ): + self.walk(root) + + with self.assertNoMessages(): + self.walk(astroid.builder.parse(""" + class SomePipeline(object): + + def apply(content): + return content.withColumn('zero', F.lit(0.0)) + + class FirstStage(object): + pass + + class SecondStage(object): + INPUTS = {} + OUTPUTS = {} + def apply(): + pass + """)) From 9f99dd91d21f1d31ceb08d78ea8eace8e5ff5a3b Mon Sep 17 00:00:00 2001 From: Lidiya Murakhovs'ka Date: Fri, 13 Oct 2017 11:09:15 -0400 Subject: [PATCH 2/3] Add functional test --- tests/functional/blank_line_after_class_required.py | 5 +++++ tests/functional/blank_line_after_class_required.txt | 1 + tests/shopify_python/test_google_styleguide.py | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/functional/blank_line_after_class_required.py create mode 100644 tests/functional/blank_line_after_class_required.txt diff --git a/tests/functional/blank_line_after_class_required.py b/tests/functional/blank_line_after_class_required.py new file mode 100644 index 0000000..010d088 --- /dev/null +++ b/tests/functional/blank_line_after_class_required.py @@ -0,0 +1,5 @@ +# pylint:disable=missing-docstring,invalid-name,too-few-public-methods +class SomeClass(object): + + def apply(self): + pass diff --git a/tests/functional/blank_line_after_class_required.txt b/tests/functional/blank_line_after_class_required.txt new file mode 100644 index 0000000..b815edd --- /dev/null +++ b/tests/functional/blank_line_after_class_required.txt @@ -0,0 +1 @@ +blank-line-after-class-required:2::No blank line after a class definition \ No newline at end of file diff --git a/tests/shopify_python/test_google_styleguide.py b/tests/shopify_python/test_google_styleguide.py index 633c99d..1e2b735 100644 --- a/tests/shopify_python/test_google_styleguide.py +++ b/tests/shopify_python/test_google_styleguide.py @@ -340,7 +340,7 @@ def apply(content): return content.withColumn('zero', F.lit(0.0)) """) with self.assertAddsMessages( - *[pylint.testutils.Message('blank-line-after-class-required', node=root.body[0])] + *[pylint.testutils.Message('blank-line-after-class-required', node=root.body[0])] ): self.walk(root) From 7cc153621a06f71b66ec3ca8c9bea460b060fe96 Mon Sep 17 00:00:00 2001 From: Lidiya Murakhovs'ka Date: Mon, 16 Oct 2017 12:28:36 -0400 Subject: [PATCH 3/3] Blank line before 1st method of class --- shopify_python/google_styleguide.py | 19 ++++++++++-------- .../blank_line_after_class_required.py | 3 +-- .../blank_line_after_class_required.txt | 2 +- .../shopify_python/test_google_styleguide.py | 20 ++++++++++++++++++- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/shopify_python/google_styleguide.py b/shopify_python/google_styleguide.py index 8a16218..5e2bd72 100644 --- a/shopify_python/google_styleguide.py +++ b/shopify_python/google_styleguide.py @@ -331,11 +331,14 @@ def __lambda_func(self, node): # type: (astroid.Lambda) -> None def __class_def_check(self, node): # type: (astroid.ClassDef) -> None """Enforce a blank line after a class definition line.""" - if isinstance(node, astroid.ClassDef): - class_def_line = node.lineno - for next_def in node.body: - if isinstance(next_def, astroid.FunctionDef): - next_line = next_def.lineno - if next_line - class_def_line < 2: - self.add_message('blank-line-after-class-required', node=node) - break + prev_line = node.lineno + + for element in node.body: + curr_line = element.lineno + blank_lines = curr_line - prev_line - 1 + if isinstance(element, astroid.FunctionDef) and blank_lines < 1: + self.add_message('blank-line-after-class-required', node=node) + break + elif isinstance(element, astroid.FunctionDef): + break + prev_line = curr_line diff --git a/tests/functional/blank_line_after_class_required.py b/tests/functional/blank_line_after_class_required.py index 010d088..c8c6ce6 100644 --- a/tests/functional/blank_line_after_class_required.py +++ b/tests/functional/blank_line_after_class_required.py @@ -1,5 +1,4 @@ # pylint:disable=missing-docstring,invalid-name,too-few-public-methods -class SomeClass(object): - +class SomeClass(object): # [blank-line-after-class-required] def apply(self): pass diff --git a/tests/functional/blank_line_after_class_required.txt b/tests/functional/blank_line_after_class_required.txt index b815edd..72e780b 100644 --- a/tests/functional/blank_line_after_class_required.txt +++ b/tests/functional/blank_line_after_class_required.txt @@ -1 +1 @@ -blank-line-after-class-required:2::No blank line after a class definition \ No newline at end of file +blank-line-after-class-required:2:SomeClass:No blank line after a class definition \ No newline at end of file diff --git a/tests/shopify_python/test_google_styleguide.py b/tests/shopify_python/test_google_styleguide.py index 1e2b735..a417470 100644 --- a/tests/shopify_python/test_google_styleguide.py +++ b/tests/shopify_python/test_google_styleguide.py @@ -338,9 +338,23 @@ def test_class_def_blank_line(self): class SomePipeline(object): def apply(content): return content.withColumn('zero', F.lit(0.0)) + + class Fact(object): + INPUTS = {} + def apply(self): + pass + + class Stage(object): + INPUTS = {} + + OUTPUTS = {} + def apply(self): + pass """) with self.assertAddsMessages( - *[pylint.testutils.Message('blank-line-after-class-required', node=root.body[0])] + *[pylint.testutils.Message('blank-line-after-class-required', node=root.body[0]), + pylint.testutils.Message('blank-line-after-class-required', node=root.body[1]), + pylint.testutils.Message('blank-line-after-class-required', node=root.body[2])] ): self.walk(root) @@ -357,6 +371,10 @@ class FirstStage(object): class SecondStage(object): INPUTS = {} OUTPUTS = {} + + def check(): + pass + def apply(): pass """))