PK5O82!>>jsondaora/__init__.py"""Interoperates @dataclass with json objects""" from jsondaora.base import jsondaora from jsondaora.dataclasses import asdataclass from jsondaora.schema import integer, string from jsondaora.serializers import dataclass_asjson, typed_dict_asjson from jsondaora.typed_dict import as_typed_dict, as_typed_dict_field __version__ = '0.5.0' __all__ = [ jsondaora.__name__, string.__name__, integer.__name__, asdataclass.__name__, as_typed_dict.__name__, as_typed_dict_field.__name__, dataclass_asjson.__name__, typed_dict_asjson.__name__, ] PKN5Ofyωjsondaora/base.pyimport dataclasses import inspect from logging import getLogger from typing import ( # type: ignore Any, Iterable, Optional, Type, _TypedDictMeta, ) from .fields import DeserializeFields, SerializeFields from .serializers import OrjsonDefaultTypes logger = getLogger(__name__) def jsondaora( type_: Optional[Type[Any]] = None, deserialize_fields: Optional[Iterable[str]] = None, serialize_fields: Optional[Iterable[str]] = None, ) -> Any: def wrap(type__: Type[Any]) -> Any: if isinstance(type__, _TypedDictMeta): set_typed_dict_fields(type__) else: type__ = dataclasses.dataclass(type__) OrjsonDefaultTypes.set_type(type__) if deserialize_fields is not None: DeserializeFields.set_type(type__, deserialize_fields) else: DeserializeFields.clean_fields(type__) if serialize_fields is not None: SerializeFields.set_type(type__, serialize_fields) else: SerializeFields.clean_fields(type__) internal_classes = inspect.getmembers( type__, lambda a: inspect.isclass(a) and not issubclass(a, type) ) for _, cls in internal_classes: wrap(cls) return type__ if type_: return wrap(type_) return wrap def set_typed_dict_fields(typed_dict_type: _TypedDictMeta) -> None: fields = {} for name, type_ in typed_dict_type.__annotations__.items(): default = getattr(typed_dict_type, name, dataclasses.MISSING) if isinstance(default, dataclasses.Field): fields[name] = default else: fields[name] = dataclasses.field(default=default) fields[name].name = name fields[name].type = type_ fields[name]._field_type = dataclasses._FIELD # type: ignore typed_dict_type.__dataclass_fields__ = fields PK2Of^WWjsondaora/dataclasses.pyimport dataclasses from typing import Any, Callable, Dict, List, Tuple, Type from .deserializers import deserialize_jsondict_fields from .exceptions import DeserializationError def asdataclass(jsondict: Dict[str, Any], cls: Type[Any]) -> Any: kwargs = deserialize_jsondict_fields(jsondict, cls) try: return cls(**kwargs) except TypeError as err: raise DeserializationError(cls, jsondict) from err def asdict( instance: Any, dict_factory: Callable[[List[Tuple[str, Any]]], Any] = dict ) -> Any: return dataclasses.asdict(instance, dict_factory=dict_factory) PK.b4O{m  jsondaora/deserializers.pyimport dataclasses from datetime import datetime from logging import getLogger from typing import ( # type: ignore TYPE_CHECKING, Any, Dict, Type, Union, _GenericAlias, ) from .exceptions import DeserializationError from .fields import DeserializeFields logger = getLogger(__name__) _ERROR_MSG = 'Invalid type={generic} for field={field}' def deserialize_jsondict_fields( jsondict: Dict[str, Any], cls: Type[Any] ) -> Dict[str, Any]: custom_fields = DeserializeFields.get_fields(cls) all_fields = dataclasses.fields(cls) deserialized = {} fields = custom_fields if custom_fields else all_fields for field in fields: value = jsondict.get(field.name) deserialized[field.name] = _deserialize_field(field, value) if custom_fields: for field in set(all_fields) - set(custom_fields): deserialized[field.name] = jsondict[field.name] return deserialized if TYPE_CHECKING: _Field = dataclasses.Field[Any] else: _Field = dataclasses.Field def deserialize_field( field_name: str, field_type: Type[Any], value: Any ) -> Any: field = dataclasses.field() field.name = field_name field.type = field_type return _deserialize_field(field, value) def _deserialize_field(field: _Field, value: Any) -> Any: field_type = field.type try: if isinstance(value, dict) and dataclasses.is_dataclass(field_type): value = deserialize_jsondict_fields(value, field_type) return field_type(**value) elif isinstance(field_type, _GenericAlias): return _deserialize_generic_type(field_type, field.name, value) elif isinstance(value, field_type): return value elif ( isinstance(field_type, type) and issubclass(field_type, str) and isinstance(value, bytes) ): return value.decode() elif ( isinstance(field_type, type) and issubclass(field_type, bytes) and isinstance(value, str) ): return value.encode() elif field_type is datetime: return datetime.strptime(value, '%Y-%m-%dT%H:%M:%S') elif value is None and field.default is None: return None elif field.default is not dataclasses.MISSING and value is None: return field_type(field.default) if value is not None: return field_type(value) raise DeserializationError(field, value) except (TypeError, ValueError) as error: raise DeserializationError(field, value, error) from error def _deserialize_generic_type(generic: Any, field: str, value: Any) -> Any: try: return _DESERIALIZERS_MAP[generic.__origin__](generic, field, value) except KeyError: raise DeserializationError( _ERROR_MSG.format(generic=generic, field=field) ) def _deserialize_union(generic: Any, field: str, value: Any) -> Any: nullable = False for arg in generic.__args__: if arg is not type(None): # noqa try: return arg(value) except TypeError: continue else: nullable = True if nullable: return None raise DeserializationError(_ERROR_MSG.format(generic=generic, field=field)) def _deserialize_list(generic: Any, field_name: str, values: Any) -> Any: field = dataclasses.field() field.name = field_name field.type = generic.__args__[0] try: return [_deserialize_field(field, value) for value in values] except TypeError as err: raise DeserializationError( _ERROR_MSG.format(generic=generic, field=field) ) from err _DESERIALIZERS_MAP = {Union: _deserialize_union, list: _deserialize_list} PKf4Ov!Zjsondaora/exceptions.pyfrom typing import Any, Dict class DataclassJsonError(Exception): ... class DeserializationError(DataclassJsonError): @property def message(self) -> str: return ' '.join(self.dict) @property def dict(self) -> Dict[str, Any]: type_name = ( self.args[0].type.__name__ if hasattr(self.args[0].type, '__name__') else str(self.args[0].type) ) message_args = {'field': self.args[0].name, 'type': type_name} if len(self.args) > 1: message_args['invalid_value'] = str(self.args[1]) if len(self.args) > 2: message_args['error'] = type(self.args[2]).__name__ return dict(**message_args) PK2O'%11jsondaora/fields.pyimport dataclasses from typing import TYPE_CHECKING, Any, Dict, Iterable, Set, Type class _Fields: if TYPE_CHECKING: types_fields_map: Dict[Type[Any], Set[dataclasses.Field[Any]]] = {} else: types_fields_map: Dict[Type[Any], Set[dataclasses.Field]] = {} @classmethod def set_type(cls, type_: Type[Any], field_names: Iterable[str]) -> None: if type_ in cls.types_fields_map: return all_fields = dataclasses.fields(type_) fields = (f for f in all_fields if f.name in field_names) cls.types_fields_map[type_] = ( fields if isinstance(fields, set) else set(fields) ) @classmethod def get_fields(cls, type_: Type[Any]) -> Any: return cls.types_fields_map.get(type_) @classmethod def clean_fields(cls, type_: Type[Any]) -> Any: return cls.types_fields_map.pop(type_, None) class DeserializeFields(_Fields): if TYPE_CHECKING: types_fields_map: Dict[Type[Any], Set[dataclasses.Field[Any]]] = {} else: types_fields_map: Dict[Type[Any], Set[dataclasses.Field]] = {} class SerializeFields(_Fields): if TYPE_CHECKING: types_fields_map: Dict[Type[Any], Set[dataclasses.Field[Any]]] = {} else: types_fields_map: Dict[Type[Any], Set[dataclasses.Field]] = {} PK2OX;;jsondaora/schema.pyfrom types import MethodType from typing import Any, Callable, Dict, Optional, Sequence, Type from .exceptions import DeserializationError def string( max_length: Optional[int] = None, min_length: Optional[int] = None ) -> 'StringField': if max_length is None and min_length is None: return str # type: ignore return StringMeta( # type: ignore 'StringField', (StringField,), {'max_length': max_length, 'min_length': min_length}, ) def integer( maximum: Optional[int] = None, minimum: Optional[int] = None ) -> 'IntegerField': if maximum is None and minimum is None: return int # type: ignore return IntegerMeta( # type: ignore 'IntegerField', (IntegerField,), {'maximum': maximum, 'minimum': minimum}, ) class JsonField: ... class StringField(JsonField): max_length: int min_length: int value: str def __init__(self, value: Any): self.value = str(value) type(self).validate(self.value) # type: ignore class IntegerField(JsonField): maximum: int minimum: int def __init__(self, value: Any): self.value = int(value) type(self).validate(self.value) # type: ignore class StringMeta(type): def __init__( cls, name: str, bases: Sequence[Type[Any]], attrs: Dict[str, Any] ): cls.max_length = attrs.get('max_length') cls.min_length = attrs.get('min_length') if cls.max_length is None and cls.min_length is not None: cls.validate = MethodType(validate_min_length, cls) elif cls.max_length is not None and cls.min_length is None: cls.validate = MethodType(validate_max_length, cls) elif cls.max_length is not None and cls.min_length is not None: cls.validate = MethodType(validate_min_length_max_length, cls) class IntegerMeta(type): maximum: int minimum: int validate: Callable[['IntegerMeta', int], bool] def __init__( cls, name: str, bases: Sequence[Type[Any]], attrs: Dict[str, Any] ): cls.maximum = attrs.get('maximum') # type: ignore cls.minimum = attrs.get('minimum') # type: ignore if cls.maximum is None and cls.minimum is not None: cls.validate = MethodType(validate_minimum, cls) elif cls.maximum is not None and cls.minimum is None: cls.validate = MethodType(validate_maximum, cls) elif cls.maximum is not None and cls.minimum is not None: cls.validate = MethodType( # type: ignore validate_minimum_maximum, cls ) def validate_minimum(cls: IntegerMeta, value: int) -> None: if not cls.minimum <= value: raise DeserializationError( f'Invalid minimum integer value: {cls.minimum} < {value}' ) def validate_maximum(cls: IntegerMeta, value: int) -> None: if not cls.maximum >= value: raise DeserializationError( f'Invalid maximum integer value: {value} < {cls.maximum}' ) def validate_minimum_maximum(cls: IntegerMeta, value: int) -> None: if not cls.minimum <= value <= cls.maximum: raise DeserializationError( f'Invalid minimum and maximum integer value: ' f'{cls.minimum} < {value} < {cls.maximum}' ) def validate_min_length(cls: StringMeta, value: str) -> None: if not cls.min_length <= len(value): # type: ignore raise DeserializationError( f'Invalid min_length string value: {cls.min_length} < {len(value)}' ) def validate_max_length(cls: StringMeta, value: str) -> None: if not cls.max_length >= len(value): # type: ignore raise DeserializationError( f'Invalid max_length string value: {len(value)} < {cls.max_length}' ) def validate_min_length_max_length(cls: StringMeta, value: str) -> None: if not cls.min_length <= len(value) <= cls.max_length: # type: ignore raise DeserializationError( f'Invalid min_length and max_length string value: ' f'{cls.min_length} < {len(value)} < {cls.max_length}' ) PK2O#jsondaora/serializers.pyimport dataclasses from typing import Any, Callable, Dict, Type, _TypedDictMeta # type: ignore import orjson from .dataclasses import asdict from .fields import SerializeFields def dataclass_asjson(instance: Any) -> bytes: return orjson.dumps( instance, default=OrjsonDefaultTypes.default_function(instance) ) def typed_dict_asjson( typed_dict: Dict[str, Any], typed_dict_type: _TypedDictMeta ) -> bytes: return orjson.dumps(_choose_typed_dict_fields(typed_dict, typed_dict_type)) def _choose_typed_dict_fields( typed_dict: Dict[str, Any], typed_dict_type: _TypedDictMeta ) -> Dict[str, Any]: fields = SerializeFields.get_fields(typed_dict_type) if not fields: return typed_dict return { f.name: ( _choose_typed_dict_fields(typed_dict[f.name], f.type) if isinstance(typed_dict[f.name], dict) else typed_dict[f.name] ) for f in fields } class OrjsonDefaultTypes: types_default_map: Dict[Type[Any], Callable[[Any], Any]] = dict() @classmethod def set_type(cls, type_: Type[Any]) -> None: if type_ in cls.types_default_map: return cls.types_default_map[type_] = cls._asdict_for_orjson for field in dataclasses.fields(type_): if dataclasses.is_dataclass(field.type): cls.types_default_map[field.type] = cls._asdict_for_orjson @classmethod def default_function(cls, instance: Any) -> Any: def wrap(v: Any) -> Any: if isinstance(v, bytes): return v.decode() return cls.types_default_map[type(v)](v) return wrap @staticmethod def _asdict_for_orjson(instance: Any) -> Dict[str, Any]: dictinst: Dict[str, Any] = asdict(instance) fields = SerializeFields.get_fields(type(instance)) if not fields: return dictinst return {f.name: dictinst[f.name] for f in fields} PKXb4Oyjsondaora/typed_dict.pyfrom typing import Any, Dict, _TypedDictMeta # type: ignore from .deserializers import deserialize_field, deserialize_jsondict_fields from .exceptions import DeserializationError def as_typed_dict( jsondict: Dict[str, Any], typed_dict_type: _TypedDictMeta ) -> Any: try: return deserialize_jsondict_fields(jsondict, typed_dict_type) except TypeError as err: raise DeserializationError(typed_dict_type, jsondict) from err def as_typed_dict_field( json: Any, field_name: str, field_typed_dict_type: _TypedDictMeta ) -> Any: try: return deserialize_field(field_name, field_typed_dict_type, json) except TypeError as err: raise DeserializationError(field_typed_dict_type, json) from err PK:3OO>~ ~ !jsondaora/tests/test_dataclass.py# type: ignore from dataclasses import dataclass from typing import List, Optional, Union from jsondaora import asdataclass, dataclass_asjson, jsondaora def tests_should_deserialize_optional_args(): @dataclass class FakeDataclass: test: str test_default: Optional[int] = None dataclass_ = asdataclass( {'test': 'test', 'test_default': '1'}, FakeDataclass ) assert dataclass_ == FakeDataclass('test', 1) def tests_should_deserialize_union_args(): @dataclass class FakeDataclass: test_union: Union[int, str] dataclass_ = asdataclass({'test_union': b'1'}, FakeDataclass) assert dataclass_ == FakeDataclass(1) def tests_should_deserialize_list_args(): @dataclass class FakeDataclass: test_list: List[int] dataclass_ = asdataclass({'test_list': [b'1', '2', 3]}, FakeDataclass) assert dataclass_ == FakeDataclass([1, 2, 3]) def tests_should_deserialize_list_args_nested(): @dataclass class FakeDataclass: test: str @dataclass class FakeDataclass2: fakes: List[FakeDataclass] fakeint: int fakes_data = [{'test': 'fake11'}, {'test': 'fake12'}, {'test': 'fake13'}] dataclass_ = asdataclass( {'fakes': fakes_data, 'fakeint': '1'}, FakeDataclass2 ) assert dataclass_ == FakeDataclass2( [ FakeDataclass('fake11'), FakeDataclass('fake12'), FakeDataclass('fake13'), ], 1, ) def tests_should_deserialize_bytes_to_string(): @dataclass class FakeDataclass: test_union: str dataclass_ = asdataclass({'test_union': b'test'}, FakeDataclass) assert dataclass_ == FakeDataclass('test') def tests_should_deserialize_nested_jsondict(): @dataclass class FakeDataclass: test: str @dataclass class FakeDataclass2: fake: FakeDataclass dataclass_ = asdataclass({'fake': {'test': b'test'}}, FakeDataclass2) assert dataclass_ == FakeDataclass2(FakeDataclass('test')) def tests_should_choose_fields_to_deserialize(): @jsondaora(deserialize_fields=('test2',)) @dataclass class FakeDataclass: test: int test2: str dataclass_ = asdataclass({'test': '1', 'test2': 2}, FakeDataclass) assert dataclass_ == FakeDataclass('1', '2') def tests_should_serialize_all_fields_with_choosen_deserialize_fields(): @jsondaora(deserialize_fields=('test2',)) @dataclass class FakeDataclass: test: int test2: str dataclass_ = asdataclass({'test': '1', 'test2': 2}, FakeDataclass) assert dataclass_asjson(dataclass_) == b'{"test":"1","test2":"2"}' PK:3Ou% % ,jsondaora/tests/test_dataclass_typed_dict.py# type: ignore from typing import List, TypedDict from jsondaora import as_typed_dict, asdataclass, jsondaora def tests_should_deserialize_nested_dataclass_typed_dict(): @jsondaora class FakeTypedDict(TypedDict): test: str @jsondaora class FakeDataclass: fake: FakeTypedDict dataclass = asdataclass({'fake': {'test': b'test'}}, FakeDataclass) assert dataclass == FakeDataclass(fake={'test': 'test'}) def tests_should_deserialize_nested_typed_dict_dataclass(): @jsondaora class FakeDataclass: test: str @jsondaora class FakeTypedDict(TypedDict): fake: FakeDataclass typed_dict = as_typed_dict({'fake': {'test': b'test'}}, FakeTypedDict) assert typed_dict == {'fake': FakeDataclass(test='test')} def tests_should_deserialize_list_args_nested_dataclass_typed_dict(): @jsondaora class FakeTypedDict(TypedDict): fakeint: int @jsondaora class FakeDataclass: fakes: List[FakeTypedDict] fakefloat: float fakes_data = [{'fakeint': '1'}, {'fakeint': '2'}, {'fakeint': '3'}] dataclass = asdataclass( {'fakes': fakes_data, 'fakefloat': '0.1'}, FakeDataclass ) assert dataclass == FakeDataclass( fakefloat=0.1, fakes=[{'fakeint': 1}, {'fakeint': 2}, {'fakeint': 3}] ) def tests_should_deserialize_list_args_nested_typed_dict_dataclass(): @jsondaora class FakeDataclass: fakeint: int @jsondaora class FakeTypedDict(TypedDict): fakes: List[FakeDataclass] fakefloat: float fakes_data = [{'fakeint': '1'}, {'fakeint': '2'}, {'fakeint': '3'}] typed_dict = as_typed_dict( {'fakes': fakes_data, 'fakefloat': '0.1'}, FakeTypedDict ) assert typed_dict == { 'fakes': [FakeDataclass(1), FakeDataclass(2), FakeDataclass(3)], 'fakefloat': 0.1, } def tests_should_deserialize_list_args_deep_nested_typed_dict_dataclass(): @jsondaora class FakeDataclass2: fakeint: int @jsondaora class FakeTypedDict2(TypedDict): fakes: List[FakeDataclass2] fakefloat: float @jsondaora class FakeDataclass: fakeint: int fake: FakeTypedDict2 @jsondaora class FakeTypedDict(TypedDict): fakes: List[FakeDataclass] fakefloat: float fakes_data = [ { 'fakeint': '1', 'fake': {'fakefloat': '0.2', 'fakes': [{'fakeint': '-1'}]}, }, { 'fakeint': '2', 'fake': {'fakefloat': '0.3', 'fakes': [{'fakeint': '-2'}]}, }, { 'fakeint': '3', 'fake': {'fakefloat': '0.4', 'fakes': [{'fakeint': '0'}]}, }, ] typed_dict = as_typed_dict( {'fakes': fakes_data, 'fakefloat': '0.1'}, FakeTypedDict ) assert typed_dict == { 'fakes': [ FakeDataclass( fakeint=1, fake={'fakefloat': 0.2, 'fakes': [FakeDataclass2(fakeint=-1)]}, ), FakeDataclass( fakeint=2, fake={'fakefloat': 0.3, 'fakes': [FakeDataclass2(fakeint=-2)]}, ), FakeDataclass( fakeint=3, fake={'fakefloat': 0.4, 'fakes': [FakeDataclass2(fakeint=0)]}, ), ], 'fakefloat': 0.1, } PK:3O(,,jsondaora/tests/test_schema.pyimport pytest from jsondaora.exceptions import DeserializationError from jsondaora.schema import integer, string def test_should_validate_minimum_integer(): Integer = integer(minimum=10) with pytest.raises(DeserializationError) as exc_info: Integer(9) assert exc_info.value.args == ('Invalid minimum integer value: 10 < 9',) def test_should_validate_maximum_integer(): Integer = integer(maximum=9) with pytest.raises(DeserializationError) as exc_info: Integer(10) assert exc_info.value.args == ('Invalid maximum integer value: 10 < 9',) def test_should_validate_min_length_string(): String = string(min_length=2) with pytest.raises(DeserializationError) as exc_info: String('1') assert exc_info.value.args == ('Invalid min_length string value: 2 < 1',) def test_should_validate_max_length_string(): String = string(max_length=2) with pytest.raises(DeserializationError) as exc_info: String('333') assert exc_info.value.args == ('Invalid max_length string value: 3 < 2',) PK93O $gg!jsondaora/tests/test_serialize.py# type: ignore from dataclasses import dataclass from jsondaora import asdataclass, dataclass_asjson, jsondaora def tests_should_serialize_string_with_orjson(): @jsondaora @dataclass class FakeDataclass: test_union: str dataclass_ = asdataclass({'test_union': b'test'}, FakeDataclass) json = dataclass_asjson(dataclass_) assert json == b'{"test_union":"test"}' def tests_should_serialize_nested_dataclasses_with_orjson(): @jsondaora @dataclass class FakeDataclass: test: int @jsondaora @dataclass class FakeDataclass2: fake: FakeDataclass dataclass_ = asdataclass({'fake': {'test': b'1'}}, FakeDataclass2) json = dataclass_asjson(dataclass_) assert json == b'{"fake":{"test":1}}' def tests_should_choose_fields_to_serialize(): @jsondaora(serialize_fields=('test2',)) @dataclass class FakeDataclass: test: int test2: str dataclass_ = asdataclass({'test': 1, 'test2': 2}, FakeDataclass) json = dataclass_asjson(dataclass_) assert json == b'{"test2":"2"}' assert dataclass_.test == 1 PKgb4O{"jsondaora/tests/test_typed_dict.py# type: ignore import dataclasses from typing import List, Optional, TypedDict, Union from jsondaora import ( as_typed_dict, as_typed_dict_field, jsondaora, typed_dict_asjson, ) def tests_should_deserialize_optional_args(): @jsondaora class FakeTypedDict(TypedDict): test: str test_default: Optional[int] = None typed_dict = as_typed_dict( {'test': 'test', 'test_default': '1'}, FakeTypedDict ) assert typed_dict == {'test': 'test', 'test_default': 1} def tests_should_deserialize_union_args(): @jsondaora class FakeTypedDict(TypedDict): test_union: Union[int, str] typed_dict = as_typed_dict({'test_union': b'1'}, FakeTypedDict) assert typed_dict == {'test_union': 1} def tests_should_deserialize_list_args(): @jsondaora class FakeTypedDict(TypedDict): test_list: List[int] typed_dict = as_typed_dict({'test_list': [b'1', '2', 3]}, FakeTypedDict) assert typed_dict == {'test_list': [1, 2, 3]} def tests_should_deserialize_list_args_nested(): @jsondaora class FakeTypedDict(TypedDict): test: str @jsondaora class FakeTypedDict2(TypedDict): fakes: List[FakeTypedDict] fakeint: int fakes_data = [{'test': 'fake11'}, {'test': 'fake12'}, {'test': 'fake13'}] typed_dict = as_typed_dict( {'fakes': fakes_data, 'fakeint': '1'}, FakeTypedDict2 ) assert typed_dict == {'fakes': fakes_data, 'fakeint': 1} def tests_should_deserialize_bytes_to_string(): @jsondaora class FakeTypedDict(TypedDict): test_union: str typed_dict = as_typed_dict({'test_union': b'test'}, FakeTypedDict) assert typed_dict == {'test_union': 'test'} def tests_should_deserialize_nested_jsondict(): @jsondaora class FakeTypedDict(TypedDict): test: str @jsondaora class FakeTypedDict2(TypedDict): fake: FakeTypedDict typed_dict = as_typed_dict({'fake': {'test': b'test'}}, FakeTypedDict2) assert typed_dict == {'fake': {'test': 'test'}} def tests_should_choose_fields_to_deserialize(): @jsondaora(deserialize_fields=('test2',)) class FakeTypedDict(TypedDict): test: int test2: str typed_dict = as_typed_dict({'test': '1', 'test2': 2}, FakeTypedDict) assert typed_dict == {'test': '1', 'test2': '2'} def tests_should_serialize_all_fields_with_choosen_deserialize_fields(): @jsondaora(deserialize_fields=('test2',)) class FakeTypedDict(TypedDict): test: int test2: str typed_dict = as_typed_dict({'test': '1', 'test2': 2}, FakeTypedDict) assert typed_dict_asjson(typed_dict, FakeTypedDict) in [ b'{"test":"1","test2":"2"}', b'{"test2":"2","test":"1"}', ] def tests_should_set_dataclass_fields_on_typed_dict(): @jsondaora class FakeTypedDict(TypedDict): test: int test2: str fields = dataclasses.fields(FakeTypedDict) assert len(fields) == 2 assert fields[0].type == int assert fields[1].type == str def tests_should_set_dataclass_fields_on_typed_dict_with_inheritance(): @jsondaora class FakeTypedDict2(TypedDict): test: float @jsondaora class FakeTypedDict(FakeTypedDict2): test2: int test3: str @jsondaora class Fake: fake: FakeTypedDict fake = Fake(FakeTypedDict2()) fake_field = type(fake).__dataclass_fields__[ # type: ignore 'fake' ] assert fake_field.type is FakeTypedDict def tests_should_deserialize_list_args_nested_as_list(): @jsondaora class FakeTypedDict(TypedDict): test: str @jsondaora class FakeTypedDict2(TypedDict): fakes: List[FakeTypedDict] fakeint: int fakes_data = [{'test': 'fake11'}, {'test': 'fake12'}, {'test': 'fake13'}] typed_dict_list = as_typed_dict_field( [{'fakes': fakes_data, 'fakeint': '1'}], 'list', List[FakeTypedDict2] ) assert typed_dict_list == [{'fakes': fakes_data, 'fakeint': 1}] PKh(O?,,!jsondaora-0.5.0.dist-info/LICENSEMIT License Copyright (c) 2019 Diogo Dutra Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!HPOjsondaora-0.5.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,szd&Y)r$[)T&UrPK!HLٙS"jsondaora-0.5.0.dist-info/METADATAX[o6~ׯ8S"iZ,bX6 v}h$$+w(ʺԎ}1\yϩ"Q xo:! iD Vy},O" QAp_ c"%\35+PI7P7#S;S*O'~+Ag,D듧'/ޜXr5"#ƧF)siBX@w)`,ZX/黜 *j`&w`Q )L}1TzU1˫_#0 1<24ǯ-#:14g\jp4eeM#4 MWiO=|*H xFi%p-W3 S63&4gƾB'⩣ Sh; N)2m;b/Ob&o\Dz Qvp/:({dFCإ`bX Q=i8[HO~3nq@_:ǩT$6KGXhi JW(<$;}O,沊.PL%7RBՓTFNUD^([n7jyA6cAT.~N<8O]ϺB'-^R W?9YA?1on+DʠxT@eQ۠躎V,uvsߌ`TNkeEhQn T閲էU\n/uvvS:Ye\m4݆VTӕ++S]!QU҄HnN`읛4V\Pb# ]&#k20[{;iWKbuS绱٩tJ4 ٚt_vݕUdT_tvD4T g{oʯpMCv֕٭̊Iv.vۀGQ$X֗|&6YMY8>j]`-?Rfiu5P:e(8D"Hk|=;?7xhVp=S&Bx: ف|g+y L_巃.Aʑsn O!A ^)h~>jsondaora/__init__.pyPKN5Ofyωqjsondaora/base.pyPK2Of^WW) jsondaora/dataclasses.pyPK.b4O{m   jsondaora/deserializers.pyPKf4Ov!Zjsondaora/exceptions.pyPK2O'%11jsondaora/fields.pyPK2OX;;`$jsondaora/schema.pyPK2O#4jsondaora/serializers.pyPKXb4Oy<jsondaora/typed_dict.pyPK:3OO>~ ~ !?jsondaora/tests/test_dataclass.pyPK:3Ou% % ,Jjsondaora/tests/test_dataclass_typed_dict.pyPK:3O(,,Xjsondaora/tests/test_schema.pyPK93O $gg!o\jsondaora/tests/test_serialize.pyPKgb4O{"ajsondaora/tests/test_typed_dict.pyPKh(O?,,!qjsondaora-0.5.0.dist-info/LICENSEPK!HPOujsondaora-0.5.0.dist-info/WHEELPK!HLٙS"vjsondaora-0.5.0.dist-info/METADATAPK!H;\pC o|jsondaora-0.5.0.dist-info/RECORDPK0