python / python/cpython

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

Abierto
#139,622 6 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

3.13 interpreter-core topic-C-API type-bug
Lenguaje dominante
Python
Estrellas
77.2k
Forks
36k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

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

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza con /tmp/demo.py y el punto de entrada PyThreadState_SetAsyncExc, reproduce el problema en Python 3.13.7 y compara Python 3.11, 3.12 y 3.14. La tarea estará terminada cuando expliques por qué TimeoutError escapa del gestor circundante y añadas una prueba de regresión para el comportamiento esperado de entrega de la excepción.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
backend
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
42/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.