@@ -868,8 +868,7 @@ class Foo "FooObject *" "&Foo_Type"
868868 "#define FOO_PROPERTY_GETTER Foo_property_get\n " ,
869869 generated )
870870 self .assertIn ("#define FOO_PROPERTY_SETTER Foo_property_set\n "
871- "#if defined(FOO_PROPERTY_GETTER) "
872- "|| defined(FOO_PROPERTY_SETTER)" ,
871+ "#if defined(FOO_PROPERTY_SETTER)" ,
873872 generated )
874873 self .assertIn ('# define FOO_PROPERTY_GETSETDEF {"property", '
875874 '(getter)FOO_PROPERTY_GETTER, '
@@ -962,21 +961,76 @@ def test_deleter(self):
962961 class Foo "FooObject *" "&Foo_Type"
963962 [clinic start generated code]*/
964963 /*[clinic input]
965- @setter
966964 @deleter
965+ @setter
967966 Foo.property
968967 value: object = NULL
969968 [clinic start generated code]*/
970969 """
971970 generated = self .clinic .parse (dedent (block ))
972971 self .assertNotIn ("if (arg == NULL) {" , generated )
973972
973+ def test_standalone_deleter (self ):
974+ block = """
975+ /*[clinic input]
976+ output everything block
977+ class Foo "FooObject *" "&Foo_Type"
978+ [clinic start generated code]*/
979+ /*[clinic input]
980+ @deleter
981+ Foo.property
982+ [clinic start generated code]*/
983+ """
984+ generated = self .clinic .parse (dedent (block ))
985+ # The deleter fills the setter slot and rejects assignment.
986+ self .assertIn ('#define FOO_PROPERTY_GETSETDEF {"property", '
987+ '(getter)NULL, (setter)Foo_property_del, NULL},' ,
988+ generated )
989+ self .assertIn ("if (arg != NULL) {" , generated )
990+ self .assertIn ("Foo_property_del_impl((FooObject *)self)" , generated )
991+
992+ def test_setter_and_standalone_deleter (self ):
993+ block = """
994+ /*[clinic input]
995+ output everything block
996+ output methoddef_ifndef buffer
997+ class Foo "FooObject *" "&Foo_Type"
998+ [clinic start generated code]*/
999+ /*[clinic input]
1000+ @setter
1001+ Foo.property
1002+
1003+ value: object
1004+ [clinic start generated code]*/
1005+ /*[clinic input]
1006+ @deleter
1007+ Foo.property
1008+ [clinic start generated code]*/
1009+ /*[clinic input]
1010+ dump buffer
1011+ [clinic start generated code]*/
1012+ """
1013+ generated = self .clinic .parse (dedent (block ))
1014+ # The setter slot is filled with a function which dispatches
1015+ # to the setter or to the deleter.
1016+ self .assertIn (" return ((setter)Foo_property_del)"
1017+ "(self, value, context);\n "
1018+ " }\n "
1019+ " return ((setter)Foo_property_set)"
1020+ "(self, value, context);" ,
1021+ generated )
1022+ self .assertIn ('#define FOO_PROPERTY_GETSETDEF {"property", '
1023+ '(getter)NULL, (setter)Foo_property_set_or_del, NULL},' ,
1024+ generated )
1025+
9741026 def test_getset_duplicate (self ):
9751027 # Only a setter defines the new value.
9761028 for annotation , parameter , err in (
9771029 ("@getter" , "" , "Cannot apply @getter to 'Foo.property' twice" ),
9781030 ("@setter" , "value: object" ,
9791031 "The setter of 'Foo.property' is already defined" ),
1032+ ("@deleter" , "" ,
1033+ "The deleter of 'Foo.property' is already defined" ),
9801034 ):
9811035 with self .subTest (annotation = annotation ):
9821036 self .clinic = _make_clinic (filename = "test.c" )
@@ -997,6 +1051,26 @@ class Foo "FooObject *" "&Foo_Type"
9971051 """
9981052 self .expect_failure (block , err , lineno = 11 )
9991053
1054+ def test_deleter_and_standalone_deleter (self ):
1055+ block = """
1056+ /*[clinic input]
1057+ class Foo "FooObject *" "&Foo_Type"
1058+ [clinic start generated code]*/
1059+ /*[clinic input]
1060+ @setter
1061+ @deleter
1062+ Foo.property
1063+
1064+ value: object = NULL
1065+ [clinic start generated code]*/
1066+ /*[clinic input]
1067+ @deleter
1068+ Foo.property
1069+ [clinic start generated code]*/
1070+ """
1071+ err = "The deleter of 'Foo.property' is already defined"
1072+ self .expect_failure (block , err , lineno = 13 )
1073+
10001074 def test_getset_different_c_basename (self ):
10011075 block = """
10021076 /*[clinic input]
@@ -3004,25 +3078,30 @@ class Foo "" ""
30043078 self .expect_failure (block , expected_error , lineno = 1 )
30053079
30063080 def test_invalid_getset (self ):
3007- block = """
3008- module foo
3009- class Foo "" ""
3010- @setter
3011- Foo.property -> int
3012- """
3013- expected_error = "@setter methods cannot define a return type"
3014- self .expect_failure (block , expected_error , lineno = 3 )
3081+ for annotation in ("@setter" , "@deleter" ):
3082+ with self .subTest (annotation = annotation ):
3083+ block = f"""
3084+ module foo
3085+ class Foo "" ""
3086+ { annotation }
3087+ Foo.property -> int
3088+ """
3089+ expected_error = ("@setter and @deleter methods cannot "
3090+ "define a return type" )
3091+ self .expect_failure (block , expected_error , lineno = 3 )
30153092
3016- block = """
3017- module foo
3018- class Foo "" ""
3019- @getter
3020- Foo.property
3021- obj: int
3022- /
3023- """
3024- expected_error = "@getter methods cannot define parameters"
3025- self .expect_failure (block , expected_error )
3093+ for annotation in ("@getter" , "@deleter" ):
3094+ with self .subTest (annotation = annotation ):
3095+ block = f"""
3096+ module foo
3097+ class Foo "" ""
3098+ { annotation }
3099+ Foo.property
3100+ obj: int
3101+ /
3102+ """
3103+ expected_error = f"{ annotation } methods cannot define parameters"
3104+ self .expect_failure (block , expected_error )
30263105
30273106 block = """
30283107 module foo
@@ -3050,8 +3129,8 @@ class Foo "" ""
30503129 block = """
30513130 module m
30523131 class Foo "" ""
3053- @setter
30543132 @deleter
3133+ @setter
30553134 Foo.property
30563135 value: object
30573136 """
@@ -3174,46 +3253,43 @@ class Foo "" ""
31743253 f"a normal callable" )
31753254 self .expect_failure (block , expected_error , lineno = 3 )
31763255
3177- def test_deleter_without_setter (self ):
3178- block = """
3179- module foo
3180- class Foo "" ""
3181- @deleter
3182- Foo.property
3183- """
3184- expected_error = "Can't set @deleter, @setter is not applied"
3185- self .expect_failure (block , expected_error , lineno = 2 )
3186-
3187- block = """
3188- module foo
3189- class Foo "" ""
3190- @deleter
3191- @setter
3192- Foo.property
3193- """
3194- self .expect_failure (block , expected_error , lineno = 2 )
3256+ def test_getter_and_deleter_disallowed_on_same_function (self ):
3257+ dup_annotations = [("@getter" , "@deleter" ), ("@deleter" , "@getter" )]
3258+ for dup in dup_annotations :
3259+ with self .subTest (dup = dup ):
3260+ block = f"""
3261+ module foo
3262+ class Foo "" ""
3263+ { dup [0 ]}
3264+ { dup [1 ]}
3265+ Foo.property
3266+ """
3267+ expected_error = (f"Can't set { dup [1 ]} , function is not "
3268+ f"a normal callable" )
3269+ self .expect_failure (block , expected_error , lineno = 3 )
31953270
3196- def test_deleter_twice (self ):
3271+ def test_duplicate_deleter (self ):
31973272 block = """
3198- module foo
3273+ module m
31993274 class Foo "" ""
3200- @setter
32013275 @deleter
32023276 @deleter
3277+ @setter
32033278 Foo.property
32043279 """
32053280 expected_error = "Cannot apply @deleter twice to the same function!"
3206- self .expect_failure (block , expected_error , lineno = 4 )
3281+ self .expect_failure (block , expected_error , lineno = 3 )
32073282
32083283 def test_getset_no_class (self ):
3209- for annotation in "@getter" , "@setter" :
3284+ for annotation in "@getter" , "@setter" , "@deleter" :
32103285 with self .subTest (annotation = annotation ):
32113286 block = f"""
32123287 module m
32133288 { annotation }
32143289 m.func
32153290 """
3216- expected_error = "@getter and @setter must be methods"
3291+ expected_error = ("@getter, @setter and @deleter "
3292+ "must be methods" )
32173293 self .expect_failure (block , expected_error , lineno = 2 )
32183294
32193295 def test_duplicate_coexist (self ):
0 commit comments