@@ -354,11 +354,10 @@ where
354354 let item_attr = args. attrs . remove ( self . index ( ) ) ;
355355 let item_meta = PropertyItemMeta :: from_attr ( ident. clone ( ) , & item_attr) ?;
356356
357- let py_name = item_meta. property_name ( ) ?;
358- let setter = item_meta. setter ( ) ?;
357+ let ( py_name, kind) = item_meta. property_name ( ) ?;
359358 args. context
360359 . getset_items
361- . add_item ( py_name, args. cfgs . to_vec ( ) , setter , ident. clone ( ) ) ?;
360+ . add_item ( py_name, args. cfgs . to_vec ( ) , kind , ident. clone ( ) ) ?;
362361 Ok ( ( ) )
363362 }
364363}
@@ -478,27 +477,37 @@ where
478477#[ derive( Default ) ]
479478#[ allow( clippy:: type_complexity) ]
480479struct GetSetNursery {
481- map : HashMap < ( String , Vec < Attribute > ) , ( Option < Ident > , Option < Ident > ) > ,
480+ map : HashMap < ( String , Vec < Attribute > ) , ( Option < Ident > , Option < Ident > , Option < Ident > ) > ,
482481 validated : bool ,
483482}
484483
484+ enum GetSetItemKind {
485+ Get ,
486+ Set ,
487+ Delete ,
488+ }
489+
485490impl GetSetNursery {
486491 fn add_item (
487492 & mut self ,
488493 name : String ,
489494 cfgs : Vec < Attribute > ,
490- setter : bool ,
495+ kind : GetSetItemKind ,
491496 item_ident : Ident ,
492497 ) -> Result < ( ) > {
493498 assert ! ( !self . validated, "new item is not allowed after validation" ) ;
494- if setter && !cfgs. is_empty ( ) {
499+ if ! matches ! ( kind , GetSetItemKind :: Get ) && !cfgs. is_empty ( ) {
495500 return Err ( syn:: Error :: new_spanned (
496501 item_ident,
497- "Property setter does not allow #[cfg]" ,
502+ "Only the getter can have #[cfg]" ,
498503 ) ) ;
499504 }
500505 let entry = self . map . entry ( ( name. clone ( ) , cfgs) ) . or_default ( ) ;
501- let func = if setter { & mut entry. 1 } else { & mut entry. 0 } ;
506+ let func = match kind {
507+ GetSetItemKind :: Get => & mut entry. 0 ,
508+ GetSetItemKind :: Set => & mut entry. 1 ,
509+ GetSetItemKind :: Delete => & mut entry. 2 ,
510+ } ;
502511 if func. is_some ( ) {
503512 return Err ( syn:: Error :: new_spanned (
504513 item_ident,
@@ -511,10 +520,10 @@ impl GetSetNursery {
511520
512521 fn validate ( & mut self ) -> Result < ( ) > {
513522 let mut errors = Vec :: new ( ) ;
514- for ( ( name, _cfgs) , ( getter, setter) ) in self . map . iter ( ) {
523+ for ( ( name, _cfgs) , ( getter, setter, deleter ) ) in self . map . iter ( ) {
515524 if getter. is_none ( ) {
516525 errors. push ( syn:: Error :: new_spanned (
517- setter. as_ref ( ) . unwrap ( ) ,
526+ setter. as_ref ( ) . or_else ( || deleter . as_ref ( ) ) . unwrap ( ) ,
518527 format ! ( "Property '{}' is missing a getter" , name) ,
519528 ) ) ;
520529 } ;
@@ -528,21 +537,33 @@ impl GetSetNursery {
528537impl ToTokens for GetSetNursery {
529538 fn to_tokens ( & self , tokens : & mut TokenStream ) {
530539 assert ! ( self . validated, "Call `validate()` before token generation" ) ;
531- tokens. extend ( self . map . iter ( ) . map ( |( ( name, cfgs) , ( getter, setter) ) | {
532- let ( constructor, setter) = match setter {
533- Some ( setter) => ( quote ! { with_get_set } , quote_spanned ! { setter. span( ) => , & Self :: #setter } ) ,
534- None => ( quote ! { with_get } , quote ! { } ) ,
535- } ;
536- quote ! {
537- #( #cfgs ) *
538- class. set_str_attr(
539- #name,
540- :: rustpython_vm:: pyobject:: PyObject :: new(
541- :: rustpython_vm:: builtins:: PyGetSet :: #constructor( #name. into( ) , & Self :: #getter #setter) ,
542- ctx. types. getset_type. clone( ) , None )
543- ) ;
544- }
545- } ) ) ;
540+ let properties = self
541+ . map
542+ . iter ( )
543+ . map ( |( ( name, cfgs) , ( getter, setter, deleter) ) | {
544+ let setter = match setter {
545+ Some ( setter) => quote_spanned ! { setter. span( ) => . with_set( & Self :: #setter) } ,
546+ None => quote ! { } ,
547+ } ;
548+ let deleter = match deleter {
549+ Some ( deleter) => {
550+ quote_spanned ! { deleter. span( ) => . with_delete( & Self :: #deleter) }
551+ }
552+ None => quote ! { } ,
553+ } ;
554+ quote ! {
555+ #( #cfgs ) *
556+ class. set_str_attr(
557+ #name,
558+ :: rustpython_vm:: pyobject:: PyObject :: new(
559+ :: rustpython_vm:: builtins:: PyGetSet :: new( #name. into( ) )
560+ . with_get( & Self :: #getter)
561+ #setter #deleter,
562+ ctx. types. getset_type. clone( ) , None )
563+ ) ;
564+ }
565+ } ) ;
566+ tokens. extend ( properties) ;
546567 }
547568}
548569
@@ -580,7 +601,7 @@ impl MethodItemMeta {
580601struct PropertyItemMeta ( ItemMetaInner ) ;
581602
582603impl ItemMeta for PropertyItemMeta {
583- const ALLOWED_NAMES : & ' static [ & ' static str ] = & [ "name" , "magic" , "setter" ] ;
604+ const ALLOWED_NAMES : & ' static [ & ' static str ] = & [ "name" , "magic" , "setter" , "deleter" ] ;
584605
585606 fn from_inner ( inner : ItemMetaInner ) -> Self {
586607 Self ( inner)
@@ -591,10 +612,25 @@ impl ItemMeta for PropertyItemMeta {
591612}
592613
593614impl PropertyItemMeta {
594- fn property_name ( & self ) -> Result < String > {
615+ fn property_name ( & self ) -> Result < ( String , GetSetItemKind ) > {
595616 let inner = self . inner ( ) ;
596617 let magic = inner. _bool ( "magic" ) ?;
597618 let setter = inner. _bool ( "setter" ) ?;
619+ let deleter = inner. _bool ( "deleter" ) ?;
620+ let kind = match ( setter, deleter) {
621+ ( false , false ) => GetSetItemKind :: Get ,
622+ ( true , false ) => GetSetItemKind :: Set ,
623+ ( false , true ) => GetSetItemKind :: Delete ,
624+ ( true , true ) => {
625+ return Err ( syn:: Error :: new_spanned (
626+ & inner. meta_ident ,
627+ format ! (
628+ "can't have both setter and deleter on a #[{}] fn" ,
629+ inner. meta_name( )
630+ ) ,
631+ ) )
632+ }
633+ } ;
598634 let name = inner. _optional_str ( "name" ) ?;
599635 let py_name = if let Some ( name) = name {
600636 name
@@ -623,6 +659,29 @@ impl PropertyItemMeta {
623659 ) ,
624660 ) ) ;
625661 }
662+ } else if deleter {
663+ if let Some ( name) = sig_name. strip_prefix ( "del_" ) {
664+ if name. is_empty ( ) {
665+ return Err ( syn:: Error :: new_spanned (
666+ & inner. meta_ident ,
667+ format ! (
668+ "A #[{}(deleter)] fn with a del_* name must \
669+ have something after \" del_\" ",
670+ inner. meta_name( )
671+ ) ,
672+ ) ) ;
673+ }
674+ name. to_string ( )
675+ } else {
676+ return Err ( syn:: Error :: new_spanned (
677+ & inner. meta_ident ,
678+ format ! (
679+ "A #[{}(deleter)] fn must either have a `name` \
680+ parameter or a fn name along the lines of \" del_*\" ",
681+ inner. meta_name( )
682+ ) ,
683+ ) ) ;
684+ }
626685 } else {
627686 sig_name
628687 } ;
@@ -632,11 +691,7 @@ impl PropertyItemMeta {
632691 name
633692 }
634693 } ;
635- Ok ( py_name)
636- }
637-
638- fn setter ( & self ) -> Result < bool > {
639- self . inner ( ) . _bool ( "setter" )
694+ Ok ( ( py_name, kind) )
640695 }
641696}
642697
0 commit comments