diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 2e93350c..3d72549f 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -33,6 +33,8 @@ Bug Fixes * Fix decorator parsing (#411). * Google-style sections no longer cause false errors when used with Numpy-style sections (#388, #424). +* D202: Allow a blank line after function docstring when followed by + declaration of an inner function or class (#395, #426). * Fix D401 and D404 checks not working for docstrings containing only one word and ending with non-alpha character (#421) 4.0.1 - August 14th, 2019 @@ -48,7 +50,6 @@ Bug Fixes the violation code table (#396). * Fixed IndentationError when parsing function arguments (#392). - 4.0.0 - July 6th, 2019 ---------------------- diff --git a/src/pydocstyle/checker.py b/src/pydocstyle/checker.py index 7de79ffc..52626fe9 100644 --- a/src/pydocstyle/checker.py +++ b/src/pydocstyle/checker.py @@ -186,8 +186,8 @@ def check_one_liners(self, definition, docstring): def check_no_blank_before(self, function, docstring): # def """D20{1,2}: No blank lines allowed around function/method docstring. - There's no blank line either before or after the docstring. - + There's no blank line either before or after the docstring unless directly + followed by an inner function or class. """ if docstring: before, _, after = function.source.partition(docstring) @@ -198,7 +198,14 @@ def check_no_blank_before(self, function, docstring): # def if blanks_before_count != 0: yield violations.D201(blanks_before_count) if not all(blanks_after) and blanks_after_count != 0: - yield violations.D202(blanks_after_count) + # Report a D202 violation if the docstring is followed by a blank line + # and the blank line is not itself followed by an inner function or + # class. + if not ( + blanks_after_count == 1 and + re(r"\s+(?:(?:class|def)\s|@)").match(after) + ): + yield violations.D202(blanks_after_count) @check_for(Class) def check_blank_before_after_class(self, class_, docstring): @@ -976,7 +983,7 @@ def is_ascii(string): def leading_space(string): """Return any leading space from `string`.""" - return re('\s*').match(string).group() + return re(r'\s*').match(string).group() def get_leading_words(line): @@ -984,7 +991,7 @@ def get_leading_words(line): For example, if `line` is " Hello world!!!", returns "Hello world". """ - result = re("[\w ]+").match(line.strip()) + result = re(r"[\w ]+").match(line.strip()) if result is not None: return result.group() diff --git a/src/tests/test_cases/functions.py b/src/tests/test_cases/functions.py new file mode 100644 index 00000000..cbc24268 --- /dev/null +++ b/src/tests/test_cases/functions.py @@ -0,0 +1,53 @@ +"""A valid module docstrings.""" + +from .expected import Expectation + +expectation = Expectation() +expect = expectation.expect + + +@expect("D201: No blank lines allowed before function docstring (found 1)") +def func_with_space_before(): + + """Test a function with space before docstring.""" + pass + + +@expect("D202: No blank lines allowed after function docstring (found 1)") +def func_with_space_after(): + """Test a function with space after docstring.""" + + pass + + +def func_with_inner_func_after(): + """Test a function with inner function after docstring.""" + + def inner(): + pass + + pass + + +def fake_decorator(decorated): + """Fake decorator used to test decorated inner func.""" + return decorated + + +def func_with_inner_decorated_func_after(): + """Test a function with inner decorated function after docstring.""" + + @fake_decorator + def inner(): + pass + + pass + + +def func_with_inner_class_after(): + """Test a function with inner class after docstring.""" + + class inner(): + pass + + pass diff --git a/src/tests/test_definitions.py b/src/tests/test_definitions.py index 1bbbca81..83109b5a 100644 --- a/src/tests/test_definitions.py +++ b/src/tests/test_definitions.py @@ -19,6 +19,7 @@ 'superfluous_quotes', 'noqa', 'sections', + 'functions', 'canonical_google_examples', 'canonical_numpy_examples', 'canonical_pep257_examples',