@@ -1185,25 +1185,27 @@ impl Constructor for PyType {
11851185
11861186 if let Some ( ref slots) = heaptype_slots {
11871187 let mut offset = base_member_count;
1188+ let class_name = typ. name ( ) . to_string ( ) ;
11881189 for member in slots. as_slice ( ) {
1190+ // Apply name mangling for private attributes (__x -> _ClassName__x)
1191+ let mangled_name = mangle_name ( & class_name, member. as_str ( ) ) ;
11891192 let member_def = PyMemberDef {
1190- name : member . to_string ( ) ,
1193+ name : mangled_name . clone ( ) ,
11911194 kind : MemberKind :: ObjectEx ,
11921195 getter : MemberGetter :: Offset ( offset) ,
11931196 setter : MemberSetter :: Offset ( offset) ,
11941197 doc : None ,
11951198 } ;
1199+ let attr_name = vm. ctx . intern_str ( mangled_name. as_str ( ) ) ;
11961200 let member_descriptor: PyRef < PyMemberDescriptor > =
11971201 vm. ctx . new_pyref ( PyMemberDescriptor {
11981202 common : PyDescriptorOwned {
11991203 typ : typ. clone ( ) ,
1200- name : vm . ctx . intern_str ( member . as_str ( ) ) ,
1204+ name : attr_name ,
12011205 qualname : PyRwLock :: new ( None ) ,
12021206 } ,
12031207 member : member_def,
12041208 } ) ;
1205-
1206- let attr_name = vm. ctx . intern_str ( member. as_str ( ) ) ;
12071209 // __slots__ attributes always get a member descriptor
12081210 // (this overrides any inherited attribute from MRO)
12091211 typ. set_attr ( attr_name, member_descriptor. into ( ) ) ;
@@ -1793,6 +1795,18 @@ fn best_base<'a>(bases: &'a [PyTypeRef], vm: &VirtualMachine) -> PyResult<&'a Py
17931795 Ok ( base. unwrap ( ) )
17941796}
17951797
1798+ /// Apply Python name mangling for private attributes.
1799+ /// `__x` becomes `_ClassName__x` if inside a class.
1800+ fn mangle_name ( class_name : & str , name : & str ) -> String {
1801+ // Only mangle names starting with __ and not ending with __
1802+ if !name. starts_with ( "__" ) || name. ends_with ( "__" ) || name. contains ( '.' ) {
1803+ return name. to_string ( ) ;
1804+ }
1805+ // Strip leading underscores from class name
1806+ let class_name = class_name. trim_start_matches ( '_' ) ;
1807+ format ! ( "_{}{}" , class_name, name)
1808+ }
1809+
17961810#[ cfg( test) ]
17971811mod tests {
17981812 use super :: * ;
0 commit comments