Repro
try:
pass
except OSError, ValueError:
pass
$ python3.14 -c "import ast; ast.parse(open('repro.py').read())"
# no error — parses successfully
Confirmed via disassembly that it compiles to the SAME bytecode as the correct except (OSError, ValueError): form (BUILD_TUPLE + CHECK_EXC_MATCH) — not the Python-2 except Type, name: binding semantics, which would be a different and even more dangerous silent misinterpretation. So functionally the 3.14 parser is currently treating an omitted-parens exception-type list as equivalent to a parenthesized tuple.
$ python3.12 -c "import ast; ast.parse(open('repro.py').read())"
File "<unknown>", line 3
except OSError, ValueError:
^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized
3.9, 3.12, and 3.13 (tested on this machine) all correctly raise SyntaxError: multiple exception types must be parenthesized (added by bpo-32892 / the PEG parser's dedicated error message for this exact Python-2 leftover). Only 3.14 accepts it.
Reproduced across independent 3.14 builds
- Homebrew
python@3.14 3.14.6 (macOS arm64)
uv's managed cpython-3.14.0-macos-aarch64-none (python-build-standalone)
- asdf-installed 3.14.4
- Official
python:3.14-slim Docker Hub image, 3.14.7 (python:3.14-slim, pulled fresh, Linux/GCC build) — rules out anything specific to this machine or macOS
All four reproduce identically.
Why this matters
This isn't purely cosmetic: ruff format (astral-sh/ruff#28928, filed as a companion issue) appears to exploit this leniency when targeting py314, actively rewriting valid except (A, B): into the unparenthesized form — producing code that is a SyntaxError on every Python version except 3.14. A tool or codebase relying on 3.14's leniency here will silently produce code broken everywhere else.
Expected
except A, B: (without parentheses, 2+ types) should continue to raise SyntaxError: multiple exception types must be parenthesized on 3.14 as it does on every prior Python 3 version, unless this is an intentional, documented grammar change for 3.14 (in which case apologies for the noise, and a pointer to the relevant PEP/what's-new entry would be very welcome — I could not find one).
Repro
Confirmed via disassembly that it compiles to the SAME bytecode as the correct
except (OSError, ValueError):form (BUILD_TUPLE + CHECK_EXC_MATCH) — not the Python-2except Type, name:binding semantics, which would be a different and even more dangerous silent misinterpretation. So functionally the 3.14 parser is currently treating an omitted-parens exception-type list as equivalent to a parenthesized tuple.3.9, 3.12, and 3.13 (tested on this machine) all correctly raise
SyntaxError: multiple exception types must be parenthesized(added by bpo-32892 / the PEG parser's dedicated error message for this exact Python-2 leftover). Only 3.14 accepts it.Reproduced across independent 3.14 builds
python@3.143.14.6 (macOS arm64)uv's managedcpython-3.14.0-macos-aarch64-none(python-build-standalone)python:3.14-slimDocker Hub image, 3.14.7 (python:3.14-slim, pulled fresh, Linux/GCC build) — rules out anything specific to this machine or macOSAll four reproduce identically.
Why this matters
This isn't purely cosmetic:
ruff format(astral-sh/ruff#28928, filed as a companion issue) appears to exploit this leniency when targeting py314, actively rewriting validexcept (A, B):into the unparenthesized form — producing code that is a SyntaxError on every Python version except 3.14. A tool or codebase relying on 3.14's leniency here will silently produce code broken everywhere else.Expected
except A, B:(without parentheses, 2+ types) should continue to raiseSyntaxError: multiple exception types must be parenthesizedon 3.14 as it does on every prior Python 3 version, unless this is an intentional, documented grammar change for 3.14 (in which case apologies for the noise, and a pointer to the relevant PEP/what's-new entry would be very welcome — I could not find one).