python-attrs / python-attrs/attrs

Why descriptor don't work with attr?

Open
#881 13 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.8k
Forks
480
Avg merge
2h 15m
Merged PRs (30d)
2

Description

I am reading Architecture Patterns with Python and at some point the frozen dataclasses just don't work with sqlalchemy, so the author suggests setting unsafe_hash=True. To avoid making the class hashable I am using the descriptor bellow. I was also using @attr.s instead of @dataclass to get some of the features it has, like validators, but for some reason the descriptor works fine with dataclass, but not with attr and I can't understand why and if there is a way to make it work. If I set init=False on the attr.s class the descriptor will work for self.vid the I am only setting inside the init, but don't work with street and number.

from dataclasses import dataclass
import attr
import uuid


class Frozen:
    """cannot change attribute if it already exists"""

    def __set_name__(self, owner, name):
        self.storage_name = '_' + name

    def __set__(self, instance, value):
        if hasattr(instance, self.storage_name):
            raise AttributeError(
                f'readonly attribute {value.__class__.__name__}')
        else:
            setattr(instance, self.storage_name, value)

    def __get__(self, instance, objtype=None):
        return getattr(instance, self.storage_name)

# Frozen descriptor works fine here
@dataclass
class Address:

    vid = Frozen()
    street = Frozen()
    number = Frozen()

    street: str
    number: str

    def __init__(self, street: str, number: str, vid=None) -> None:
        self.street = street
        self.number = number
        self.vid = vid if vid else uuid.uuid4().hex

# Frozen descriptor don't work here
@attr.s
class Address:

    vid = Frozen()
    street = Frozen()
    number = Frozen()

    street: str = attr.ib
    number: str = attr.ib

    def __init__(self, street: str, number: str, vid=None) -> None:
        self.street = street
        self.number = number
        self.vid = vid if vid else uuid.uuid4().hex

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No repository file or test is named. Reproduce the comparison between the dataclass and @attr.s examples, focusing on the Frozen descriptor, generated initialization, and the custom init. Done means establishing whether this is expected behavior and identifying the appropriate fix or documentation change.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.