@@ -63,6 +63,7 @@ impl FromStr for AttrName {
6363
6464#[ derive( Default ) ]
6565struct ImplContext {
66+ is_trait : bool ,
6667 attribute_items : ItemNursery ,
6768 method_items : MethodNursery ,
6869 getset_items : GetSetNursery ,
@@ -232,7 +233,10 @@ pub(crate) fn impl_pyclass_impl(attr: PunctuatedNestedMeta, item: Item) -> Resul
232233 }
233234 }
234235 Item :: Trait ( mut trai) => {
235- let mut context = ImplContext :: default ( ) ;
236+ let mut context = ImplContext {
237+ is_trait : true ,
238+ ..Default :: default ( )
239+ } ;
236240 let mut has_extend_slots = false ;
237241 for item in & trai. items {
238242 let has = match item {
@@ -710,21 +714,16 @@ pub(crate) fn impl_pyexception_impl(attr: PunctuatedNestedMeta, item: Item) -> R
710714 } ;
711715
712716 // Check if with(Constructor) is specified. If Constructor trait is used, don't generate slot_new
713- let mut has_slot_new = false ;
714-
715717 let mut extra_attrs = Vec :: new ( ) ;
718+ let mut with_items = vec ! [ ] ;
716719 for nested in & attr {
717720 if let NestedMeta :: Meta ( Meta :: List ( MetaList { path, nested, .. } ) ) = nested {
718721 // If we already found the constructor trait, no need to keep looking for it
719- if !has_slot_new && path. is_ident ( "with" ) {
720- // Check if Constructor is in the list
722+ if path. is_ident ( "with" ) {
721723 for meta in nested {
722- if let NestedMeta :: Meta ( Meta :: Path ( p) ) = meta
723- && p. is_ident ( "Constructor" )
724- {
725- has_slot_new = true ;
726- }
724+ with_items. push ( meta. get_ident ( ) . expect ( "with() has non-ident item" ) . clone ( ) ) ;
727725 }
726+ continue ;
728727 }
729728 extra_attrs. push ( NestedMeta :: Meta ( Meta :: List ( MetaList {
730729 path : path. clone ( ) ,
@@ -734,43 +733,40 @@ pub(crate) fn impl_pyexception_impl(attr: PunctuatedNestedMeta, item: Item) -> R
734733 }
735734 }
736735
737- let mut has_slot_init = false ;
736+ let with_contains = |with_items : & [ Ident ] , s : & str | {
737+ // Check if Constructor is in the list
738+ with_items. iter ( ) . any ( |ident| ident == s)
739+ } ;
740+
738741 let syn:: ItemImpl {
739742 generics,
740743 self_ty,
741744 items,
742745 ..
743746 } = & imp;
744- for item in items {
745- // FIXME: better detection or correct wrapper implementation
746- let Some ( ident) = item. get_ident ( ) else {
747- continue ;
748- } ;
749- let item_name = ident. to_string ( ) ;
750- match item_name. as_str ( ) {
751- "slot_new" => {
752- has_slot_new = true ;
753- }
754- "slot_init" => {
755- has_slot_init = true ;
756- }
757- _ => continue ,
758- }
759- }
760-
761- // TODO: slot_new, slot_init must be Constructor or Initializer later
762747
763- let slot_new = if has_slot_new {
748+ let slot_new = if with_contains ( & with_items , "Constructor" ) {
764749 quote ! ( )
765750 } else {
751+ with_items. push ( Ident :: new ( "Constructor" , Span :: call_site ( ) ) ) ;
766752 quote ! {
767- #[ pyslot]
768- pub fn slot_new(
769- cls: :: rustpython_vm:: builtins:: PyTypeRef ,
770- args: :: rustpython_vm:: function:: FuncArgs ,
771- vm: & :: rustpython_vm:: VirtualMachine ,
772- ) -> :: rustpython_vm:: PyResult {
773- <Self as :: rustpython_vm:: class:: PyClassDef >:: Base :: slot_new( cls, args, vm)
753+ impl :: rustpython_vm:: types:: Constructor for #self_ty {
754+ type Args = :: rustpython_vm:: function:: FuncArgs ;
755+
756+ fn slot_new(
757+ cls: :: rustpython_vm:: builtins:: PyTypeRef ,
758+ args: :: rustpython_vm:: function:: FuncArgs ,
759+ vm: & :: rustpython_vm:: VirtualMachine ,
760+ ) -> :: rustpython_vm:: PyResult {
761+ <Self as :: rustpython_vm:: class:: PyClassDef >:: Base :: slot_new( cls, args, vm)
762+ }
763+ fn py_new(
764+ _cls: & :: rustpython_vm:: Py <:: rustpython_vm:: builtins:: PyType >,
765+ _args: Self :: Args ,
766+ _vm: & :: rustpython_vm:: VirtualMachine
767+ ) -> :: rustpython_vm:: PyResult <Self > {
768+ unreachable!( "slot_new is defined" )
769+ }
774770 }
775771 }
776772 } ;
@@ -779,19 +775,29 @@ pub(crate) fn impl_pyexception_impl(attr: PunctuatedNestedMeta, item: Item) -> R
779775 // from `BaseException` in `SimpleExtendsException` macro.
780776 // See: `(initproc)BaseException_init`
781777 // spell-checker:ignore initproc
782- let slot_init = if has_slot_init {
778+ let slot_init = if with_contains ( & with_items , "Initializer" ) {
783779 quote ! ( )
784780 } else {
785- // FIXME: this is a generic logic for types not only for exceptions
781+ with_items . push ( Ident :: new ( "Initializer" , Span :: call_site ( ) ) ) ;
786782 quote ! {
787- #[ pyslot]
788- #[ pymethod( name="__init__" ) ]
789- pub fn slot_init(
790- zelf: :: rustpython_vm:: PyObjectRef ,
791- args: :: rustpython_vm:: function:: FuncArgs ,
792- vm: & :: rustpython_vm:: VirtualMachine ,
793- ) -> :: rustpython_vm:: PyResult <( ) > {
794- <Self as :: rustpython_vm:: class:: PyClassDef >:: Base :: slot_init( zelf, args, vm)
783+ impl :: rustpython_vm:: types:: Initializer for #self_ty {
784+ type Args = :: rustpython_vm:: function:: FuncArgs ;
785+
786+ fn slot_init(
787+ zelf: :: rustpython_vm:: PyObjectRef ,
788+ args: :: rustpython_vm:: function:: FuncArgs ,
789+ vm: & :: rustpython_vm:: VirtualMachine ,
790+ ) -> :: rustpython_vm:: PyResult <( ) > {
791+ <Self as :: rustpython_vm:: class:: PyClassDef >:: Base :: slot_init( zelf, args, vm)
792+ }
793+
794+ fn init(
795+ _zelf: :: rustpython_vm:: PyRef <Self >,
796+ _args: Self :: Args ,
797+ _vm: & :: rustpython_vm:: VirtualMachine
798+ ) -> :: rustpython_vm:: PyResult <( ) > {
799+ unreachable!( "slot_init is defined" )
800+ }
795801 }
796802 }
797803 } ;
@@ -803,13 +809,13 @@ pub(crate) fn impl_pyexception_impl(attr: PunctuatedNestedMeta, item: Item) -> R
803809 } ;
804810
805811 Ok ( quote ! {
806- #[ pyclass( flags( BASETYPE , HAS_DICT ) #extra_attrs_tokens) ]
812+ #[ pyclass( flags( BASETYPE , HAS_DICT ) , with ( # ( #with_items ) , * ) #extra_attrs_tokens) ]
807813 impl #generics #self_ty {
808814 #( #items) *
809-
810- #slot_new
811- #slot_init
812815 }
816+
817+ #slot_new
818+ #slot_init
813819 } )
814820}
815821
@@ -892,6 +898,23 @@ where
892898 let item_meta = MethodItemMeta :: from_attr ( ident. clone ( ) , & item_attr) ?;
893899
894900 let py_name = item_meta. method_name ( ) ?;
901+
902+ // Disallow __new__ and __init__ as pymethod in impl blocks (not in traits)
903+ if !args. context . is_trait {
904+ if py_name == "__new__" {
905+ return Err ( syn:: Error :: new (
906+ ident. span ( ) ,
907+ "#[pymethod] cannot define '__new__'. Use #[pyclass(with(Constructor))] instead." ,
908+ ) ) ;
909+ }
910+ if py_name == "__init__" {
911+ return Err ( syn:: Error :: new (
912+ ident. span ( ) ,
913+ "#[pymethod] cannot define '__init__'. Use #[pyclass(with(Initializer))] instead." ,
914+ ) ) ;
915+ }
916+ }
917+
895918 let raw = item_meta. raw ( ) ?;
896919 let sig_doc = text_signature ( func. sig ( ) , & py_name) ;
897920
0 commit comments