Skip to content

Commit c587aff

Browse files
committed
Issue #146
2.13.1
1 parent 914e1d5 commit c587aff

File tree

10 files changed

+252
-191
lines changed

10 files changed

+252
-191
lines changed

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
license="MIT",
3030
long_description=long_description,
3131
url="http://github.com/loyada/typedpy",
32-
download_url ="https://github.com/loyada/typedpy/archive/v2.13.0.tar.gz",
32+
download_url ="https://github.com/loyada/typedpy/archive/v2.13.1.tar.gz",
3333
keywords=['testing', 'type-safe', 'strict', 'schema', 'validation'],
34-
version='2.13.0'
34+
version='2.13.1'
3535
)
3636

3737
# coverage run --source=typedpy/ setup.py test

tests/test_structure_extend.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from typedpy import ImmutableStructure, Map, Structure, mappers, Serializer, Extend
5+
from typedpy import Deserializer, ImmutableStructure, Map, Structure, mappers, Serializer, Extend
66

77

88
class Blah(Structure):
@@ -106,4 +106,5 @@ class Bar(Extend[Foo], Structure):
106106
_serialization_mapper = mappers.TO_LOWERCASE
107107

108108
assert Bar._serialization_mapper == mappers.TO_LOWERCASE
109-
assert Bar._deserialization_mapper is None
109+
assert not hasattr(Bar, "_deserialization_mapper")
110+
assert Deserializer(Bar).deserialize({"X": "a", "Y": "b"}) == Bar(x="a", y="b")

typedpy/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
FinalStructure,
1414
ImmutableField,
1515
unique,
16-
AbstractStructure
16+
AbstractStructure,
1717
)
1818

1919
from typedpy.structures_reuse import (
@@ -85,7 +85,7 @@
8585
serialize_field,
8686
FunctionCall,
8787
deserialize_single_field,
88-
HasTypes
88+
HasTypes,
8989
)
9090

9191
from .serialization_wrappers import (
@@ -117,4 +117,4 @@
117117

118118
from .commons import nested, deep_get, first_in, flatten, default_factories
119119

120-
from .keysof import keys_of
120+
from .keysof import keys_of

typedpy/commons.py

Lines changed: 88 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ def wrap_val(v):
1919
def _is_dunder(name):
2020
"""Returns True if a __dunder__ name, False otherwise."""
2121
return (
22-
len(name) > 4
23-
and name[:2] == name[-2:] == "__"
24-
and name[2] != "_"
25-
and name[-3] != "_"
22+
len(name) > 4
23+
and name[:2] == name[-2:] == "__"
24+
and name[2] != "_"
25+
and name[-3] != "_"
2626
)
2727

2828

@@ -37,21 +37,20 @@ def raise_errs_if_needed(errors):
3737
raise errors[0].__class__(messages) from errors[0]
3838

3939

40-
4140
def first_in(my_list: Iterable, ignore_none: bool = False) -> Optional:
4241
"""
43-
get the first in an Iterable (i.e. list, tuple, generator, dict, etc.).
44-
Optionally ignoring None values.
42+
get the first in an Iterable (i.e. list, tuple, generator, dict, etc.).
43+
Optionally ignoring None values.
4544
46-
Arguments:
47-
my_list(Iterable):
48-
The input iterable, such as a list
49-
ignore_none(bool): optional
50-
whether or not to ignore None values. Default is False
45+
Arguments:
46+
my_list(Iterable):
47+
The input iterable, such as a list
48+
ignore_none(bool): optional
49+
whether or not to ignore None values. Default is False
5150
5251
53-
Returns:
54-
The first value found in the input, or None if none found
52+
Returns:
53+
The first value found in the input, or None if none found
5554
"""
5655
if ignore_none:
5756
return next(filter(None, iter(my_list)), None)
@@ -60,28 +59,28 @@ def first_in(my_list: Iterable, ignore_none: bool = False) -> Optional:
6059

6160
def nested(func, default=None):
6261
"""
63-
get a nested value if it exists, or return the given default if it doesn't
62+
get a nested value if it exists, or return the given default if it doesn't
6463
65-
Arguments:
66-
func(function):
67-
A function with no arguments that returns the nested value
68-
default:
69-
the default value, in case the nested value does not exist
64+
Arguments:
65+
func(function):
66+
A function with no arguments that returns the nested value
67+
default:
68+
the default value, in case the nested value does not exist
7069
7170
72-
Returns:
73-
the nested attribute(s) or the default value. For example:
71+
Returns:
72+
the nested attribute(s) or the default value. For example:
7473
75-
For example:
74+
For example:
7675
77-
.. code-block:: python
76+
.. code-block:: python
7877
79-
d = D(c=C(b=B(a=A(i=5))))
80-
assert nested(lambda: d.c.b.a.i) == 5
78+
d = D(c=C(b=B(a=A(i=5))))
79+
assert nested(lambda: d.c.b.a.i) == 5
8180
82-
d.c.foo = [1, 2, 3]
83-
assert nested(lambda: d.c.foo[100]) is None
84-
assert nested(lambda: d.c.foo[2]) == 3
81+
d.c.foo = [1, 2, 3]
82+
assert nested(lambda: d.c.foo[100]) is None
83+
assert nested(lambda: d.c.foo[2]) == 3
8584
"""
8685
try:
8786
return func()
@@ -91,27 +90,27 @@ def nested(func, default=None):
9190

9291
def flatten(iterable, ignore_none=False) -> list:
9392
"""
94-
Flatten an iterable completely, getting rid of None values
93+
Flatten an iterable completely, getting rid of None values
9594
96-
Arguments:
97-
iterable(Iterable):
98-
the input
99-
ignore_none(bool): optional
100-
whether to skip None values or not. Default is False.
95+
Arguments:
96+
iterable(Iterable):
97+
the input
98+
ignore_none(bool): optional
99+
whether to skip None values or not. Default is False.
101100
102-
Returns:
103-
A flattened list
101+
Returns:
102+
A flattened list
104103
105-
.. code-block:: python
104+
.. code-block:: python
106105
107-
flatten(
108-
[
109-
[[1]],
110-
[[2], 3, None, (5,)],
111-
[]
112-
], ignore_none=True) == [1, 2, 3, 5]
106+
flatten(
107+
[
108+
[[1]],
109+
[[2], 3, None, (5,)],
110+
[]
111+
], ignore_none=True) == [1, 2, 3, 5]
113112
114-
"""
113+
"""
115114
res = (
116115
sum([flatten(x, ignore_none) for x in iterable], [])
117116
if isinstance(iterable, (list, tuple, Generator))
@@ -124,29 +123,29 @@ def flatten(iterable, ignore_none=False) -> list:
124123

125124
def deep_get(dictionary, deep_key, default=None, do_flatten=False):
126125
"""
127-
Get a nested value from within a dictionary. Supports also nested lists, in
128-
which case the result is a a list of values
126+
Get a nested value from within a dictionary. Supports also nested lists, in
127+
which case the result is a a list of values
129128
130-
Arguments:
131-
dictionary(dict):
132-
the input
133-
deep_key(str):
134-
nested key of the form aaa.bbb.ccc.ddd
135-
default:
136-
the default value, in case the path does not exist
137-
do_flatten(bool): optional
138-
flatten the outputs, in case the result is multiple outputs
129+
Arguments:
130+
dictionary(dict):
131+
the input
132+
deep_key(str):
133+
nested key of the form aaa.bbb.ccc.ddd
134+
default:
135+
the default value, in case the path does not exist
136+
do_flatten(bool): optional
137+
flatten the outputs, in case the result is multiple outputs
139138
140-
Returns:
141-
the nested attribute(s) or the default value. For example:
139+
Returns:
140+
the nested attribute(s) or the default value. For example:
142141
143-
For example:
142+
For example:
144143
145-
.. code-block:: python
144+
.. code-block:: python
146145
147-
example = {"a": {"b": [{"c": [None, {"d": [1]}]}, {"c": [None, {"d": [2]}, {"d": 3}]}, {"c": []}]}}
148-
assert deep_get(example, "a.b.c.d") == [[[1]], [[2], 3], []]
149-
assert deep_get(example, "a.b.c.d", do_flatten=True) == [1, 2, 3]
146+
example = {"a": {"b": [{"c": [None, {"d": [1]}]}, {"c": [None, {"d": [2]}, {"d": 3}]}, {"c": []}]}}
147+
assert deep_get(example, "a.b.c.d") == [[[1]], [[2], 3], []]
148+
assert deep_get(example, "a.b.c.d", do_flatten=True) == [1, 2, 3]
150149
151150
"""
152151

@@ -159,36 +158,40 @@ def _get_next_level(d: Optional[Union[Mapping, Iterable]], key, default):
159158
return default
160159

161160
keys = deep_key.split(".")
162-
result = reduce(lambda d, key: _get_next_level(d, key, default) if d else default, keys, dictionary)
161+
result = reduce(
162+
lambda d, key: _get_next_level(d, key, default) if d else default,
163+
keys,
164+
dictionary,
165+
)
163166
if isinstance(result, (list, tuple, Generator)) and do_flatten:
164167
result = flatten(result)
165168
return result
166169

167170

168171
def default_factories(func):
169172
"""
170-
A function decorator that allows to have default values that are generators of the actual
171-
default values to be used. This is useful when the default values are mutable, like
172-
dicts or lists
173+
A function decorator that allows to have default values that are generators of the actual
174+
default values to be used. This is useful when the default values are mutable, like
175+
dicts or lists
173176
174-
Arguments:
175-
iterable(Iterable):
176-
the input
177-
ignore_none(bool): optional
178-
whether to skip None values or not. Default is False.
177+
Arguments:
178+
iterable(Iterable):
179+
the input
180+
ignore_none(bool): optional
181+
whether to skip None values or not. Default is False.
179182
180-
Returns:
181-
A flattened list
183+
Returns:
184+
A flattened list
182185
183-
For example:
186+
For example:
184187
185-
.. code-block:: python
188+
.. code-block:: python
186189
187-
@default_factories
188-
def func(a, b = 0, c = list, d = dict):
189-
return a, b, c, d
190+
@default_factories
191+
def func(a, b = 0, c = list, d = dict):
192+
return a, b, c, d
190193
191-
assert func(1) == 1, 0, [] , {}
194+
assert func(1) == 1, 0, [] , {}
192195
193196
"""
194197
func_signature = signature(func, follow_wrapped=True)
@@ -199,7 +202,11 @@ def decorated(*args, **kwargs):
199202

200203
for k, v in func_signature.parameters.items():
201204
if k not in bound.arguments:
202-
default = v.default() if callable(v.default) and v.default != v.empty else v.default
205+
default = (
206+
v.default()
207+
if callable(v.default) and v.default != v.empty
208+
else v.default
209+
)
203210
if v.default != v.empty:
204211
kwargs[k] = default
205212
return func(*args, **kwargs)

typedpy/enum.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def __getitem__(cls, values):
1414

1515
def _all_values_from_single_enum(values):
1616
clazz = first_in(values).__class__
17-
if not(isinstance(clazz, (type,)) and issubclass(clazz, enum.Enum)):
17+
if not (isinstance(clazz, (type,)) and issubclass(clazz, enum.Enum)):
1818
return False
1919
return all([v.__class__ is clazz for v in values])
2020

@@ -28,9 +28,9 @@ class Enum(SerializableField, metaclass=_EnumMeta):
2828
allowed values. Can be of any type.
2929
Alternatively, can be an enum.Enum type. See example below.
3030
When defined with an enum.Enum, serialization converts to strings,
31-
while deserialization expects strings (unless using serialization_by_value).
31+
while deserialization expects strings (unless using serialization_by_value).
3232
In this case, strings are converted to the original enum values.
33-
33+
3434
Another option is assign a list of specific values from an enum.Enum class.
3535
In this case, it will work like asigning an Enum class, but allowing only specific values
3636
of that enum (see example below).
@@ -80,9 +80,9 @@ class Action(Structure):
8080
8181
8282
assert Deserializer(Action).deserialize({"command": "delete stream"}).command is StreamCommand.delete
83-
83+
8484
An example of allowing only specific values of an Enum class, referencging StreamCommand in the previous example:
85-
85+
8686
.. code-block:: python
8787
8888
class Action(Structure):
@@ -98,15 +98,22 @@ class Action(Structure):
9898
def __init__(self, *args, values, serialization_by_value: bool = False, **kwargs):
9999
if not values:
100100
raise ValueError("Enum requires values parameters")
101-
self._is_enum = (isinstance(values, (type,)) and issubclass(values, enum.Enum)
102-
or _all_values_from_single_enum(values))
101+
self._is_enum = (
102+
isinstance(values, (type,))
103+
and issubclass(values, enum.Enum)
104+
or _all_values_from_single_enum(values)
105+
)
103106
self.serialization_by_value = serialization_by_value
104107

105108
if self._is_enum:
106-
self._enum_class = values if isinstance(values, (type,)) else first_in(values).__class__
109+
self._enum_class = (
110+
values if isinstance(values, (type,)) else first_in(values).__class__
111+
)
107112
if serialization_by_value:
108113
self._enum_by_value = {e.value: e for e in self._enum_class}
109-
self._valid_enum_values = [v for v in self._enum_class] if isinstance(values, (type,)) else values
114+
self._valid_enum_values = (
115+
[v for v in self._enum_class] if isinstance(values, (type,)) else values
116+
)
110117
self.values = list(values)
111118
else:
112119
self.values = values
@@ -134,7 +141,9 @@ def serialize(self, value):
134141
if self._is_enum:
135142
if self.serialization_by_value:
136143
if not isinstance(value.value, (bool, str, int, float)):
137-
raise TypeError(f"{self._name}: Cannot serialize value: {value.value}")
144+
raise TypeError(
145+
f"{self._name}: Cannot serialize value: {value.value}"
146+
)
138147
return value.value if self.serialization_by_value else value.name
139148
return value
140149

typedpy/fields.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def err_prefix():
262262
)
263263
if self.pattern is not None and not self._compiled_pattern.match(value):
264264
raise ValueError(
265-
f'{err_prefix()}Does not match regular expression: {wrap_val(self.pattern)}'
265+
f"{err_prefix()}Does not match regular expression: {wrap_val(self.pattern)}"
266266
)
267267

268268
def __set__(self, instance, value):

0 commit comments

Comments
 (0)