﻿"""
Zadanie 04: Łączenie wyjątków (raise from)

Zadanie uczy mapowania błędów niskopoziomowych na wysokopoziomowe (biznesowe)
przy pomocy "raise ... from e" w celu utworzenia łańcucha przyczynowości błędów
(Exception Chaining). Pokazuje również jak ukryć przyczynę przez "raise ... from None".
"""

class DatabaseError(Exception):
    """
    Autorski błąd wysokiego poziomu reprezentujący problem z warstwą bazy danych,
    bez wnikania w detale implementacji (np. pliki, sieć).
    """
    def __init__(self, message: str, tabela_lub_operacja: str = "Nieznana"):
        super().__init__(message)
        self.tabela_lub_operacja = tabela_lub_operacja


def zapisz_dane(symuluj_blad: bool = True):
    """
    Symulacja niskopoziomowego dostępu do danych, która może zawieść z przyczyn
    sprzętowych lub systemowych (np. brak miejsca na dysku, co rzuca OSError).
    """
    print("Próba fizycznego zapisu danych na dysku...")
    if symuluj_blad:
        # Symulujemy błąd systemu operacyjnego z konkretnym kodem Errno 28.
        raise OSError("[Errno 28] No space left on device")
    print("Zapisano poprawnie.")


def wykonaj_transakcje_z_historia():
    """
    Wykonuje transakcję i mapuje niskopoziomowe błędy na DatabaseError
    zachowując przy tym widoczną, pierwotną przyczynę błędu systemowego (from e).
    """
    try:
        zapisz_dane()
    except OSError as e:
        # Poniższa instrukcja podmienia typ rzucanego błędu na nasz,
        # dając mu bardziej zrozumiałą, biznesową nazwę.
        # Jednak dodanie słówka "from e" tworzy łańcuch i informuje Python,
        # że "DatabaseError został bezpośrednio wywołany przez OSError".
        raise DatabaseError("Nie udało się zapisać transakcji do bazy.", "Tabela_Uzytkownicy") from e


def wykonaj_transakcje_z_ukryciem():
    """
    Wykonuje transakcję, ale ukrywa detale implementacyjne błędu systemowego.
    Zamiast "from e" używa "from None", co odcina historię błędu.
    Przydatne w celach bezpieczeństwa (np. by nie ujawniać ścieżek do plików).
    """
    try:
        zapisz_dane()
    except OSError:
        raise DatabaseError("Wystąpił błąd ogólny podczas zapisu.", "Zapisz_Rekord") from None


if __name__ == "__main__":
    print(">>> 1. Demonstracja zachowania historii błędu (raise ... from e)")
    try:
        wykonaj_transakcje_z_historia()
    except Exception:
        import traceback
        # Zobaczymy tutaj komunikat: "The above exception was the direct cause of the following exception:"
        traceback.print_exc()

    print("\n" + "="*50 + "\n")

    print(">>> 2. Demonstracja ukrywania historii błędu (raise ... from None)")
    try:
        wykonaj_transakcje_z_ukryciem()
    except Exception:
        import traceback
        # Tutaj zobaczymy tylko DatabaseError, błąd OSError zostanie zablokowany
        # z widoku dla ostatecznego raportowania.
        traceback.print_exc()
