File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -517,7 +517,7 @@ def excepthook(
517517 self .flush ()
518518
519519
520- class AttributeDict (dict ):
520+ class AttributeDict (dict [ str , T ], typing . Generic [ T ] ):
521521 """
522522 A dict that can be accessed with .attribute.
523523
@@ -563,13 +563,13 @@ class AttributeDict(dict):
563563 AttributeError: No such attribute: spam
564564 """
565565
566- def __getattr__ (self , name : str ) -> typing . Any :
566+ def __getattr__ (self , name : str ) -> T :
567567 if name in self :
568568 return self [name ]
569569 else :
570570 raise AttributeError (f'No such attribute: { name } ' )
571571
572- def __setattr__ (self , name : str , value : typing . Any ) -> None :
572+ def __setattr__ (self , name : str , value : T ) -> None :
573573 self [name ] = value
574574
575575 def __delattr__ (self , name : str ) -> None :
Original file line number Diff line number Diff line change @@ -175,6 +175,20 @@ def test_attribute_dict_set_get_del() -> None:
175175 del attrs .spam
176176
177177
178+ def test_attribute_dict_generic_value_type () -> None :
179+ # The generic upgrade lets homogeneous, explicitly-typed instances flow the
180+ # real value type. Runtime contract asserted here; the static benefit
181+ # (reveal_type -> int, mis-typed assignment flagged) is checked by pyright.
182+ attrs : utils .AttributeDict [int ] = utils .AttributeDict ()
183+ attrs .count = 5
184+ assert attrs .count == 5
185+ assert attrs ['count' ] == 5
186+
187+ mixed = utils .AttributeDict (a = 1 , b = 'x' )
188+ assert mixed .a == 1
189+ assert mixed .b == 'x'
190+
191+
178192def test_stream_wrapper_unwrap_restores_excepthook () -> None :
179193 # Regression: C7 - unwrap_stdout/unwrap_stderr left the custom
180194 # excepthook installed forever.
You can’t perform that action at this time.
0 commit comments