python / python/cpython

FrameLocalsProxy `|=` and `update()` mishandle errors raised while merging mappings

Abierto
#153,418 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

interpreter-core type-bug
Lenguaje dominante
Python
Estrellas
77.2k
Forks
35.9k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

Bug report

Bug description:

Bug description

FrameLocalsProxy does not correctly propagate exceptions raised while merging a dict subclass or another supported mapping-like object.

There are two related problems in Objects/frameobject.c:

  1. framelocalsproxy_inplace_or() returns Py_NotImplemented when framelocalsproxy_merge() fails. If the merge failed because Python code raised an exception, the slot returns a normal object while an exception is still set. In a pydebug build this triggers a fatal _Py_CheckSlotResult error.
  2. framelocalsproxy_update() replaces any failure from framelocalsproxy_merge() with a generic TypeError, masking the original exception.

This is a sub-issue of https://github.com/python/cpython/issues/146102 with gist details
A minimal reproducer is below.

import operator
import sys

class BadDict(dict):
    def keys(self):
        raise RuntimeError("keys() failed!")

def repro_update():
    locs = sys._getframe().f_locals
    print(type(locs))

    try:
        locs.update(BadDict())
    except TypeError as exc:
        print("BUG: update() masked RuntimeError as TypeError:", exc)
    except RuntimeError as exc:
        print("OK: update() propagated RuntimeError:", exc)

def repro_inplace_or():
    locs = sys._getframe().f_locals
    print(type(locs))

    try:
        operator.ior(locs, BadDict())
    except RuntimeError as exc:
        print("OK: |= propagated RuntimeError:", exc)
    except BaseException as exc:
        print("BUG: |= raised wrong exception:", type(exc).__name__, exc)
    else:
        print("BUG: |= swallowed RuntimeError")

repro_update()
repro_inplace_or()

operator.ior(locs, BadDict()) is used to exercise the same nb_inplace_or slot as locs |= BadDict().

Actual behavior

For .update(), the original RuntimeError from BadDict.keys() is replaced with a generic TypeError:

<class 'FrameLocalsProxy'>
BUG: update() masked RuntimeError as TypeError: update() argument must be dict or another FrameLocalsProxy

For |= on a pydebug build, the interpreter aborts because the slot reports success while an exception remains set:

<class 'FrameLocalsProxy'>
Fatal Python error: _Py_CheckSlotResult: Slot |= of type FrameLocalsProxy succeeded with an exception set
Python runtime state: initialized
Traceback (most recent call last):
  File "/tmp/repro_ior.py", line 6, in keys
    raise RuntimeError("keys() failed!")
RuntimeError: keys() failed!
Aborted (core dumped)

Expected behavior

Both FrameLocalsProxy.update() and FrameLocalsProxy.__ior__() should propagate the original exception raised while merging:

<class 'FrameLocalsProxy'>
OK: update() propagated RuntimeError: keys() failed!
<class 'FrameLocalsProxy'>
OK: |= propagated RuntimeError: keys() failed!

I will submit a PR to fix this

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-153421

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

Empieza en Objects/frameobject.c, en framelocalsproxy_inplace_or() y framelocalsproxy_update(), y luego ejecuta el reproductor mínimo con BadDict.keys() provocando RuntimeError. Se considera terminado cuando ambas operaciones propaguen la excepción original sin un fallo de slot en una compilación de depuración, con cobertura de regresión para el comportamiento notificado.

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

Evaluación

Stack tecnológico
c, python
Área
backend
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Estancado
Claridad
Bien especificado
Aptitud para principiantes
25/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.