Skip to content

Commit 346519b

Browse files
authored
Warn user not to override special magic methods (RustPython#6625)
1 parent 3218410 commit 346519b

File tree

1 file changed

+97
-9
lines changed

1 file changed

+97
-9
lines changed

crates/derive-impl/src/pyclass.rs

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -909,18 +909,106 @@ where
909909

910910
let py_name = item_meta.method_name()?;
911911

912-
// Disallow __new__ and __init__ as pymethod in impl blocks (not in traits)
912+
// Disallow slot methods - they should be defined via trait implementations
913+
// These are exposed as wrapper_descriptor via add_operators from SLOT_DEFS
913914
if !args.context.is_trait {
914-
if py_name == "__new__" {
915-
return Err(syn::Error::new(
916-
ident.span(),
917-
"#[pymethod] cannot define '__new__'. Use #[pyclass(with(Constructor))] instead.",
918-
));
919-
}
920-
if py_name == "__init__" {
915+
const FORBIDDEN_SLOT_METHODS: &[(&str, &str)] = &[
916+
// Constructor/Initializer traits
917+
("__new__", "Constructor"),
918+
("__init__", "Initializer"),
919+
// Representable trait
920+
// ("__repr__", "Representable"),
921+
// ("__str__", "???"), // allow __str__
922+
// Hashable trait
923+
("__hash__", "Hashable"),
924+
// Callable trait
925+
("__call__", "Callable"),
926+
// GetAttr/SetAttr traits
927+
// NOTE: __getattribute__, __setattr__, __delattr__ are intentionally NOT forbidden
928+
// because they need pymethod for subclass override mechanism to work properly.
929+
// GetDescriptor/SetDescriptor traits
930+
// ("__get__", "GetDescriptor"),
931+
// ("__set__", "SetDescriptor"),
932+
// ("__delete__", "SetDescriptor"),
933+
// AsNumber trait
934+
("__add__", "AsNumber"),
935+
("__radd__", "AsNumber"),
936+
("__iadd__", "AsNumber"),
937+
("__sub__", "AsNumber"),
938+
("__rsub__", "AsNumber"),
939+
("__isub__", "AsNumber"),
940+
("__mul__", "AsNumber"),
941+
("__rmul__", "AsNumber"),
942+
("__imul__", "AsNumber"),
943+
("__truediv__", "AsNumber"),
944+
("__rtruediv__", "AsNumber"),
945+
("__itruediv__", "AsNumber"),
946+
("__floordiv__", "AsNumber"),
947+
("__rfloordiv__", "AsNumber"),
948+
("__ifloordiv__", "AsNumber"),
949+
("__mod__", "AsNumber"),
950+
("__rmod__", "AsNumber"),
951+
("__imod__", "AsNumber"),
952+
("__pow__", "AsNumber"),
953+
("__rpow__", "AsNumber"),
954+
("__ipow__", "AsNumber"),
955+
("__divmod__", "AsNumber"),
956+
("__rdivmod__", "AsNumber"),
957+
("__matmul__", "AsNumber"),
958+
("__rmatmul__", "AsNumber"),
959+
("__imatmul__", "AsNumber"),
960+
("__lshift__", "AsNumber"),
961+
("__rlshift__", "AsNumber"),
962+
("__ilshift__", "AsNumber"),
963+
("__rshift__", "AsNumber"),
964+
("__rrshift__", "AsNumber"),
965+
("__irshift__", "AsNumber"),
966+
("__and__", "AsNumber"),
967+
("__rand__", "AsNumber"),
968+
("__iand__", "AsNumber"),
969+
("__or__", "AsNumber"),
970+
("__ror__", "AsNumber"),
971+
("__ior__", "AsNumber"),
972+
("__xor__", "AsNumber"),
973+
("__rxor__", "AsNumber"),
974+
("__ixor__", "AsNumber"),
975+
("__neg__", "AsNumber"),
976+
("__pos__", "AsNumber"),
977+
("__abs__", "AsNumber"),
978+
("__invert__", "AsNumber"),
979+
("__int__", "AsNumber"),
980+
("__float__", "AsNumber"),
981+
("__index__", "AsNumber"),
982+
("__bool__", "AsNumber"),
983+
// AsSequence trait
984+
// ("__len__", "AsSequence (or AsMapping)"),
985+
// ("__contains__", "AsSequence"),
986+
// AsMapping trait
987+
// ("__getitem__", "AsMapping (or AsSequence)"),
988+
// ("__setitem__", "AsMapping (or AsSequence)"),
989+
// ("__delitem__", "AsMapping (or AsSequence)"),
990+
// IterNext trait
991+
// ("__iter__", "IterNext"),
992+
// ("__next__", "IterNext"),
993+
// Comparable trait
994+
("__eq__", "Comparable"),
995+
("__ne__", "Comparable"),
996+
("__lt__", "Comparable"),
997+
("__le__", "Comparable"),
998+
("__gt__", "Comparable"),
999+
("__ge__", "Comparable"),
1000+
];
1001+
1002+
if let Some((_, trait_name)) = FORBIDDEN_SLOT_METHODS
1003+
.iter()
1004+
.find(|(method, _)| *method == py_name.as_str())
1005+
{
9211006
return Err(syn::Error::new(
9221007
ident.span(),
923-
"#[pymethod] cannot define '__init__'. Use #[pyclass(with(Initializer))] instead.",
1008+
format!(
1009+
"#[pymethod] cannot define '{py_name}'. Use `impl {trait_name} for ...` instead. \
1010+
Slot methods are exposed as wrapper_descriptor automatically.",
1011+
),
9241012
));
9251013
}
9261014
}

0 commit comments

Comments
 (0)