@@ -796,6 +796,19 @@ impl ExecutingFrame<'_> {
796796 . top_value ( )
797797 . downcast_ref :: < PyDict > ( )
798798 . expect ( "exact dict expected" ) ;
799+
800+ // For dictionary unpacking {**x}, x must be a mapping
801+ // Check if the object has the mapping protocol (keys method)
802+ if vm
803+ . get_method ( other. clone ( ) , vm. ctx . intern_str ( "keys" ) )
804+ . is_none ( )
805+ {
806+ return Err ( vm. new_type_error ( format ! (
807+ "'{}' object is not a mapping" ,
808+ other. class( ) . name( )
809+ ) ) ) ;
810+ }
811+
799812 dict. merge_object ( other, vm) ?;
800813 Ok ( None )
801814 }
@@ -1527,11 +1540,12 @@ impl ExecutingFrame<'_> {
15271540 let size = size as usize ;
15281541 let map_obj = vm. ctx . new_dict ( ) ;
15291542 for obj in self . pop_multiple ( size) {
1530- // Take all key-value pairs from the dict:
1531- let dict: PyDictRef = obj. downcast ( ) . map_err ( |obj| {
1532- vm. new_type_error ( format ! ( "'{}' object is not a mapping" , obj. class( ) . name( ) ) )
1533- } ) ?;
1534- for ( key, value) in dict {
1543+ // Use keys() method for all mapping objects to preserve order
1544+ Self :: iterate_mapping_keys ( vm, & obj, "keyword argument" , |key| {
1545+ // Check for keyword argument restrictions
1546+ if key. downcast_ref :: < PyStr > ( ) . is_none ( ) {
1547+ return Err ( vm. new_type_error ( "keywords must be strings" . to_owned ( ) ) ) ;
1548+ }
15351549 if map_obj. contains_key ( & * key, vm) {
15361550 let key_repr = & key. repr ( vm) ?;
15371551 let msg = format ! (
@@ -1540,8 +1554,11 @@ impl ExecutingFrame<'_> {
15401554 ) ;
15411555 return Err ( vm. new_type_error ( msg) ) ;
15421556 }
1557+
1558+ let value = obj. get_item ( & * key, vm) ?;
15431559 map_obj. set_item ( & * key, value, vm) ?;
1544- }
1560+ Ok ( ( ) )
1561+ } ) ?;
15451562 }
15461563
15471564 self . push_value ( map_obj. into ( ) ) ;
@@ -1586,17 +1603,18 @@ impl ExecutingFrame<'_> {
15861603
15871604 fn collect_ex_args ( & mut self , vm : & VirtualMachine , has_kwargs : bool ) -> PyResult < FuncArgs > {
15881605 let kwargs = if has_kwargs {
1589- let kw_dict: PyDictRef = self . pop_value ( ) . downcast ( ) . map_err ( |_| {
1590- // TODO: check collections.abc.Mapping
1591- vm. new_type_error ( "Kwargs must be a dict." . to_owned ( ) )
1592- } ) ?;
1606+ let kw_obj = self . pop_value ( ) ;
15931607 let mut kwargs = IndexMap :: new ( ) ;
1594- for ( key, value) in kw_dict. into_iter ( ) {
1595- let key = key
1608+
1609+ // Use keys() method for all mapping objects to preserve order
1610+ Self :: iterate_mapping_keys ( vm, & kw_obj, "argument after **" , |key| {
1611+ let key_str = key
15961612 . payload_if_subclass :: < PyStr > ( vm)
15971613 . ok_or_else ( || vm. new_type_error ( "keywords must be strings" . to_owned ( ) ) ) ?;
1598- kwargs. insert ( key. as_str ( ) . to_owned ( ) , value) ;
1599- }
1614+ let value = kw_obj. get_item ( & * key, vm) ?;
1615+ kwargs. insert ( key_str. as_str ( ) . to_owned ( ) , value) ;
1616+ Ok ( ( ) )
1617+ } ) ?;
16001618 kwargs
16011619 } else {
16021620 IndexMap :: new ( )
@@ -1608,6 +1626,28 @@ impl ExecutingFrame<'_> {
16081626 Ok ( FuncArgs { args, kwargs } )
16091627 }
16101628
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+
16111651 #[ inline]
16121652 fn execute_call ( & mut self , args : FuncArgs , vm : & VirtualMachine ) -> FrameResult {
16131653 let func_ref = self . pop_value ( ) ;
0 commit comments