Remove parentheses around multiple exception types on Python 3.14+ (#20768)

Summary
--

This PR implements the black preview style from
https://github.com/psf/black/pull/4720. As of Python 3.14, you're
allowed to omit the parentheses around groups of exceptions, as long as
there's no `as` binding:

**3.13**

```pycon
Python 3.13.4 (main, Jun  4 2025, 17:37:06) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try: ...
... except (Exception, BaseException): ...
...
Ellipsis
>>> try: ...
... except Exception, BaseException: ...
...
  File "<python-input-1>", line 2
    except Exception, BaseException: ...
           ^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized
```

**3.14**

```pycon
Python 3.14.0rc2 (main, Sep  2 2025, 14:20:56) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try: ...
... except Exception, BaseException: ...
...
Ellipsis
>>> try: ...
... except (Exception, BaseException): ...
...
Ellipsis
>>> try: ...
... except Exception, BaseException as e: ...
...
  File "<python-input-2>", line 2
    except Exception, BaseException as e: ...
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized when using 'as'
```

I think this ended up being pretty straightforward, at least once Micha
showed me where to start :)

Test Plan
--

New tests

At first I thought we were deviating from black in how we handle
comments within the exception type tuple, but I think this applies to
how we format all tuples, not specifically with the new preview style.
This commit is contained in:
Brent Westbrook 2025-10-14 11:17:45 -04:00 committed by GitHub
parent 1ed9b215b9
commit 591e9bbccb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 497 additions and 450 deletions

View file

@ -0,0 +1,8 @@
[
{
"target_version": "3.13"
},
{
"target_version": "3.14"
}
]

View file

@ -166,3 +166,40 @@ else:
finally:
pass
try:
pass
# These parens can be removed on 3.14+ but not earlier
except (BaseException, Exception, ValueError):
pass
# But black won't remove these parentheses
except (ZeroDivisionError,):
pass
except ( # We wrap these and preserve the parens
BaseException, Exception, ValueError):
pass
except (
BaseException,
# Same with this comment
Exception,
ValueError
):
pass
try:
pass
# They can also be omitted for `except*`
except* (BaseException, Exception, ValueError):
pass
# But parentheses are still required in the presence of an `as` binding
try:
pass
except (BaseException, Exception, ValueError) as e:
pass
try:
pass
except* (BaseException, Exception, ValueError) as e:
pass