新版本的变更 / Python 3.11

import json

CONFIG = '{"port": "eighty"}'


# *** before: ***
# context could be added only by REPLACING the exception, which changed
# its type and buried the original one deeper in the chain
def read_port_old(text):
    data = json.loads(text)
    try:
        return int(data["port"])
    except ValueError as error:
        raise ValueError("bad config: " + str(error))


try:
    read_port_old(CONFIG)
except ValueError as error:
    print("old:", error)


# *** in version 3.11: ***
def read_port(text, source="config.json"):
    data = json.loads(text)
    try:
        return int(data["port"])
    except ValueError as error:
        error.add_note("while reading " + source) # the exception is kept as it
        #is
        error.add_note("value was: " + repr(data["port"]))
        raise


try:
    read_port(CONFIG)
except ValueError as error:
    print("new:", error)
    for note in error.__notes__:      # the traceback prints them by itself
        print("  note:", note)

# the type, the message and the place of the raise all stay untouched, so
# an except of the caller still sees the very same ValueError