Loading LICENSE.txt 0 → 100644 +501 −0 File added.Preview size limit exceeded, changes collapsed. Show changes serializer_utils/deserializer.py +35 −10 Original line number Original line Diff line number Diff line from typing import Optional from typing import Optional, Union from typeguard import check_type from typeguard import check_type Loading Loading @@ -86,7 +86,7 @@ class Deserializable(metaclass=BaseMeta): return fields return fields def deserialize(t, d, try_all=False): def get_deserialization_class(t, d, try_all=False): for sc in t.__subclasses__(): for sc in t.__subclasses__(): if hasattr(sc, '_discriminators'): if hasattr(sc, '_discriminators'): for discriminator in sc._discriminators: for discriminator in sc._discriminators: Loading @@ -94,17 +94,42 @@ def deserialize(t, d, try_all=False): break break else: else: try: try: return deserialize(sc, d) return get_deserialization_class(sc, t, try_all) except TypeError as e: except TypeError as e: if not try_all: if not try_all: raise e raise e return t if hasattr(t, '_abstract') and t._abstract: raise TypeError('Cannot deserialize into {}: is abstract.'.format(t.__name__)) instance = t() def deserialize(rule: Rule, data, try_all=False, key=None): for k, rule in t.get_attrs().items(): # In case of primitive types, attempt to assign. v = rule.validate(k, d[k] if k in d else None) try: return rule.validate(key, data) except TypeError: pass if type(rule.type) is type(Union): for arg in rule.type.__args__: try: return deserialize(Rule(arg), data, try_all, key) except TypeError: pass raise TypeError('{} did not match any of {}.'.format(type(data), rule.type.__args__)) if issubclass(rule.type, Deserializable): if not isinstance(data, dict): raise TypeError('Cannot deserialize non-dict into class.') cls = get_deserialization_class(rule.type, data, try_all) if hasattr(cls, '_abstract') and cls._abstract: raise TypeError('Cannot deserialize into {}: is abstract.'.format(cls.__name__)) instance = cls() for k, r in cls.get_attrs().items(): v = deserialize(r, data[k] if k in data else r.default, try_all, key=k) setattr(instance, k, v) setattr(instance, k, v) return instance return instance raise TypeError('Unable to find a deserialization candidate for {} in {}.'.format(data, rule)) No newline at end of file test.py +26 −8 Original line number Original line Diff line number Diff line from time import sleep from time import sleep from typing import Optional from typing import Optional from serializer_utils.deserializer import Deserializable from serializer_utils.deserializer import Deserializable, Rule from serializer_utils.annotations import abstract, discriminate from serializer_utils.annotations import abstract, discriminate Loading Loading @@ -32,16 +32,34 @@ class RefundLine(ReceiptLine): return 'RefundLine(super={})'.format(super(RefundLine, self).__repr__()) return 'RefundLine(super={})'.format(super(RefundLine, self).__repr__()) class Test(Deserializable): tf: Optional[ReceiptLine] def __repr__(self): return 'Test(tf={})'.format(self.tf.__repr__()) if __name__ == '__main__': if __name__ == '__main__': import sys import sys import traceback import traceback from serializer_utils.deserializer import deserialize from serializer_utils.deserializer import deserialize def print_result(function, *args, **kwargs): try: try: print(deserialize(ReceiptLine, {'type': 'transaction', 'name': 'asdf', 'article_id': 5, 'amount': 5})) print(function(*args, **kwargs)) print(deserialize(ReceiptLine, {'type': 'refund', 'name': 'asdf', 'pk': 5})) except: print(deserialize(ReceiptLine, {'type': 'other', 'name': 'asdf'})) except TypeError as err: sleep(0.05) sleep(0.05) print(traceback.format_exc(), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) fns = [ [Rule(ReceiptLine), {'type': 'transaction', 'name': 'asdf', 'article_id': 5, 'amount': 5}], [Rule(ReceiptLine), {'type': 'refund', 'name': 'asdf', 'pk': 5}], [Rule(ReceiptLine), {'type': 'other', 'name': 'asdf'}], [Rule(ReceiptLine), {'type': 'transaction'}], [Rule(ReceiptLine), {'type': 'transaction', 'name': 'asdf', 'pk': 'no'}], [Rule(Test), {'tf': {'type': 'refund', 'name': 'asdf', 'pk': 5}}] ] [print_result(deserialize, *entry) for entry in fns] Loading
LICENSE.txt 0 → 100644 +501 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
serializer_utils/deserializer.py +35 −10 Original line number Original line Diff line number Diff line from typing import Optional from typing import Optional, Union from typeguard import check_type from typeguard import check_type Loading Loading @@ -86,7 +86,7 @@ class Deserializable(metaclass=BaseMeta): return fields return fields def deserialize(t, d, try_all=False): def get_deserialization_class(t, d, try_all=False): for sc in t.__subclasses__(): for sc in t.__subclasses__(): if hasattr(sc, '_discriminators'): if hasattr(sc, '_discriminators'): for discriminator in sc._discriminators: for discriminator in sc._discriminators: Loading @@ -94,17 +94,42 @@ def deserialize(t, d, try_all=False): break break else: else: try: try: return deserialize(sc, d) return get_deserialization_class(sc, t, try_all) except TypeError as e: except TypeError as e: if not try_all: if not try_all: raise e raise e return t if hasattr(t, '_abstract') and t._abstract: raise TypeError('Cannot deserialize into {}: is abstract.'.format(t.__name__)) instance = t() def deserialize(rule: Rule, data, try_all=False, key=None): for k, rule in t.get_attrs().items(): # In case of primitive types, attempt to assign. v = rule.validate(k, d[k] if k in d else None) try: return rule.validate(key, data) except TypeError: pass if type(rule.type) is type(Union): for arg in rule.type.__args__: try: return deserialize(Rule(arg), data, try_all, key) except TypeError: pass raise TypeError('{} did not match any of {}.'.format(type(data), rule.type.__args__)) if issubclass(rule.type, Deserializable): if not isinstance(data, dict): raise TypeError('Cannot deserialize non-dict into class.') cls = get_deserialization_class(rule.type, data, try_all) if hasattr(cls, '_abstract') and cls._abstract: raise TypeError('Cannot deserialize into {}: is abstract.'.format(cls.__name__)) instance = cls() for k, r in cls.get_attrs().items(): v = deserialize(r, data[k] if k in data else r.default, try_all, key=k) setattr(instance, k, v) setattr(instance, k, v) return instance return instance raise TypeError('Unable to find a deserialization candidate for {} in {}.'.format(data, rule)) No newline at end of file
test.py +26 −8 Original line number Original line Diff line number Diff line from time import sleep from time import sleep from typing import Optional from typing import Optional from serializer_utils.deserializer import Deserializable from serializer_utils.deserializer import Deserializable, Rule from serializer_utils.annotations import abstract, discriminate from serializer_utils.annotations import abstract, discriminate Loading Loading @@ -32,16 +32,34 @@ class RefundLine(ReceiptLine): return 'RefundLine(super={})'.format(super(RefundLine, self).__repr__()) return 'RefundLine(super={})'.format(super(RefundLine, self).__repr__()) class Test(Deserializable): tf: Optional[ReceiptLine] def __repr__(self): return 'Test(tf={})'.format(self.tf.__repr__()) if __name__ == '__main__': if __name__ == '__main__': import sys import sys import traceback import traceback from serializer_utils.deserializer import deserialize from serializer_utils.deserializer import deserialize def print_result(function, *args, **kwargs): try: try: print(deserialize(ReceiptLine, {'type': 'transaction', 'name': 'asdf', 'article_id': 5, 'amount': 5})) print(function(*args, **kwargs)) print(deserialize(ReceiptLine, {'type': 'refund', 'name': 'asdf', 'pk': 5})) except: print(deserialize(ReceiptLine, {'type': 'other', 'name': 'asdf'})) except TypeError as err: sleep(0.05) sleep(0.05) print(traceback.format_exc(), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) fns = [ [Rule(ReceiptLine), {'type': 'transaction', 'name': 'asdf', 'article_id': 5, 'amount': 5}], [Rule(ReceiptLine), {'type': 'refund', 'name': 'asdf', 'pk': 5}], [Rule(ReceiptLine), {'type': 'other', 'name': 'asdf'}], [Rule(ReceiptLine), {'type': 'transaction'}], [Rule(ReceiptLine), {'type': 'transaction', 'name': 'asdf', 'pk': 'no'}], [Rule(Test), {'tf': {'type': 'refund', 'name': 'asdf', 'pk': 5}}] ] [print_result(deserialize, *entry) for entry in fns]