Repository: psf/black

Issue:
Extra newlines added before function with fmt skip on decorator
        I've just tested this out with a medium-sized codebase. All as expected apart from this one issue with a comment at the end of a function getting 2 newlines added above it. Removing `@decorator1` stops the comment getting moved, as does removing `# fmt:skip`.

<img width="1094" alt="image" src="https://user-images.githubusercontent.com/3770312/208406615-62a360b0-d41e-4e00-b95c-fc3baa3eb89e.png">

https://black.vercel.app/?version=stable&state=_Td6WFoAAATm1rRGAgAhARYAAAB0L-Wj4ADZAIZdAD2IimZxl1N_WlbvK5V9KEd0suDTtKdXyoIEqib4HPPAOaGeijv-QM5F2CIWMxNomuS8WtfuVtc-CBav7wmp196mwOSJGXUmaRTdIwYr1wf881xD3biQ1s52KZSzb20ApVmRJ9HPRaEOhoVMkb9jDbRHrld5-g2LTBdcrPwjnT90_-ZGPvMAAAAAsri7GeyV3k8AAaIB2gEAAEJ-yuKxxGf7AgAAAAAEWVo=

_Originally posted by @jgillard in https://github.com/psf/black/issues/3407#issuecomment-1357440439_
      


Discussion:
I tested this out and it happens only if both of the decorators on `bar` are present and the second one has `# fmt: skip`.

Copy-pastable sample:
```python
def foo():
    pass
    # Comment on next below

@decorator1
@decorator2  # fmt: skip
def bar():
    pass
```
This has the same underlying issue as https://github.com/psf/black/issues/3438, I'll take a look this week.

Resolving patch:
diff --git a/src/black/lines.py b/src/black/lines.py
--- a/src/black/lines.py
+++ b/src/black/lines.py
@@ -520,7 +520,8 @@ def maybe_empty_lines(self, current_line: Line) -> LinesBlock:
                 and (self.semantic_leading_comment is None or before)
             ):
                 self.semantic_leading_comment = block
-        elif not current_line.is_decorator:
+        # `or before` means this decorator already has an empty line before
+        elif not current_line.is_decorator or before:
             self.semantic_leading_comment = None
 
         self.previous_line = current_line


Test patch:
diff --git a/tests/data/preview/comments9.py b/tests/data/preview/comments9.py
--- a/tests/data/preview/comments9.py
+++ b/tests/data/preview/comments9.py
@@ -114,6 +114,31 @@ def first_method(self):
         pass
 
 
+# Regression test for https://github.com/psf/black/issues/3454.
+def foo():
+    pass
+    # Trailing comment that belongs to this function
+
+
+@decorator1
+@decorator2  # fmt: skip
+def bar():
+    pass
+
+
+# Regression test for https://github.com/psf/black/issues/3454.
+def foo():
+    pass
+    # Trailing comment that belongs to this function.
+    # NOTE this comment only has one empty line below, and the formatter
+    # should enforce two blank lines.
+
+@decorator1
+# A standalone comment
+def bar():
+    pass
+
+
 # output
 
 
@@ -252,3 +277,29 @@ class MyClass:
     # More comments.
     def first_method(self):
         pass
+
+
+# Regression test for https://github.com/psf/black/issues/3454.
+def foo():
+    pass
+    # Trailing comment that belongs to this function
+
+
+@decorator1
+@decorator2  # fmt: skip
+def bar():
+    pass
+
+
+# Regression test for https://github.com/psf/black/issues/3454.
+def foo():
+    pass
+    # Trailing comment that belongs to this function.
+    # NOTE this comment only has one empty line below, and the formatter
+    # should enforce two blank lines.
+
+
+@decorator1
+# A standalone comment
+def bar():
+    pass