Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7f7673d

Browse files
authored
Support 3.11 / PEP 654 syntax (psf#3016)
1 parent 712f8b3 commit 7f7673d

10 files changed

Lines changed: 223 additions & 1 deletion

File tree

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353

5454
### Parser
5555

56+
- [PEP 654](https://peps.python.org/pep-0654/#except) syntax (for example,
57+
`except *ExceptionGroup:`) is now supported (#3016)
58+
5659
<!-- Changes to the parser or to version autodetection -->
5760

5861
### Performance

src/black/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,6 +1296,13 @@ def get_features_used( # noqa: C901
12961296
):
12971297
features.add(Feature.ANN_ASSIGN_EXTENDED_RHS)
12981298

1299+
elif (
1300+
n.type == syms.except_clause
1301+
and len(n.children) >= 2
1302+
and n.children[1].type == token.STAR
1303+
):
1304+
features.add(Feature.EXCEPT_STAR)
1305+
12991306
return features
13001307

13011308

src/black/linegen.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,15 @@ def normalize_invisible_parens(
915915
node.insert_child(index, Leaf(token.LPAR, ""))
916916
node.append_child(Leaf(token.RPAR, ""))
917917
break
918+
elif (
919+
index == 1
920+
and child.type == token.STAR
921+
and node.type == syms.except_clause
922+
):
923+
# In except* (PEP 654), the star is actually part of
924+
# of the keyword. So we need to skip the insertion of
925+
# invisible parentheses to work more precisely.
926+
continue
918927

919928
elif not (isinstance(child, Leaf) and is_multiline_string(child)):
920929
wrap_in_parentheses(node, child, visible=False)

src/black/mode.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class TargetVersion(Enum):
3030
PY38 = 8
3131
PY39 = 9
3232
PY310 = 10
33+
PY311 = 11
3334

3435

3536
class Feature(Enum):
@@ -47,6 +48,7 @@ class Feature(Enum):
4748
PATTERN_MATCHING = 11
4849
UNPACKING_ON_FLOW = 12
4950
ANN_ASSIGN_EXTENDED_RHS = 13
51+
EXCEPT_STAR = 14
5052
FORCE_OPTIONAL_PARENTHESES = 50
5153

5254
# __future__ flags
@@ -116,6 +118,21 @@ class Feature(Enum):
116118
Feature.ANN_ASSIGN_EXTENDED_RHS,
117119
Feature.PATTERN_MATCHING,
118120
},
121+
TargetVersion.PY311: {
122+
Feature.F_STRINGS,
123+
Feature.NUMERIC_UNDERSCORES,
124+
Feature.TRAILING_COMMA_IN_CALL,
125+
Feature.TRAILING_COMMA_IN_DEF,
126+
Feature.ASYNC_KEYWORDS,
127+
Feature.FUTURE_ANNOTATIONS,
128+
Feature.ASSIGNMENT_EXPRESSIONS,
129+
Feature.RELAXED_DECORATORS,
130+
Feature.POS_ONLY_ARGUMENTS,
131+
Feature.UNPACKING_ON_FLOW,
132+
Feature.ANN_ASSIGN_EXTENDED_RHS,
133+
Feature.PATTERN_MATCHING,
134+
Feature.EXCEPT_STAR,
135+
},
119136
}
120137

121138

src/black/nodes.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901
401401
elif p.type == syms.sliceop:
402402
return NO
403403

404+
elif p.type == syms.except_clause:
405+
if t == token.STAR:
406+
return NO
407+
404408
return SPACE
405409

406410

src/blib2to3/Grammar.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ try_stmt: ('try' ':' suite
118118
with_stmt: 'with' asexpr_test (',' asexpr_test)* ':' suite
119119

120120
# NB compile.c makes sure that the default except clause is last
121-
except_clause: 'except' [test [(',' | 'as') test]]
121+
except_clause: 'except' ['*'] [test [(',' | 'as') test]]
122122
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
123123

124124
# Backward compatibility cruft to support:

tests/data/pep_654.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
try:
2+
raise OSError("blah")
3+
except* ExceptionGroup as e:
4+
pass
5+
6+
7+
try:
8+
async with trio.open_nursery() as nursery:
9+
# Make two concurrent calls to child()
10+
nursery.start_soon(child)
11+
nursery.start_soon(child)
12+
except* ValueError:
13+
pass
14+
15+
try:
16+
try:
17+
raise ValueError(42)
18+
except:
19+
try:
20+
raise TypeError(int)
21+
except* Exception:
22+
pass
23+
1 / 0
24+
except Exception as e:
25+
exc = e
26+
27+
try:
28+
try:
29+
raise FalsyEG("eg", [TypeError(1), ValueError(2)])
30+
except* TypeError as e:
31+
tes = e
32+
raise
33+
except* ValueError as e:
34+
ves = e
35+
pass
36+
except Exception as e:
37+
exc = e
38+
39+
try:
40+
try:
41+
raise orig
42+
except* (TypeError, ValueError) as e:
43+
raise SyntaxError(3) from e
44+
except BaseException as e:
45+
exc = e
46+
47+
try:
48+
try:
49+
raise orig
50+
except* OSError as e:
51+
raise TypeError(3) from e
52+
except ExceptionGroup as e:
53+
exc = e

tests/data/pep_654_style.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
try:
2+
raise OSError("blah")
3+
except * ExceptionGroup as e:
4+
pass
5+
6+
7+
try:
8+
async with trio.open_nursery() as nursery:
9+
# Make two concurrent calls to child()
10+
nursery.start_soon(child)
11+
nursery.start_soon(child)
12+
except *ValueError:
13+
pass
14+
15+
try:
16+
try:
17+
raise ValueError(42)
18+
except:
19+
try:
20+
raise TypeError(int)
21+
except *(Exception):
22+
pass
23+
1 / 0
24+
except Exception as e:
25+
exc = e
26+
27+
try:
28+
try:
29+
raise FalsyEG("eg", [TypeError(1), ValueError(2)])
30+
except \
31+
*TypeError as e:
32+
tes = e
33+
raise
34+
except * ValueError as e:
35+
ves = e
36+
pass
37+
except Exception as e:
38+
exc = e
39+
40+
try:
41+
try:
42+
raise orig
43+
except *(TypeError, ValueError, *OTHER_EXCEPTIONS) as e:
44+
raise SyntaxError(3) from e
45+
except BaseException as e:
46+
exc = e
47+
48+
try:
49+
try:
50+
raise orig
51+
except\
52+
* OSError as e:
53+
raise TypeError(3) from e
54+
except ExceptionGroup as e:
55+
exc = e
56+
57+
# output
58+
59+
try:
60+
raise OSError("blah")
61+
except* ExceptionGroup as e:
62+
pass
63+
64+
65+
try:
66+
async with trio.open_nursery() as nursery:
67+
# Make two concurrent calls to child()
68+
nursery.start_soon(child)
69+
nursery.start_soon(child)
70+
except* ValueError:
71+
pass
72+
73+
try:
74+
try:
75+
raise ValueError(42)
76+
except:
77+
try:
78+
raise TypeError(int)
79+
except* (Exception):
80+
pass
81+
1 / 0
82+
except Exception as e:
83+
exc = e
84+
85+
try:
86+
try:
87+
raise FalsyEG("eg", [TypeError(1), ValueError(2)])
88+
except* TypeError as e:
89+
tes = e
90+
raise
91+
except* ValueError as e:
92+
ves = e
93+
pass
94+
except Exception as e:
95+
exc = e
96+
97+
try:
98+
try:
99+
raise orig
100+
except* (TypeError, ValueError, *OTHER_EXCEPTIONS) as e:
101+
raise SyntaxError(3) from e
102+
except BaseException as e:
103+
exc = e
104+
105+
try:
106+
try:
107+
raise orig
108+
except* OSError as e:
109+
raise TypeError(3) from e
110+
except ExceptionGroup as e:
111+
exc = e

tests/test_black.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,12 @@ def test_get_features_used(self) -> None:
794794
self.assertEqual(
795795
black.get_features_used(node), {Feature.ANN_ASSIGN_EXTENDED_RHS}
796796
)
797+
node = black.lib2to3_parse("try: pass\nexcept Something: pass")
798+
self.assertEqual(black.get_features_used(node), set())
799+
node = black.lib2to3_parse("try: pass\nexcept (*Something,): pass")
800+
self.assertEqual(black.get_features_used(node), set())
801+
node = black.lib2to3_parse("try: pass\nexcept *Group: pass")
802+
self.assertEqual(black.get_features_used(node), {Feature.EXCEPT_STAR})
797803

798804
def test_get_features_used_for_future_flags(self) -> None:
799805
for src, features in [

tests/test_format.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
"parenthesized_context_managers",
7373
]
7474

75+
PY311_CASES: List[str] = [
76+
"pep_654",
77+
"pep_654_style",
78+
]
79+
7580
PREVIEW_CASES: List[str] = [
7681
# string processing
7782
"cantfit",
@@ -227,6 +232,13 @@ def test_patma_invalid() -> None:
227232
exc_info.match("Cannot parse: 10:11")
228233

229234

235+
@pytest.mark.parametrize("filename", PY311_CASES)
236+
def test_python_311(filename: str) -> None:
237+
source, expected = read_data(filename)
238+
mode = black.Mode(target_versions={black.TargetVersion.PY311})
239+
assert_format(source, expected, mode, minimum_version=(3, 11))
240+
241+
230242
def test_python_2_hint() -> None:
231243
with pytest.raises(black.parsing.InvalidInput) as exc_info:
232244
assert_format("print 'daylily'", "print 'daylily'")

0 commit comments

Comments
 (0)