1515
1616from sunset .lock import SettingsLock
1717from sunset .notifier import Notifier
18- from sunset .protocols import BaseField , Field , ItemTemplate , UpdateNotifier
18+ from sunset .protocols import BaseField , Field , UpdateNotifier
1919from sunset .sets import WeakNonHashableSet
2020from sunset .stringutils import collate_by_prefix , split_on
2121
@@ -81,16 +81,12 @@ def __new__(cls) -> Self:
8181 # one here.
8282
8383 cls_parents = cls .__bases__
84- dataclass_fields : list [tuple [str , type | GenericAlias , ItemTemplate ]] = []
85- potential_fields = [
86- (name , getattr (cls , name , None ))
87- for name in dir (cls )
88- if not name .startswith ("__" )
89- ]
84+ dataclass_fields : list [tuple [str , type | GenericAlias , Field ]] = []
85+ potential_fields = [(name , getattr (cls , name , None )) for name in dir (cls )]
9086
9187 for name , attr in potential_fields :
92- if inspect .isclass (attr ):
93- if attr .__name__ == name :
88+ if inspect .isclass (attr ) and issubclass ( attr , Bunch ) :
89+ if name in ( attr .__name__ , dataclass_attr ) :
9490 # This is probably a class definition that just happens
9591 # to be located inside the containing Bunch definition.
9692 # This is fine.
@@ -99,29 +95,32 @@ def __new__(cls) -> Self:
9995
10096 msg = (
10197 f"Field '{ name } ' in the definition of '{ cls .__name__ } ' is"
102- " uninstantiated. Did you forget the parentheses ?"
98+ f " uninstantiated. Did you mean ' { attr . __name__ } ()' ?"
10399 )
104100 raise TypeError (msg )
105101
106- if isinstance (attr , ItemTemplate ):
107- # Safety check: make sure the user isn't accidentally overriding an
108- # existing attribute. We do however allow overriding an attribute
109- # with an attribute of the same type, which allows the user to
110- # override a Key with a Key of the same type but a different
111- # default value, for instance.
112-
113- for cls_parent in cls_parents :
114- if (
115- parent_attr := getattr (cls_parent , name , None )
116- ) is not None and type (parent_attr ) is not type (attr ):
117- msg = (
118- f"Field '{ name } ' in the definition of"
119- f" '{ cls .__name__ } ' overrides attribute of the"
120- " same name declared in parent class"
121- f" '{ cls_parent .__name__ } '; consider renaming"
122- f" this field to '{ name } _' for instance"
123- )
124- raise TypeError (msg )
102+ if not isinstance (attr , Field ):
103+ # Not actually a field, then.
104+ continue
105+
106+ # Safety check: make sure the user isn't accidentally overriding an
107+ # existing attribute. We do however allow overriding an attribute
108+ # with an attribute of the same type, which allows the user to
109+ # override a Key with a Key of the same type but a different
110+ # default value, for instance.
111+
112+ for cls_parent in cls_parents :
113+ if (
114+ parent_attr := getattr (cls_parent , name , None )
115+ ) is not None and type (parent_attr ) is not type (attr ):
116+ msg = (
117+ f"Field '{ name } ' in the definition of"
118+ f" '{ cls .__name__ } ' overrides attribute of the"
119+ " same name declared in parent class"
120+ f" '{ cls_parent .__name__ } '; consider renaming"
121+ f" this field to '{ name } _' for instance"
122+ )
123+ raise TypeError (msg )
125124
126125 # Create a proper field from the attribute.
127126
@@ -156,7 +155,7 @@ def __new__(cls) -> Self:
156155
157156 # Create an instance of the dataclass.
158157
159- new_cls = super ().__new__ (dataclass_class )
158+ new_cls : Self = super ().__new__ (dataclass_class )
160159
161160 # Set up the fields that were identified above as instance attributes.
162161
@@ -321,7 +320,8 @@ def isSet(self) -> bool:
321320
322321 @SettingsLock .with_write_lock
323322 def clear (self ) -> None :
324- [field .clear () for field in self ._fields .values ()]
323+ for field in self ._fields .values ():
324+ field .clear ()
325325
326326 @SettingsLock .with_read_lock
327327 def dumpFields (self ) -> Iterable [tuple [str , str | None ]]:
0 commit comments