﻿import time

class Timer:
    """
    Narzędzie implementujące protokół menedżera kontekstu w Pythonie.
    Pozwala na precyzyjny, automatyczny pomiar czasu z użyciem bloku with.
    """
    def __init__(self):
        self.start_time = 0.0

    def __enter__(self):
        """
        Uruchamiana na wejściu do bloku 'with'.
        Zapisuje stan licznika czasu w momencie startu wykonywanych operacji.
        Zwraca referencję do obiektu (często przydatne przy aliasach np. 'with Timer() as t').
        """
        # perf_counter zwraca bardzo dokładny czas (idealny do pomiarów wydajności)
        self.start_time = time.perf_counter()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        Zawsze wywoływana przy opuszczaniu bloku 'with', niezależnie 
        od poprawności wykonania lub napotkania błędu krytycznego (wyjątku).
        Parametry exc_ przechwytują ewentualny błąd i pozwalają na własną reakcję.
        """
        end_time = time.perf_counter()
        czas_trwania = end_time - self.start_time
        
        # Wyświetlamy precyzyjnie sformatowany log
        print(f"[TIMER] Czas wykonania operacji: {czas_trwania:.4f} sekundy.")
        
        # Opcjonalnie moglibyśmy analizować exc_type, by wyciszyć lub zalogować błąd:
        if exc_type is not None:
            print(f"[TIMER-ERROR] Wystąpił błąd klasy {exc_type.__name__}: {exc_val}")
            
        # Gdy __exit__ zwróci False lub None, jeśli był wyjątek,
        # to po wyjściu z 'with' zostanie on naturalnie "wyrzucony" do poziomu wyżej.
        # Gdyby zwrócić tu True, system uznałby błąd za obłużony i wyciszył jego przebieg.
        return False

if __name__ == "__main__":
    print("--- Scenariusz 1: Oczekiwanie (time.sleep) ---")
    with Timer():
        print("Oczekiwanie 1.5 sekundy...")
        time.sleep(1.5)
        print("Gotowe!")
        
    print("\n--- Scenariusz 2: Operacja mocno obciążająca procesor ---")
    with Timer() as t: # przypisujemy menedżer do 't' by zademonstrować, że można tak robić
        print("Sumuję pętlą do 10 milionów, proszę o cierpliwość...")
        suma = 0
        for i in range(10_000_000):
            suma += i
            
    print("\n--- Scenariusz 3: Bezpieczeństwo i gwarantowany pomiar pomimo awarii ---")
    try:
        with Timer():
            print("Zaraz wykonam dzielenie przez zero!")
            time.sleep(0.3)
            # Rzucamy testowo błąd, a mimo to Timer zatrzyma czas w __exit__!
            wynik = 1 / 0 
    except ZeroDivisionError as e:
        print(f"System złapał wyjątek poza kontekstem with: {e}")

