dataclass-extensions 0.2.6__tar.gz → 0.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/PKG-INFO +15 -7
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/README.md +14 -6
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/decode.py +11 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/registrable.py +13 -19
- dataclass_extensions-0.2.8/src/dataclass_extensions/version.py +1 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions.egg-info/PKG-INFO +15 -7
- dataclass_extensions-0.2.6/src/dataclass_extensions/version.py +0 -1
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/LICENSE +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/pyproject.toml +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/setup.cfg +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/__init__.py +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/encode.py +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/py.typed +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/types.py +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/utils.py +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions.egg-info/SOURCES.txt +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions.egg-info/dependency_links.txt +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions.egg-info/requires.txt +0 -0
- {dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataclass-extensions
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Additional functionality for Python dataclasses
|
|
5
5
|
Author-email: Pete Walsh <epwalsh10@gmail.com>
|
|
6
6
|
License: Apache License
|
|
@@ -263,7 +263,6 @@ assert decode(FruitBasket, encode(basket)) == basket
|
|
|
263
263
|
|
|
264
264
|
You can also define how to encode/decode non-dataclass types:
|
|
265
265
|
|
|
266
|
-
|
|
267
266
|
```python
|
|
268
267
|
from dataclasses import dataclass
|
|
269
268
|
from dataclass_extensions import decode, encode
|
|
@@ -280,11 +279,12 @@ class Bar:
|
|
|
280
279
|
encode.register_encoder(lambda foo: {"x": foo.x}, Foo)
|
|
281
280
|
decode.register_decoder(lambda d: Foo(d["x"]), Foo)
|
|
282
281
|
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
bar = Bar(foo=Foo(10))
|
|
283
|
+
assert encode(bar) == {"foo": {"x": 10}}
|
|
284
|
+
assert decode(Bar, encode(bar)) == bar
|
|
285
285
|
```
|
|
286
286
|
|
|
287
|
-
###
|
|
287
|
+
### Polymorphism through registrable subclasses
|
|
288
288
|
|
|
289
289
|
```python
|
|
290
290
|
from dataclasses import dataclass
|
|
@@ -307,6 +307,7 @@ class Banana(Fruit):
|
|
|
307
307
|
class Apple(Fruit):
|
|
308
308
|
calories: int = 150
|
|
309
309
|
price: float = 1.50
|
|
310
|
+
variety: str = "Granny Smith"
|
|
310
311
|
|
|
311
312
|
@dataclass
|
|
312
313
|
class FruitBasket:
|
|
@@ -314,7 +315,14 @@ class FruitBasket:
|
|
|
314
315
|
count: int
|
|
315
316
|
|
|
316
317
|
basket = FruitBasket(fruit=Apple(), count=2)
|
|
317
|
-
|
|
318
|
-
|
|
318
|
+
assert encode(basket) == {
|
|
319
|
+
"fruit": {
|
|
320
|
+
"type": "apple", # corresponds to the registered name
|
|
321
|
+
"calories": 150,
|
|
322
|
+
"price": 1.5,
|
|
323
|
+
"variety": "Granny Smith",
|
|
324
|
+
},
|
|
325
|
+
"count": 2,
|
|
326
|
+
}
|
|
319
327
|
assert decode(FruitBasket, encode(basket)) == basket
|
|
320
328
|
```
|
|
@@ -37,7 +37,6 @@ assert decode(FruitBasket, encode(basket)) == basket
|
|
|
37
37
|
|
|
38
38
|
You can also define how to encode/decode non-dataclass types:
|
|
39
39
|
|
|
40
|
-
|
|
41
40
|
```python
|
|
42
41
|
from dataclasses import dataclass
|
|
43
42
|
from dataclass_extensions import decode, encode
|
|
@@ -54,11 +53,12 @@ class Bar:
|
|
|
54
53
|
encode.register_encoder(lambda foo: {"x": foo.x}, Foo)
|
|
55
54
|
decode.register_decoder(lambda d: Foo(d["x"]), Foo)
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
bar = Bar(foo=Foo(10))
|
|
57
|
+
assert encode(bar) == {"foo": {"x": 10}}
|
|
58
|
+
assert decode(Bar, encode(bar)) == bar
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
###
|
|
61
|
+
### Polymorphism through registrable subclasses
|
|
62
62
|
|
|
63
63
|
```python
|
|
64
64
|
from dataclasses import dataclass
|
|
@@ -81,6 +81,7 @@ class Banana(Fruit):
|
|
|
81
81
|
class Apple(Fruit):
|
|
82
82
|
calories: int = 150
|
|
83
83
|
price: float = 1.50
|
|
84
|
+
variety: str = "Granny Smith"
|
|
84
85
|
|
|
85
86
|
@dataclass
|
|
86
87
|
class FruitBasket:
|
|
@@ -88,7 +89,14 @@ class FruitBasket:
|
|
|
88
89
|
count: int
|
|
89
90
|
|
|
90
91
|
basket = FruitBasket(fruit=Apple(), count=2)
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
assert encode(basket) == {
|
|
93
|
+
"fruit": {
|
|
94
|
+
"type": "apple", # corresponds to the registered name
|
|
95
|
+
"calories": 150,
|
|
96
|
+
"price": 1.5,
|
|
97
|
+
"variety": "Granny Smith",
|
|
98
|
+
},
|
|
99
|
+
"count": 2,
|
|
100
|
+
}
|
|
93
101
|
assert decode(FruitBasket, encode(basket)) == basket
|
|
94
102
|
```
|
{dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/decode.py
RENAMED
|
@@ -115,6 +115,17 @@ def _coerce(
|
|
|
115
115
|
except ValueError:
|
|
116
116
|
pass
|
|
117
117
|
|
|
118
|
+
if allowed_type is int:
|
|
119
|
+
if _safe_isinstance(value, (int, float)) and (value_as_int := int(value)) == value:
|
|
120
|
+
return value_as_int
|
|
121
|
+
elif _safe_isinstance(value, str):
|
|
122
|
+
try:
|
|
123
|
+
value_as_float = float(value)
|
|
124
|
+
if (value_as_int := int(value_as_float)) == value_as_float:
|
|
125
|
+
return value_as_int
|
|
126
|
+
except ValueError:
|
|
127
|
+
pass
|
|
128
|
+
|
|
118
129
|
origin = getattr(allowed_type, "__origin__", None)
|
|
119
130
|
args = getattr(allowed_type, "__args__", None)
|
|
120
131
|
if (origin is list or origin is collections.abc.MutableSequence) and _safe_isinstance(
|
{dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/registrable.py
RENAMED
|
@@ -17,6 +17,8 @@ if sys.version_info < (3, 11):
|
|
|
17
17
|
class Registrable:
|
|
18
18
|
_registry: ClassVar[dict[str, Type[Registrable]]]
|
|
19
19
|
_default_type: ClassVar[str | None]
|
|
20
|
+
registered_name: ClassVar[str | None]
|
|
21
|
+
registered_base: ClassVar[Type[Registrable] | None]
|
|
20
22
|
|
|
21
23
|
type: dataclasses.InitVar[str | None] = dataclasses.field(
|
|
22
24
|
default=None, kw_only=True, repr=False
|
|
@@ -24,16 +26,14 @@ class Registrable:
|
|
|
24
26
|
|
|
25
27
|
def __new__(cls, *args, type: str | None = None, **kwargs):
|
|
26
28
|
del args, kwargs
|
|
27
|
-
if type is not None and
|
|
28
|
-
not hasattr(cls, "registered_name") or type != cls.registered_name # type: ignore
|
|
29
|
-
):
|
|
29
|
+
if type is not None and type != cls.registered_name:
|
|
30
30
|
if type not in cls._registry:
|
|
31
31
|
raise KeyError(
|
|
32
32
|
f"'{type}' is not registered name for {cls.__name__}. "
|
|
33
33
|
f"Available choices are: {list(cls._registry.keys())}"
|
|
34
34
|
)
|
|
35
35
|
return super().__new__(cls._registry[type])
|
|
36
|
-
elif cls._default_type is not None and
|
|
36
|
+
elif cls._default_type is not None and cls.registered_name is None:
|
|
37
37
|
return super().__new__(cls._registry[cls._default_type])
|
|
38
38
|
else:
|
|
39
39
|
return super().__new__(cls)
|
|
@@ -44,6 +44,10 @@ class Registrable:
|
|
|
44
44
|
cls._registry = {}
|
|
45
45
|
if not hasattr(cls, "_default_type"):
|
|
46
46
|
cls._default_type = None
|
|
47
|
+
if not hasattr(cls, "registered_name"):
|
|
48
|
+
cls.registered_name = None
|
|
49
|
+
if not hasattr(cls, "registered_base"):
|
|
50
|
+
cls.registered_base = None
|
|
47
51
|
|
|
48
52
|
@classmethod
|
|
49
53
|
def register(cls, name: str, default: bool = False) -> Callable[[Type[R]], Type[R]]:
|
|
@@ -64,28 +68,18 @@ class Registrable:
|
|
|
64
68
|
else:
|
|
65
69
|
cls._default_type = name
|
|
66
70
|
|
|
67
|
-
fields = [
|
|
68
|
-
(f.name, f.type, f) for f in dataclasses.fields(subclass) if f.name != "type" # type: ignore
|
|
69
|
-
] + [
|
|
70
|
-
("registered_name", ClassVar[str], name), # type: ignore
|
|
71
|
-
("registered_base", ClassVar[R], cls), # type: ignore
|
|
72
|
-
("type", dataclasses.InitVar[str | None], dataclasses.field(default=name, kw_only=True, repr=False)), # type: ignore
|
|
73
|
-
]
|
|
74
|
-
subclass = dataclasses.make_dataclass(
|
|
75
|
-
subclass.__name__,
|
|
76
|
-
fields, # type: ignore
|
|
77
|
-
bases=(subclass,),
|
|
78
|
-
)
|
|
79
71
|
cls._registry[name] = subclass
|
|
80
|
-
|
|
72
|
+
subclass.registered_name = name
|
|
73
|
+
subclass.registered_base = cls
|
|
74
|
+
return subclass # type: ignore
|
|
81
75
|
|
|
82
76
|
return register_subclass
|
|
83
77
|
|
|
84
78
|
@classmethod
|
|
85
79
|
def get_registered_name(cls: Type[R], subclass: Type[R] | None = None) -> str:
|
|
86
80
|
if subclass is None:
|
|
87
|
-
if
|
|
88
|
-
return cls.registered_name
|
|
81
|
+
if cls.registered_name is not None:
|
|
82
|
+
return cls.registered_name
|
|
89
83
|
else:
|
|
90
84
|
raise ValueError(
|
|
91
85
|
f"class {cls.__name__} is not a registered subclass of any base registrable class"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.2.8"
|
{dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataclass-extensions
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Additional functionality for Python dataclasses
|
|
5
5
|
Author-email: Pete Walsh <epwalsh10@gmail.com>
|
|
6
6
|
License: Apache License
|
|
@@ -263,7 +263,6 @@ assert decode(FruitBasket, encode(basket)) == basket
|
|
|
263
263
|
|
|
264
264
|
You can also define how to encode/decode non-dataclass types:
|
|
265
265
|
|
|
266
|
-
|
|
267
266
|
```python
|
|
268
267
|
from dataclasses import dataclass
|
|
269
268
|
from dataclass_extensions import decode, encode
|
|
@@ -280,11 +279,12 @@ class Bar:
|
|
|
280
279
|
encode.register_encoder(lambda foo: {"x": foo.x}, Foo)
|
|
281
280
|
decode.register_decoder(lambda d: Foo(d["x"]), Foo)
|
|
282
281
|
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
bar = Bar(foo=Foo(10))
|
|
283
|
+
assert encode(bar) == {"foo": {"x": 10}}
|
|
284
|
+
assert decode(Bar, encode(bar)) == bar
|
|
285
285
|
```
|
|
286
286
|
|
|
287
|
-
###
|
|
287
|
+
### Polymorphism through registrable subclasses
|
|
288
288
|
|
|
289
289
|
```python
|
|
290
290
|
from dataclasses import dataclass
|
|
@@ -307,6 +307,7 @@ class Banana(Fruit):
|
|
|
307
307
|
class Apple(Fruit):
|
|
308
308
|
calories: int = 150
|
|
309
309
|
price: float = 1.50
|
|
310
|
+
variety: str = "Granny Smith"
|
|
310
311
|
|
|
311
312
|
@dataclass
|
|
312
313
|
class FruitBasket:
|
|
@@ -314,7 +315,14 @@ class FruitBasket:
|
|
|
314
315
|
count: int
|
|
315
316
|
|
|
316
317
|
basket = FruitBasket(fruit=Apple(), count=2)
|
|
317
|
-
|
|
318
|
-
|
|
318
|
+
assert encode(basket) == {
|
|
319
|
+
"fruit": {
|
|
320
|
+
"type": "apple", # corresponds to the registered name
|
|
321
|
+
"calories": 150,
|
|
322
|
+
"price": 1.5,
|
|
323
|
+
"variety": "Granny Smith",
|
|
324
|
+
},
|
|
325
|
+
"count": 2,
|
|
326
|
+
}
|
|
319
327
|
assert decode(FruitBasket, encode(basket)) == basket
|
|
320
328
|
```
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = "0.2.6"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/__init__.py
RENAMED
|
File without changes
|
{dataclass_extensions-0.2.6 → dataclass_extensions-0.2.8}/src/dataclass_extensions/encode.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|