Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More coverage #503

Merged
merged 1 commit into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/cattrs/strategies/_class_methods.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"""Strategy for using class-specific (un)structuring methods."""

from inspect import signature
from typing import Any, Callable, Optional, Type, TypeVar

from cattrs import BaseConverter
from .. import BaseConverter

T = TypeVar("T")

Expand Down
86 changes: 86 additions & 0 deletions tests/preconf/test_pyyaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Pyyaml-specific tests."""
from datetime import date, datetime, timezone

from attrs import define
from hypothesis import given
from pytest import raises

from cattrs._compat import FrozenSetSubscriptable
from cattrs.errors import ClassValidationError
from cattrs.preconf.pyyaml import make_converter

from .._compat import is_py38
from ..test_preconf import Everything, everythings, native_unions


@given(everythings())
def test_pyyaml(everything: Everything):
from yaml import safe_dump, safe_load

converter = make_converter()
unstructured = converter.unstructure(everything)
raw = safe_dump(unstructured)
assert converter.structure(safe_load(raw), Everything) == everything


@given(everythings())
def test_pyyaml_converter(everything: Everything):
converter = make_converter()
raw = converter.dumps(everything)
assert converter.loads(raw, Everything) == everything


@given(everythings())
def test_pyyaml_converter_unstruct_collection_overrides(everything: Everything):
converter = make_converter(
unstruct_collection_overrides={FrozenSetSubscriptable: sorted}
)
raw = converter.unstructure(everything)
assert raw["a_frozenset"] == sorted(raw["a_frozenset"])


@given(
union_and_val=native_unions(include_bools=not is_py38), # Literal issues on 3.8
detailed_validation=...,
)
def test_pyyaml_unions(union_and_val: tuple, detailed_validation: bool):
"""Native union passthrough works."""
converter = make_converter(detailed_validation=detailed_validation)
type, val = union_and_val

assert converter.structure(val, type) == val


@given(detailed_validation=...)
def test_pyyaml_dates(detailed_validation: bool):
"""Pyyaml dates work."""
converter = make_converter(detailed_validation=detailed_validation)

@define
class A:
datetime: datetime
date: date

data = """
datetime: 1970-01-01T00:00:00Z
date: 1970-01-01"""
assert converter.loads(data, A) == A(
datetime(1970, 1, 1, tzinfo=timezone.utc), date(1970, 1, 1)
)

bad_data = """
datetime: 1
date: 1
"""

with raises(ClassValidationError if detailed_validation else Exception) as exc_info:
converter.loads(bad_data, A)

if detailed_validation:
assert (
repr(exc_info.value.exceptions[0])
== "Exception('Expected datetime, got 1')"
)
assert (
repr(exc_info.value.exceptions[1]) == "ValueError('Expected date, got 1')"
)
26 changes: 26 additions & 0 deletions tests/strategies/test_class_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,29 @@ def create(depth: int) -> Union["Nested", None]:
converter = BaseConverter()
use_class_methods(converter, "_structure", "_unstructure")
assert structured == converter.structure(converter.unstructure(structured), Nested)


def test_edge_cases():
"""Test some edge cases, for coverage."""

@define
class Bad:
a: int

@classmethod
def _structure(cls):
"""This has zero args, so can't work."""

@classmethod
def _unstructure(cls):
"""This has zero args, so can't work."""

converter = BaseConverter()

use_class_methods(converter, "_structure", "_unstructure")

# The methods take the wrong number of args, so this should fail.
with pytest.raises(TypeError):
converter.structure({"a": 1}, Bad)
with pytest.raises(TypeError):
converter.unstructure(Bad(1))
60 changes: 0 additions & 60 deletions tests/test_preconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
AbstractSet,
Counter,
FrozenSet,
FrozenSetSubscriptable,
Mapping,
MutableMapping,
MutableSequence,
Expand All @@ -48,7 +47,6 @@
from cattrs.preconf.cbor2 import make_converter as cbor2_make_converter
from cattrs.preconf.json import make_converter as json_make_converter
from cattrs.preconf.msgpack import make_converter as msgpack_make_converter
from cattrs.preconf.pyyaml import make_converter as pyyaml_make_converter
from cattrs.preconf.tomlkit import make_converter as tomlkit_make_converter
from cattrs.preconf.ujson import make_converter as ujson_make_converter

Expand Down Expand Up @@ -581,64 +579,6 @@ def test_bson_unions(union_and_val: tuple, detailed_validation: bool):
assert converter.structure(val, type) == val


@given(everythings())
def test_pyyaml(everything: Everything):
from yaml import safe_dump, safe_load

converter = pyyaml_make_converter()
unstructured = converter.unstructure(everything)
raw = safe_dump(unstructured)
assert converter.structure(safe_load(raw), Everything) == everything


@given(everythings())
def test_pyyaml_converter(everything: Everything):
converter = pyyaml_make_converter()
raw = converter.dumps(everything)
assert converter.loads(raw, Everything) == everything


@given(everythings())
def test_pyyaml_converter_unstruct_collection_overrides(everything: Everything):
converter = pyyaml_make_converter(
unstruct_collection_overrides={FrozenSetSubscriptable: sorted}
)
raw = converter.unstructure(everything)
assert raw["a_frozenset"] == sorted(raw["a_frozenset"])


@given(
union_and_val=native_unions(
include_bools=sys.version_info[:2] != (3, 8) # Literal issues on 3.8
),
detailed_validation=...,
)
def test_pyyaml_unions(union_and_val: tuple, detailed_validation: bool):
"""Native union passthrough works."""
converter = pyyaml_make_converter(detailed_validation=detailed_validation)
type, val = union_and_val

assert converter.structure(val, type) == val


@given(detailed_validation=...)
def test_pyyaml_dates(detailed_validation: bool):
"""Pyyaml dates work."""
converter = pyyaml_make_converter(detailed_validation=detailed_validation)

@define
class A:
datetime: datetime
date: date

data = """
datetime: 1970-01-01T00:00:00Z
date: 1970-01-01"""
assert converter.loads(data, A) == A(
datetime(1970, 1, 1, tzinfo=timezone.utc), date(1970, 1, 1)
)


@given(
everythings(
min_key_length=1,
Expand Down
Loading