with смотрит на результат __exit__: истинное значение означает «исключение обработано»,
и оно исчезает. Выполнение продолжается после блока, и checkout сообщает об успехе.
Без return __exit__ возвращает None: откат выполнен, а исключение летит дальше
к обработчику запроса. Он и должен решить, что ответить покупателю.
Эталонное решение
STOCK = {1: 5}
class Reservation:
def __init__(self, product_id, quantity):
self.product_id = product_id
self.quantity = quantity
def __enter__(self):
STOCK[self.product_id] -= self.quantity
return self
def __exit__(self, exc_type, exc, tb):
if exc_type is not None:
STOCK[self.product_id] += self.quantity
def charge(card):
if card == "0000":
raise ConnectionError("платёжный сервис недоступен")
def checkout(product_id, quantity, card):
with Reservation(product_id, quantity):
charge(card)
return "заказ оформлен"
try:
print(checkout(1, 2, "0000"))
except ConnectionError as e:
print("ошибка:", e)
print("на складе:", STOCK[1])