Three PyIter_Next loops are missing PyErr_Occurred() checks after the loop terminates. PyIter_Next returns NULL for both StopIteration (normal) and errors. Without the check, iteration errors are silently swallowed.
The observe/unobserve functions in the same file (catom.cpp:227,264,294) correctly have if(PyErr_Occurred()) return 0; after their loops — the pattern was not propagated.
1. CAtom_setstate (catom.cpp:472-484)
Reproducer:
import gc; gc.disable()
import os
from atom.api import Atom, Int, Str
class EvilDict(dict):
def __iter__(self):
yield "x"
raise RuntimeError("iteration bomb")
b = Atom.__new__(type("MyAtom", (Atom,), {"x": Int(0)}))
try:
b.__setstate__(EvilDict({"x": 10}))
print("__setstate__ succeeded (BUG - error swallowed)")
except RuntimeError:
print("RuntimeError propagated (correct)")
os._exit(0)
On debug builds: Fatal Python error: _Py_CheckFunctionResult: a function returned a result with an exception set. On release builds: error silently swallowed.
2. validate_set (atomset.cpp:76-88)
Same pattern — iteration error during set validation silently ignored, returns partially validated set.
3. SortedMap_new (sortedmap.cpp:320-328)
Same pattern — iteration error during SortedMap construction silently ignored.
Also: CAtom_getstate missing PyErr_Clear (catom.cpp:403-407)
After PyObject_GetAttr fails with AttributeError (attribute not found), the continue proceeds without PyErr_Clear(), leaving the exception set. The comment says "it is not an error if the attribute is not present" but the exception is not actually cleared.
Also: CAtom_getstate returns NULL without exception (catom.cpp)
On certain error paths returns 0 (NULL) without ensuring an exception is set → SystemError: returned NULL without exception.
Fix for all PyIter_Next sites — add after each loop:
if( PyErr_Occurred() )
return 0;
Fix for getstate PyErr_Clear — add PyErr_Clear(); before continue at line 407.
Found by cext-review-toolkit.
Three
PyIter_Nextloops are missingPyErr_Occurred()checks after the loop terminates.PyIter_Nextreturns NULL for both StopIteration (normal) and errors. Without the check, iteration errors are silently swallowed.The
observe/unobservefunctions in the same file (catom.cpp:227,264,294) correctly haveif(PyErr_Occurred()) return 0;after their loops — the pattern was not propagated.1.
CAtom_setstate(catom.cpp:472-484)Reproducer:
On debug builds:
Fatal Python error: _Py_CheckFunctionResult: a function returned a result with an exception set. On release builds: error silently swallowed.2.
validate_set(atomset.cpp:76-88)Same pattern — iteration error during set validation silently ignored, returns partially validated set.
3.
SortedMap_new(sortedmap.cpp:320-328)Same pattern — iteration error during SortedMap construction silently ignored.
Also:
CAtom_getstatemissingPyErr_Clear(catom.cpp:403-407)After
PyObject_GetAttrfails withAttributeError(attribute not found), thecontinueproceeds withoutPyErr_Clear(), leaving the exception set. The comment says "it is not an error if the attribute is not present" but the exception is not actually cleared.Also:
CAtom_getstatereturns NULL without exception (catom.cpp)On certain error paths returns 0 (NULL) without ensuring an exception is set →
SystemError: returned NULL without exception.Fix for all PyIter_Next sites — add after each loop:
Fix for getstate PyErr_Clear — add
PyErr_Clear();beforecontinueat line 407.Found by cext-review-toolkit.