(cls, is_frozen, weakref_slot, defined_fields)
| 1327 | |
| 1328 | |
| 1329 | def _add_slots(cls, is_frozen, weakref_slot, defined_fields): |
| 1330 | # Need to create a new class, since we can't set __slots__ after a |
| 1331 | # class has been created, and the @dataclass decorator is called |
| 1332 | # after the class is created. |
| 1333 | |
| 1334 | # Make sure __slots__ isn't already set. |
| 1335 | if '__slots__' in cls.__dict__: |
| 1336 | raise TypeError(f'{cls.__name__} already specifies __slots__') |
| 1337 | |
| 1338 | # Create a new dict for our new class. |
| 1339 | cls_dict = dict(cls.__dict__) |
| 1340 | field_names = tuple(f.name for f in fields(cls)) |
| 1341 | # Make sure slots don't overlap with those in base classes. |
| 1342 | inherited_slots = set( |
| 1343 | itertools.chain.from_iterable(map(_get_slots, cls.__mro__[1:-1])) |
| 1344 | ) |
| 1345 | |
| 1346 | cls_dict["__slots__"] = _create_slots( |
| 1347 | defined_fields, inherited_slots, field_names, weakref_slot, |
| 1348 | ) |
| 1349 | |
| 1350 | for field_name in field_names: |
| 1351 | # Remove our attributes, if present. They'll still be |
| 1352 | # available in _MARKER. |
| 1353 | cls_dict.pop(field_name, None) |
| 1354 | |
| 1355 | # Remove __dict__ and `__weakref__` descriptors. |
| 1356 | # They'll be added back if applicable. |
| 1357 | cls_dict.pop('__dict__', None) |
| 1358 | cls_dict.pop('__weakref__', None) # gh-102069 |
| 1359 | |
| 1360 | # And finally create the class. |
| 1361 | qualname = getattr(cls, '__qualname__', None) |
| 1362 | newcls = type(cls)(cls.__name__, cls.__bases__, cls_dict) |
| 1363 | if qualname is not None: |
| 1364 | newcls.__qualname__ = qualname |
| 1365 | |
| 1366 | if is_frozen: |
| 1367 | # Need this for pickling frozen classes with slots. |
| 1368 | if '__getstate__' not in cls_dict: |
| 1369 | newcls.__getstate__ = _dataclass_getstate |
| 1370 | if '__setstate__' not in cls_dict: |
| 1371 | newcls.__setstate__ = _dataclass_setstate |
| 1372 | |
| 1373 | # Fix up any closures which reference __class__. This is used to |
| 1374 | # fix zero argument super so that it points to the correct class |
| 1375 | # (the newly created one, which we're returning) and not the |
| 1376 | # original class. We can break out of this loop as soon as we |
| 1377 | # make an update, since all closures for a class will share a |
| 1378 | # given cell. |
| 1379 | for member in newcls.__dict__.values(): |
| 1380 | # If this is a wrapped function, unwrap it. |
| 1381 | member = inspect.unwrap(member) |
| 1382 | |
| 1383 | if isinstance(member, types.FunctionType): |
| 1384 | if _update_func_cell_for__class__(member, cls, newcls): |
| 1385 | break |
| 1386 | elif isinstance(member, property): |
no test coverage detected
searching dependent graphs…