What does PEP 758 change about `except` clauses in Python 3.14?
answer
- A comma that used to mean something else
- Python 2's ghost finally exorcised
- Parentheses became optional in one place
- One keyword still forces the brackets
- PEP 758, shipped in 3.14
basics
~10 sPython 3.14 allows several exception types in an except or except* clause without parentheses, as in except ValueError, TypeError:. Parentheses are still required when the clause also binds a name with as.
solid answer
~40 sBefore 3.14, a clause naming more than one exception type had to parenthesize them: `except (ValueError, TypeError):`. PEP 758 relaxes the grammar so the bare comma-separated list is accepted, for both `except` and `except*`. The one restriction is the `as` clause: `except ValueError, TypeError as exc:` is still a `SyntaxError`, with the message *multiple exception types must be parenthesized when using 'as'*, because that spelling is exactly the Python 2 form where the second name bound the exception instance. The change is syntax only -- it does not alter which exceptions are caught, or in what order clauses are tried. It is also 3.14-only, so a library supporting older interpreters cannot use it: the parenthesized form still parses everywhere and remains the safe default in shared code.
code
python · 9 linestry:
int("340x")
except ValueError, TypeError:
print("unparenthesized type list works on 3.14")
try:
int("340x")
except (ValueError, TypeError) as exc:
print("parentheses are required with as:", type(exc).__name__)go deeper
Just recall the shape: on Python 3.14 you may list exception types after except without parentheses. The parenthesized form works everywhere, so keep writing that until you know your interpreter floor.
Explain the history and the restriction: the comma used to be Python 2's binding syntax, PEP 758 reclaims it in 3.14, and as still forces parentheses because that exact spelling was the ambiguous one.
Show the judgment about adoption -- syntax-level features cannot be feature-detected at runtime, so a SyntaxError at import is the failure mode, and a library's supported-version floor decides the answer before taste does.
Own the version-floor policy: when the organisation moves its minimum interpreter, which new syntax becomes allowed, and how that is enforced consistently rather than negotiated file by file.
## The old rule and where it came from In Python 2, `except ValueError, e:` meant "catch `ValueError` and bind the instance to `e`" -- the comma was the binding syntax, not a list separator. To catch two types you had to write `except (ValueError, TypeError):`, and the parentheses were what told the parser you meant a tuple of types rather than a type and a target name. Python 3 replaced the binding comma with `as` and rejected the old spelling outright, but it kept the requirement that multiple types be parenthesized. That was a compatibility measure: for years, `except ValueError, e:` in a Python 3 file was almost always ported-from-2 code, and a clear `SyntaxError` was far kinder than silently catching two types, one of which the author had meant as a variable name. ## What PEP 758 does By 3.14 that transition is long over, so PEP 758 -- *Allow except and except\* expressions without parentheses* -- relaxes the grammar. Both of these now parse: ```python except ValueError, TypeError: except* ConnectionError, TimeoutError: ``` The change is purely syntactic. The clause still matches by `issubclass`, clauses are still tried top to bottom, and the runtime semantics are exactly those of the parenthesized form. ## The one restriction Adding `as` brings the ambiguity back, so the parentheses remain mandatory there: ```python except (ValueError, TypeError) as exc: # fine except ValueError, TypeError as exc: # SyntaxError ``` The compiler's message is explicit: *multiple exception types must be parenthesized when using 'as'*. The reasoning is that `except A, B as e:` could plausibly be read as the Python 2 form with an extra type, and CPython would rather refuse than guess. ## What it is not Three things PEP 758 does *not* do, and each has been a wrong answer in an interview: * It does not let you write `except ValueError | TypeError:`. The union expression compiles, but at runtime the interpreter reports *catching classes that do not inherit from BaseException is not allowed*, because the `X | Y` union object is not an exception class. * It does not change matching, ordering, or anything about which handler wins. * It does not affect `except*` semantics beyond the parentheses -- exception-group matching is unchanged. ## Should you use it? Only in code that targets 3.14 and above, and even then it is a taste call. The parenthesized form parses on every Python 3, so any library with a lower floor keeps it -- and mixed styles inside one codebase are worse than either style consistently applied. Where it does help is long clauses in application code that has already pinned a modern interpreter: ```python try: rate = load_cached_rate(currency) except KeyError, ValueError, TimeoutError: rate = fetch_rate(currency) ``` The comma form reads a little more like the rest of the language, which is the whole justification the PEP claims; nobody argues it changes anything material. ## Where this shows up in an interview Rarely as a question in its own right, and no interviewer should hold it against a candidate who has not tracked 3.14's syntax additions. It appears more often as a detail inside a broader conversation about handler syntax -- someone reads a 3.14 snippet aloud and wants to know whether you noticed that the parentheses are gone, or asks what would happen if you added `as exc` to it. The useful thing to carry is the pair of facts: unparenthesized lists are legal from 3.14, and `as` still forces parentheses. ## Version summary `except (A, B):` -- every Python 3. `except A, B:` -- 3.14 and later only; a `SyntaxError` on 3.13 and earlier. `except A, B as e:` -- a `SyntaxError` on every version including 3.14. `except*` gained the same relaxation in 3.14, having arrived, parenthesized, in 3.11 with PEP 654.
- Why was `except A, B:` a `SyntaxError` for the whole of Python 3 until now?Because in Python 2 the comma bound the exception instance: `except ValueError, e:` caught one type and named it `e`. Accepting the same spelling as a two-type list in early Python 3 would have silently changed the meaning of ported code. PEP 758 only became safe once that migration was long finished, which is why it landed in 3.14 rather than in 3.0.
- Does the relaxation apply to `except*` as well?Yes. PEP 758 covers both clause forms, so `except* ConnectionError, TimeoutError:` parses on 3.14. It is syntax only -- exception-group matching, sub-group splitting and the rule that `except*` cannot be mixed with plain `except` in one `try` are all unchanged.
- Would you adopt this style in a library?Not unless the library's minimum supported version is 3.14, because the code simply will not parse on anything older -- and a `SyntaxError` at import time is the least forgiving kind of incompatibility. In an application pinned to a modern interpreter it is a defensible style choice, provided the codebase applies it consistently rather than mixing both forms.
saying these in an interview costs you the question
- Thinks the comma binds the exception like Python 2 did
- Writes `except A, B as e:` and expects it to compile
- Believes PEP 758 changed which exceptions get caught
- Claims the unparenthesized form works on 3.12
- Says `except A | B:` is the new supported spelling
- Uses the 3.14 syntax in a library supporting older versions