diff --git a/atom/src/atomdict.cpp b/atom/src/atomdict.cpp index df754119..0c87309f 100644 --- a/atom/src/atomdict.cpp +++ b/atom/src/atomdict.cpp @@ -155,17 +155,21 @@ PyObject* AtomDict_setdefault( AtomDict* self, PyObject* args ) cppy::ptr key_ptr( validate_key( self, key ) ); if ( !key_ptr ) return 0; - PyObject* value = PyDict_GetItem( pyobject_cast( self ), key_ptr.get() ); + PyObject* value = PyDict_GetItemWithError( pyobject_cast( self ), key_ptr.get() ); if( value ) { return cppy::incref( value ); } + if( PyErr_Occurred() ) + { + return 0; + } if( AtomDict_ass_subscript( self, key_ptr.get(), dfv ) < 0 ) { return 0; } // Get the dictionary from the dict itself in case it was coerced. - return cppy::incref( PyDict_GetItem( pyobject_cast( self ), key_ptr.get() ) ); + return cppy::xincref( PyDict_GetItemWithError( pyobject_cast( self ), key_ptr.get() ) ); } @@ -310,7 +314,7 @@ static PyObject* DefaultAtomDict_missing( DefaultAtomDict* self, PyObject* key ) return 0; } // Get the dictionary from the dict itself in case it was coerced. - value_ptr = cppy::incref( PyDict_GetItem( pyobject_cast( self ), key_ptr.get() ) ); + value_ptr = cppy::xincref( PyDict_GetItem( pyobject_cast( self ), key_ptr.get() ) ); } return value_ptr.release(); } diff --git a/atom/src/atomlist.cpp b/atom/src/atomlist.cpp index 030ef923..6467ced5 100644 --- a/atom/src/atomlist.cpp +++ b/atom/src/atomlist.cpp @@ -727,7 +727,11 @@ class AtomCListHandler : public AtomListHandler // if the superclass call succeeds, then this is safe. Py_ssize_t i = -1; if( nargs == 1 ) + { i = PyLong_AsSsize_t( args[0] ); + if( PyErr_Occurred() ) + return 0; + } if( i < 0 ) i += size; cppy::ptr index( PyLong_FromSsize_t( i ) ); diff --git a/test_requirements.txt b/test_requirements.txt index d647248c..c4bfc755 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,7 +1,7 @@ mypy pyrefly ty -pytest +pytest < 9.2 pytest-cov pytest-benchmark psutil diff --git a/tests/test_atomdefaultdict.py b/tests/test_atomdefaultdict.py index fa5210b3..08130b74 100644 --- a/tests/test_atomdefaultdict.py +++ b/tests/test_atomdefaultdict.py @@ -191,6 +191,18 @@ class A(Atom): assert content is a.d[1] +def test_missing_raises_key_hash_error(): + class BadKey: + def __hash__(self): + raise RuntimeError("hash failed") + + class A(Atom): + d = DefaultDict(missing=int) + + with pytest.raises(RuntimeError, match="hash failed"): + A().d[BadKey()] + + def test_update(default_atom_dict): """Test update a dict.""" default_atom_dict.untyped.update({"": 1}) diff --git a/tests/test_atomlist.py b/tests/test_atomlist.py index 34e81bbc..ddfc45e5 100644 --- a/tests/test_atomlist.py +++ b/tests/test_atomlist.py @@ -528,6 +528,16 @@ def test_container_pop(container_model, kind): assert change["item"] == 0 +def test_container_pop_index_object_raises_typeerror(): + model = ContainerModel() + model.untyped = [10, 20, 30] + model.typed = [10, 20, 30] + with pytest.raises(TypeError): + model.untyped.pop(Indexer(0)) + with pytest.raises(TypeError): + model.typed.pop(Indexer(0)) + + @pytest.mark.parametrize("kind", ("untyped", "typed")) def test_container_reverse(container_model, kind): mlist = getattr(container_model, kind)