Menedżer kontekstu to obiekt, który definiuje zachowanie wejścia i wyjścia z bloku with, zapewniając automatyczne zarządzanie zasobami (plikami, połączeniami itp.). Jest realizowany za pomocą metod __enter__ i __exit__ w klasie lub przez dekorator @contextmanager z modułu contextlib.
class FileManager: def __init__(self, nazwa_pliku, tryb): self.nazwa_pliku = nazwa_pliku self.tryb = tryb self.plik = None def __enter__(self): self.plik = open(self.nazwa_pliku, self.tryb) return self.plik def __exit__(self, typ_wyjątku, wartosc_wyjątku, traceback): if self.plik: self.plik.close() with FileManager('test.txt', 'w') as f: f.write('Cześć')
Niuanse: Ważne jest, aby poprawnie obsługiwać wyjątki w __exit__, zwracając True, aby stłumić błędy, i być ostrożnym z zasobami, aby nie pozostawić ich otwartych w przypadku błędów.
Pytanie: Jeśli w bloku with wystąpi wyjątek, czy Python wywołuje metodę __exit__? Jak przekazywane są parametry błędu?
Odpowiedź: Tak, metoda __exit__ jest zawsze wywoływana, nawet jeśli występuje wyjątek. Przekazywane są do niej typ wyjątku, jego wartość i ślad stosu. Jeśli __exit__ zwraca True, wyjątek jest stłumiony.
class Simple: def __enter__(self): print('Wejście') def __exit__(self, typ_wyjątku, wartosc_wyjątku, traceback): print('Wyjście') print(typ_wyjątku, wartosc_wyjątku) return True # błąd nie "wypływa" na zewnątrz with Simple(): raise ValueError('bum!')
Historia
__exit__ w własnym menedżerze pracy z deskryptorami plików — w przypadku wyjątku plik nie był zamykany, co prowadziło do wycieków deskryptorów plików i awarii podczas pracy z dużą ilością plików.Historia
True w __exit__ dla wszystkich wyjątków. To "tłumiło" błędy i prowadziło do niezauważalnych awarii i naruszeń integralności danych, ponieważ logika uważała, że transakcja zakończyła się sukcesem.Historia
Skorzystano z dekoratora @contextmanager z modułu contextlib, ale zapomniano o obsłudze wyjątków wewnątrz yield, przez co połączenie z gniazdem pozostawało otwarte przy awarii kodu i serwer "zawieszał się" z otwartymi portami.