ruff/crates/ruff_python_formatter/src/expression
Brent Westbrook 591e9bbccb
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.
2025-10-14 11:17:45 -04:00
..
binary_like.rs Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00
expr_attribute.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_await.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_bin_op.rs Fix f-string formatting in assignment statement (#14454) 2024-11-26 15:07:18 +05:30
expr_bool_op.rs Move {AnyNodeRef, AstNode} to ruff_python_ast crate root (#8030) 2023-10-18 00:01:18 +00:00
expr_boolean_literal.rs Split `Constant` to individual literal nodes (#8064) 2023-10-30 12:13:23 +05:30
expr_bytes_literal.rs Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00
expr_call.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_compare.rs Fix f-string formatting in assignment statement (#14454) 2024-11-26 15:07:18 +05:30
expr_dict.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_dict_comp.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_ellipsis_literal.rs Split `Constant` to individual literal nodes (#8064) 2023-10-30 12:13:23 +05:30
expr_f_string.rs Implement template strings (#17851) 2025-05-30 15:00:56 -05:00
expr_generator.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_if.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_ipy_escape_command.rs Formatter parentheses support for `IpyEscapeCommand` (#8207) 2023-10-25 14:01:50 +00:00
expr_lambda.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_list.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_list_comp.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_name.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_named.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_none_literal.rs Split `Constant` to individual literal nodes (#8064) 2023-10-30 12:13:23 +05:30
expr_number_literal.rs Update Rust toolchain to 1.89 (#19807) 2025-08-07 18:21:50 +02:00
expr_set.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_set_comp.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_slice.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_starred.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_string_literal.rs Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00
expr_subscript.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_t_string.rs Implement template strings (#17851) 2025-05-30 15:00:56 -05:00
expr_tuple.rs Remove parentheses around multiple exception types on Python 3.14+ (#20768) 2025-10-14 11:17:45 -04:00
expr_unary_op.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
expr_yield.rs Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00
expr_yield_from.rs Move {AnyNodeRef, AstNode} to ruff_python_ast crate root (#8030) 2023-10-18 00:01:18 +00:00
mod.rs [ty] AST garbage collection (#18482) 2025-06-13 08:40:11 -04:00
operator.rs Split implicit concatenated strings before binary expressions (#7145) 2023-09-08 06:51:26 +00:00
parentheses.rs Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00