@@ -155,6 +155,51 @@ test_frozenset_add_in_capi(PyObject *self, PyObject *Py_UNUSED(obj))
155155 return NULL ;
156156}
157157
158+ static PyObject *
159+ test_set_contains_does_not_convert_unhashable_key (PyObject * self , PyObject * Py_UNUSED (obj ))
160+ {
161+ // See https://docs.python.org/3/c-api/set.html#c.PySet_Contains
162+ PyObject * outer_set = PySet_New (NULL );
163+
164+ PyObject * needle = PySet_New (NULL );
165+ if (needle == NULL ) {
166+ Py_DECREF (outer_set );
167+ return NULL ;
168+ }
169+
170+ PyObject * num = PyLong_FromLong (42 );
171+ if (num == NULL ) {
172+ Py_DECREF (outer_set );
173+ Py_DECREF (needle );
174+ return NULL ;
175+ }
176+
177+ if (PySet_Add (needle , num ) < 0 ) {
178+ Py_DECREF (outer_set );
179+ Py_DECREF (needle );
180+ Py_DECREF (num );
181+ return NULL ;
182+ }
183+
184+ int result = PySet_Contains (outer_set , needle );
185+
186+ Py_DECREF (num );
187+ Py_DECREF (needle );
188+ Py_DECREF (outer_set );
189+
190+ if (result < 0 ) {
191+ if (PyErr_ExceptionMatches (PyExc_TypeError )) {
192+ PyErr_Clear ();
193+ Py_RETURN_NONE ;
194+ }
195+ return NULL ;
196+ }
197+
198+ PyErr_SetString (PyExc_AssertionError ,
199+ "PySet_Contains should have raised TypeError for unhashable key" );
200+ return NULL ;
201+ }
202+
158203static PyMethodDef test_methods [] = {
159204 {"set_check" , set_check , METH_O },
160205 {"set_checkexact" , set_checkexact , METH_O },
@@ -174,6 +219,8 @@ static PyMethodDef test_methods[] = {
174219 {"set_clear" , set_clear , METH_O },
175220
176221 {"test_frozenset_add_in_capi" , test_frozenset_add_in_capi , METH_NOARGS },
222+ {"test_set_contains_does_not_convert_unhashable_key" ,
223+ test_set_contains_does_not_convert_unhashable_key , METH_NOARGS },
177224
178225 {NULL },
179226};
0 commit comments