diff --git a/flake8_clean_block.py b/flake8_clean_block.py index 831a207..c64c02b 100644 --- a/flake8_clean_block.py +++ b/flake8_clean_block.py @@ -1,5 +1,5 @@ import ast -from typing import Generator, Tuple, Type, Any, List +from typing import Generator, Tuple, Type, Any, List, Union import importlib.metadata as importlib_metadata @@ -15,12 +15,16 @@ ast.ExceptHandler, ) +ModuleLikeTypes = Union[ + ast.Mod, ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, +] + class Visitor(ast.NodeVisitor): def __init__(self) -> None: self.problems: List[Tuple[int, int]] = [] - def generic_visit(self, node: ast.Module) -> None: + def generic_visit(self, node: ModuleLikeTypes) -> None: self._check_one_node(node) def _check_one_node(self, node) -> None: @@ -38,6 +42,12 @@ def _check_one_node(self, node) -> None: def _check_a_list_of_items(self, item_list: list) -> None: for i, this_item in enumerate(item_list): + if isinstance( + this_item, + (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef), + ): + self.generic_visit(this_item) # as if it were an ast.Module + if isinstance(this_item, BLOCKS_REQUIRING_INDENT): self._check_one_node(this_item) if i < len(item_list) - 1: # not the last item diff --git a/setup.cfg b/setup.cfg index a7e1469..2a79570 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = flake8_clean_block -version = 0.1.0 +version = 0.1.1 description = flake8 plugin which enforces blank line after if/for/while/with/try blocks long_description = file: README.md long_description_content_type = text/markdown diff --git a/tests/not_ok_cases.py b/tests/not_ok_cases.py index 725072e..ff68d78 100644 --- a/tests/not_ok_cases.py +++ b/tests/not_ok_cases.py @@ -1,12 +1,12 @@ -case_1_src = """a = 2 +case_1a_src = """a = 2 for i in range(5): a += 1 print(a) """ -case_1 = (case_1_src, [3]) # "3" means the violation happens on Line 3 +case_1a = (case_1a_src, [3]) # "3" means the violation happens on Line 3 -case_1a_src = """ +case_1b_src = """ a = 2 for i in range(5): a += 1 @@ -16,17 +16,17 @@ print(a) print(b) """ -case_1a = (case_1a_src, [6, 7]) # the violation happens on Lines 6 & 7 +case_1b = (case_1b_src, [6, 7]) # the violation happens on Lines 6 & 7 -case_1b_src = """ +case_1c_src = """ for i in range(5): a += 2 print(a) """ -case_1b = (case_1b_src, [2]) +case_1c = (case_1c_src, [2]) -case_2_src = """ +case_2a_src = """ if a == 1: print(a) if b == 2: @@ -36,10 +36,10 @@ while True: print('a') """ -case_2 = [case_2_src, [3, 5, 7]] +case_2a = [case_2a_src, [3, 5, 7]] -case_2a_src = """ +case_2b_src = """ if a == 1: print(a) elif a == 2: @@ -53,7 +53,7 @@ while True: print('a') """ -case_2a = [case_2a_src, [7, 9, 11]] +case_2b = [case_2b_src, [7, 9, 11]] case_3_src = """a = 2 @@ -78,7 +78,7 @@ case_4 = (case_4_src, [4]) -case_5_src = """ +case_5a_src = """ if x < 5: print(x) elif y == 2: @@ -87,10 +87,10 @@ raise ValueError return 2 """ -case_5 = (case_5_src, [7]) +case_5a = (case_5a_src, [7]) -case_5a_src = """ +case_5b_src = """ if x < 5: print(x) while z > 0: @@ -107,24 +107,24 @@ raise ValueError # <-- violation here return 2 """ -case_5a = [case_5a_src, [12, 15]] +case_5b = [case_5b_src, [12, 15]] -case_6_src = """ +case_6a_src = """ while True: print(1) print(2) """ -case_6 = [case_6_src, [3]] +case_6a = [case_6a_src, [3]] -case_6a_src = """ +case_6b_src = """ while True: print(1) print(2)""" -case_6a = [case_6a_src, [2]] +case_6b = [case_6b_src, [2]] -case_7_src = """ +case_7a_src = """ try: f = open('myfile.txt') except OSError as err: @@ -135,10 +135,10 @@ except ValueError: print("asdf") """ -case_7 = [case_7_src, [7]] +case_7a = [case_7a_src, [7]] -case_7a_src = """ +case_7b_src = """ try: f = open('myfile.txt') except OSError as err: @@ -155,7 +155,7 @@ print(b) print('a') """ -case_7a = [case_7a_src, [7, 11, 15]] +case_7b = [case_7b_src, [7, 11, 15]] case_8_src = """ @@ -183,25 +183,25 @@ case_9 = [case_9_src, [6]] -case_10_src = """import pickle +case_10a_src = """import pickle with open('filename.txt', 'r') as fp: data = pickle.load(fp) print(data) """ -case_10 = [case_10_src, [3]] +case_10a = [case_10a_src, [3]] -case_10a_src = """import pickle +case_10b_src = """import pickle with open('filename.txt', 'r') as fp: data = pickle.load(fp) for ii in range(10): print(ii) print(data) """ -case_10a = [case_10a_src, [5]] +case_10b = [case_10b_src, [5]] -case_10b_src = """import pickle +case_10c_src = """import pickle with open('filename.txt', 'r') as fp: data = pickle.load(fp) for ii in range(10): @@ -209,27 +209,69 @@ xyz = 1 print(data) """ -case_10b = [case_10b_src, [5, 6]] +case_10c = [case_10c_src, [5, 6]] + + +case_11a_src = """ +def some_func(arg1: list, arg2: list) -> int: + for i in range(len(arg1)): + for j in range(len(arg2)): + print(i) + print(j) + return 5 + +if True: + some_func([1, 2, 3], [2, 3]) +print('Good morning') +""" +case_11a = [case_11a_src, [5, 6, 10]] + + +case_11b_src = """ +class MyClass: + def __init__(self, arg1): + if arg1 == 2: + self.my_attr = 1 + self.my_attr = 2 + + @classmethod + def do_something(cls, arg1): + for i in range(20): + print(i) + print(arg1) + + def do_something_else(self, arg1, arg2): + if 5 in arg1: + print(arg1) + for j in arg2: + foo = 3 + 4 + return 5 + + +""" +case_11b = [case_11b_src, [5, 11, 16, 18]] def collect_all_cases(): return ( - case_1, case_1a, case_1b, - case_2, + case_1c, case_2a, + case_2b, case_3, case_4, - case_5, case_5a, - case_6, + case_5b, case_6a, - case_7, + case_6b, case_7a, + case_7b, case_8, case_9, - case_10, case_10a, case_10b, + case_10c, + case_11a, + case_11b, ) diff --git a/tests/ok_cases.py b/tests/ok_cases.py index b00dcea..46a07c0 100644 --- a/tests/ok_cases.py +++ b/tests/ok_cases.py @@ -1,40 +1,40 @@ case_0 = '' # trivial case: no code at all -case_1 = """a = 2 +case_1a = """a = 2 for i in range(5): a += 1 print(a) """ -case_1a = case_1.replace('\n\n', '\n\n\n\n\n') # OK to have more blank lines +case_1b = case_1a.replace('\n\n', '\n\n\n\n\n') # OK to have more blank lines -case_1b = case_1.replace( # OK to have comments on blank lines +case_1c = case_1a.replace( # OK to have comments on blank lines '\n\n', '\n# END OF "IF" BLOCK\n', ) -case_1c = case_1.replace('\n\n', '\n \n') # OK to have spaces on blank lines +case_1d = case_1a.replace('\n\n', '\n \n') # OK to have spaces on blank lines -case_1d = """for i in range(5): +case_1e = """for i in range(5): a += 2""" # no newline at the end -case_1e = "for i in range(5): a += 2" +case_1f = "for i in range(5): a += 2" -case_1f = """ +case_1g = """ for i in range(5): a += 2 print(a) """ -case_2 = """ +case_2a = """ if a == 1: print(a) elif a == 2: @@ -53,10 +53,10 @@ """ -case_2a = case_2.replace('\n\n', '\n# SOME COMMENT\n') +case_2b = case_2a.replace('\n\n', '\n# SOME COMMENT\n') -case_3 = """a = 2 +case_3a = """a = 2 for i in range(5): for j in range(3): a += j @@ -67,7 +67,7 @@ """ -case_3a = case_3.replace( # OK to have comments on blank lines +case_3b = case_3a.replace( # OK to have comments on blank lines '\n\n', '\n# END OF INDENTED BLOCK\n', ) @@ -84,7 +84,7 @@ """ -case_5 = """ +case_5a = """ if x < 5: print(x) elif y == 2: @@ -96,7 +96,7 @@ """ -case_5a = """ +case_5b = """ if x < 5: print(x) while z > 0: @@ -125,7 +125,7 @@ """ -case_7 = """ +case_7a = """ try: f = open('myfile.txt') except OSError as err: @@ -138,7 +138,7 @@ """ -case_7a = """ +case_7b = """ try: f = open('myfile.txt') s = f.readline() @@ -161,7 +161,7 @@ """ -case_7b = """ +case_7c = """ try: a = 1 except TypeError as err: @@ -198,7 +198,7 @@ print('Hello world!')""" -case_10 = """import pickle +case_10a = """import pickle with open('filename.txt', 'r') as fp: data = pickle.load(fp) @@ -206,7 +206,7 @@ """ -case_10a = """import pickle +case_10b = """import pickle with open('filename.txt', 'r') as fp: data = pickle.load(fp) for ii in range(10): @@ -216,7 +216,7 @@ """ -case_10b = """import pickle +case_10c = """import pickle with open('filename.txt', 'r') as fp: data = pickle.load(fp) for ii in range(10): @@ -228,18 +228,64 @@ """ -# Case 11 is OK, because it's out of the scope of this plugin. +case_11a = """ +def some_func(arg1: list, arg2: list) -> int: + for i in range(len(arg1)): + for j in range(len(arg2)): + print(i) + + print(j) + + return 5 + +if True: + some_func([1, 2, 3], [2, 3]) + +print('Good morning') +""" + + +case_11b = """ +class MyClass: + def __init__(self, arg1): + if arg1 == 2: + self.my_attr = 1 + + self.my_attr = 2 + + @classmethod + def do_something(cls, arg1): + for i in range(20): + print(i) + + print(arg1) + + def do_something_else(self, arg1, arg2): + if 5 in arg1: + print(arg1) + + for j in arg2: + foo = 3 + 4 + + return 5 + + +""" + + +# This is OK, because it's out of the scope of this plugin. # It can be caught by E3 in pycodestyle: # https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes -case_11 = """ +case_99a = """ def some_func(arg1, arg2): print(2) a = 2 """ -# Similar to Case 11, this is also OK. -case_11a = """ +# Similar to Case 99a, this is also OK, because it's out of the scope of this +# plugin. +case_99b = """ def some_func(arg1, arg2): return arg1 + arg2 a = 3 @@ -249,29 +295,31 @@ def some_func(arg1, arg2): def collect_all_cases(): return ( case_0, - case_1, - case_2, - case_2a, - case_1d, - case_1e, - case_1f, case_1a, case_1b, case_1c, - case_3, + case_1d, + case_1e, + case_1f, + case_1g, + case_2a, + case_2b, case_3a, + case_3b, case_4, - case_5, case_5a, + case_5b, case_6, - case_7, case_7a, case_7b, + case_7c, case_8, case_9, - case_10, case_10a, case_10b, - case_11, + case_10c, case_11a, + case_11b, + case_99a, + case_99b, )