Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Maxim Avanov
committed
Apr 18, 2020
1 parent
22d7d33
commit 2e983b6
Showing
13 changed files
with
307 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,64 @@ | ||
import pytest | ||
|
||
import typeit | ||
from typeit.compat import PY_VERSION | ||
|
||
if PY_VERSION >= (3, 7): | ||
from dataclasses import dataclass | ||
|
||
from dataclasses import dataclass | ||
|
||
|
||
def test_dataclasses(): | ||
def test_dataclasses(): | ||
|
||
@dataclass | ||
class InventoryItem: | ||
name: str | ||
unit_price: float | ||
quantity_on_hand: int | ||
@dataclass | ||
class InventoryItem: | ||
name: str | ||
unit_price: float | ||
quantity_on_hand: int | ||
|
||
overrides = { | ||
(InventoryItem, 'quantity_on_hand'): 'quantity' | ||
} | ||
overrides = { | ||
(InventoryItem, 'quantity_on_hand'): 'quantity' | ||
} | ||
|
||
mk_inv, serialize_inv = typeit.TypeConstructor.override(overrides).apply_on(InventoryItem) | ||
mk_inv, serialize_inv = typeit.TypeConstructor.override(overrides).apply_on(InventoryItem) | ||
|
||
serialized = { | ||
'name': 'test', | ||
'unit_price': 1.0, | ||
'quantity': 5, | ||
} | ||
x = mk_inv(serialized) | ||
assert isinstance(x, InventoryItem) | ||
assert serialize_inv(x) == serialized | ||
serialized = { | ||
'name': 'test', | ||
'unit_price': 1.0, | ||
'quantity': 5, | ||
} | ||
x = mk_inv(serialized) | ||
assert isinstance(x, InventoryItem) | ||
assert serialize_inv(x) == serialized | ||
|
||
def test_with_default_values(): | ||
def test_with_default_values(): | ||
|
||
@dataclass | ||
class X: | ||
one: int | ||
two: int = 2 | ||
three: int = 3 | ||
@dataclass | ||
class X: | ||
one: int | ||
two: int = 2 | ||
three: int = 3 | ||
|
||
data = {'one': 1} | ||
data = {'one': 1} | ||
|
||
mk_x, serialize_x = typeit.TypeConstructor ^ X | ||
x = mk_x(data) | ||
assert serialize_x(x) == {'one': 1, 'two': 2, 'three': 3} | ||
mk_x, serialize_x = typeit.TypeConstructor ^ X | ||
x = mk_x(data) | ||
assert serialize_x(x) == {'one': 1, 'two': 2, 'three': 3} | ||
|
||
def test_inherited_dataclasses(): | ||
@dataclass | ||
class X: | ||
x: int | ||
def test_inherited_dataclasses(): | ||
@dataclass | ||
class X: | ||
x: int | ||
|
||
@dataclass | ||
class Y(X): | ||
y: str | ||
@dataclass | ||
class Y(X): | ||
y: str | ||
|
||
data_invalid = {'y': 'string'} | ||
data_valid = {'x': 1, 'y': 'string'} | ||
data_invalid = {'y': 'string'} | ||
data_valid = {'x': 1, 'y': 'string'} | ||
|
||
mk_y, serialize_y = typeit.TypeConstructor ^ Y | ||
mk_y, serialize_y = typeit.TypeConstructor ^ Y | ||
|
||
with pytest.raises(typeit.Error): | ||
mk_y(data_invalid) | ||
with pytest.raises(typeit.Error): | ||
mk_y(data_invalid) | ||
|
||
y = mk_y(data_valid) | ||
assert isinstance(y, Y) | ||
assert isinstance(y, X) | ||
y = mk_y(data_valid) | ||
assert isinstance(y, Y) | ||
assert isinstance(y, X) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import json | ||
from typing import NamedTuple, Optional | ||
|
||
from typeit.custom_types import JsonString | ||
from typeit import TypeConstructor | ||
|
||
|
||
def test_json_string_direct_application(): | ||
mk_js, serialize_js = TypeConstructor ^ JsonString[int] | ||
js = mk_js("5") | ||
assert isinstance(js, JsonString) | ||
assert js.data == 5 | ||
assert serialize_js(js) == "5" | ||
|
||
|
||
def test_json_string_structures(): | ||
class Z(NamedTuple): | ||
z: int | ||
|
||
class X(NamedTuple): | ||
x: JsonString[str] | ||
y: JsonString[int] | ||
z: JsonString[Optional[Z]] | ||
|
||
mk_x, serialize_x = TypeConstructor ^ X | ||
|
||
data_x1 = dict( | ||
x=json.dumps("1"), | ||
y=json.dumps(2), | ||
z=json.dumps(None) | ||
) | ||
x1 = mk_x(data_x1) | ||
assert isinstance(x1, X) | ||
assert x1.x.data == "1" | ||
assert x1.y.data == 2 | ||
assert x1.z.data is None | ||
assert serialize_x(x1) == data_x1 | ||
|
||
data_x2 = dict( | ||
x=json.dumps("1"), | ||
y=json.dumps(2), | ||
z=json.dumps({'z': 3}) | ||
) | ||
x2 = mk_x(data_x2) | ||
assert isinstance(x2.z.data, Z) | ||
assert x2.z.data.z == 3 | ||
assert serialize_x(x2) == data_x2 | ||
|
||
|
||
def test_nested_json_string(): | ||
class NestedOpt(NamedTuple): | ||
opt: JsonString[JsonString[Optional[int]]] | ||
|
||
mk_opt, serialize_opt = TypeConstructor ^ NestedOpt | ||
|
||
data_1 = dict( | ||
opt=json.dumps(json.dumps(None)) | ||
) | ||
opt = mk_opt(data_1) | ||
assert isinstance(opt, NestedOpt) | ||
assert opt.opt.data.data is None | ||
assert serialize_opt(opt) == data_1 | ||
|
||
data_2 = dict( | ||
opt=json.dumps(json.dumps(1)) | ||
) | ||
opt = mk_opt(data_2) | ||
assert isinstance(opt, NestedOpt) | ||
assert opt.opt.data.data == 1 | ||
assert serialize_opt(opt) == data_2 | ||
|
||
|
||
def test_nested_optional_json_string(): | ||
class NestedOpt(NamedTuple): | ||
opt: JsonString[Optional[JsonString[Optional[int]]]] | ||
|
||
mk_opt, serialize_opt = TypeConstructor ^ NestedOpt | ||
|
||
data_1 = dict( | ||
opt=json.dumps(None) | ||
) | ||
opt1 = mk_opt(data_1) | ||
assert opt1.opt.data is None | ||
|
||
data_2 = dict( | ||
opt=json.dumps(json.dumps(1)) | ||
) | ||
opt2 = mk_opt(data_2) | ||
assert isinstance(opt2, NestedOpt) | ||
assert opt2.opt.data is not None | ||
assert opt2.opt.data.data == 1 | ||
assert serialize_opt(opt2) == data_2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
from . import flags | ||
from . import sums | ||
from . import custom_types | ||
from .combinator.constructor import type_constructor, TypeConstructor | ||
from .schema.errors import Error | ||
|
||
__all__ = ('TypeConstructor', 'type_constructor', 'flags', 'sums', 'Error') | ||
__all__ = ('TypeConstructor', 'type_constructor', 'flags', 'sums', 'Error', 'custom_types') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .json_string import JsonString | ||
|
||
__all__ = ('JsonString', ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import json | ||
from dataclasses import dataclass | ||
from typing import Generic, TypeVar | ||
|
||
from ..schema import Invalid | ||
from ..schema.types import Structure | ||
|
||
|
||
T = TypeVar('T') | ||
|
||
|
||
@dataclass(frozen=True) | ||
class JsonString(Generic[T]): | ||
data: T | ||
|
||
|
||
class JsonStringSchema(Structure): | ||
def __init__(self, *args, json=json, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.json = json | ||
|
||
def deserialize(self, node, cstruct: str) -> JsonString: | ||
""" Converts input string value ``cstruct`` to ``PortMapping`` | ||
""" | ||
try: | ||
data = self.json.loads(cstruct) | ||
except Exception as e: | ||
raise Invalid(node, | ||
f'Value is not a JSON string', | ||
cstruct | ||
) from e | ||
return super().deserialize(node, {'data': data}) | ||
|
||
def serialize(self, node, appstruct: JsonString) -> str: | ||
""" Converts ``PortMapping`` back to string value suitable for YAML config | ||
""" | ||
serialized = super().serialize(node, appstruct) | ||
return self.json.dumps(serialized['data']) |
Oops, something went wrong.