]> 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:

Support 3.11 / PEP 654 syntax (#3016)
authorBatuhan Taskaya <isidentical@gmail.com>
Fri, 15 Apr 2022 16:25:07 +0000 (19:25 +0300)
committerGitHub <noreply@github.com>
Fri, 15 Apr 2022 16:25:07 +0000 (12:25 -0400)
CHANGES.md
src/black/__init__.py
src/black/linegen.py
src/black/mode.py
src/black/nodes.py
src/blib2to3/Grammar.txt
tests/data/pep_654.py [new file with mode: 0644]
tests/data/pep_654_style.py [new file with mode: 0644]
tests/test_black.py
tests/test_format.py

index b21c319d5e0e9535c7814df2e9092fd36fb8fdd9..566077b1dbc9edf35dd031c2980134032125e684 100644 (file)
@@ -53,6 +53,9 @@
 
 ### Parser
 
 
 ### Parser
 
+- [PEP 654](https://peps.python.org/pep-0654/#except) syntax (for example,
+  `except *ExceptionGroup:`) is now supported (#3016)
+
 <!-- Changes to the parser or to version autodetection -->
 
 ### Performance
 <!-- Changes to the parser or to version autodetection -->
 
 ### Performance
index 3a2d1cb88981f12936c09b0dfbdfc561c61cfa06..3a1ce24f059e3c0bd57d73770d394d1956306aa7 100644 (file)
@@ -1296,6 +1296,13 @@ def get_features_used(  # noqa: C901
         ):
             features.add(Feature.ANN_ASSIGN_EXTENDED_RHS)
 
         ):
             features.add(Feature.ANN_ASSIGN_EXTENDED_RHS)
 
+        elif (
+            n.type == syms.except_clause
+            and len(n.children) >= 2
+            and n.children[1].type == token.STAR
+        ):
+            features.add(Feature.EXCEPT_STAR)
+
     return features
 
 
     return features
 
 
index caffbab0cbc3d4966239de6897f1419d1c00c725..91fdeef8f2f94a3e1b7b8cb244b19723beac9b85 100644 (file)
@@ -915,6 +915,15 @@ def normalize_invisible_parens(
                     node.insert_child(index, Leaf(token.LPAR, ""))
                     node.append_child(Leaf(token.RPAR, ""))
                 break
                     node.insert_child(index, Leaf(token.LPAR, ""))
                     node.append_child(Leaf(token.RPAR, ""))
                 break
+            elif (
+                index == 1
+                and child.type == token.STAR
+                and node.type == syms.except_clause
+            ):
+                # In except* (PEP 654), the star is actually part of
+                # of the keyword. So we need to skip the insertion of
+                # invisible parentheses to work more precisely.
+                continue
 
             elif not (isinstance(child, Leaf) and is_multiline_string(child)):
                 wrap_in_parentheses(node, child, visible=False)
 
             elif not (isinstance(child, Leaf) and is_multiline_string(child)):
                 wrap_in_parentheses(node, child, visible=False)
index 34905702a541e4a212c43244fff9cffb5fa2fb62..6bd4ce1442118d7f10e661bae25db9bdcfb7a41b 100644 (file)
@@ -30,6 +30,7 @@ class TargetVersion(Enum):
     PY38 = 8
     PY39 = 9
     PY310 = 10
     PY38 = 8
     PY39 = 9
     PY310 = 10
+    PY311 = 11
 
 
 class Feature(Enum):
 
 
 class Feature(Enum):
@@ -47,6 +48,7 @@ class Feature(Enum):
     PATTERN_MATCHING = 11
     UNPACKING_ON_FLOW = 12
     ANN_ASSIGN_EXTENDED_RHS = 13
     PATTERN_MATCHING = 11
     UNPACKING_ON_FLOW = 12
     ANN_ASSIGN_EXTENDED_RHS = 13
+    EXCEPT_STAR = 14
     FORCE_OPTIONAL_PARENTHESES = 50
 
     # __future__ flags
     FORCE_OPTIONAL_PARENTHESES = 50
 
     # __future__ flags
@@ -116,6 +118,21 @@ VERSION_TO_FEATURES: Dict[TargetVersion, Set[Feature]] = {
         Feature.ANN_ASSIGN_EXTENDED_RHS,
         Feature.PATTERN_MATCHING,
     },
         Feature.ANN_ASSIGN_EXTENDED_RHS,
         Feature.PATTERN_MATCHING,
     },
+    TargetVersion.PY311: {
+        Feature.F_STRINGS,
+        Feature.NUMERIC_UNDERSCORES,
+        Feature.TRAILING_COMMA_IN_CALL,
+        Feature.TRAILING_COMMA_IN_DEF,
+        Feature.ASYNC_KEYWORDS,
+        Feature.FUTURE_ANNOTATIONS,
+        Feature.ASSIGNMENT_EXPRESSIONS,
+        Feature.RELAXED_DECORATORS,
+        Feature.POS_ONLY_ARGUMENTS,
+        Feature.UNPACKING_ON_FLOW,
+        Feature.ANN_ASSIGN_EXTENDED_RHS,
+        Feature.PATTERN_MATCHING,
+        Feature.EXCEPT_STAR,
+    },
 }
 
 
 }
 
 
index d18d4bde8724ab669a27d54e9cef6cdc64699fa0..37b96a498d63f8ae067a555b8fb51642381a6181 100644 (file)
@@ -401,6 +401,10 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str:  # noqa: C901
     elif p.type == syms.sliceop:
         return NO
 
     elif p.type == syms.sliceop:
         return NO
 
+    elif p.type == syms.except_clause:
+        if t == token.STAR:
+            return NO
+
     return SPACE
 
 
     return SPACE
 
 
index 0ce6cf39111566aecb7a6a9d6d0d86d08f5132e1..1de5416551399f54fd59016f2bc1ba7deda0bb9f 100644 (file)
@@ -118,7 +118,7 @@ try_stmt: ('try' ':' suite
 with_stmt: 'with' asexpr_test (',' asexpr_test)*  ':' suite
 
 # NB compile.c makes sure that the default except clause is last
 with_stmt: 'with' asexpr_test (',' asexpr_test)*  ':' suite
 
 # NB compile.c makes sure that the default except clause is last
-except_clause: 'except' [test [(',' | 'as') test]]
+except_clause: 'except' ['*'] [test [(',' | 'as') test]]
 suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
 
 # Backward compatibility cruft to support:
 suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
 
 # Backward compatibility cruft to support:
diff --git a/tests/data/pep_654.py b/tests/data/pep_654.py
new file mode 100644 (file)
index 0000000..387c081
--- /dev/null
@@ -0,0 +1,53 @@
+try:
+    raise OSError("blah")
+except* ExceptionGroup as e:
+    pass
+
+
+try:
+    async with trio.open_nursery() as nursery:
+        # Make two concurrent calls to child()
+        nursery.start_soon(child)
+        nursery.start_soon(child)
+except* ValueError:
+    pass
+
+try:
+    try:
+        raise ValueError(42)
+    except:
+        try:
+            raise TypeError(int)
+        except* Exception:
+            pass
+        1 / 0
+except Exception as e:
+    exc = e
+
+try:
+    try:
+        raise FalsyEG("eg", [TypeError(1), ValueError(2)])
+    except* TypeError as e:
+        tes = e
+        raise
+    except* ValueError as e:
+        ves = e
+        pass
+except Exception as e:
+    exc = e
+
+try:
+    try:
+        raise orig
+    except* (TypeError, ValueError) as e:
+        raise SyntaxError(3) from e
+except BaseException as e:
+    exc = e
+
+try:
+    try:
+        raise orig
+    except* OSError as e:
+        raise TypeError(3) from e
+except ExceptionGroup as e:
+    exc = e
diff --git a/tests/data/pep_654_style.py b/tests/data/pep_654_style.py
new file mode 100644 (file)
index 0000000..568e5e3
--- /dev/null
@@ -0,0 +1,111 @@
+try:
+    raise OSError("blah")
+except               * ExceptionGroup as e:
+    pass
+
+
+try:
+    async with trio.open_nursery() as nursery:
+        # Make two concurrent calls to child()
+        nursery.start_soon(child)
+        nursery.start_soon(child)
+except *ValueError:
+    pass
+
+try:
+    try:
+        raise ValueError(42)
+    except:
+        try:
+            raise TypeError(int)
+        except *(Exception):
+            pass
+        1 / 0
+except Exception as e:
+    exc = e
+
+try:
+    try:
+        raise FalsyEG("eg", [TypeError(1), ValueError(2)])
+    except \
+        *TypeError as e:
+        tes = e
+        raise
+    except  *  ValueError as e:
+        ves = e
+        pass
+except Exception as e:
+    exc = e
+
+try:
+    try:
+        raise orig
+    except *(TypeError, ValueError, *OTHER_EXCEPTIONS) as e:
+        raise SyntaxError(3) from e
+except BaseException as e:
+    exc = e
+
+try:
+    try:
+        raise orig
+    except\
+        * OSError as e:
+        raise TypeError(3) from e
+except ExceptionGroup as e:
+    exc = e
+
+# output
+
+try:
+    raise OSError("blah")
+except* ExceptionGroup as e:
+    pass
+
+
+try:
+    async with trio.open_nursery() as nursery:
+        # Make two concurrent calls to child()
+        nursery.start_soon(child)
+        nursery.start_soon(child)
+except* ValueError:
+    pass
+
+try:
+    try:
+        raise ValueError(42)
+    except:
+        try:
+            raise TypeError(int)
+        except* (Exception):
+            pass
+        1 / 0
+except Exception as e:
+    exc = e
+
+try:
+    try:
+        raise FalsyEG("eg", [TypeError(1), ValueError(2)])
+    except* TypeError as e:
+        tes = e
+        raise
+    except* ValueError as e:
+        ves = e
+        pass
+except Exception as e:
+    exc = e
+
+try:
+    try:
+        raise orig
+    except* (TypeError, ValueError, *OTHER_EXCEPTIONS) as e:
+        raise SyntaxError(3) from e
+except BaseException as e:
+    exc = e
+
+try:
+    try:
+        raise orig
+    except* OSError as e:
+        raise TypeError(3) from e
+except ExceptionGroup as e:
+    exc = e
index 20cc9f7379f31c7abb920d32ba93346833ce2f4a..f6663fa579718a7b73584a08356d0541877d61cf 100644 (file)
@@ -794,6 +794,12 @@ class BlackTestCase(BlackBaseTestCase):
         self.assertEqual(
             black.get_features_used(node), {Feature.ANN_ASSIGN_EXTENDED_RHS}
         )
         self.assertEqual(
             black.get_features_used(node), {Feature.ANN_ASSIGN_EXTENDED_RHS}
         )
+        node = black.lib2to3_parse("try: pass\nexcept Something: pass")
+        self.assertEqual(black.get_features_used(node), set())
+        node = black.lib2to3_parse("try: pass\nexcept (*Something,): pass")
+        self.assertEqual(black.get_features_used(node), set())
+        node = black.lib2to3_parse("try: pass\nexcept *Group: pass")
+        self.assertEqual(black.get_features_used(node), {Feature.EXCEPT_STAR})
 
     def test_get_features_used_for_future_flags(self) -> None:
         for src, features in [
 
     def test_get_features_used_for_future_flags(self) -> None:
         for src, features in [
index fd5f596b6d54b75065ef4ff0aab0287266936fde..1916146e84dc570a36a38a0e940e0c207c5b6ce2 100644 (file)
@@ -72,6 +72,11 @@ PY310_CASES: List[str] = [
     "parenthesized_context_managers",
 ]
 
     "parenthesized_context_managers",
 ]
 
+PY311_CASES: List[str] = [
+    "pep_654",
+    "pep_654_style",
+]
+
 PREVIEW_CASES: List[str] = [
     # string processing
     "cantfit",
 PREVIEW_CASES: List[str] = [
     # string processing
     "cantfit",
@@ -227,6 +232,13 @@ def test_patma_invalid() -> None:
     exc_info.match("Cannot parse: 10:11")
 
 
     exc_info.match("Cannot parse: 10:11")
 
 
+@pytest.mark.parametrize("filename", PY311_CASES)
+def test_python_311(filename: str) -> None:
+    source, expected = read_data(filename)
+    mode = black.Mode(target_versions={black.TargetVersion.PY311})
+    assert_format(source, expected, mode, minimum_version=(3, 11))
+
+
 def test_python_2_hint() -> None:
     with pytest.raises(black.parsing.InvalidInput) as exc_info:
         assert_format("print 'daylily'", "print 'daylily'")
 def test_python_2_hint() -> None:
     with pytest.raises(black.parsing.InvalidInput) as exc_info:
         assert_format("print 'daylily'", "print 'daylily'")