@@ -796,6 +796,19 @@ impl ExecutingFrame<'_> {
796
796
. top_value ( )
797
797
. downcast_ref :: < PyDict > ( )
798
798
. 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
+
799
812
dict. merge_object ( other, vm) ?;
800
813
Ok ( None )
801
814
}
@@ -1527,11 +1540,12 @@ impl ExecutingFrame<'_> {
1527
1540
let size = size as usize ;
1528
1541
let map_obj = vm. ctx . new_dict ( ) ;
1529
1542
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
+ }
1535
1549
if map_obj. contains_key ( & * key, vm) {
1536
1550
let key_repr = & key. repr ( vm) ?;
1537
1551
let msg = format ! (
@@ -1540,8 +1554,11 @@ impl ExecutingFrame<'_> {
1540
1554
) ;
1541
1555
return Err ( vm. new_type_error ( msg) ) ;
1542
1556
}
1557
+
1558
+ let value = obj. get_item ( & * key, vm) ?;
1543
1559
map_obj. set_item ( & * key, value, vm) ?;
1544
- }
1560
+ Ok ( ( ) )
1561
+ } ) ?;
1545
1562
}
1546
1563
1547
1564
self . push_value ( map_obj. into ( ) ) ;
@@ -1586,17 +1603,18 @@ impl ExecutingFrame<'_> {
1586
1603
1587
1604
fn collect_ex_args ( & mut self , vm : & VirtualMachine , has_kwargs : bool ) -> PyResult < FuncArgs > {
1588
1605
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 ( ) ;
1593
1607
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
1596
1612
. payload_if_subclass :: < PyStr > ( vm)
1597
1613
. 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
+ } ) ?;
1600
1618
kwargs
1601
1619
} else {
1602
1620
IndexMap :: new ( )
@@ -1608,6 +1626,28 @@ impl ExecutingFrame<'_> {
1608
1626
Ok ( FuncArgs { args, kwargs } )
1609
1627
}
1610
1628
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
+
1611
1651
#[ inline]
1612
1652
fn execute_call ( & mut self , args : FuncArgs , vm : & VirtualMachine ) -> FrameResult {
1613
1653
let func_ref = self . pop_value ( ) ;
0 commit comments