Exceptions and the Python 2 except syntax

The comma syntax, string exceptions, sys.exc_info, and a mechanical rewrite to the Python 3 form that a compiler would catch.

The comma form

# Python 2
try:
    value = int(raw_input("number: "))
except ValueError, exc:              # binds the exception to exc
    print "bad number:", exc
except (IOError, OSError), exc:
    print "io problem:", exc
except Exception, exc:
    raise RuntimeError("wrapped: %s" % exc)
# Python 3 - identical semantics, different syntax
try:
    value = int(input("number: "))
except ValueError as exc:
    print("bad number:", exc)
except (IOError, OSError) as exc:
    print("io problem:", exc)
except Exception as exc:
    raise RuntimeError("wrapped: %s" % exc) from exc
  • except E, e became except E as e. The comma form is a syntax error in Python 3, so it is caught by the parser.
  • raise X, args became raise X(args) in 2.6 and was removed in 3.
  • raise E, e with three arguments (raise E, V, T) is gone entirely; use with_traceback instead.

String exceptions and exc_info

def parse(text):
    if not text:
        raise "empty input"          # legal in Python 2.5 and earlier only
    return int(text)

try:
    parse("")
except ValueError:
    print "caught"

try:
    1 / 0
except ZeroDivisionError:
    import sys
    kind, value, tb = sys.exc_info()
    print kind, value
    # Python 2 keeps the last exception alive until the next one or the end of the except block
    del tb
⚠️
In Python 2 an exception variable leaks out of the except block and holds a reference to the traceback, which keeps frames and their locals alive. Delete it explicitly (del exc) in long-running processes, or you will chase a memory leak that has nothing to do with your own objects.

A mechanical rewrite

Python 2Python 3
except E, e:except E as e:
raise E, vraise E(v)
raise E, v, tbraise E(v).with_traceback(tb)
raise "message"raise Exception("message")
StandardErrorException
sys.exc_typesys.exc_info()[0]
bare except: swallowing everythingStill legal, still a bug in most cases
# 2to3 handles the syntax; review every change it makes
2to3 -w -n app/ | tee 2to3.log

# then look for the patterns it deliberately leaves alone
grep -rn "except:" app/ | grep -v "except Exception"

The compiler catches the syntax changes. What it does not catch is semantics: an except that was silently swallowing everything still swallows everything, and a bare except: also caught KeyboardInterrupt and SystemExit.

FAQ

Did Python 2 allow multiple exception types on one line?
Yes, as a tuple: except (ValueError, TypeError), e:. The tuple must be parenthesised, since a tuple of types is the only accepted form.
Why does my exception handler hold memory after it finishes?
In Python 2 the traceback is kept alive by the exception variable and by sys.exc_info() until the next exception. Explicitly del the variable inside the handler if the process is long-lived.

Files, the codecs module and text vs binary I/O Migrating to Python 3

Last refreshed 2026-09-18.