66import keyword
77import builtins
88import functools
9+ import itertools
910import abc
1011import _thread
1112from types import FunctionType , GenericAlias
@@ -1122,6 +1123,20 @@ def _dataclass_setstate(self, state):
11221123 object .__setattr__ (self , field .name , value )
11231124
11241125
1126+ def _get_slots (cls ):
1127+ match cls .__dict__ .get ('__slots__' ):
1128+ case None :
1129+ return
1130+ case str (slot ):
1131+ yield slot
1132+ # Slots may be any iterable, but we cannot handle an iterator
1133+ # because it will already be (partially) consumed.
1134+ case iterable if not hasattr (iterable , '__next__' ):
1135+ yield from iterable
1136+ case _:
1137+ raise TypeError (f"Slots of '{ cls .__name__ } ' cannot be determined" )
1138+
1139+
11251140def _add_slots (cls , is_frozen ):
11261141 # Need to create a new class, since we can't set __slots__
11271142 # after a class has been created.
@@ -1133,7 +1148,13 @@ def _add_slots(cls, is_frozen):
11331148 # Create a new dict for our new class.
11341149 cls_dict = dict (cls .__dict__ )
11351150 field_names = tuple (f .name for f in fields (cls ))
1136- cls_dict ['__slots__' ] = field_names
1151+ # Make sure slots don't overlap with those in base classes.
1152+ inherited_slots = set (
1153+ itertools .chain .from_iterable (map (_get_slots , cls .__mro__ [1 :- 1 ]))
1154+ )
1155+ cls_dict ["__slots__" ] = tuple (
1156+ itertools .filterfalse (inherited_slots .__contains__ , field_names )
1157+ )
11371158 for field_name in field_names :
11381159 # Remove our attributes, if present. They'll still be
11391160 # available in _MARKER.
0 commit comments