@@ -3377,35 +3377,115 @@ def test_non_frozen_normal_derived(self):
33773377 class D :
33783378 x : int
33793379 y : int = 10
3380+ z : int = 1
3381+
3382+ @property
3383+ def readonly (self ) -> int :
3384+ return self .x
3385+
3386+ @property
3387+ def prop (self ) -> int :
3388+ return self .z
3389+
3390+ @prop .setter
3391+ def prop (self , val : int ) -> None :
3392+ object .__setattr__ (self , 'z' , val )
3393+
3394+ @prop .deleter
3395+ def prop (self ) -> None :
3396+ object .__setattr__ (self , 'z' , 0 )
3397+
3398+ d = D (5 )
3399+ self .assertEqual (d .x , 5 )
3400+ self .assertEqual (d .y , 10 )
3401+ self .assertEqual (d .z , 1 )
3402+ self .assertEqual (d .readonly , 5 )
3403+ self .assertEqual (d .prop , 1 )
3404+
3405+ with self .assertRaises (FrozenInstanceError ):
3406+ d .x = 5
3407+ with self .assertRaises (FrozenInstanceError ):
3408+ d .readonly = 5
3409+ with self .assertRaises (FrozenInstanceError ):
3410+ d .z = 5
3411+ with self .assertRaises (FrozenInstanceError ):
3412+ d .prop = 5
3413+ with self .assertRaises (FrozenInstanceError ):
3414+ del d .prop
3415+
3416+ self .assertEqual (d .x , 5 )
3417+ self .assertEqual (d .y , 10 )
3418+ self .assertEqual (d .z , 1 )
3419+ self .assertEqual (d .readonly , 5 )
3420+ self .assertEqual (d .prop , 1 )
33803421
33813422 class S (D ):
33823423 pass
33833424
33843425 s = S (3 )
33853426 self .assertEqual (s .x , 3 )
33863427 self .assertEqual (s .y , 10 )
3428+ self .assertEqual (s .z , 1 )
3429+ self .assertEqual (s .readonly , 3 )
3430+ self .assertEqual (s .prop , 1 )
3431+ # Can set new attrs:
33873432 s .cached = True
3433+ self .assertTrue (s .cached )
3434+ # Can mutate them:
3435+ s .cached = False
3436+ self .assertFalse (s .cached )
3437+
3438+ # Can also change writable properties:
3439+ with self .assertRaisesRegex (
3440+ AttributeError ,
3441+ 'object has no setter' ,
3442+ ) as cm :
3443+ s .readonly = 5
3444+ self .assertNotIsInstance (cm .exception , FrozenInstanceError )
3445+ s .prop = 2
3446+ self .assertEqual (s .x , 3 )
3447+ self .assertEqual (s .readonly , 3 )
3448+ self .assertEqual (s .prop , 2 )
3449+ self .assertEqual (s .z , 2 )
33883450
33893451 # But can't change the frozen attributes.
33903452 with self .assertRaises (FrozenInstanceError ):
33913453 s .x = 5
33923454 with self .assertRaises (FrozenInstanceError ):
33933455 s .y = 5
3456+ with self .assertRaises (FrozenInstanceError ):
3457+ s .z = 5
33943458 self .assertEqual (s .x , 3 )
33953459 self .assertEqual (s .y , 10 )
3396- self .assertEqual (s .cached , True )
3460+ self .assertEqual (s .z , 2 )
3461+ self .assertIs (s .cached , False )
33973462
33983463 with self .assertRaises (FrozenInstanceError ):
33993464 del s .x
34003465 self .assertEqual (s .x , 3 )
34013466 with self .assertRaises (FrozenInstanceError ):
34023467 del s .y
34033468 self .assertEqual (s .y , 10 )
3469+ with self .assertRaisesRegex (
3470+ AttributeError ,
3471+ 'object has no deleter' ,
3472+ ) as cm :
3473+ del s .readonly
3474+ self .assertNotIsInstance (cm .exception , FrozenInstanceError )
3475+ self .assertEqual (s .x , 3 )
3476+ self .assertEqual (s .readonly , 3 )
34043477 del s .cached
34053478 self .assertNotHasAttr (s , 'cached' )
3406- with self .assertRaises (AttributeError ) as cm :
3479+ with self .assertRaisesRegex (
3480+ AttributeError ,
3481+ "object has no attribute 'cached'" ,
3482+ ) as cm :
34073483 del s .cached
34083484 self .assertNotIsInstance (cm .exception , FrozenInstanceError )
3485+ del s .prop
3486+ self .assertEqual (s .z , 0 )
3487+ self .assertEqual (s .prop , 0 )
3488+ del s .prop
34093489
34103490 def test_non_frozen_normal_derived_from_empty_frozen (self ):
34113491 @dataclass (frozen = True )
0 commit comments