]> git.madduck.net Git - etc/vim.git/commitdiff

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

More concise formatting for dummy implementations (#3796)
authorShantanu <12621235+hauntsaninja@users.noreply.github.com>
Fri, 4 Aug 2023 02:11:21 +0000 (19:11 -0700)
committerGitHub <noreply@github.com>
Fri, 4 Aug 2023 02:11:21 +0000 (19:11 -0700)
CHANGES.md
src/black/linegen.py
src/black/lines.py
src/black/mode.py
tests/data/miscellaneous/force_py36.py
tests/data/preview/comments7.py
tests/data/preview/dummy_implementations.py [new file with mode: 0644]

index 1de08792f7532b783db469fe7c97341a836f8028..d084498f404ab4696847adbd93be0e550d2c7de5 100644 (file)
@@ -14,6 +14,8 @@
 
 <!-- Changes that affect Black's preview style -->
 
+- More concise formatting for dummy implementations (#3796)
+
 ### Configuration
 
 <!-- Changes to how Black can be configured -->
index 5ef3bbd170578159249eb9f850676767a9bc1f56..507e860190f0c8a31701f5a316e46fa8099ac1e7 100644 (file)
@@ -281,7 +281,9 @@ class LineGenerator(Visitor[Line]):
 
     def visit_suite(self, node: Node) -> Iterator[Line]:
         """Visit a suite."""
-        if self.mode.is_pyi and is_stub_suite(node):
+        if (
+            self.mode.is_pyi or Preview.dummy_implementations in self.mode
+        ) and is_stub_suite(node):
             yield from self.visit(node.children[2])
         else:
             yield from self.visit_default(node)
@@ -296,7 +298,9 @@ class LineGenerator(Visitor[Line]):
 
         is_suite_like = node.parent and node.parent.type in STATEMENT
         if is_suite_like:
-            if self.mode.is_pyi and is_stub_body(node):
+            if (
+                self.mode.is_pyi or Preview.dummy_implementations in self.mode
+            ) and is_stub_body(node):
                 yield from self.visit_default(node)
             else:
                 yield from self.line(+1)
@@ -305,7 +309,7 @@ class LineGenerator(Visitor[Line]):
 
         else:
             if (
-                not self.mode.is_pyi
+                not (self.mode.is_pyi or Preview.dummy_implementations in self.mode)
                 or not node.parent
                 or not is_stub_suite(node.parent)
             ):
index 016a489310d492cb2d19aecc68720aa23c832e4d..0a307b45efff745ab07b744f1f99068c1b81c37c 100644 (file)
@@ -165,6 +165,13 @@ class Line:
             and second_leaf.value == "def"
         )
 
+    @property
+    def is_stub_def(self) -> bool:
+        """Is this line a function definition with a body consisting only of "..."?"""
+        return self.is_def and self.leaves[-4:] == [Leaf(token.COLON, ":")] + [
+            Leaf(token.DOT, ".") for _ in range(3)
+        ]
+
     @property
     def is_class_paren_empty(self) -> bool:
         """Is this a class with no base classes but using parentheses?
@@ -578,6 +585,8 @@ class EmptyLineTracker:
             first_leaf.prefix = ""
         else:
             before = 0
+
+        user_had_newline = bool(before)
         depth = current_line.depth
 
         previous_def = None
@@ -589,7 +598,7 @@ class EmptyLineTracker:
             if self.mode.is_pyi:
                 if depth and not current_line.is_def and self.previous_line.is_def:
                     # Empty lines between attributes and methods should be preserved.
-                    before = min(1, before)
+                    before = 1 if user_had_newline else 0
                 elif (
                     Preview.blank_line_after_nested_stub_class in self.mode
                     and previous_def.is_class
@@ -624,7 +633,9 @@ class EmptyLineTracker:
                     before = 2
 
         if current_line.is_decorator or current_line.is_def or current_line.is_class:
-            return self._maybe_empty_lines_for_class_or_def(current_line, before)
+            return self._maybe_empty_lines_for_class_or_def(
+                current_line, before, user_had_newline
+            )
 
         if (
             self.previous_line
@@ -648,8 +659,8 @@ class EmptyLineTracker:
             return 0, 0
         return before, 0
 
-    def _maybe_empty_lines_for_class_or_def(
-        self, current_line: Line, before: int
+    def _maybe_empty_lines_for_class_or_def(  # noqa: C901
+        self, current_line: Line, before: int, user_had_newline: bool
     ) -> Tuple[int, int]:
         if not current_line.is_decorator:
             self.previous_defs.append(current_line)
@@ -715,6 +726,14 @@ class EmptyLineTracker:
                 newlines = 0
         else:
             newlines = 1 if current_line.depth else 2
+            # If a user has left no space after a dummy implementation, don't insert
+            # new lines. This is useful for instance for @overload or Protocols.
+            if (
+                Preview.dummy_implementations in self.mode
+                and self.previous_line.is_stub_def
+                and not user_had_newline
+            ):
+                newlines = 0
         if comment_to_add_newlines is not None:
             previous_block = comment_to_add_newlines.previous_block
             if previous_block is not None:
index 4d979afd84de332135ac0052c494e9be842e8352..282c1669da74f5275b818befeed14634eb186a51 100644 (file)
@@ -182,6 +182,7 @@ class Preview(Enum):
     skip_magic_trailing_comma_in_subscript = auto()
     wrap_long_dict_values_in_parens = auto()
     wrap_multiple_context_managers_in_parens = auto()
+    dummy_implementations = auto()
 
 
 class Deprecated(UserWarning):
index cad935e525a46057349becfcc1845ba37b2b28e3..4c9b70336e748ecc7c0384338ee7606b6496c95f 100644 (file)
@@ -1,6 +1,6 @@
 # The input source must not contain any Py36-specific syntax (e.g. argument type
 # annotations, trailing comma after *rest) or this test becomes invalid.
-def long_function_name(argument_one, argument_two, argument_three, argument_four, argument_five, argument_six, *rest): ...
+def long_function_name(argument_one, argument_two, argument_three, argument_four, argument_five, argument_six, *rest): pass
 # output
 # The input source must not contain any Py36-specific syntax (e.g. argument type
 # annotations, trailing comma after *rest) or this test becomes invalid.
@@ -13,4 +13,4 @@ def long_function_name(
     argument_six,
     *rest,
 ):
-    ...
+    pass
index ec2dc501d8ef8382e132ed9b9bbd497ddaebe36a..8b1224017e5208878cd7ef51dad87df81bc85eef 100644 (file)
@@ -278,8 +278,7 @@ class C:
     )
     def test_fails_invalid_post_data(
         self, pyramid_config, db_request, post_data, message
-    ):
-        ...
+    ): ...
 
 
 square = Square(4)  # type: Optional[Square]
diff --git a/tests/data/preview/dummy_implementations.py b/tests/data/preview/dummy_implementations.py
new file mode 100644 (file)
index 0000000..e07c25e
--- /dev/null
@@ -0,0 +1,99 @@
+from typing import NoReturn, Protocol, Union, overload
+
+
+def dummy(a): ...
+def other(b): ...
+
+
+@overload
+def a(arg: int) -> int: ...
+@overload
+def a(arg: str) -> str: ...
+@overload
+def a(arg: object) -> NoReturn: ...
+def a(arg: Union[int, str, object]) -> Union[int, str]:
+    if not isinstance(arg, (int, str)):
+        raise TypeError
+    return arg
+
+class Proto(Protocol):
+    def foo(self, a: int) -> int:
+        ...
+
+    def bar(self, b: str) -> str: ...
+    def baz(self, c: bytes) -> str:
+        ...
+
+
+def dummy_two():
+    ...
+@dummy
+def dummy_three():
+    ...
+
+def dummy_four():
+    ...
+
+@overload
+def b(arg: int) -> int: ...
+
+@overload
+def b(arg: str) -> str: ...
+@overload
+def b(arg: object) -> NoReturn: ...
+
+def b(arg: Union[int, str, object]) -> Union[int, str]:
+    if not isinstance(arg, (int, str)):
+        raise TypeError
+    return arg
+
+# output
+
+from typing import NoReturn, Protocol, Union, overload
+
+
+def dummy(a): ...
+def other(b): ...
+
+
+@overload
+def a(arg: int) -> int: ...
+@overload
+def a(arg: str) -> str: ...
+@overload
+def a(arg: object) -> NoReturn: ...
+def a(arg: Union[int, str, object]) -> Union[int, str]:
+    if not isinstance(arg, (int, str)):
+        raise TypeError
+    return arg
+
+
+class Proto(Protocol):
+    def foo(self, a: int) -> int: ...
+
+    def bar(self, b: str) -> str: ...
+    def baz(self, c: bytes) -> str: ...
+
+
+def dummy_two(): ...
+@dummy
+def dummy_three(): ...
+
+
+def dummy_four(): ...
+
+
+@overload
+def b(arg: int) -> int: ...
+
+
+@overload
+def b(arg: str) -> str: ...
+@overload
+def b(arg: object) -> NoReturn: ...
+
+
+def b(arg: Union[int, str, object]) -> Union[int, str]:
+    if not isinstance(arg, (int, str)):
+        raise TypeError
+    return arg