classification
Title: Frozen dataclasses with slots raise TypeError
Type: behavior Stage: patch review
Components: Library (Lib) Versions: Python 3.11, Python 3.10
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: eric.smith Nosy List: AlexWaygood, eric.smith, trey
Priority: normal Keywords: patch

Created on 2021-11-25 01:53 by trey, last changed 2021-12-02 17:20 by AlexWaygood.

Pull Requests
URL Status Linked Edit
PR 29895 open AlexWaygood, 2021-12-02 17:20
Messages (4)
msg406973 - (view) Author: Trey Hunner (trey) * Date: 2021-11-25 01:53
When making a dataclass with slots=True and frozen=True, assigning to an invalid attribute raises a TypeError rather than a FrozenInstanceError:

>>> from dataclasses import dataclass
>>> @dataclass(frozen=True, slots=True)
... class Vector:
...     x: float
...     y: float
...     z: float
...
>>> v = Vector(1, 2, 3)
>>> v.a = 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 5, in __setattr__
TypeError: super(type, obj): obj must be an instance or subtype of type
msg406974 - (view) Author: Alex Waygood (AlexWaygood) * (Python triager) Date: 2021-11-25 02:27
This looks to be due to the fact that `slots=True` leads to the creation of an entirely new class (see line 1102), meaning that in the `super(cls, self)` calls in lines 611 and 618 (in the `_frozen_get_del_attr` function, responsible for generating `__setattr__` and `__delattr__` methods), `self` is no longer an instance of `cls`.

I believe this can be fixed by tweaking `_frozen_get_del_attr` so that `cls` in the generated `__setattr__` and `__delattr__` methods is dynamically computed (`cls = type(self)`), rather than read from a closure, as is currently the case.
msg406995 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2021-11-25 13:20
I think the error should be AttributeError, which is what you'd get if the class weren't frozen.
msg407540 - (view) Author: Alex Waygood (AlexWaygood) * (Python triager) Date: 2021-12-02 16:55
You get the same error if you subclass a frozen dataclass, then try to set an attribute that is not one of the superclass's __slots__:

```
>>> @dataclass(slots=True, frozen=True)
... class Point:
...     x: int
...     y: int
... 
...     
>>> class Subclass(Point): pass
... 
>>> s = Subclass(1, 2)
>>> s.z = 5
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    s.z = 5
  File "<string>", line 7, in __setattr__
TypeError: super(type, obj): obj must be an instance or subtype of type
```
History
Date User Action Args
2021-12-02 17:20:40AlexWaygoodsetkeywords: + patch
stage: patch review
pull_requests: + pull_request28119
2021-12-02 16:55:46AlexWaygoodsetmessages: + msg407540
2021-11-25 13:20:38eric.smithsetmessages: + msg406995
2021-11-25 07:01:30eric.smithsetversions: + Python 3.11
2021-11-25 07:01:15eric.smithsetassignee: eric.smith
2021-11-25 03:12:58xtreaksetnosy: + eric.smith
2021-11-25 02:27:53AlexWaygoodsetnosy: + AlexWaygood
messages: + msg406974
components: + Library (Lib)
2021-11-25 01:53:51treycreate