FrameLocalsProxy `|=` and `update()` mishandle errors raised while merging mappings
Ninguém assumiu esta issue ainda.
- 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:
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:
framelocalsproxy_inplace_or()returnsPy_NotImplementedwhenframelocalsproxy_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_CheckSlotResulterror.framelocalsproxy_update()replaces any failure fromframelocalsproxy_merge()with a genericTypeError, 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
Guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Direção de pesquisa
Comece em Objects/frameobject.c, em framelocalsproxy_inplace_or() e framelocalsproxy_update(), e depois execute o reprodutor mínimo com BadDict.keys() gerando RuntimeError. Está concluído quando ambas as operações propagarem a exceção original sem uma falha de slot em um build de debug, com cobertura de regressão para o comportamento relatado.
Escrita pelo modelo de indexação a partir do texto da issue.
Avaliação
- Stack de tecnologia
- c, python
- Domínio
- backend
- Tipo de issue
- Bug
- Dificuldade
- 3/5
- Tempo estimado
- 1-2 dias
- Status de atividade
- Estagnada
- Clareza
- Claramente especificada
- Facilidade para iniciantes
- 25/100