Skip to content

Commit

Permalink
test: 🚨 Test for DeprecationWarning's, too
Browse files Browse the repository at this point in the history
  • Loading branch information
ddanier committed Jul 18, 2023
1 parent e541d59 commit d0c0a0f
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 53 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/flake8-mypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ jobs:
poetry install
- name: Lint with flake8
run: |
poetry run flake8 .
poetry run mypy .
poetry run flake8 pydantic_changedetect tests
poetry run mypy pydantic_changedetect
179 changes: 138 additions & 41 deletions tests/test_changedetect.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,17 @@ def test_copy_keeps_state():
def test_copy_keeps_state_with_v1_api():
obj = Something(id=1)

assert not obj.copy().model_has_changed
assert obj.copy().model_changed_fields == set()
with pytest.warns(DeprecationWarning):
assert not obj.copy().model_has_changed
with pytest.warns(DeprecationWarning):
assert obj.copy().model_changed_fields == set()

obj.id = 2

assert obj.copy().model_has_changed
assert obj.copy().model_changed_fields == {"id"}
with pytest.warns(DeprecationWarning):
assert obj.copy().model_has_changed
with pytest.warns(DeprecationWarning):
assert obj.copy().model_changed_fields == {"id"}


@pytest.mark.skipif(PYDANTIC_V1, reason="pydantic v1 does not support model_dump()")
Expand All @@ -132,7 +136,22 @@ def test_export_as_dict():
assert obj.model_dump(exclude_unchanged=True) == {"id": 2}


# Test on pydantic v2, too - pydantic has a compatibility layer for this
@pytest.mark.skipif(PYDANTIC_V1, reason="pydantic v1 does not trigger warnings")
def test_export_as_dict_with_v1_api_on_v2():
obj = Something(id=1)

with pytest.warns(DeprecationWarning):
assert obj.dict() == {"id": 1}
with pytest.warns(DeprecationWarning):
assert obj.dict(exclude_unchanged=True) == {}

obj.id = 2

with pytest.warns(DeprecationWarning):
assert obj.dict(exclude_unchanged=True) == {"id": 2}


@pytest.mark.skipif(PYDANTIC_V2, reason="pydantic v2 does trigger warnings")
def test_export_as_dict_with_v1_api():
obj = Something(id=1)

Expand Down Expand Up @@ -160,12 +179,15 @@ def test_export_as_json():
def test_export_as_json_with_v1_api_on_v2():
obj = Something(id=1)

assert obj.json() == '{"id":1}'
assert obj.json(exclude_unchanged=True) == '{}'
with pytest.warns(DeprecationWarning):
assert obj.json() == '{"id":1}'
with pytest.warns(DeprecationWarning):
assert obj.json(exclude_unchanged=True) == '{}'

obj.id = 2

assert obj.json(exclude_unchanged=True) == '{"id":2}'
with pytest.warns(DeprecationWarning):
assert obj.json(exclude_unchanged=True) == '{"id":2}'


@pytest.mark.skipif(PYDANTIC_V2, reason="pydantic v2 does have a slightly different result")
Expand All @@ -192,7 +214,22 @@ def test_export_include_is_intersect():
assert something.model_dump(exclude_unchanged=True, include={'id'}) == {"id": 2}


# Test on pydantic v2, too - pydantic has a compatibility layer for this
@pytest.mark.skipif(PYDANTIC_V1, reason="pydantic v1 does not trigger warnings")
def test_export_include_is_intersect_with_v1_api_on_v2():
something = Something(id=1)

with pytest.warns(DeprecationWarning):
assert something.dict(exclude_unchanged=True, include={'name'}) == {}

something.id = 2

with pytest.warns(DeprecationWarning):
assert something.dict(exclude_unchanged=True, include=set()) == {}
with pytest.warns(DeprecationWarning):
assert something.dict(exclude_unchanged=True, include={'id'}) == {"id": 2}


@pytest.mark.skipif(PYDANTIC_V2, reason="pydantic v2 does trigger warnings")
def test_export_include_is_intersect_with_v1_api():
something = Something(id=1)

Expand All @@ -216,7 +253,21 @@ def test_changed_base_is_resetable():
assert something.model_dump(exclude_unchanged=True) == {}


# Test on pydantic v2, too - pydantic has a compatibility layer for this
@pytest.mark.skipif(PYDANTIC_V1, reason="pydantic v1 does not trigger warnings")
def test_changed_base_is_resetable_with_v1_api_on_v2():
something = Something(id=1)
something.id = 2

with pytest.warns(DeprecationWarning):
assert something.dict(exclude_unchanged=True) == {"id": 2}

something.model_reset_changed()

with pytest.warns(DeprecationWarning):
assert something.dict(exclude_unchanged=True) == {}


@pytest.mark.skipif(PYDANTIC_V2, reason="pydantic v2 does trigger warnings")
def test_changed_base_is_resetable_with_v1_api():
something = Something(id=1)
something.id = 2
Expand All @@ -231,22 +282,22 @@ def test_changed_base_is_resetable_with_v1_api():
def test_pickle_keeps_state():
obj = Something(id=1)

assert not pickle.loads(pickle.dumps(obj)).has_changed
assert pickle.loads(pickle.dumps(obj)).__changed_fields__ == set()
assert not pickle.loads(pickle.dumps(obj)).model_has_changed
assert pickle.loads(pickle.dumps(obj)).model_changed_fields == set()

obj.id = 2

assert pickle.loads(pickle.dumps(obj)).has_changed
assert pickle.loads(pickle.dumps(obj)).__changed_fields__ == {"id"}
assert pickle.loads(pickle.dumps(obj)).model_has_changed
assert pickle.loads(pickle.dumps(obj)).model_changed_fields == {"id"}


def test_pickle_even_works_when_changed_state_is_missing():
obj = SomethingWithBrokenPickleState(id=1)
obj.id = 2

# Now we cannot use the changed state, but nothing fails
assert not pickle.loads(pickle.dumps(obj)).has_changed
assert pickle.loads(pickle.dumps(obj)).__changed_fields__ == set()
assert not pickle.loads(pickle.dumps(obj)).model_has_changed
assert pickle.loads(pickle.dumps(obj)).model_changed_fields == set()


def test_stores_original():
Expand Down Expand Up @@ -346,8 +397,21 @@ class SomethingPrivate(Something):
assert something.model_has_changed is False


@pytest.mark.skipif(PYDANTIC_V1, reason="pydantic v1 does not support model_construct()")
def test_model_construct_works():
something = Something.model_construct(id=1)

assert something.model_has_changed is False

something.id = 2

assert something.model_has_changed is True


@pytest.mark.skipif(PYDANTIC_V1, reason="pydantic v1 does not trigger warnings")
def test_construct_works():
something = Something.construct(id=1)
with pytest.warns(DeprecationWarning):
something = Something.construct(id=1)

assert something.model_has_changed is False

Expand All @@ -356,35 +420,68 @@ def test_construct_works():
assert something.model_has_changed is True


def test_compatibility_methods_work():
something = Something(id=1)
@pytest.mark.skipif(PYDANTIC_V2, reason="pydantic v2 does trigger warnings")
def test_construct_works():
something = Something.construct(id=1)

assert something.has_changed is False
assert not something.__self_changed_fields__
assert not something.__changed_fields__
assert not something.__changed_fields_recursive__
assert something.__original__ == {}
assert something.model_has_changed is False

something.id = 2

assert something.has_changed is True
assert something.__self_changed_fields__ == {"id"}
assert something.__changed_fields__ == {"id"}
assert something.__changed_fields_recursive__ == {"id"}
assert something.__original__ == {"id": 1}
assert something.model_has_changed is True

something.reset_changed()

assert something.has_changed is False
assert not something.__self_changed_fields__
assert not something.__changed_fields__
assert not something.__changed_fields_recursive__
assert something.__original__ == {}
def test_compatibility_methods_work():
something = Something(id=1)

something.set_changed("id", original=1)
with pytest.warns(DeprecationWarning):
assert something.has_changed is False
with pytest.warns(DeprecationWarning):
assert not something.__self_changed_fields__
with pytest.warns(DeprecationWarning):
assert not something.__changed_fields__
with pytest.warns(DeprecationWarning):
assert not something.__changed_fields_recursive__
with pytest.warns(DeprecationWarning):
assert something.__original__ == {}

something.id = 2

assert something.has_changed is True
assert something.__self_changed_fields__ == {"id"}
assert something.__changed_fields__ == {"id"}
assert something.__changed_fields_recursive__ == {"id"}
assert something.__original__ == {"id": 1}
with pytest.warns(DeprecationWarning):
assert something.has_changed is True
with pytest.warns(DeprecationWarning):
assert something.__self_changed_fields__ == {"id"}
with pytest.warns(DeprecationWarning):
assert something.__changed_fields__ == {"id"}
with pytest.warns(DeprecationWarning):
assert something.__changed_fields_recursive__ == {"id"}
with pytest.warns(DeprecationWarning):
assert something.__original__ == {"id": 1}

with pytest.warns(DeprecationWarning):
something.reset_changed()

with pytest.warns(DeprecationWarning):
assert something.has_changed is False
with pytest.warns(DeprecationWarning):
assert not something.__self_changed_fields__
with pytest.warns(DeprecationWarning):
assert not something.__changed_fields__
with pytest.warns(DeprecationWarning):
assert not something.__changed_fields_recursive__
with pytest.warns(DeprecationWarning):
assert something.__original__ == {}

with pytest.warns(DeprecationWarning):
something.set_changed("id", original=1)

with pytest.warns(DeprecationWarning):
assert something.has_changed is True
with pytest.warns(DeprecationWarning):
assert something.__self_changed_fields__ == {"id"}
with pytest.warns(DeprecationWarning):
assert something.__changed_fields__ == {"id"}
with pytest.warns(DeprecationWarning):
assert something.__changed_fields_recursive__ == {"id"}
with pytest.warns(DeprecationWarning):
assert something.__original__ == {"id": 1}
30 changes: 20 additions & 10 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,23 @@ class AbstractClass(BaseClass, abc.ABC):


def test_safe_issubclass():
assert safe_issubclass(NormalClass, BaseClass)
assert safe_issubclass(AbstractClass, BaseClass)
with pytest.warns(DeprecationWarning):
assert safe_issubclass(NormalClass, BaseClass)
with pytest.warns(DeprecationWarning):
assert safe_issubclass(AbstractClass, BaseClass)


def test_safe_issubclass_for_type_definitions():
assert safe_issubclass(List[str], BaseClass) is False
assert safe_issubclass(Dict[str, str], BaseClass) is False
with pytest.warns(DeprecationWarning):
assert safe_issubclass(List[str], BaseClass) is False
with pytest.warns(DeprecationWarning):
assert safe_issubclass(Dict[str, str], BaseClass) is False

if sys.version_info >= (3, 9):
assert safe_issubclass(list[str], BaseClass) is False
assert safe_issubclass(dict[str, str], BaseClass) is False
with pytest.warns(DeprecationWarning):
assert safe_issubclass(list[str], BaseClass) is False
with pytest.warns(DeprecationWarning):
assert safe_issubclass(dict[str, str], BaseClass) is False


def test_ensure_normal_issubclass_raises_an_issue():
Expand All @@ -41,12 +47,16 @@ def test_ensure_normal_issubclass_raises_an_issue():


def test_safe_issubclass_for_type_definitions_for_abstract():
assert safe_issubclass(List[str], AbstractClass) is False
assert safe_issubclass(Dict[str, str], AbstractClass) is False
with pytest.warns(DeprecationWarning):
assert safe_issubclass(List[str], AbstractClass) is False
with pytest.warns(DeprecationWarning):
assert safe_issubclass(Dict[str, str], AbstractClass) is False

if sys.version_info >= (3, 9):
assert safe_issubclass(list[str], AbstractClass) is False
assert safe_issubclass(dict[str, str], AbstractClass) is False
with pytest.warns(DeprecationWarning):
assert safe_issubclass(list[str], AbstractClass) is False
with pytest.warns(DeprecationWarning):
assert safe_issubclass(dict[str, str], AbstractClass) is False


class SomeModel(ChangeDetectionMixin, pydantic.BaseModel):
Expand Down

0 comments on commit d0c0a0f

Please sign in to comment.