def test_not_a_dataclass(module): """Raises when object is not a dataclass.""" class A: x: int with pytest.raises(desert.exceptions.NotAnAttrsClassOrDataclass): desert.schema_class(A)
def test_raise_unknown_type(module): """Raise UnknownType for failed inferences.""" @module.dataclass class A: x: list with pytest.raises(desert.exceptions.UnknownType): desert.schema_class(A)
def test_raise_unknown_generic(module): """Raise UnknownType for unknown generics.""" @module.dataclass class A: x: t.Sequence[int] with pytest.raises(desert.exceptions.UnknownType): desert.schema_class(A)
def test_raise_unknown_generic(module: DataclassModule) -> None: """Raise UnknownType for unknown generics.""" @module.dataclass class A: x: UnknownGeneric[int] with pytest.raises(desert.exceptions.UnknownType): desert.schema_class(A)
def test_raise_unknown_type(module: DataclassModule) -> None: """Raise UnknownType for failed inferences.""" @module.dataclass class A: x: list # type: ignore[type-arg] with pytest.raises(desert.exceptions.UnknownType): desert.schema_class(A)
def build(self, cls): schema = desert.schema_class(cls)() prepped = self.prep(cls) try: return schema.load(prepped) except marshmallow.exceptions.ValidationError as e: raise exceptions.ValidationError(*e.args) from e
def test_optional_present(module): """Setting an optional type allows passing None.""" @module.dataclass class A: x: t.Optional[int] data = desert.schema_class(A)().load({"x": None}) assert data == A(None)
def test_attr_factory(): """Attrs default factory instantiates the factory type if no value is passed.""" @attr.dataclass class A: x: t.List[int] = attr.ib(factory=list) data = desert.schema_class(A)().load({}) assert data == A([])
def test_dataclasses_factory(): """Dataclasses default factory instantiates the factory type if no value is passed.""" @dataclasses.dataclass class A: x: t.List[int] = dataclasses.field(default_factory=list) data = desert.schema_class(A)().load({}) assert data == A([])
def test_list(module: DataclassModule, annotation_class: type) -> None: """Build a generic list *without* setting a factory on the dataclass.""" cls = type("A", (object,), {"__annotations__": {"y": annotation_class[int]}}) # type: ignore[index] A = module.dataclass(cls) schema = desert.schema_class(A)() data = schema.load({"y": [1]}) assert data == A([1])
def test_optional(module): """Setting an optional type makes the default None.""" @module.dataclass class A: x: t.Optional[int] data = desert.schema_class(A)().load({}) assert data == A(None)
def test_list(module): """Build a generic list *without* setting a factory on the dataclass.""" @module.dataclass class A: y: t.List[int] schema = desert.schema_class(A)() data = schema.load({"y": [1]}) assert data == A([1])
def test_validation(module): """Passing the wrong keys will raise ValidationError.""" @module.dataclass class A: x: int schema = desert.schema_class(A)() with pytest.raises(marshmallow.exceptions.ValidationError): schema.load({"y": 5})
def test_simple(module): """Load dict into a dataclass instance.""" @module.dataclass class A: x: int data = desert.schema_class(A)().load(data={"x": 5}) assert data == A(x=5)
def test_optional(module: DataclassModule) -> None: """Setting an optional type makes the default None.""" @module.dataclass class A: x: t.Optional[int] data = desert.schema_class(A)().load({}) assert data == A(None) # type: ignore[call-arg]
def test_dict(module: DataclassModule, annotation_class: type) -> None: """Build a dict without setting a factory on the dataclass.""" cls = type("A", (object,), {"__annotations__": {"y": annotation_class[int, int]}}) # type: ignore[index] A = module.dataclass(cls) schema = desert.schema_class(A)() data = schema.load({"y": {1: 2, 3: 4}}) assert data == A({1: 2, 3: 4})
def __init__(self, api_key: str) -> None: """Initialize RawGAPIAsync with api_key. Args: api_key: api_key for RawG. See https://rawg.io/apidocs """ self.api_key = api_key self._game_schema_class = desert.schema_class( Game, meta={"unknown": marshmallow.EXCLUDE})
def test_non_init(module): """Non-init attributes are not included in schema""" @module.dataclass class A: x: int y: str = module.field(default="can't init this", init=False) schema = desert.schema_class(A)() assert "y" not in schema.fields
def test_dict(module): """Build a dict without setting a factory on the dataclass.""" @module.dataclass class A: y: t.Dict[int, int] schema = desert.schema_class(A)() data = schema.load({"y": {1: 2, 3: 4}}) assert data == A({1: 2, 3: 4})
def test_simple(module: DataclassModule) -> None: """Load dict into a dataclass instance.""" @module.dataclass class A: x: int data = desert.schema_class(A)().load(data={"x": 5}) assert data == A(x=5) # type: ignore[call-arg]
def test_ignore_unknown_fields(module): """Enable unknown fields with meta argument.""" @module.dataclass class A: x: int schema_class = desert.schema_class(A, meta={"unknown": marshmallow.EXCLUDE}) schema = schema_class() data = schema.load({"x": 1, "y": 2}) assert data == A(x=1)
def test_set_default(module): """Setting a default value in the dataclass makes passing it optional.""" @module.dataclass class A: x: int = 1 schema = desert.schema_class(A)() data = schema.load({"x": 1}) assert data == A(1) data = schema.load({}) assert data == A(1)
def test_union(module, value, assert_dump_load): """Deserialize one of several types.""" @module.dataclass class A: x: t.Union[int, str] schema = desert.schema_class(A)() dumped = {"x": value} loaded = A(value) assert_dump_load(schema=schema, loaded=loaded, dumped=dumped)
def test_metadata_marshmallow_field_loads(module): """Marshmallow field can be specified via metadata dict""" @module.dataclass class A: x: decimal.Decimal = module.field( metadata={ "marshmallow_field": marshmallow.fields.Decimal(as_string=True) }) schema = desert.schema_class(A)() assert schema.loads('{"x": "1.3"}') == A(decimal.Decimal("1.3"))
def test_set_default(module: DataclassModule) -> None: """Setting a default value in the dataclass makes passing it optional.""" @module.dataclass class A: x: int = 1 schema = desert.schema_class(A)() data = schema.load({"x": 1}) assert data == A(1) # type: ignore[call-arg] data = schema.load({}) assert data == A(1) # type: ignore[call-arg]
def test_nested(module): """One object can hold instances of another.""" @module.dataclass class A: x: int @module.dataclass class B: y: A data = desert.schema_class(B)().load({"y": {"x": 5}}) assert data == B(A(5))
def test_newtype(module, assert_dump_load): """An instance of NewType delegates to its supertype.""" MyInt = t.NewType("MyInt", int) @module.dataclass class A: x: MyInt schema = desert.schema_class(A)() dumped = {"x": 1} loaded = A(x=1) assert_dump_load(schema=schema, loaded=loaded, dumped=dumped)
def test_nested(module: DataclassModule) -> None: """One object can hold instances of another.""" @module.dataclass class A: x: int @module.dataclass class B: y: A data = desert.schema_class(B)().load({"y": {"x": 5}}) assert data == B(A(5)) # type: ignore[call-arg]
def test_tuple(module, assert_dump_load): """Round trip a tuple. The tuple is converted to list only for dumps(), not during dump(). """ @module.dataclass class A: x: t.Tuple[int, bool] schema = desert.schema_class(A)() dumped = {"x": (1, False)} loaded = A(x=(1, False)) assert_dump_load(schema=schema, loaded=loaded, dumped=dumped)
def test_forward_reference(module, assert_dump_load): # pragma: no cover """Build schemas from classes that are defined below their containing class.""" @module.dataclass class A: x: "B" @module.dataclass class B: y: int schema = desert.schema_class(A)() dumped = {"x": {"y": 1}} loaded = A((B(1))) assert_dump_load(schema=schema, loaded=loaded, dumped=dumped)