def load(name):
if name == "bad":
raise ValueError("cannot parse " + name)
if name == "missing":
raise FileNotFoundError(name)
return len(name)
# *** before: ***
# several failures at once had to be packed into one exception by hand,
# and the caller unpacked the list itself - try/except could not pick out
# the kind of error it knew how to handle
class MultiError(Exception):
def __init__(self, errors):
super().__init__(errors)
self.errors = errors
def load_all_old(names):
total, errors = 0, []
for name in names:
try:
total += load(name)
except Exception as error:
errors.append(error)
if errors:
raise MultiError(errors)
return total
try:
load_all_old(["alpha", "bad", "missing"])
except MultiError as group:
for error in group.errors:
print("old:", type(error).__name__, error)
# *** in version 3.11: ***
def load_all(names):
total, errors = 0, []
for name in names:
try:
total += load(name)
except Exception as error:
errors.append(error)
if errors:
raise ExceptionGroup("loading failed", errors)
return total
# except* takes ITS part of the group and leaves the rest to the others,
# so more than one branch may run for a single raise
try:
load_all(["alpha", "bad", "missing"])
except* ValueError as group:
print("values:", [str(e) for e in group.exceptions])
except* FileNotFoundError as group:
print("missing:", [str(e) for e in group.exceptions])
# a plain except catches the WHOLE group, not a part of it
try:
load_all(["bad"])
except ExceptionGroup as group:
print("whole:", group.message, len(group.exceptions))