Repository: psf/black

Issue:
Conflict in blank lines after module docstring and before function
Using macOS:

```sh
$ black --version
black, 23.10.1 (compiled: yes)
Python (CPython) 3.10.12
```

Take this code:

```python
"""This is a test case, note blank lines next."""
def go():
    print("Do stuff!")
go()
```

And run this:

```
$ black --diff blanks.py 
--- blanks.py	2023-11-06 18:04:21.775563+00:00
+++ blanks.py	2023-11-06 18:04:37.405865+00:00
@@ -1,4 +1,8 @@
 """This is a test case, note blank lines next."""
+
+
 def go():
     print("Do stuff!")
+
+
 go()
would reformat blanks.py

All done! ✨ 🍰 ✨
1 file would be reformatted.
```

Note current default behavior is to put two blank lines between the module docstring and following function. This is as expected, quoting https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html

> 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 and classes. 

Now in preview mode:

```
$ black --diff blanks.py --preview
--- blanks.py	2023-11-06 18:04:21.775563+00:00
+++ blanks.py	2023-11-06 18:04:42.146632+00:00
@@ -1,4 +1,7 @@
 """This is a test case, note blank lines next."""
+
 def go():
     print("Do stuff!")
+
+
 go()
```

This now only has one line between the module docstring and following function. This seems like an unintended consequence of #1872.




Discussion:
Agree we should keep two lines here.

This is somewhat unlikely in practice as usually there will be imports between the docstring and the first function. We should keep two blank lines though if the first statement after the docstring is a class or function definition.
Unless someone is already working on it, I'll send a PR since we are also affected by this.

Resolving patch:
diff --git a/src/black/lines.py b/src/black/lines.py
--- a/src/black/lines.py
+++ b/src/black/lines.py
@@ -578,6 +578,7 @@ def maybe_empty_lines(self, current_line: Line) -> LinesBlock:
             and self.previous_block.previous_block is None
             and len(self.previous_block.original_line.leaves) == 1
             and self.previous_block.original_line.is_triple_quoted_string
+            and not (current_line.is_class or current_line.is_def)
         ):
             before = 1
 


Test patch:
diff --git a/tests/data/cases/module_docstring_followed_by_class.py b/tests/data/cases/module_docstring_followed_by_class.py
new file mode 100644
--- /dev/null
+++ b/tests/data/cases/module_docstring_followed_by_class.py
@@ -0,0 +1,11 @@
+# flags: --preview
+"""Two blank lines between module docstring and a class."""
+class MyClass:
+    pass
+
+# output
+"""Two blank lines between module docstring and a class."""
+
+
+class MyClass:
+    pass
diff --git a/tests/data/cases/module_docstring_followed_by_function.py b/tests/data/cases/module_docstring_followed_by_function.py
new file mode 100644
--- /dev/null
+++ b/tests/data/cases/module_docstring_followed_by_function.py
@@ -0,0 +1,11 @@
+# flags: --preview
+"""Two blank lines between module docstring and a function def."""
+def function():
+    pass
+
+# output
+"""Two blank lines between module docstring and a function def."""
+
+
+def function():
+    pass