@@ -1541,19 +1541,11 @@ impl ExecutingFrame<'_> {
15411541 let map_obj = vm. ctx . new_dict ( ) ;
15421542 for obj in self . pop_multiple ( size) {
15431543 // Use keys() method for all mapping objects to preserve order
1544- let Some ( keys_method) = vm. get_method ( obj. clone ( ) , vm. ctx . intern_str ( "keys" ) ) else {
1545- return Err (
1546- vm. new_type_error ( format ! ( "'{}' object is not a mapping" , obj. class( ) . name( ) ) )
1547- ) ;
1548- } ;
1549-
1550- let keys = keys_method?. call ( ( ) , vm) ?. get_iter ( vm) ?;
1551- while let PyIterReturn :: Return ( key) = keys. next ( vm) ? {
1544+ Self :: iterate_mapping_keys ( vm, & obj, "keyword argument" , |key| {
15521545 // Check for keyword argument restrictions
15531546 if key. downcast_ref :: < PyStr > ( ) . is_none ( ) {
15541547 return Err ( vm. new_type_error ( "keywords must be strings" . to_owned ( ) ) ) ;
15551548 }
1556-
15571549 if map_obj. contains_key ( & * key, vm) {
15581550 let key_repr = & key. repr ( vm) ?;
15591551 let msg = format ! (
@@ -1565,7 +1557,8 @@ impl ExecutingFrame<'_> {
15651557
15661558 let value = obj. get_item ( & * key, vm) ?;
15671559 map_obj. set_item ( & * key, value, vm) ?;
1568- }
1560+ Ok ( ( ) )
1561+ } ) ?;
15691562 }
15701563
15711564 self . push_value ( map_obj. into ( ) ) ;
@@ -1614,19 +1607,14 @@ impl ExecutingFrame<'_> {
16141607 let mut kwargs = IndexMap :: new ( ) ;
16151608
16161609 // Use keys() method for all mapping objects to preserve order
1617- let Some ( keys_method) = vm. get_method ( kw_obj. clone ( ) , vm. ctx . intern_str ( "keys" ) ) else {
1618- return Err ( vm. new_type_error ( "argument after ** must be a mapping" . to_owned ( ) ) ) ;
1619- } ;
1620-
1621- // Handle custom mapping objects like OrderedDict using keys() method
1622- let keys = keys_method?. call ( ( ) , vm) ?. get_iter ( vm) ?;
1623- while let PyIterReturn :: Return ( key) = keys. next ( vm) ? {
1610+ Self :: iterate_mapping_keys ( vm, & kw_obj, "argument after **" , |key| {
16241611 let key_str = key
16251612 . payload_if_subclass :: < PyStr > ( vm)
16261613 . ok_or_else ( || vm. new_type_error ( "keywords must be strings" . to_owned ( ) ) ) ?;
16271614 let value = kw_obj. get_item ( & * key, vm) ?;
16281615 kwargs. insert ( key_str. as_str ( ) . to_owned ( ) , value) ;
1629- }
1616+ Ok ( ( ) )
1617+ } ) ?;
16301618 kwargs
16311619 } else {
16321620 IndexMap :: new ( )
@@ -1638,6 +1626,28 @@ impl ExecutingFrame<'_> {
16381626 Ok ( FuncArgs { args, kwargs } )
16391627 }
16401628
1629+ /// Helper function to iterate over mapping keys using the keys() method.
1630+ /// This ensures proper order preservation for OrderedDict and other custom mappings.
1631+ fn iterate_mapping_keys < F > (
1632+ vm : & VirtualMachine ,
1633+ mapping : & PyObjectRef ,
1634+ error_prefix : & str ,
1635+ mut key_handler : F ,
1636+ ) -> PyResult < ( ) >
1637+ where
1638+ F : FnMut ( PyObjectRef ) -> PyResult < ( ) > ,
1639+ {
1640+ let Some ( keys_method) = vm. get_method ( mapping. clone ( ) , vm. ctx . intern_str ( "keys" ) ) else {
1641+ return Err ( vm. new_type_error ( format ! ( "{} must be a mapping" , error_prefix) ) ) ;
1642+ } ;
1643+
1644+ let keys = keys_method?. call ( ( ) , vm) ?. get_iter ( vm) ?;
1645+ while let PyIterReturn :: Return ( key) = keys. next ( vm) ? {
1646+ key_handler ( key) ?;
1647+ }
1648+ Ok ( ( ) )
1649+ }
1650+
16411651 #[ inline]
16421652 fn execute_call ( & mut self , args : FuncArgs , vm : & VirtualMachine ) -> FrameResult {
16431653 let func_ref = self . pop_value ( ) ;
0 commit comments