python / python/cpython

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

Aberta
#139,622 6 comentários 0 reações 0 responsáveis Ver no GitHub

Ninguém assumiu esta issue ainda.

3.13 interpreter-core topic-C-API type-bug
Linguagem predominante
Python
Estrelas
77.2k
Forks
36k
Métricas de merge de PRs
Métricas de PR pendentes

Descrição

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

Guia de contribuição

Abrir o guia de contribuição

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Direção de pesquisa

Comece com /tmp/demo.py e o ponto de entrada PyThreadState_SetAsyncExc, reproduza o problema no Python 3.13.7 e compare com Python 3.11, 3.12 e 3.14. O trabalho estará concluído quando você explicar por que TimeoutError escapa do handler ao redor e adicionar um teste de regressão para o comportamento esperado de entrega da exceção.

Escrita pelo modelo de indexação a partir do texto da issue.

Avaliação

Stack de tecnologia
python
Domínio
backend
Tipo de issue
Bug
Dificuldade
4/5
Tempo estimado
3-5 dias
Status de atividade
Estagnada
Clareza
Razoavelmente clara
Facilidade para iniciantes
42/100

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.