Gdy asynchroniczny strumień się kończy, a run loop zawiódł: zachowanie wyjątku na granicy

Opublikowano: 10 września 2026

Objaw awarii: stream_events kończy się bez ujawnienia wyjątku run loopa

Zakończenie asynchronicznego strumienia zdarzeń nie jest samo w sobie dowodem, że bazowa praca się powiodła. W opisanym tu przypadku awarii metoda odpowiedzi modelu rzuca RuntimeError zanim zaczyna zwracać zdarzenia strumienia. W zweryfikowanej rewizji konsumpcja stream_events rzuca RuntimeError z komunikatem run loop boom.

Ta awaria musi pozostać widoczna dla wywołującego. W przeciwnym razie kod czekający na granicę strumienia może pomylić zakończenie z udaną pracą i rozpocząć akcję zależną bez wcześniejszego sprawdzenia wyniku run loopa.

Granica wyjątków: definicja run_loop_exception i stany zwracające None

Patch dodaje właściwość run_loop_exception. Gdy zadanie run loopa w tle się zakończyło i nie zostało anulowane, właściwość zwraca wyjątek rzucony przez to zadanie.

Właściwość zwraca None w trzech stanach: gdy run loop zakończył się bez błędu, gdy jeszcze się nie zakończył oraz gdy został anulowany. Stany te odróżniają zaobserwowaną awarię run loopa od zadania niedokończonego lub anulowanego i od czystego zakończenia.

Granica jest więc jawnie zdefiniowana: wywołujący może inspectować osiadły wyjątek run loopa zamiast traktować zakończenie strumienia jako kompletny wynik operacji.

Wyścig: ponowne sprawdzenie po osiadnięciu run loopa

W zweryfikowanej rewizji patch sprawdza ponownie błędy także po pełnym osiadnięciu run loopa. Obsługuje to zaobserwowany przypadek, w którym wyjątek „wyprzedza" sentinel strumienia: granica strumienia może zostać osiągnięta, zanim finalny stan wyjątku zadania w tle będzie dostępny, ale ścieżka zapisanego wyjątku może ujawnić awarię po zakończeniu osiadania.

To zachowuje relację przyczynową między zadaniem run loopa a konsumentem strumienia. Konsument nie musi wnioskować o sukcesie z sentinela; może zaobserwować wyjątek zarejestrowany przez osiadłe zadanie.

Oś awarii: model regresji rzuca RuntimeError przed zwróceniem zdarzeń

Przypadek regresji używa modelu, którego metody odpowiedzi rzucają RuntimeError przed zwróceniem zdarzeń strumienia. Oś awarii wygląda tak:

  1. Run loop wywołuje właściwą metodę odpowiedzi modelu.
  2. Metoda odpowiedzi rzuca RuntimeError z komunikatem run loop boom przed wyprodukowaniem zdarzeń strumienia.
  3. Zadanie run loopa osiada z tym wyjątkiem.
  4. Konsument strumienia obserwuje awarię, gdy konsumuje stream_events.
  5. Po konsumpcji nieudanego strumienia result.run_loop_exception jest nie-nullowy i zawiera ten sam RuntimeError.

Zapisany wyjątek nie zastępuje awarii strumienia. Jest to jawna granica inspekcji, która pozostaje dostępna po osiadnięciu zadania w tle.

Kształt regresji: asercje awarii strumienia i zapisanego run_loop_exception

Regresja musi pokrywać obie obserwowalne strony granicy. Po pierwsze, konsumpcja stream_events w przypadku awarii musi rzucać RuntimeError z komunikatem run loop boom. Po drugie, po konsumpcji nieudanego strumienia result.run_loop_exception musi być nie-nullowy i zawierać ten sam RuntimeError.

Trzymanie tych asercji razem chroni łańcuch przyczynowy: strumień raportuje awarię, a wynik zachowuje osiadły wyjątek do inspekcji. Test sprawdzający wyłącznie konsumpcję strumienia nie zweryfikowałby granicy zapisanego wyjątku; test sprawdzający wyłącznie właściwość nie zweryfikowałby awarii widocznej dla konsumenta.

Oś sukcesu: czysty strumień zostawia run_loop_exception jako None

Przypadek sukcesu asertuje, że run_loop_exception jest None po zakończeniu stream_events bez błędu. To komplementarny niezmiennik przypadku awarii: czysto zakończony strumień nie wypełnia zapisanego wyjątku.

Razem przypadki awarii i sukcesu odróżniają zakończenie z wyjątkiem od czystego zakończenia. Zachowują też udokumentowane zachowanie None dla run loopa, który zakończył się bez błędu.

Weryfikacja: 11-testowa suitka cancel-streaming na CPython 3.13.13

W zweryfikowanej rewizji upstreamu suitka tests/test_cancel_streaming.py zebrała 11 testów i wszystkie 11 przeszło na CPython 3.13.13.

To ograniczona weryfikacja suitki testów cancel-streaming w tej rewizji. Nie ustala zachowania poza zweryfikowaną rewizją ani nie formułuje uniwersalnej tezy o protokołach strumieniowania asynchronicznego.

Wniosek operacyjny: zinspectuj osiadły wyjątek przed startem pracy zależnej

Dla tego API traktuj zakończenie asynchronicznego strumienia zdarzeń jako niewystarczający dowód udanej pracy. Wyjątek run loopa musi pozostać obserwowalny po zakończeniu strumienia, a wywołujący powinni zachować jawną granicę inspekcji wyjątku przed uruchomieniem pracy zależnej.

Kształt focused regression jest równie ważny: użyj metody odpowiedzi rzucającej RuntimeError przed zwróceniem zdarzeń, asertuj awarię zaobserwowaną przy konsumpcji stream_events, asertuj że run_loop_exception zachowuje ten sam wyjątek, i osobno asertuj, że czysty strumień zostawia właściwość jako None.

Komentarze (0)

Brak komentarzy.

Dodaj komentarz

Komentarze są publikowane po moderacji. Adres e-mail nie będzie widoczny publicznie.