python / python/cpython

Python 3.13: PyThreadState_SetAsyncExc might deliver the exception outside of the thread

Offen
#139,622 6 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

3.13 interpreter-core topic-C-API type-bug
Vorherrschende Sprache
Python
Sterne
77.2k
Forks
35.9k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

Bug report

Bug description:

I have the following naive demo that uses PyThreadState_SetAsyncExc to implement a time-limited interruptible context manager:

import contextlib
import ctypes
import sys
import threading
import time


class Interruptible:
    def __init__(self, seconds: float):
        self.seconds = seconds
        self.finished = False

    def __enter__(self):
        self._thread = threading.Thread(
            target=self._interrupt_in,
            args=(threading.get_ident(), self.seconds),
            daemon=True,
        )
        self._thread.start()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.finished = True

    def _interrupt_in(self, tid: int, seconds: float) -> None:
        time.sleep(self.seconds)
        print(f"Slept for {seconds}")
        if self.finished:
            print("Finished, not interrupting")
            return
        else:
            print("Not finished, interrupting")
            ctypes.pythonapi.PyThreadState_SetAsyncExc(
                ctypes.c_long(tid), ctypes.py_object(TimeoutError)
            )


def busy_loop():
    s = time.monotonic()
    while time.monotonic() - s < 0.5:
        pass


print(f"Python version: {sys.version}")

for i in range(100):
    try:
        with Interruptible(seconds=0.1):
            s = time.monotonic()
            while time.monotonic() - s < 0.5:
                pass
            # NOTE: If we replace above block with the equivalent busy_loop(), it works.
            # busy_loop()
    except TimeoutError:
        print(f"Iteration {i + 1}: ✓ TimeoutError caught")
    else:
        print(f"Iteration {i + 1}: ✗ TimeoutError NOT raised")
    time.sleep(0.01)  # Small delay between iterations

In Python 3.13, this might fail with something like:

❯ python /tmp/demo.py
Python version: 3.13.7 (main, Sep 29 2025, 18:42:10) [GCC 11.4.0]
Slept for 0.1
Not finished, interrupting
Iteration 1: ✓ TimeoutError caught
Slept for 0.1
Not finished, interrupting
Iteration 2: ✓ TimeoutError caught
Slept for 0.1
Not finished, interrupting
Iteration 3: ✓ TimeoutError caught
Slept for 0.1
Not finished, interrupting
Iteration 4: ✓ TimeoutError caught
Slept for 0.1
Not finished, interrupting
Traceback (most recent call last):
  File "/tmp/demo.py", line 50, in <module>
    while time.monotonic() - s < 0.5:
          ^^^^^^^^^^^^^^^^^^^^^^^^^^
TimeoutError

Meaning the TimeoutError is raised but not caught by the try/except block. A few other observations:

  1. If I wrap the "work" inside the context manager to a wrapper function busy_loop(), it's NOT reproducible.
  2. I tested Python 3.11.13, 3.12.11, 3.13.7, and 3.14.0rc3, it is only reproducible in Python 3.13.7.
CPython versions tested on:

3.13, 3.12, 3.14, 3.11

Operating systems tested on:

Linux

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne mit /tmp/demo.py und dem Einstiegspunkt PyThreadState_SetAsyncExc, reproduziere das Problem unter Python 3.13.7 und vergleiche Python 3.11, 3.12 und 3.14. Die Aufgabe ist erledigt, wenn du erklärst, warum TimeoutError den umgebenden Handler verlässt, und einen Regressionstest für das erwartete Verhalten bei der Ausnahmezustellung hinzufügst.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
backend
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
42/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.