`__hash__` TypeError in v0.12.1
Closed this issue Β· 6 comments
Describe the bug
After upgrading from v0.12.0 to v0.12.1 my test suite fails with a lot of these errors:
self = Service({'id': None, 'created_at': None, 'updated_at': None, 'more sensitive fields')
name = 'sensitive', value = UUID('53cadcf0-cdce-4101-8218-22c96d3edd39')
def __setattr__(self, name: str, value: Any) -> None: # noqa CCR001
"""
Overwrites setattr in pydantic parent as otherwise descriptors are not called.
:param name: name of the attribute to set
:type name: str
:param value: value of the attribute to set
:type value: Any
:return: None
:rtype: None
"""
> prev_hash = hash(self)
E TypeError: __hash__ method should return an integer
.venv/lib/python3.11/site-packages/ormar/models/newbasemodel.py:184: TypeError
I'm not sure, but I think every ormar model initialization fails with the same error. Any clue as to why? π
Can you provide a sample code to reproduce? Cause the whole tet suite pass on win and linux for python 3.7-3.10 π
Looks like the issue goes away if I specify id
on my models. The id
field is auto-incrementing and defined on a base model like this:
class ModelBase(ormar.Model):
id: int = ormar.Integer(autoincrement=True, primary_key=True)
created_at: datetime = ormar.DateTime(timezone=True, server_default=func.now())
updated_at: datetime = ormar.DateTime(timezone=True, server_default=func.now(), onupdate=func.now())
class Meta:
abstract = True
When I run MyModel.objects.create(...)
without an explicit id
it fails with the above error.
In the __setattr__
method, for prev_hash = hash(self)
, self
is a model instance with id
set as None
. Might that be relevant?
Just adding this here in case that's enough to shed some light on the issue. Will try to dedicate some more time to creating a reproducible example later if needed π
Well, it's calculated as this:
def __hash__(self) -> int:
if getattr(self, "__cached_hash__", None) is not None:
return self.__cached_hash__ or 0
if self.pk is not None:
ret = hash(str(self.pk) + self.__class__.__name__)
else:
vals = {
k: v
for k, v in self.__dict__.items()
if k not in self.extract_related_names()
}
ret = hash(str(vals) + self.__class__.__name__)
object.__setattr__(self, "__cached_hash__", ret)
return ret
It might be the case that it does not play well in some cases with inheritance (although we have tests for that too, not sure if base classes have id's though, don't remember).
Ok, I'll come back with a full reproducible example when I get a chance π
I'm sorry @collerek, this is a false positive. After a little bit of digging, I found this π
class ModelBase(ormar.Model):
id: int = ormar.Integer(autoincrement=True, primary_key=True)
created_at: datetime = ormar.DateTime(timezone=True, server_default=func.now())
updated_at: datetime = ormar.DateTime(timezone=True, server_default=func.now(), onupdate=func.now())
class Meta:
abstract = True
# about 100 lines of code here
def __hash__(self) -> int: # <-- this is the culprit!
return self.id
My bad. Thanks for the help!
No worries, glad that it's sorted out