MagicStack / MagicStack/uvloop
uvloop debug mode crashes on Python 3.14 during async generator finalization (traceback.walk_stack sees non-frame object)
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- Cython
- Estrellas
- 11.9k
- Forks
- 615
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Descripción
Overview
When an async generator is finalized under uvloop debug mode, uvloop’s extract_stack() calls traceback.walk_stack() on Python 3.14. The stack walker receives a non-frame object (e.g. _asyncio.TaskStepMethWrapper) and crashes (traceback.walk_stack_generator) or raises AttributeError. It's worth mentioning this does NOT happen with asyncio debug.
Environment
- uvloop 0.22.1
- Python 3.14.0
- macOS (Darwin 24.6.0, arm64)
- Debug mode enabled (PYTHONASYNCIODEBUG=1 or uvloop.run(debug=True))
Reproduction
- Set
PYTHONFAULTHANDLER=1 PYTHONASYNCIODEBUG=1as environment variables. - Run the following script with
python3 uvloop_python314_bug_repro.py
"""uvloop_python314_bug_repro.py
Minimal repro: uvloop 0.22.1 + Python 3.14 debug async-gen finalization crash.
This creates a nested async-generator chain and breaks on a "[DONE]" sentinel
without closing the inner iterator. Under uvloop debug, async generator
finalization schedules aclose() via call_soon_threadsafe, which calls
extract_stack(). On Python 3.14, traceback.walk_stack sees a non-frame object
(for example, _asyncio.TaskStepMethWrapper) and raises AttributeError. The
unpatched build can segfault in traceback.walk_stack_generator.
Run:
uv run --active --no-sync python uvloop_python314_bug_repro.py
"""
from __future__ import annotations
import asyncio
import gc
import os
import sys
from typing import AsyncIterator
import uvloop
os.environ["PYTHONASYNCIODEBUG"] = "1"
REPEATS = 1
class FakeResponse:
async def aiter_raw(self) -> AsyncIterator[bytes]:
payloads = [
b"data: one\n\n",
b"data: [DONE]\n\n",
b"data: trailing\n\n",
]
for payload in payloads:
yield payload
await asyncio.sleep(0)
async def aclose(self) -> None:
await asyncio.sleep(0)
class Decoder:
async def _aiter_chunks(self, source: AsyncIterator[bytes]) -> AsyncIterator[bytes]:
async for chunk in source:
yield chunk
async def aiter_bytes(self, source: AsyncIterator[bytes]) -> AsyncIterator[str]:
async for chunk in self._aiter_chunks(source):
for line in chunk.splitlines():
yield line.decode("utf-8")
class AsyncStream:
def __init__(self) -> None:
self.response = FakeResponse()
self.decoder = Decoder()
self._iterator: AsyncIterator[str] = self.__stream__()
def __aiter__(self) -> AsyncIterator[str]:
return self._iterator
async def __stream__(self) -> AsyncIterator[str]:
iterator = self._iter_events()
try:
async for event in iterator:
if event.endswith("[DONE]"):
break
yield event
finally:
await self.response.aclose()
async def _iter_events(self) -> AsyncIterator[str]:
async for event in self.decoder.aiter_bytes(self.response.aiter_raw()):
yield event
async def exercise_once() -> None:
stream = AsyncStream()
async for _ in stream:
pass
del stream
gc.collect()
await asyncio.sleep(0)
async def main() -> None:
loop = asyncio.get_running_loop()
print(f"Python: {sys.version}")
print(f"Loop: {type(loop).__name__}")
print(f"Debug: {loop.get_debug()}")
for _ in range(REPEATS):
await exercise_once()
await asyncio.sleep(0.1)
if __name__ == "__main__":
print(f"uvloop: {uvloop.__version__}")
uvloop.run(main(), debug=True)
Expected
Clean exit (debug stack capture should never crash the process).
Actual
Fatal Python error: Segmentation fault
File ".../traceback.py", line 393 in walk_stack_generator
...
Ideas
Python 3.14 changed frame introspection behavior. During async generator finalization, uvloop's debug stack capture (cbhandles.pyx:extract_stack) calls traceback.walk_stack(). The stack can contain non-frame objects like _asyncio.TaskStepMethWrapper which lack the f_back attribute that walk_stack_generator expects.
The crash path:
- Async generator breaks early (e.g., on
[DONE]sentinel) - GC finalizes the generator
- Finalizer schedules
aclose()viacall_soon_threadsafe - uvloop debug captures stack →
traceback.walk_stack()→ crash
This happens during async generator finalization; the finalizer schedules aclose() via call_soon_threadsafe, which triggers uvloop’s debug stack capture. A defensive guard before calling traceback.walk_stack (e.g. require a real frame, or swallow AttributeError/TypeError) avoids the crash.
Workaround
Explicitly close/break async generator chains before they go out of scope, or disable uvloop debug mode on Python 3.14.
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Comienza con cbhandles.pyx:extract_stack y el comportamiento de traceback.walk_stack en Python 3.14; después reproduce el fallo usando uvloop_python314_bug_repro.py con el modo de depuración habilitado. Sigue la finalización de los generadores asíncronos a través de call_soon_threadsafe y verifica que la reproducción termine correctamente sin un AttributeError ni un fallo de segmentació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
- Tranquilo
- Claridad
- Bastante claro
- Aptitud para principiantes
- 48/100