Skip to content

slotted dataclasses not calling descriptor field __set__ #132946

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
thejcannon opened this issue Apr 25, 2025 · 7 comments
Open

slotted dataclasses not calling descriptor field __set__ #132946

thejcannon opened this issue Apr 25, 2025 · 7 comments
Assignees
Labels
stdlib Python modules in the Lib dir topic-dataclasses type-bug An unexpected behavior, bug, or error

Comments

@thejcannon
Copy link
Contributor

thejcannon commented Apr 25, 2025

Bug report

Bug description:

(Sorry to be finding these funny little dudes)

import dataclasses


class DescriptorFieldType:
    def __set_name__(self, owner, name: str) -> None:
        self.name = name

    def __get__(self, instance, owner):
        if instance is None:
            return None
        return instance.__dict__[self.name]

    def __set__(self, instance, value) -> None:
        raise ValueError("Unsettable")


@dataclasses.dataclass(slots=True)
class Slotted:
    field: DescriptorFieldType = DescriptorFieldType()

Slotted()

@dataclasses.dataclass
class Unslotted:
    field: DescriptorFieldType = DescriptorFieldType()

Unslotted()

Will error with:

Traceback (most recent call last):
  File "[...]", line 27, in <module>
    Unslotted()
  File "<string>", line 3, in __init__
  File "[...]", line 14, in __set__
    raise ValueError("Unsettable")
ValueError: Unsettable

(errors on Unslotted but not Slotted)

CPython versions tested on:

3.11

Operating systems tested on:

No response

Linked PRs

@thejcannon thejcannon added the type-bug An unexpected behavior, bug, or error label Apr 25, 2025
@picnixz picnixz added stdlib Python modules in the Lib dir topic-dataclasses labels Apr 25, 2025
@picnixz
Copy link
Member

picnixz commented Apr 25, 2025

cc @ericvsmith

@sobolevn
Copy link
Member

My wild guess: maybe this happens because we recreate Slotted class?
It would be really interesting to find out. Thanks a lot!

@sobolevn sobolevn self-assigned this Apr 25, 2025
@thejcannon
Copy link
Contributor Author

Only the finest issues for my two favorite python features 🙃

@ericvsmith
Copy link
Member

From https://docs.python.org/2/reference/datamodel.html?highlight=__slots__#__slots__:

slots are implemented at the class level by creating descriptors (Implementing Descriptors) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by slots; otherwise, the class attribute would overwrite the descriptor assignment.

It might be possible to detect this and raise an error, if someone wants to look into it.

@sobolevn
Copy link
Member

I found one more problem related to descriptors in @dataclass:

class DescriptorFieldType:
    def __get__(self, instance, owner):
        print(instance, owner)
        if instance is None:
            return 1
        return 2

@dataclass
class Regular:
    one: DescriptorFieldType = DescriptorFieldType()

assert Regular.one == 1
assert Regular().one == 2  # fails, is actually `1`

I propose to discus this in a separate issue.

@thejcannon
Copy link
Contributor Author

I tried to consider if dataclasses could then wrap/replace the slots descriptors in custom descriptors that called into the user provided one, but:

  • oof, descriptors on descriptors
  • that still wouldn't work since the user descriptor wouldn't be able to set or get attributes

Since this isn't a dataclasses bug, feel free to close. (The alternative being raise an error as @ericvsmith suggests)

@thejcannon
Copy link
Contributor Author

Ah, wait I think raising an error is the right way to go:

# This declaration doesn't error
@dataclass(slots=True)
class SlottedDataclass:
    name: Descriptor = Descriptor()

# This declaration errors
class Slotted:
    __slots__ = ("name",)

    name = Descriptor()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir topic-dataclasses type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

4 participants