Skip to content

Commit

Permalink
Generate a hash function when allow_mutation is False
Browse files Browse the repository at this point in the history
The generated hash is the hash of the dict_value
of the BaseModel.__dict__ attribute.

closes pydantic#1880
  • Loading branch information
rhuille committed Aug 30, 2020
1 parent 44616e3 commit 18f90fc
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/usage/model_config.md
Expand Up @@ -24,7 +24,7 @@ Options:
and `'allow'` will assign the attributes to the model.

**`allow_mutation`**
: whether or not models are faux-immutable, i.e. whether `__setattr__` is allowed (default: `True`)
: whether or not models are faux-immutable, i.e. whether `__setattr__` is allowed (default: `True`). `False` will make the model immutable and hashable i.e. a default `__hash__` function is generated for you, but you can still overwrite it.

**`use_enum_values`**
: whether to populate models with the `value` property of enums, rather than the raw enum.
Expand Down
1 change: 1 addition & 0 deletions pydantic/main.py
Expand Up @@ -313,6 +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,
**{n: v for n, v in namespace.items() if n not in fields},
}

Expand Down
25 changes: 25 additions & 0 deletions tests/test_main.py
Expand Up @@ -355,6 +355,19 @@ class Config:
assert '"TestModel" object has no field "b"' in exc_info.value.args[0]


def test_mutable_are_not_hashable():
class TestModel(BaseModel):
a: int = 10

class Config:
allow_mutation = True

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


def test_immutability():
class TestModel(BaseModel):
a: int = 10
Expand All @@ -373,6 +386,18 @@ class Config:
assert '"TestModel" object has no field "b"' in exc_info.value.args[0]


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

class Config:
allow_mutation = False

m = TestModel()
assert m.__hash__ is not None
assert isinstance(hash(m), int)


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

0 comments on commit 18f90fc

Please sign in to comment.