The idea behind this change is that we stop looking into previous body to determine if there should be a blank before a function or class definition.
Input:
```python
import sys
if sys.version_info > (3, 7):
    class Nested1:
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    class Nested2:
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
if sys.version_info > (3, 7):
    def nested1():
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    def nested2():
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
```
Stable style
```python
import sys
if sys.version_info > (3, 7):
    class Nested1:
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    class Nested2:
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
if sys.version_info > (3, 7):
    def nested1():
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    def nested2():
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
```
In the stable formatting, we have a blank line sometimes, not depending on the previous statement on the same level, but on the last (potentially nested) statement in the previous body.
#2783/#3564 fixes this for classes in preview style:
```python
import sys
if sys.version_info > (3, 7):
    class Nested1:
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    class Nested2:
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
if sys.version_info > (3, 7):
    def nested1():
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    def nested2():
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
```
This PR additionally fixes this for function definitions:
```python
if sys.version_info > (3, 7):
    if sys.platform == "win32":
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    if sys.platform != "win32":
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
if sys.version_info > (3, 8):
    if sys.platform == "win32":
        assignment = 1
        def function_definition(self): ...
    class F1: ...
    if sys.platform != "win32":
        def function_definition(self): ...
        assignment = 1
    class F2: ...
```
You can see the effect of this change on typeshed in https://github.com/konstin/typeshed/pull/1/files. As baseline, the preview mode changes without this PR are at https://github.com/konstin/typeshed/pull/2.
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
 - For stubs, enforce one blank line after a nested class with a body other than just
   `...` (#3564)
 - Improve handling of multiline strings by changing line split behavior (#1879)
+- In stub files, add a blank line between a statement with a body (e.g an
+  `if sys.version_info > (3, x):`) and a function definition on the same level. (#3862)
 
 ### Parser
 
 
                     newlines = 0
                 else:
                     newlines = 1
+            # Remove case `self.previous_line.depth > current_line.depth` below when
+            # this becomes stable.
+            #
+            # Don't inspect the previous line if it's part of the body of the previous
+            # statement in the same level, we always want a blank line if there's
+            # something with a body preceding.
+            elif (
+                Preview.blank_line_between_nested_and_def_stub_file in current_line.mode
+                and self.previous_line.depth > current_line.depth
+            ):
+                newlines = 1
             elif (
                 current_line.is_def or current_line.is_decorator
             ) and not self.previous_line.is_def:
 
 
     add_trailing_comma_consistently = auto()
     blank_line_after_nested_stub_class = auto()
+    blank_line_between_nested_and_def_stub_file = auto()
     hex_codes_in_unicode_sequences = auto()
     improved_async_statements_handling = auto()
     multiline_string_handling = auto()
 
+++ /dev/null
-class Outer:
-    class InnerStub: ...
-    outer_attr_after_inner_stub: int
-    class Inner:
-        inner_attr: int
-    outer_attr: int
-
-# output
-class Outer:
-    class InnerStub: ...
-    outer_attr_after_inner_stub: int
-
-    class Inner:
-        inner_attr: int
-
-    outer_attr: int
 
--- /dev/null
+import sys
+
+class Outer:
+    class InnerStub: ...
+    outer_attr_after_inner_stub: int
+    class Inner:
+        inner_attr: int
+    outer_attr: int
+
+if sys.version_info > (3, 7):
+    if sys.platform == "win32":
+        assignment = 1
+        def function_definition(self): ...
+    def f1(self) -> str: ...
+    if sys.platform != "win32":
+        def function_definition(self): ...
+        assignment = 1
+    def f2(self) -> str: ...
+
+# output
+
+import sys
+
+class Outer:
+    class InnerStub: ...
+    outer_attr_after_inner_stub: int
+
+    class Inner:
+        inner_attr: int
+
+    outer_attr: int
+
+if sys.version_info > (3, 7):
+    if sys.platform == "win32":
+        assignment = 1
+        def function_definition(self): ...
+
+    def f1(self) -> str: ...
+    if sys.platform != "win32":
+        def function_definition(self): ...
+        assignment = 1
+
+    def f2(self) -> str: ...
\ No newline at end of file
 
     assert_format(source, expected, mode)
 
 
-def test_nested_class_stub() -> None:
+def test_nested_stub() -> None:
     mode = replace(DEFAULT_MODE, is_pyi=True, preview=True)
-    source, expected = read_data("miscellaneous", "nested_class_stub.pyi")
+    source, expected = read_data("miscellaneous", "nested_stub.pyi")
     assert_format(source, expected, mode)