@@ -767,6 +767,102 @@ def test_ignore_preprocessor_in_comments(self):
767767 """ )
768768 self .clinic .parse (raw )
769769
770+ def test_getset_in_ifdef (self ):
771+ block = """
772+ /*[clinic input]
773+ output everything block
774+ class Foo "FooObject *" "&Foo_Type"
775+ [clinic start generated code]*/
776+ #ifdef CONDITION
777+ /*[clinic input]
778+ @getter
779+ Foo.property
780+ [clinic start generated code]*/
781+ /*[clinic input]
782+ @setter
783+ Foo.property
784+ [clinic start generated code]*/
785+ #endif
786+ """
787+ generated = self .clinic .parse (dedent (block ))
788+ self .assertIn ("#if defined(CONDITION)" , generated )
789+ # The getset is undefined if the condition is false.
790+ self .assertIn ("#ifndef FOO_PROPERTY_GETSETDEF\n "
791+ " #define FOO_PROPERTY_GETSETDEF\n "
792+ "#endif /* !defined(FOO_PROPERTY_GETSETDEF) */" ,
793+ generated )
794+
795+ def test_getset_duplicate (self ):
796+ for annotation in "@getter" , "@setter" :
797+ with self .subTest (annotation = annotation ):
798+ self .clinic = _make_clinic (filename = "test.c" )
799+ block = f"""
800+ /*[clinic input]
801+ class Foo "FooObject *" "&Foo_Type"
802+ [clinic start generated code]*/
803+ /*[clinic input]
804+ { annotation }
805+ Foo.property
806+ [clinic start generated code]*/
807+ /*[clinic input]
808+ { annotation }
809+ Foo.property
810+ [clinic start generated code]*/
811+ """
812+ kind = 'setter' if annotation == '@setter' else 'getter'
813+ err = f"Cannot apply @{ kind } to 'Foo.property' twice"
814+ self .expect_failure (block , err , lineno = 10 )
815+
816+ def test_getset_different_c_basename (self ):
817+ block = """
818+ /*[clinic input]
819+ class Foo "FooObject *" "&Foo_Type"
820+ [clinic start generated code]*/
821+ /*[clinic input]
822+ @getter
823+ Foo.property as foo_get
824+ [clinic start generated code]*/
825+ /*[clinic input]
826+ @setter
827+ Foo.property as foo_set
828+ [clinic start generated code]*/
829+ """
830+ err = "The accessors of 'Foo.property' must have the same C basename"
831+ self .expect_failure (block , err , lineno = 10 )
832+
833+ def test_setter_deletion_check (self ):
834+ block = """
835+ /*[clinic input]
836+ output everything block
837+ class Foo "FooObject *" "&Foo_Type"
838+ [clinic start generated code]*/
839+ /*[clinic input]
840+ @setter
841+ Foo.property
842+ [clinic start generated code]*/
843+ """
844+ generated = self .clinic .parse (dedent (block ))
845+ self .assertIn ("if (value == NULL) {" , generated )
846+ self .assertIn ("\" attribute 'property' of '%.100s' objects "
847+ "cannot be deleted\" " , generated )
848+
849+ def test_deleter (self ):
850+ # @deleter means that the setter is called with NULL to delete
851+ # the attribute, so it checks the value itself.
852+ block = """
853+ /*[clinic input]
854+ output everything block
855+ class Foo "FooObject *" "&Foo_Type"
856+ [clinic start generated code]*/
857+ /*[clinic input]
858+ @setter
859+ @deleter
860+ Foo.property
861+ [clinic start generated code]*/
862+ """
863+ generated = self .clinic .parse (dedent (block ))
864+ self .assertNotIn ("if (value == NULL) {" , generated )
865+
770866
771867class ParseFileUnitTest (TestCase ):
772868 def expect_parsing_failure (
@@ -2528,7 +2624,7 @@ class Foo "" ""
25282624 { annotation }
25292625 Foo.property -> int
25302626 """
2531- expected_error = f" { annotation } method cannot define a return type"
2627+ expected_error = "@getter and @setter methods cannot define a return type"
25322628 self .expect_failure (block , expected_error , lineno = 3 )
25332629
25342630 block = f"""
@@ -2539,7 +2635,7 @@ class Foo "" ""
25392635 obj: int
25402636 /
25412637 """
2542- expected_error = f" { annotation } methods cannot define parameters"
2638+ expected_error = "@getter and @setter methods cannot define parameters"
25432639 self .expect_failure (block , expected_error )
25442640
25452641 def test_setter_docstring (self ):
@@ -2582,9 +2678,51 @@ class Foo "" ""
25822678 { dup [1 ]}
25832679 Foo.property -> int
25842680 """
2585- expected_error = "Cannot apply both @getter and @setter to the same function!"
2681+ expected_error = (f"Can't set { dup [1 ]} , "
2682+ f"function is not a normal callable" )
25862683 self .expect_failure (block , expected_error , lineno = 3 )
25872684
2685+ def test_deleter_without_setter (self ):
2686+ block = """
2687+ module foo
2688+ class Foo "" ""
2689+ @deleter
2690+ Foo.property
2691+ """
2692+ expected_error = "Can't set @deleter, @setter is not applied"
2693+ self .expect_failure (block , expected_error , lineno = 2 )
2694+
2695+ block = """
2696+ module foo
2697+ class Foo "" ""
2698+ @deleter
2699+ @setter
2700+ Foo.property
2701+ """
2702+ self .expect_failure (block , expected_error , lineno = 2 )
2703+
2704+ def test_deleter_twice (self ):
2705+ block = """
2706+ module foo
2707+ class Foo "" ""
2708+ @setter
2709+ @deleter
2710+ @deleter
2711+ Foo.property
2712+ """
2713+ expected_error = "Cannot apply @deleter twice to the same function!"
2714+ self .expect_failure (block , expected_error , lineno = 4 )
2715+
2716+ def test_setter_and_deleter (self ):
2717+ function = self .parse_function ("""
2718+ module foo
2719+ class Foo "" ""
2720+ @setter
2721+ @deleter
2722+ Foo.property
2723+ """ , signatures_in_block = 3 , function_index = 2 )
2724+ self .assertEqual (function .kind , FunctionKind .SETTER_AND_DELETER )
2725+
25882726 def test_getset_no_class (self ):
25892727 for annotation in "@getter" , "@setter" :
25902728 with self .subTest (annotation = annotation ):
0 commit comments