From 2057bf6faef88b23273d7dd0c2f366ee8284314a Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C5=81ukasz=20Langa?= Date: Tue, 29 May 2018 00:47:52 -0700 Subject: [PATCH] Clean up PEP 257 support I documented the new behavior, added it to the change log, greatly expanded tests, added support for inner defs and classes, and added Luka to ACKS. Fixes #196 --- README.md | 21 ++- black.py | 12 +- tests/class_blank_parentheses.py | 1 - tests/class_methods_new_line.py | 228 +++++++++++++++++++++++++++++++ tests/function2.py | 25 ++++ 5 files changed, 275 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 41ad465..17c67f7 100644 --- a/README.md +++ b/README.md @@ -275,9 +275,17 @@ are always reformatted to fit minimal space, this whitespace is lost. It will also insert proper spacing before and after function definitions. It's one line before and after inner functions and two lines before and -after module-level functions. *Black* will not put empty lines between -function/class definitions and standalone comments that immediately precede -the given function/class. +after module-level functions and classes. *Black* will not put empty +lines between function/class definitions and standalone comments that +immediately precede the given function/class. + +*Black* will enforce single empty lines between a class-level docstring +and the first following field or method. This conforms to +[PEP 257](https://www.python.org/dev/peps/pep-0257/#multi-line-docstrings). + +*Black* won't insert empty lines after function docstrings unless that +empty line is required due to an inner function starting immediately +after. ### Trailing commas @@ -689,6 +697,9 @@ More details can be found in [CONTRIBUTING](CONTRIBUTING.md). * Python grammar pickle caches are stored with the formatting caches, making *Black* work in environments where site-packages is not user-writable (#192) +* *Black* now enforces a PEP 257 empty line after a class-level docstring + (and/or fields) and the first method + * fixed invalid code produced when standalone comments were present in a trailer that was omitted from line splitting on a large expression (#237) @@ -701,6 +712,9 @@ More details can be found in [CONTRIBUTING](CONTRIBUTING.md). a trailer that was omitted from line splitting on a large expression (#238) +* fixed extra empty line between a class declaration and the first + method if no class docstring or fields are present (#219) + ### 18.5b0 @@ -959,6 +973,7 @@ Multiple contributions by: * [Ivan Katanić](mailto:ivan.katanic@gmail.com) * [Jelle Zijlstra](mailto:jelle.zijlstra@gmail.com) * [Jonas Obrist](mailto:ojiidotch@gmail.com) +* [Luka Sterbic](mailto:luka.sterbic@gmail.com) * [Miguel Gaiowski](mailto:miggaiowski@gmail.com) * [Osaetin Daniel](mailto:osaetindaniel@gmail.com) * [Sunil Kapil](mailto:snlkapil@gmail.com) diff --git a/black.py b/black.py index 7dc6ef8..dc67991 100644 --- a/black.py +++ b/black.py @@ -989,14 +989,11 @@ class Line: @property def is_triple_quoted_string(self) -> bool: - """Is the line a triple quoted docstring?""" + """Is the line a triple quoted string?""" return ( bool(self) and self.leaves[0].type == token.STRING - and ( - self.leaves[0].value.startswith('"""') - or self.leaves[0].value.startswith("'''") - ) + and self.leaves[0].value.startswith(('"""', "'''")) ) def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool: @@ -1257,9 +1254,8 @@ 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 + if self.previous_line.depth < current_line.depth and ( + self.previous_line.is_class or self.previous_line.is_def ): return 0, 0 diff --git a/tests/class_blank_parentheses.py b/tests/class_blank_parentheses.py index d586cac..1a5721a 100644 --- a/tests/class_blank_parentheses.py +++ b/tests/class_blank_parentheses.py @@ -48,7 +48,6 @@ def public_func_with_blank_parentheses(): def class_under_the_func_with_blank_parentheses(): - class InsideFunc: pass diff --git a/tests/class_methods_new_line.py b/tests/class_methods_new_line.py index b098271..9a96ffe 100644 --- a/tests/class_methods_new_line.py +++ b/tests/class_methods_new_line.py @@ -1,8 +1,16 @@ class ClassSimplest: pass +class ClassWithSingleField: + a = 1 +class ClassWithJustTheDocstring: + """Just a docstring.""" class ClassWithInit: def __init__(self): pass +class ClassWithTheDocstringAndInit: + """Just a docstring.""" + def __init__(self): + pass class ClassWithInitAndVars: cls_var = 100 def __init__(self): @@ -12,6 +20,84 @@ class ClassWithInitAndVarsAndDocstring: cls_var = 100 def __init__(self): pass +class ClassWithDecoInit: + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVars: + cls_var = 100 + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstring: + """Test class""" + cls_var = 100 + @deco + def __init__(self): + pass +class ClassSimplestWithInner: + class Inner: + pass +class ClassSimplestWithInnerWithDocstring: + class Inner: + """Just a docstring.""" + def __init__(self): + pass +class ClassWithSingleFieldWithInner: + a = 1 + class Inner: + pass +class ClassWithJustTheDocstringWithInner: + """Just a docstring.""" + class Inner: + pass +class ClassWithInitWithInner: + class Inner: + pass + def __init__(self): + pass +class ClassWithInitAndVarsWithInner: + cls_var = 100 + class Inner: + pass + def __init__(self): + pass +class ClassWithInitAndVarsAndDocstringWithInner: + """Test class""" + cls_var = 100 + class Inner: + pass + def __init__(self): + pass +class ClassWithDecoInitWithInner: + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsWithInner: + cls_var = 100 + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstringWithInner: + """Test class""" + cls_var = 100 + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstringWithInner2: + """Test class""" + class Inner: + pass + cls_var = 100 + @deco + def __init__(self): + pass # output @@ -21,11 +107,26 @@ class ClassSimplest: pass +class ClassWithSingleField: + a = 1 + + +class ClassWithJustTheDocstring: + """Just a docstring.""" + + class ClassWithInit: def __init__(self): pass +class ClassWithTheDocstringAndInit: + """Just a docstring.""" + + def __init__(self): + pass + + class ClassWithInitAndVars: cls_var = 100 @@ -40,3 +141,130 @@ class ClassWithInitAndVarsAndDocstring: def __init__(self): pass + + +class ClassWithDecoInit: + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVars: + cls_var = 100 + + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVarsAndDocstring: + """Test class""" + + cls_var = 100 + + @deco + def __init__(self): + pass + + +class ClassSimplestWithInner: + class Inner: + pass + + +class ClassSimplestWithInnerWithDocstring: + class Inner: + """Just a docstring.""" + + def __init__(self): + pass + + +class ClassWithSingleFieldWithInner: + a = 1 + + class Inner: + pass + + +class ClassWithJustTheDocstringWithInner: + """Just a docstring.""" + + class Inner: + pass + + +class ClassWithInitWithInner: + class Inner: + pass + + def __init__(self): + pass + + +class ClassWithInitAndVarsWithInner: + cls_var = 100 + + class Inner: + pass + + def __init__(self): + pass + + +class ClassWithInitAndVarsAndDocstringWithInner: + """Test class""" + + cls_var = 100 + + class Inner: + pass + + def __init__(self): + pass + + +class ClassWithDecoInitWithInner: + class Inner: + pass + + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVarsWithInner: + cls_var = 100 + + class Inner: + pass + + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVarsAndDocstringWithInner: + """Test class""" + + cls_var = 100 + + class Inner: + pass + + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVarsAndDocstringWithInner2: + """Test class""" + + class Inner: + pass + + cls_var = 100 + + @deco + def __init__(self): + pass diff --git a/tests/function2.py b/tests/function2.py index e262e05..0c9da12 100644 --- a/tests/function2.py +++ b/tests/function2.py @@ -12,6 +12,15 @@ def f( very_long_argument_name2=very_long_value_for_the_argument, **kwargs, ) +def g(): + "Docstring." + def inner(): + pass + print("Inner defs should breathe a little.") +def h(): + def inner(): + pass + print("Inner defs should breathe a little.") # output @@ -26,3 +35,19 @@ def f(a, **kwargs) -> A: very_long_argument_name2=very_long_value_for_the_argument, **kwargs, ) + + +def g(): + "Docstring." + + def inner(): + pass + + print("Inner defs should breathe a little.") + + +def h(): + def inner(): + pass + + print("Inner defs should breathe a little.") -- 2.39.5