From e818260f1a223929d8f2056f484b73eaa8687622 Mon Sep 17 00:00:00 2001 From: Luka Sterbic Date: Tue, 29 May 2018 08:48:59 +0200 Subject: [PATCH 1/1] Class new line between docstrings / vars / methods (#219) Partially addresses #144 --- black.py | 28 +++++++++++++++++++++ tests/class_blank_parentheses.py | 2 -- tests/class_methods_new_line.py | 42 ++++++++++++++++++++++++++++++++ tests/comments4.py | 1 - tests/composition.py | 1 - tests/test_black.py | 8 ++++++ 6 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 tests/class_methods_new_line.py diff --git a/black.py b/black.py index 05ec00c..7dc6ef8 100644 --- a/black.py +++ b/black.py @@ -987,6 +987,18 @@ class Line: and self.leaves[3].value == ")" ) + @property + def is_triple_quoted_string(self) -> bool: + """Is the line a triple quoted docstring?""" + return ( + bool(self) + and self.leaves[0].type == token.STRING + and ( + self.leaves[0].value.startswith('"""') + or self.leaves[0].value.startswith("'''") + ) + ) + def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool: """If so, needs to be split before emitting.""" for leaf in self.leaves: @@ -1194,6 +1206,7 @@ class EmptyLineTracker: the prefix of the first leaf consists of optional newlines. Those newlines are consumed by `maybe_empty_lines()` and included in the computation. """ + is_pyi: bool = False previous_line: Optional[Line] = None previous_after: int = 0 @@ -1244,6 +1257,12 @@ class EmptyLineTracker: if self.previous_line.is_decorator: return 0, 0 + if ( + self.previous_line.is_class + and self.previous_line.depth != current_line.depth + ): + return 0, 0 + if ( self.previous_line.is_comment and self.previous_line.depth == current_line.depth @@ -1275,6 +1294,13 @@ class EmptyLineTracker: ): return (before or 1), 0 + if ( + self.previous_line + and self.previous_line.is_class + and current_line.is_triple_quoted_string + ): + return before, 1 + return before, 0 @@ -1285,6 +1311,7 @@ class LineGenerator(Visitor[Line]): Note: destroys the tree it's visiting by mutating prefixes of its leaves in ways that will no longer stringify to valid Python code on the tree. """ + is_pyi: bool = False current_line: Line = Factory(Line) remove_u_prefix: bool = False @@ -2768,6 +2795,7 @@ def gen_python_files_in_dir(path: Path) -> Iterator[Path]: @dataclass class Report: """Provides a reformatting counter. Can be rendered with `str(report)`.""" + check: bool = False quiet: bool = False change_count: int = 0 diff --git a/tests/class_blank_parentheses.py b/tests/class_blank_parentheses.py index bf19a26..d586cac 100644 --- a/tests/class_blank_parentheses.py +++ b/tests/class_blank_parentheses.py @@ -39,7 +39,6 @@ class ClassWithSpaceParentheses: class ClassWithEmptyFunc(object): - def func_with_blank_parentheses(): return 5 @@ -55,7 +54,6 @@ def class_under_the_func_with_blank_parentheses(): class NormalClass: - def func_for_testing(self, first, second): sum = first + second return sum diff --git a/tests/class_methods_new_line.py b/tests/class_methods_new_line.py new file mode 100644 index 0000000..b098271 --- /dev/null +++ b/tests/class_methods_new_line.py @@ -0,0 +1,42 @@ +class ClassSimplest: + pass +class ClassWithInit: + def __init__(self): + pass +class ClassWithInitAndVars: + cls_var = 100 + def __init__(self): + pass +class ClassWithInitAndVarsAndDocstring: + """Test class""" + cls_var = 100 + def __init__(self): + pass + + +# output + + +class ClassSimplest: + pass + + +class ClassWithInit: + def __init__(self): + pass + + +class ClassWithInitAndVars: + cls_var = 100 + + def __init__(self): + pass + + +class ClassWithInitAndVarsAndDocstring: + """Test class""" + + cls_var = 100 + + def __init__(self): + pass diff --git a/tests/comments4.py b/tests/comments4.py index 944714d..013684c 100644 --- a/tests/comments4.py +++ b/tests/comments4.py @@ -1,5 +1,4 @@ class C: - @pytest.mark.parametrize( ("post_data", "message"), [ diff --git a/tests/composition.py b/tests/composition.py index f8cd5ed..ac63e46 100644 --- a/tests/composition.py +++ b/tests/composition.py @@ -1,5 +1,4 @@ class C: - def test(self) -> None: with patch("black.out", print): self.assertEqual( diff --git a/tests/test_black.py b/tests/test_black.py index a7d04cc..f5114a7 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -363,6 +363,14 @@ class BlackTestCase(unittest.TestCase): black.assert_equivalent(source, actual) black.assert_stable(source, actual, line_length=ll) + @patch("black.dump_to_file", dump_to_stderr) + def test_new_line_between_class_and_code(self) -> None: + source, expected = read_data("class_methods_new_line") + actual = fs(source) + self.assertFormatEqual(expected, actual) + black.assert_equivalent(source, actual) + black.assert_stable(source, actual, line_length=ll) + def test_report(self) -> None: report = black.Report() out_lines = [] -- 2.39.5