Skip to content

Commit

Permalink
feat: ✨ Add more types to be automatically checked for equality
Browse files Browse the repository at this point in the history
  • Loading branch information
ddanier committed Apr 23, 2024
1 parent ab3aa35 commit b0e4336
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
32 changes: 30 additions & 2 deletions pydantic_changedetect/changedetect.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import decimal
import warnings
from typing import (
Expand Down Expand Up @@ -32,6 +33,14 @@
Model = TypeVar("Model", bound="ChangeDetectionMixin")

NO_VALUE = object()
COMPARABLE_TYPES = (
str,
int, float,
bool,
decimal.Decimal,
datetime.datetime, datetime.date, datetime.time, datetime.timedelta,
pydantic.BaseModel,
)


class ChangeDetectionMixin(pydantic.BaseModel):
Expand Down Expand Up @@ -221,11 +230,30 @@ def model_set_changed(self, *fields: str, original: Any = NO_VALUE) -> None:
self.model_self_changed_fields.add(name)

def _model_value_is_comparable_type(self, value: Any) -> bool:
if isinstance(value, (list, set, tuple)):
return all(
self._model_value_is_comparable_type(i)
for i
in value
)
elif isinstance(value, dict):
return all(
(
self._model_value_is_comparable_type(k)
and self._model_value_is_comparable_type(v)
)
for k, v
in value.items()
)

return (
value is None
or isinstance(value, (str, int, float, bool, decimal.Decimal))
or isinstance(value, COMPARABLE_TYPES)
)

def _model_value_is_actually_unchanged(self, value1: Any, value2: Any) -> bool:
return value1 == value2

@no_type_check
def __setattr__(self, name, value) -> None: # noqa: ANN001
self_compat = PydanticCompat(self)
Expand Down Expand Up @@ -257,7 +285,7 @@ def __setattr__(self, name, value) -> None: # noqa: ANN001
if (
self._model_value_is_comparable_type(original_value)
and self._model_value_is_comparable_type(current_value)
and original_value == current_value
and self._model_value_is_actually_unchanged(original_value, current_value)
):
has_changed = False

Expand Down
17 changes: 16 additions & 1 deletion tests/test_changedetect.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import decimal
import pickle
from typing import Any, Dict, List, Optional, Tuple, Union
Expand Down Expand Up @@ -53,6 +54,10 @@ class SomethingWithDifferentValueTypes(ChangeDetectionMixin, pydantic.BaseModel)
f: Union[float, None] = None
b: Union[bool, None] = None
d: Union[decimal.Decimal, None] = None
ddt: Union[datetime.datetime, None] = None
dd: Union[datetime.date, None] = None
dt: Union[datetime.time, None] = None
dtd: Union[datetime.timedelta, None] = None
m: Union[Something, None] = None


Expand Down Expand Up @@ -437,8 +442,18 @@ class SomethingPrivate(Something):
("b", True, True, False),
("d", decimal.Decimal(1), decimal.Decimal(2), True),
("d", decimal.Decimal(1), decimal.Decimal(1), False),
("ddt", datetime.datetime(1970, 1,1, 0, 0), datetime.datetime(1970, 1,1, 0, 1), True),
("ddt", datetime.datetime(1970, 1,1, 0, 0), datetime.datetime(1970, 1,2, 0, 0), True),
("ddt", datetime.datetime(1970, 1,1, 0, 0), datetime.datetime(1970, 1,1, 0, 0), False),
("dd", datetime.date(1970, 1,1), datetime.date(1970, 1,2), True),
("dd", datetime.date(1970, 1,1), datetime.date(1970, 1,1), False),
("dt", datetime.time(12, 34), datetime.time(12, 56), True),
("dt", datetime.time(12, 34), datetime.time(12, 34), False),
("dtd", datetime.timedelta(days=1), datetime.timedelta(seconds=1), True),
("dtd", datetime.timedelta(hours=1), datetime.timedelta(seconds=3600), False),
("dtd", datetime.timedelta(days=1), datetime.timedelta(days=1), False),
("m", Something(id=1), Something(id=2), True),
("m", Something(id=1), Something(id=1), True), # models will always be counted as changed
("m", Something(id=1), Something(id=1), False),
],
)
def test_value_types_checked_for_equality(
Expand Down

0 comments on commit b0e4336

Please sign in to comment.