@@ -42,15 +42,74 @@ impl PyNamespace {
4242 ) ;
4343 result. into_pytuple ( vm)
4444 }
45+
46+ #[ pymethod]
47+ fn __replace__ ( zelf : PyObjectRef , args : FuncArgs , vm : & VirtualMachine ) -> PyResult {
48+ if !args. args . is_empty ( ) {
49+ return Err ( vm. new_type_error ( "__replace__() takes no positional arguments" ) ) ;
50+ }
51+
52+ // Create a new instance of the same type
53+ let cls: PyObjectRef = zelf. class ( ) . to_owned ( ) . into ( ) ;
54+ let result = cls. call ( ( ) , vm) ?;
55+
56+ // Copy the current namespace dict to the new instance
57+ let src_dict = zelf. dict ( ) . unwrap ( ) ;
58+ let dst_dict = result. dict ( ) . unwrap ( ) ;
59+ for ( key, value) in src_dict {
60+ dst_dict. set_item ( & * key, value, vm) ?;
61+ }
62+
63+ // Update with the provided kwargs
64+ for ( name, value) in args. kwargs {
65+ let name = vm. ctx . new_str ( name) ;
66+ result. set_attr ( & name, value, vm) ?;
67+ }
68+
69+ Ok ( result)
70+ }
4571}
4672
4773impl Initializer for PyNamespace {
4874 type Args = FuncArgs ;
4975
5076 fn init ( zelf : PyRef < Self > , args : Self :: Args , vm : & VirtualMachine ) -> PyResult < ( ) > {
51- if !args. args . is_empty ( ) {
52- return Err ( vm. new_type_error ( "no positional arguments expected" ) ) ;
77+ // SimpleNamespace accepts 0 or 1 positional argument (a mapping)
78+ if args. args . len ( ) > 1 {
79+ return Err ( vm. new_type_error ( format ! (
80+ "{} expected at most 1 positional argument, got {}" ,
81+ zelf. class( ) . name( ) ,
82+ args. args. len( )
83+ ) ) ) ;
5384 }
85+
86+ // If there's a positional argument, treat it as a mapping
87+ if let Some ( mapping) = args. args . first ( ) {
88+ // Convert to dict if not already
89+ let dict: PyRef < PyDict > = if let Some ( d) = mapping. downcast_ref :: < PyDict > ( ) {
90+ d. to_owned ( )
91+ } else {
92+ // Call dict() on the mapping
93+ let dict_type: PyObjectRef = vm. ctx . types . dict_type . to_owned ( ) . into ( ) ;
94+ dict_type
95+ . call ( ( mapping. clone ( ) , ) , vm) ?
96+ . downcast ( )
97+ . map_err ( |_| vm. new_type_error ( "dict() did not return a dict" ) ) ?
98+ } ;
99+
100+ // Validate keys are strings and set attributes
101+ for ( key, value) in dict. into_iter ( ) {
102+ let key_str = key. downcast_ref :: < crate :: builtins:: PyStr > ( ) . ok_or_else ( || {
103+ vm. new_type_error ( format ! (
104+ "keywords must be strings, not '{}'" ,
105+ key. class( ) . name( )
106+ ) )
107+ } ) ?;
108+ zelf. as_object ( ) . set_attr ( key_str, value, vm) ?;
109+ }
110+ }
111+
112+ // Apply keyword arguments (these override positional mapping values)
54113 for ( name, value) in args. kwargs {
55114 let name = vm. ctx . new_str ( name) ;
56115 zelf. as_object ( ) . set_attr ( & name, value, vm) ?;
0 commit comments