From 7a409d47cf2d60f20d1592a13c547b69d8f05595 Mon Sep 17 00:00:00 2001 From: Kang Hyojun Date: Sat, 2 Jul 2016 16:33:19 +0900 Subject: [PATCH 1/2] Validate/Serialize boxed type properly if aliased many time #4 --- nirum/serialize.py | 7 ++- nirum/validate.py | 15 +++++- tests/conftest.py | 100 +++++++++++++++++++++++++++++++++++++++- tests/serialize_test.py | 5 ++ tests/validate_test.py | 4 ++ 5 files changed, 127 insertions(+), 4 deletions(-) diff --git a/nirum/serialize.py b/nirum/serialize.py index 01a54a8..90e2330 100644 --- a/nirum/serialize.py +++ b/nirum/serialize.py @@ -9,7 +9,12 @@ def serialize_boxed_type(data): - return data.value + try: + serialize_data = data.value.__nirum_serialize__() + except AttributeError: + serialize_data = data.value + finally: + return serialize_data def serialize_type_with_names(data, names): diff --git a/nirum/validate.py b/nirum/validate.py index 08eaa30..e04fdca 100644 --- a/nirum/validate.py +++ b/nirum/validate.py @@ -5,8 +5,19 @@ __all__ = 'validate_boxed_type', 'validate_record_type', 'validate_union_type', -def validate_boxed_type(boxed, type_hint): - if not isinstance(boxed, type_hint): +def validate_boxed_type(boxed, type_hint) -> bool: + actual_boxed_val_type = type(boxed) + while True: + try: + actual_boxed_val_type = actual_boxed_val_type.__nirum_boxed_type__ + except AttributeError: + break + while True: + try: + type_hint = type_hint.__nirum_boxed_type__ + except AttributeError: + break + if actual_boxed_val_type != type_hint: raise TypeError('{0} expected, found: {1}'.format(type_hint, type(boxed))) return boxed diff --git a/tests/conftest.py b/tests/conftest.py index 3d69fff..755f7ae 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,12 +5,15 @@ from nirum.serialize import serialize_record_type, serialize_boxed_type from nirum.deserialize import deserialize_record_type, deserialize_boxed_type -from nirum.validate import validate_record_type, validate_union_type +from nirum.validate import (validate_boxed_type, validate_record_type, + validate_union_type) from nirum.constructs import NameDict class Offset: + __nirum_boxed_type__ = float + def __init__(self, value: float) -> None: self.value = value @@ -195,3 +198,98 @@ def fx_rectangle_type(): @fixture def fx_rectangle(fx_rectangle_type, fx_point): return fx_rectangle_type(fx_point, fx_point) + + +class A: + + __nirum_boxed_type__ = str + + def __init__(self, value: str) -> None: + validate_boxed_type(value, str) + self.value = value # type: Text + + def __eq__(self, other) -> bool: + return (isinstance(other, A) and + self.value == other.value) + + def __hash__(self) -> int: + return hash(self.value) + + def __nirum_serialize__(self) -> str: + return serialize_boxed_type(self) + + @classmethod + def __nirum_deserialize__( + cls: type, value: typing.Mapping[str, typing.Any] + ) -> 'A': + return deserialize_boxed_type(cls, value) + + def __repr__(self) -> str: + return '{0.__module__}.{0.__qualname__}({1!r})'.format( + type(self), self.value + ) + + +class B: + + __nirum_boxed_type__ = A + + def __init__(self, value: A) -> None: + validate_boxed_type(value, A) + self.value = value # type: A + + def __eq__(self, other) -> bool: + return (isinstance(other, B) and + self.value == other.value) + + def __hash__(self) -> int: + return hash(self.value) + + def __nirum_serialize__(self) -> str: + return serialize_boxed_type(self) + + @classmethod + def __nirum_deserialize__( + cls: type, value: typing.Mapping[str, typing.Any] + ) -> 'B': + return deserialize_boxed_type(cls, value) + + def __repr__(self) -> str: + return '{0.__module__}.{0.__qualname__}({1!r})'.format( + type(self), self.value + ) + + +class C: + + __nirum_boxed_type__ = A + + def __init__(self, value: B) -> None: + validate_boxed_type(value, B) + self.value = value # type: B + + def __eq__(self, other) -> bool: + return (isinstance(other, C) and + self.value == other.value) + + def __hash__(self) -> int: + return hash(self.value) + + def __nirum_serialize__(self) -> str: + return serialize_boxed_type(self) + + @classmethod + def __nirum_deserialize__( + cls: type, value: typing.Mapping[str, typing.Any] + ) -> 'C': + return deserialize_boxed_type(cls, value) + + def __repr__(self) -> str: + return '{0.__module__}.{0.__qualname__}({1!r})'.format( + type(self), self.value + ) + + +@fixture +def fx_layered_boxed_types(): + return A, B, C diff --git a/tests/serialize_test.py b/tests/serialize_test.py index 87f47c1..a753f2a 100644 --- a/tests/serialize_test.py +++ b/tests/serialize_test.py @@ -6,6 +6,11 @@ def test_serialize_boxed_type(fx_offset): assert serialize_boxed_type(fx_offset) == fx_offset.value +def test_serialize_layered_boxed_type(fx_layered_boxed_types): + actual = fx_layered_boxed_types[1](fx_layered_boxed_types[0]('test')) + assert actual.__nirum_serialize__() == 'test' + + def test_serialize_record_type(fx_point): assert serialize_record_type(fx_point) == {'_type': 'point', 'x': 3.14, 'top': 1.592} diff --git a/tests/validate_test.py b/tests/validate_test.py index 36d7d13..71f8190 100644 --- a/tests/validate_test.py +++ b/tests/validate_test.py @@ -28,3 +28,7 @@ def test_validate_union_type(fx_rectangle, fx_rectangle_type, fx_point): with raises(TypeError): validate_union_type(fx_rectangle_type(1, 1)) + + +def test_validate_layered_boxed_types(fx_layered_boxed_types): + assert validate_boxed_type('test', fx_layered_boxed_types[1]) From 19aaaecc7a3aad80a6b569926514f3b1896442b2 Mon Sep 17 00:00:00 2001 From: Kang Hyojun Date: Sat, 2 Jul 2016 21:59:07 +0900 Subject: [PATCH 2/2] Call __nirum_serialize__ when it is callable only --- nirum/serialize.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/nirum/serialize.py b/nirum/serialize.py index 90e2330..248d0b8 100644 --- a/nirum/serialize.py +++ b/nirum/serialize.py @@ -9,12 +9,11 @@ def serialize_boxed_type(data): - try: - serialize_data = data.value.__nirum_serialize__() - except AttributeError: - serialize_data = data.value - finally: - return serialize_data + value = data.value + serialize = getattr(value, '__nirum_serialize__', None) + if callable(serialize): + return serialize() + return value def serialize_type_with_names(data, names):