@@ -93,7 +93,7 @@ impl PySetInner {
93
93
}
94
94
95
95
fn contains ( & self , needle : & PyObjectRef , vm : & VirtualMachine ) -> PyResult < bool > {
96
- self . content . contains ( vm, needle)
96
+ self . retry_op_with_frozenset ( needle , vm , |needle , vm| self . content . contains ( vm, needle) )
97
97
}
98
98
99
99
fn compare (
@@ -213,11 +213,11 @@ impl PySetInner {
213
213
}
214
214
215
215
fn remove ( & self , item : PyObjectRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
216
- self . content . delete ( vm, item)
216
+ self . retry_op_with_frozenset ( & item , vm , |item , vm| self . content . delete ( vm, item. clone ( ) ) )
217
217
}
218
218
219
219
fn discard ( & self , item : & PyObjectRef , vm : & VirtualMachine ) -> PyResult < bool > {
220
- self . content . delete_if_exists ( vm, item)
220
+ self . retry_op_with_frozenset ( item , vm , |item , vm| self . content . delete_if_exists ( vm, item) )
221
221
}
222
222
223
223
fn clear ( & self ) {
@@ -285,6 +285,42 @@ impl PySetInner {
285
285
fn hash ( & self , vm : & VirtualMachine ) -> PyResult < PyHash > {
286
286
crate :: utils:: hash_iter_unordered ( self . content . keys ( ) . iter ( ) , vm)
287
287
}
288
+
289
+ // Run operation, on failure, if item is a set/set subclass, convert it
290
+ // into a frozenset and try the operation again. Propagates original error
291
+ // on failure to convert and restores item in KeyError on failure (remove).
292
+ fn retry_op_with_frozenset < T , F > (
293
+ & self ,
294
+ item : & PyObjectRef ,
295
+ vm : & VirtualMachine ,
296
+ op : F ,
297
+ ) -> PyResult < T >
298
+ where
299
+ F : Fn ( & PyObjectRef , & VirtualMachine ) -> PyResult < T > ,
300
+ {
301
+ op ( item, vm) . or_else ( |original_err| {
302
+ item. payload_if_subclass :: < PySet > ( vm)
303
+ // Keep original error around.
304
+ . ok_or ( original_err)
305
+ . and_then ( |set| {
306
+ op (
307
+ & PyFrozenSet {
308
+ inner : set. inner . copy ( ) ,
309
+ }
310
+ . into_object ( vm) ,
311
+ vm,
312
+ )
313
+ // If operation raised KeyError, report original set (set.remove)
314
+ . map_err ( |op_err| {
315
+ if op_err. isinstance ( & vm. ctx . exceptions . key_error ) {
316
+ vm. new_key_error ( item. clone ( ) )
317
+ } else {
318
+ op_err
319
+ }
320
+ } )
321
+ } )
322
+ } )
323
+ }
288
324
}
289
325
290
326
fn extract_set ( obj : & PyObjectRef ) -> Option < & PySetInner > {
0 commit comments