Skip to content

Commit

Permalink
Generated hash is the same as builtin dataclass
Browse files Browse the repository at this point in the history
Before:
```
class A(BaseModel):
    x: int
    y: List[int]

    class Config:
        allow_mutation = False

a = A(x=1, y=[1,2,3])
d = {a: 2}
d[a]
```
does not raise any error.

After:
It raises a Type error: "unhashable type: 'list'" because
`y` field is not hashable.

NB:
This copy the behavior of builtin dataclass where the hash
function is the hash of the *tuple* of the fields.

In the previous commit, I was using the hash of the
*dict_values* of the fields dict, which does not raise
if values are not hashable.
  • Loading branch information
rhuille committed Sep 6, 2020
1 parent d678207 commit b1a84dc
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 2 deletions.
2 changes: 1 addition & 1 deletion pydantic/main.py
Expand Up @@ -313,7 +313,7 @@ def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901
'__schema_cache__': {},
'__json_encoder__': staticmethod(json_encoder),
'__custom_root_type__': _custom_root_type,
'__hash__': (lambda self: hash(self.__dict__.values())) if not config.allow_mutation else None,
'__hash__': (lambda self: hash(tuple(self.__dict__.values()))) if not config.allow_mutation else None,
**{n: v for n, v in namespace.items() if n not in fields},
}

Expand Down
16 changes: 15 additions & 1 deletion tests/test_main.py
Expand Up @@ -386,7 +386,7 @@ class Config:
assert '"TestModel" object has no field "b"' in exc_info.value.args[0]


def test_immutable_are_hashable():
def test_immutable_with_hashable_fields_are_hashable():
class TestModel(BaseModel):
a: int = 10

Expand All @@ -398,6 +398,20 @@ class Config:
assert isinstance(hash(m), int)


def test_immutable_with_unhashable_fields_are_not_hashable():
class TestModel(BaseModel):
a: int = 10
y: List[int] = [1, 2, 3]

class Config:
allow_mutation = False

m = TestModel()
with pytest.raises(TypeError) as exc_info:
hash(m)
assert "unhashable type: 'list'" in exc_info.value.args[0]


def test_const_validates():
class Model(BaseModel):
a: int = Field(3, const=True)
Expand Down

0 comments on commit b1a84dc

Please sign in to comment.