Visitar URL original
__slots__ xor __dict__ , name mangling by youknowone · Pull Request #6392 · RustPython/RustPython · GitHub
Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
mangle_name for __ prefixed members
  • Loading branch information
youknowone committed Dec 10, 2025
commit 8718dc422c52b12cedafa6147b3ecbcda696d1ad
22 changes: 18 additions & 4 deletions crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1185,25 +1185,27 @@ impl Constructor for PyType {

if let Some(ref slots) = heaptype_slots {
let mut offset = base_member_count;
let class_name = typ.name().to_string();
for member in slots.as_slice() {
// Apply name mangling for private attributes (__x -> _ClassName__x)
let mangled_name = mangle_name(&class_name, member.as_str());
let member_def = PyMemberDef {
name: member.to_string(),
name: mangled_name.clone(),
kind: MemberKind::ObjectEx,
getter: MemberGetter::Offset(offset),
setter: MemberSetter::Offset(offset),
doc: None,
};
let attr_name = vm.ctx.intern_str(mangled_name.as_str());
let member_descriptor: PyRef<PyMemberDescriptor> =
vm.ctx.new_pyref(PyMemberDescriptor {
common: PyDescriptorOwned {
typ: typ.clone(),
name: vm.ctx.intern_str(member.as_str()),
name: attr_name,
qualname: PyRwLock::new(None),
},
member: member_def,
});

let attr_name = vm.ctx.intern_str(member.as_str());
// __slots__ attributes always get a member descriptor
// (this overrides any inherited attribute from MRO)
typ.set_attr(attr_name, member_descriptor.into());
Expand Down Expand Up @@ -1793,6 +1795,18 @@ fn best_base<'a>(bases: &'a [PyTypeRef], vm: &VirtualMachine) -> PyResult<&'a Py
Ok(base.unwrap())
}

/// Apply Python name mangling for private attributes.
/// `__x` becomes `_ClassName__x` if inside a class.
fn mangle_name(class_name: &str, name: &str) -> String {
// Only mangle names starting with __ and not ending with __
if !name.starts_with("__") || name.ends_with("__") || name.contains('.') {
return name.to_string();
}
// Strip leading underscores from class name
let class_name = class_name.trim_start_matches('_');
format!("_{}{}", class_name, name)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading