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
17.3.0 cmp/default/hash/WeakKeyDictionary interaction #289
Comments
Traceback:
|
I think the proper solution to this is to eliminate the nonsense combination of One possible solution is to make |
The bad commit courtesy of |
relating #170 |
I just woke up but looking at this it seems like this unearthed former buggy behavior? Wasn’t it comparing the attribute definition before? |
How? All I see is eq?
I don’t quite follow, isn’t that the only way how you can compare mutable objects by their values? I’m rather surprised that Weakref uses eq, but I’ll have to look at it later when I’m at a computer. |
So it seems like the answer to the question what’s happening is buried somewhere in C code and as far as I can see, this is absolutely not a regression in What @glyph’s code was doing is adding a half-initialized instance into a This is btw why I’ve fought so long and so hard against passing half-initialized instances around. I was bound to get my morning ruined like this eventually. If you’d like to follow along, this is equivalent to the reproducer above: >>> import weakref
... d = weakref.WeakKeyDictionary()
... class C:
... __hash__ = object.__hash__
... def __eq__(self, other):
... return self.x == other.x and self.y == other.y
... def __init__(self):
... d[self] = 1
... self.x = 1
... d[self]
... self.y = 2
>>> C()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in __init__
File "/Users/hynek/.virtualenvs/attrs/lib/python3.6/weakref.py", line 394, in __getitem__
return self.data[ref(key)]
File "<stdin>", line 6, in __eq__
AttributeError: 'C' object has no attribute 'y'
'C' object has no attribute 'y' Now the question is: why does What I believe it comes down is some internal stuff in >>> class C:
... __hash__ = object.__hash__
... def __eq__(self, other):
... raise Exception("WTF")
>>> d = {}
>>> wr1 = weakref.ref(c)
>>> wr2 = weakref.ref(c)
>>> d[wr1] = 1
>>> d[wr2]
1 but this doesn’t: >>> d = {}
>>> wr1 = weakref.ref(c, lambda *a, **kw: None)
>>> wr2 = weakref.ref(c, lambda *a, **kw: None)
>>> d[wr1] = 1
>>> d[wr2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __eq__
Exception: WTF
WTF (it’s enough for one of the refs to have a callback). If someone feels like diving into the C implementation of All that to say: what do you want me to do about it? Add a stern warning about half-initialized instances and that they may behave weirdly? |
Upon further consideration, you're right; the code was previously buggy, and this makes it a little easier to tell. However, |
I think that when I filed this issue originally I was under the misguided impression that |
(Also: maybe Klein should stop trying to hash instances with a |
I still can’t wrap my head around the fact that it’s supposed to be a mistake to make an object hashable by ID but comparable by value – to me that’s like comparing pointers/references vs comparing values. If I knew what unpractical tire fire this is when I started writing attrs, hash and cmp would have been off by default. It’s so upsetting someone ever thought this might be a good idea and I bet 99.999% of Python programmers don’t realize this. I think I’ll have to write a blog about all of this to order my thoughts and look forward to the reactions. |
I'm happy to explain why |
The question is what next. I want to move to semantic enums medium term but even then people will probably be confused. As an immediate solution I guess I could rewrite the post for attrs and make it the first narrative doc? |
On 17.3.0, an
@attr.s(hash=False)
class which has anattr.ib
with a@default
that looks itself up in aWeakKeyDictionary
into which it has previously been placed by an earlier@default
raises an AttributeError on construction, unless@attr.s(cmp=False)
is also set.This worked properly on 17.2.0.
The text was updated successfully, but these errors were encountered: