def test_forbid_mapping(self): factory = Factory(default_schema=Schema(unknown=Unknown.FORBID, name_mapping={"a": "a_mapped"}), ) serialized = {"a_mapped": "AA"} factory.load(serialized, Data)
from typing import Optional, Dict from dataclasses import dataclass from dataclass_factory import Factory, Schema @dataclass class Sub: b: str @dataclass class Data: a: str unknown: Optional[Dict] = None sub: Optional[Sub] = None serialized = { "a": "A1", "b": "B2", "c": "C3", } factory = Factory(default_schema=Schema(unknown=["unknown", "sub"])) data = factory.load(serialized, Data) assert data == Data(a="A1", unknown={"b": "B2", "c": "C3"}, sub=Sub("B2"))
def setUp(self) -> None: self.factory = Factory()
class Qwerty: qwerty: List[str] @dataclass class ComplexTodo: id: int title: str description: Qwerty # my factory = Factory(schemas={ SimpleTodo: DSchema( name_mapping={ "desc": ("description", "qwerty", 0), } ) }) simple_serializer = factory.serializer(List[SimpleTodo]) complex_serializer = factory.serializer(List[ComplexTodo]) # test simple_todos = [SimpleTodo( id=i, title="title %s" % i, desc="5some long description %s %s %s" % (i, i * 10, i) ) for i in range(10)] complex_todos = [ComplexTodo( id=i,
def test_should_raise_when_invalid_int_field_provided(self): try: Factory(debug_path=True).parser(Foo)({"a": "20x", "b": 20}) self.assertTrue(False, "ValueError exception expected") except InvalidFieldError as exc: self.assertEqual(['a'], exc.field_path)
desc: str # marshmallow class TodoSchema(Schema): id = fields.Integer() title = fields.Str() description = fields.Str(attribute="desc") todo_schema = TodoSchema(many=True) # my factory = Factory(schemas={ Todo: DSchema( name_mapping={"desc": "description"} ) }) serializer = factory.serializer(List[Todo]) # test todos = [Todo( id=i, title="title %s" % i, desc="5some long description %s %s %s" % (i, i * 10, i) ) for i in range(10)] def do1(): return serializer(todos)
def test_none(self): data = {"styled_name": 1, "trailed_name": 2, "NameToMap": 3} d = Data(1, 2, 3) serializer = Factory().serializer(Data) self.assertEqual(serializer(d), data)
def test_list(self): factory = Factory() self.assertEqual(factory.dump([A(1)]), [{"value": 1}])
def test_dict(self): factory = Factory() self.assertEqual(factory.dump({"a": A(1)}), {"a": {"value": 1}})
import json from enum import Enum from typing import Dict, Union from dataclasses import dataclass, field from dataclass_factory import Factory, Schema class A(Enum): X = "x" Y = 1 @dataclass class Data: a: A dict_: Dict[str, Union[int, float]] dictw_: Dict[str, Union[int, float]] = field(default_factory=dict) optional_num: int = 0 factory = Factory(schemas={A: Schema(description="My super `A` class")}) print(json.dumps(factory.json_schema(Data), indent=2)) print(json.dumps(factory.json_schema_definitions(), indent=2))
from dataclasses import dataclass from dataclass_factory import Factory, Schema, NameStyle factory = Factory(default_schema=Schema(name_style=NameStyle.camel)) @dataclass class Person: first_name: str last_name: str person = Person("ivan", "petrov") serial_person = {"FirstName": "ivan", "LastName": "petrov"} assert factory.dump(person) == serial_person
class Telegram: def __init__(self, bot_token, connector=None, close_session=True, proxy=None, message_callback=None): self.bot_token = bot_token self.session = aiohttp.ClientSession(connector=connector) self.close_session = close_session self.proxy = proxy self.loop = asyncio.get_event_loop() self.url = "https://api.telegram.org/bot%s/" % bot_token self.running = False self.factory = Factory(default_schema=Schema( trim_trailing_underscore=True)) self.callback = message_callback self.me = None async def method(self, method_name, params=None): if params is None: params = {} async with self.session.post(self.url + method_name, data=params, proxy=self.proxy) as response: return loads(await response.text()) async def get_updates(self, offset, timeout, limit): params = { 'timeout': timeout, 'offset': offset, 'allowed_updates': '["message", "callback_query"]' } # TODO: remove it if limit: params['limit'] = limit updates = await self.method("getUpdates", params) return self.factory.load(updates, Updates) async def process_message(self, update): if not self.callback: return # skip update because message callback is not set msg = Message(update.message, self) cb_res = self.callback(msg) if iscoroutine(cb_res): await cb_res async def process_callback_query(self, update): if not self.callback: return query = CallbackQueryHandler(update.callback_query, self) cb_res = self.callback(query) if iscoroutine(cb_res): await cb_res async def process_updates(self, updates): for update in updates.result: if update.message: self.loop.create_task(self.process_message(update)) elif update.callback_query: self.loop.create_task(self.process_callback_query(update)) async def _loop(self, updates_limit, timeout): self.running = True self.me = self.factory.load((await self.method("getMe"))['result'], User) offset = None while self.running: updates = await self.get_updates(offset, timeout, updates_limit) if updates.result: offset = updates.result[-1].update_id + 1 self.loop.create_task(self.process_updates(updates)) def set_callback(self, callback): self.callback = callback def poll(self, updates_limit=None, timeout=None): self.loop.run_until_complete(self._loop(updates_limit, timeout)) polling = poll # alias for poll method def __del__(self): if self.close_session: self.loop.run_until_complete(self.session.close())
def test_ignore_fields_with_init_false(self): serial = {"a": "A", "b": "B"} factory = Factory() self.assertTrue(factory.load(serial, Data))
def test_include(self): factory = Factory(default_schema=Schema(unknown=Unknown.STORE, ), ) serialized = {"a": "AA", "b": "b"} data = factory.load(serialized, DataWithExtras) self.assertEqual(data.a, "AA") self.assertEqual(data.extras, {"b": "b"})
class Qwerty: qwerty: List[str] @dataclass class ComplexTodo: id: int title: str description: Qwerty # my factory = Factory(schemas={ SimpleTodo: Schema(name_mapping={ "desc": ("description", "qwerty", 0), }) }, debug_path=True) simple_parser = factory.parser(List[SimpleTodo]) complex_parser = factory.parser(List[ComplexTodo]) # test todos = [{ "description": { "qwerty": ["5some long description %s %s %s" % (i, i * 10, i)], }, "id": i, "title": "title %s" % i, } for i in range(10)]
def test_optional(self): factory = Factory() y = factory.load({"x": None}, Data) self.assertEqual(y, Data(None))
from typing import Union from dataclasses import dataclass from dataclass_factory import Factory, Schema from dataclass_factory.schema_helpers import type_checker @dataclass class Item: name: str type: str = "item" @dataclass class Group: name: str type: str = "group" Something = Union[Item, Group] # Available types factory = Factory(schemas={ Item: Schema(pre_parse=type_checker("item", field="type")), Group: Schema(pre_parse=type_checker("group")), # `type` is default name for checked field }) assert factory.load({"name": "some name", "type": "group"}, Something) == Group("some name")
def test_optional_bool(self): factory = Factory() self.assertEqual(factory.load(None, Optional[bool]), None)
class TestLiteral(TestCase): def setUp(self) -> None: self.factory = Factory() @params(*LITERALS) def test_literal_fail(self, literal): abc = literal["a", "b", "c"] one = literal[1] with self.assertRaises(ValueError): self.factory.load("d", abc) with self.assertRaises(ValueError): self.factory.load(1.0, one) @params(*LITERALS) def test_literal(self, literal): abc = literal["a", "b", "c"] one = literal[1] self.assertEqual(self.factory.load("a", abc), "a") self.assertEqual(self.factory.load("b", abc), "b") self.assertEqual(self.factory.load("c", abc), "c") self.assertEqual(self.factory.load(1, one), 1) self.assertEqual(self.factory.dump("a", abc), "a") self.assertEqual(self.factory.dump("b", abc), "b") self.assertEqual(self.factory.dump("c", abc), "c") self.assertEqual(self.factory.dump("Z", abc), "Z") self.assertEqual(self.factory.dump(1, one), 1)
def test_underscore(self): factory = Factory(default_schema=schema) self.assertEqual(factory.dump(DataUnderscore(100)), {"from": 100})
def _init_factory(self): return Factory()
def test_optional(self): factory = Factory(default_schema=schema) self.assertEqual(factory.dump(Data()), {}) self.assertEqual(factory.dump(Data(1, [], "test")), {}) self.assertEqual(factory.dump(Data(2, [], "test")), {"x": 2})
# marshmallow class TodoSchema(Schema): id = fields.Integer() title = fields.Str() description = fields.Str(attribute="desc") @post_load def post(self, data, **kwargs): return Todo(**data) todo_schema = TodoSchema(many=True) # my factory = Factory( schemas={Todo: DSchema(name_mapping={"desc": "description"})}) parser = factory.parser(List[Todo]) # my debug factory_debug = Factory( schemas={Todo: DSchema(name_mapping={"desc": "description"})}, debug_path=True) parser_debug = factory_debug.parser(List[Todo]) # pydantic class PydTodo(BaseModel): id: int title: str description: str
@validate("int_field") # use original field name in class def validate_field(self, data): if data > 100: raise ValueError return data * 100 # validator can change value # this validator will be called before parsing field @validate("complex_field", pre=True) def validate_field_pre(self, data): return data["value"] @validate("info") def validate_stub(self, data): return self.SOMETHING # validator can access schema fields @dataclass class My: int_field: int complex_field: int info: str factory = Factory(schemas={ My: MySchema(name_style=NameStyle.upper_snake) # name style does not affect how validators are bound to fields }) result = factory.load({"INT_FIELD": 1, "COMPLEX_FIELD": {"value": 42}, "INFO": "ignored"}, My) assert result == My(100, 42, "Some string")
from dataclasses import dataclass import sys import unittest from unittest import TestCase from dataclass_factory import Factory @dataclass class Model: name: str factory = Factory() @unittest.skipUnless(sys.version_info[:2] >= (3, 9), "requires Python 3.9+") class TestTypeHintingGenericsInStandartCollections(TestCase): def test_dict(self): data = { "model": { "name": "name1", }, "model2": { "name": "name2", }, } expected = {"model": Model("name1"), "model2": Model("name2")} self.assertEqual(expected, factory.load(data, dict[str, Model])) def test_list(self):
def test_slots(self): self.assertRaises(ValueError, Factory().serializer, SlotsClass)
class TestGeneric(TestCase): def setUp(self) -> None: self.factory = Factory() def test_simple_int(self): foo = Foo[int](1) foo_serial = {"value": 1} self.assertEqual(self.factory.load(foo_serial, Foo[int]), foo) self.assertEqual(self.factory.dump(foo, Foo[int]), foo_serial) def test_simple_str(self): foo = Foo[str]("hello") foo_serial = {"value": "hello"} self.assertEqual(self.factory.load(foo_serial, Foo[str]), foo) self.assertEqual(self.factory.dump(foo, Foo[str]), foo_serial) def test_implicit_simple(self): foo = Foo(1) foo_serial = {"value": 1} self.assertEqual(self.factory.load(foo_serial, Foo[int]), foo) self.assertEqual(self.factory.dump(foo), foo_serial) def test_two_vars(self): foo = FooBar(1, "str", 3) foo_serial = {"value": 1, "value2": "str", "value3": 3} self.assertEqual(self.factory.load(foo_serial, FooBar[int, str]), foo) self.assertEqual(self.factory.dump(foo), foo_serial) def test_inner(self): baz = FooBaz(Foo(1)) baz_serial = {"foo": {"value": 1}} self.assertEqual(self.factory.load(baz_serial, FooBaz[int]), baz) self.assertEqual(self.factory.dump(baz), baz_serial) def test_inner2(self): baz = Foo(FooBaz(Foo(1))) baz_serial = {"value": {"foo": {"value": 1}}} self.assertEqual(self.factory.load(baz_serial, Foo[FooBaz[int]]), baz) self.assertEqual(self.factory.dump(baz, Foo[FooBaz[int]]), baz_serial) self.assertEqual(self.factory.dump(baz), baz_serial) def test_schema_load(self): factory = Factory( schemas={ FakeFoo[str]: Schema(name_mapping={"value": "s"}), FakeFoo: Schema(name_mapping={"value": "v"}), }) data = {"v": "hello", "i": 42, "s": "SSS"} self.assertEqual(factory.load(data, FakeFoo[str]), FakeFoo("SSS")) self.assertEqual(factory.load(data, FakeFoo[int]), FakeFoo("hello")) def test_schema_dump(self): factory = Factory( schemas={ FakeFoo[str]: Schema(name_mapping={"value": "s"}), FakeFoo: Schema(name_mapping={"value": "v"}), }) # self.assertEqual(factory.dump(FakeFoo("hello"), FakeFoo[str]), {"s": "hello"}) self.assertEqual(factory.dump(FakeFoo("hello")), {"v": "hello"}) def test_schema_dump_inner(self): factory = Factory( schemas={ FooBaz[int]: Schema(name_mapping={"foo": "bar"}), Foo[int]: Schema(name_mapping={"value": "v"}) }) self.assertEqual(factory.dump(FooBaz(Foo(1)), FooBaz[int]), {"bar": { "v": 1 }})
def test_slots_with_dict(self): self.assertRaises(ValueError, Factory().serializer, SlotsWithDict)
class TestSerializer(unittest.TestCase): def setUp(self) -> None: self.factory = Factory() def test_plain(self): serializer = self.factory.serializer(D) d = D(100, "hello") self.assertEqual( serializer(d), { "a": 100, "c": "hello" }, ) def test_list(self): serializer = self.factory.serializer(ListD) d1 = D(100, "hello") d2 = D(200, "hello2") dlist = ListD( [d1, d2], [123, 456, 789], ) data = { "data": [ { "a": 100, "c": "hello" }, { "a": 200, "c": "hello2" }, ], "ints": [123, 456, 789], } self.assertEqual( serializer(dlist), data, ) def test_dict(self): serializer = self.factory.serializer(DictD) d1 = D(100, "hello") d2 = D(200, "hello2") dlist = DictD({"1": d1, "two": d2}, {"hello": "world", "foo": "bar"}) data = { "data": { "1": { "a": 100, "c": "hello" }, "two": { "a": 200, "c": "hello2" }, }, "strs": { "hello": "world", "foo": "bar" } } self.assertEqual( serializer(dlist), data, ) def test_optional(self): serializer = self.factory.serializer(Optional[D]) d1 = D(100, "hello") data1 = {"a": 100, "c": "hello"} self.assertEqual( serializer(d1), data1, ) self.assertIs( serializer(None), None, ) def test_any(self): serializer = self.factory.serializer(Any) d1 = D(100, "hello") data1 = {"a": 100, "c": "hello"} self.assertEqual( serializer(d1), data1, ) self.assertIs( serializer(None), None, ) def test_enum(self): self.assertEqual(self.factory.dump(State.one), "1") self.assertEqual(self.factory.dump(State.two, State), "two")
def test_forbid(self): factory = Factory(default_schema=Schema(unknown=Unknown.FORBID, ), ) serialized = {"a": "AA", "b": "b"} with self.assertRaises(ValueError): factory.load(serialized, Data)