value-object-pattern 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- value_object_pattern/__init__.py +3 -2
- value_object_pattern/models/__init__.py +5 -1
- value_object_pattern/models/enumeration_value_object.py +169 -0
- value_object_pattern/models/value_object.py +54 -5
- {value_object_pattern-0.2.0.dist-info → value_object_pattern-0.4.0.dist-info}/METADATA +1 -1
- {value_object_pattern-0.2.0.dist-info → value_object_pattern-0.4.0.dist-info}/RECORD +8 -7
- {value_object_pattern-0.2.0.dist-info → value_object_pattern-0.4.0.dist-info}/WHEEL +0 -0
- {value_object_pattern-0.2.0.dist-info → value_object_pattern-0.4.0.dist-info}/licenses/LICENSE.md +0 -0
value_object_pattern/__init__.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
__version__ = '0.
|
1
|
+
__version__ = '0.4.0'
|
2
2
|
|
3
3
|
from .decorators import process, validation
|
4
|
-
from .models import ValueObject
|
4
|
+
from .models import EnumerationValueObject, ValueObject
|
5
5
|
|
6
6
|
__all__ = (
|
7
|
+
'EnumerationValueObject',
|
7
8
|
'ValueObject',
|
8
9
|
'process',
|
9
10
|
'validation',
|
@@ -0,0 +1,169 @@
|
|
1
|
+
"""
|
2
|
+
EnumerationValueObject module.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from enum import Enum
|
6
|
+
from inspect import isclass
|
7
|
+
from sys import version_info
|
8
|
+
from typing import Any, Generic, TypeVar, get_args, get_origin
|
9
|
+
|
10
|
+
if version_info >= (3, 12):
|
11
|
+
from typing import override # pragma: no cover
|
12
|
+
else:
|
13
|
+
from typing_extensions import override # pragma: no cover
|
14
|
+
|
15
|
+
from value_object_pattern.decorators import process, validation
|
16
|
+
from value_object_pattern.models.value_object import ValueObject
|
17
|
+
|
18
|
+
E = TypeVar('E', bound=Enum)
|
19
|
+
|
20
|
+
|
21
|
+
class EnumerationValueObject(ValueObject[str | E], Generic[E]): # noqa: UP046
|
22
|
+
"""
|
23
|
+
EnumerationValueObject is a value object that ensures the provided value is from an enumeration.
|
24
|
+
|
25
|
+
Example:
|
26
|
+
```python
|
27
|
+
from enum import Enum, unique
|
28
|
+
|
29
|
+
from value_object_pattern import EnumerationValueObject
|
30
|
+
|
31
|
+
|
32
|
+
@unique
|
33
|
+
class ColorEnumeration(Enum):
|
34
|
+
RED = 1
|
35
|
+
GREEN = 2
|
36
|
+
BLUE = 3
|
37
|
+
|
38
|
+
|
39
|
+
class ColorValueObject(EnumerationValueObject[ColorEnumeration]):
|
40
|
+
pass
|
41
|
+
|
42
|
+
|
43
|
+
red = ColorValueObject(value=ColorEnumeration.RED)
|
44
|
+
green = ColorValueObject(value='GREEN')
|
45
|
+
print(repr(red), repr(green))
|
46
|
+
# >>> ColorValueObject(value=ColorEnumeration.RED) ColorValueObject(value=ColorEnumeration.GREEN)
|
47
|
+
```
|
48
|
+
"""
|
49
|
+
|
50
|
+
_enumeration: type[E]
|
51
|
+
|
52
|
+
@override
|
53
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
54
|
+
"""
|
55
|
+
Initializes the class.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
**kwargs (Any): Keyword arguments.
|
59
|
+
|
60
|
+
Raises:
|
61
|
+
TypeError: If the class parameter is not an Enum subclass.
|
62
|
+
TypeError: If the class is not parameterized.
|
63
|
+
"""
|
64
|
+
super().__init_subclass__(**kwargs)
|
65
|
+
|
66
|
+
for base in getattr(cls, '__orig_bases__', ()):
|
67
|
+
if get_origin(tp=base) is EnumerationValueObject:
|
68
|
+
enumeration, *_ = get_args(tp=base)
|
69
|
+
|
70
|
+
if not (isclass(object=enumeration) and issubclass(enumeration, Enum)):
|
71
|
+
raise TypeError(f'EnumerationValueObject[...] <<<{enumeration}>>> must be an Enum subclass. Got <<<{type(enumeration).__name__}>>> type.') # noqa: E501 # fmt: skip
|
72
|
+
|
73
|
+
cls._enumeration = enumeration
|
74
|
+
return
|
75
|
+
|
76
|
+
raise TypeError('EnumerationValueObject must be parameterized, e.g. "class ColorValueObject(EnumerationValueObject[ColorEnumeration])".') # noqa: E501 # fmt: skip
|
77
|
+
|
78
|
+
@override
|
79
|
+
def __repr__(self) -> str:
|
80
|
+
"""
|
81
|
+
Returns a detailed string representation of the value object.
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
str: A string representation of the value object in the format 'ClassName(value=value)'.
|
85
|
+
|
86
|
+
Example:
|
87
|
+
```python
|
88
|
+
from enum import Enum, unique
|
89
|
+
|
90
|
+
from value_object_pattern import EnumerationValueObject
|
91
|
+
|
92
|
+
|
93
|
+
@unique
|
94
|
+
class ColorEnumeration(Enum):
|
95
|
+
RED = 1
|
96
|
+
GREEN = 2
|
97
|
+
BLUE = 3
|
98
|
+
|
99
|
+
|
100
|
+
class ColorValueObject(EnumerationValueObject[ColorEnumeration]):
|
101
|
+
pass
|
102
|
+
|
103
|
+
|
104
|
+
red = ColorValueObject(value=ColorEnumeration.RED)
|
105
|
+
print(repr(red))
|
106
|
+
# >>> ColorValueObject(value=ColorEnumeration.RED)
|
107
|
+
```
|
108
|
+
"""
|
109
|
+
return f'{self.__class__.__name__}(value={self.value.__class__.__name__}.{self.value.name})'
|
110
|
+
|
111
|
+
@process(order=0)
|
112
|
+
def _ensure_value_is_stored_as_enumeration(self, value: str | E) -> E:
|
113
|
+
"""
|
114
|
+
Ensures the value object `value` is stored as an enumeration.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
value (str | E): The provided value. It can be the name of the member or the member itself.
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
E: The processed value.
|
121
|
+
"""
|
122
|
+
if isinstance(value, str):
|
123
|
+
return self._enumeration[value.upper()]
|
124
|
+
|
125
|
+
return value
|
126
|
+
|
127
|
+
@validation(order=0)
|
128
|
+
def _ensure_value_is_from_enumeration(self, value: str | E) -> None:
|
129
|
+
"""
|
130
|
+
Ensures the value object `value` is from the enumeration.
|
131
|
+
|
132
|
+
Args:
|
133
|
+
value (str | E): The provided value. It can be the name of the member or the member itself.
|
134
|
+
|
135
|
+
Raises:
|
136
|
+
TypeError: If the `value` is not from the enumeration.
|
137
|
+
"""
|
138
|
+
if isinstance(value, self._enumeration):
|
139
|
+
return
|
140
|
+
|
141
|
+
if isinstance(value, str) and value in self._enumeration.__members__:
|
142
|
+
return
|
143
|
+
|
144
|
+
raise TypeError(f'EnumerationValueObject value <<<{value}>>> must be from the enumeration <<<{self._title}>>>.') # noqa: E501 # fmt: skip
|
145
|
+
|
146
|
+
@override
|
147
|
+
@property
|
148
|
+
def value(self) -> E:
|
149
|
+
"""
|
150
|
+
Returns the value object value.
|
151
|
+
|
152
|
+
Returns:
|
153
|
+
E: The value object value.
|
154
|
+
|
155
|
+
Example:
|
156
|
+
```python
|
157
|
+
from value_object_pattern import ValueObject
|
158
|
+
|
159
|
+
|
160
|
+
class IntegerValueObject(ValueObject[int]):
|
161
|
+
pass
|
162
|
+
|
163
|
+
|
164
|
+
integer = IntegerValueObject(value=10)
|
165
|
+
print(integer.value)
|
166
|
+
# >>> 10
|
167
|
+
```
|
168
|
+
"""
|
169
|
+
return self._value # type: ignore[return-value]
|
@@ -15,7 +15,7 @@ else:
|
|
15
15
|
T = TypeVar('T')
|
16
16
|
|
17
17
|
|
18
|
-
class ValueObject(ABC, Generic[T]):
|
18
|
+
class ValueObject(ABC, Generic[T]): # noqa: UP046
|
19
19
|
"""
|
20
20
|
ValueObject generic type.
|
21
21
|
|
@@ -34,13 +34,14 @@ class ValueObject(ABC, Generic[T]):
|
|
34
34
|
```
|
35
35
|
"""
|
36
36
|
|
37
|
-
__slots__ = ('_title', '_value')
|
38
|
-
__match_args__ = ('_title', '_value')
|
37
|
+
__slots__ = ('_parameter', '_title', '_value')
|
38
|
+
__match_args__ = ('_parameter', '_title', '_value')
|
39
39
|
|
40
40
|
_value: T
|
41
41
|
_title: str
|
42
|
+
_parameter: str
|
42
43
|
|
43
|
-
def __init__(self, *, value: T, title: str | None = None) -> None:
|
44
|
+
def __init__(self, *, value: T, title: str | None = None, parameter: str | None = None) -> None:
|
44
45
|
"""
|
45
46
|
ValueObject value object constructor.
|
46
47
|
|
@@ -48,10 +49,16 @@ class ValueObject(ABC, Generic[T]):
|
|
48
49
|
value (T): The value to store in the value object.
|
49
50
|
title (str | None, optional): The title of the value object when raising exceptions, if title is None, the
|
50
51
|
class name is used instead. Defaults to None.
|
52
|
+
parameter (str | None, optional): The parameter name of the value object when raising exceptions, if
|
53
|
+
parameter is None, the string "value" is used instead. Defaults to None.
|
51
54
|
|
52
55
|
Raises:
|
53
56
|
TypeError: If the title is not a string.
|
57
|
+
ValueError: If the title is an empty string.
|
54
58
|
ValueError: If the title contains leading or trailing whitespaces.
|
59
|
+
TypeError: If the parameter is not a string.
|
60
|
+
ValueError: If the parameter is an empty string.
|
61
|
+
ValueError: If the parameter contains leading or trailing whitespaces.
|
55
62
|
|
56
63
|
Example:
|
57
64
|
```python
|
@@ -71,12 +78,28 @@ class ValueObject(ABC, Generic[T]):
|
|
71
78
|
title = self.__class__.__name__
|
72
79
|
|
73
80
|
if type(title) is not str:
|
74
|
-
raise TypeError(f'ValueObject
|
81
|
+
raise TypeError(f'ValueObject title <<<{title}>>> must be a string. Got <<<{type(title).__name__}>>> instead.') # noqa: E501 # fmt: skip
|
82
|
+
|
83
|
+
if title == '':
|
84
|
+
raise ValueError(f'ValueObject title <<<{title}>>> must not be an empty string.') # noqa: E501 # fmt: skip
|
75
85
|
|
76
86
|
if title.strip() != title:
|
77
87
|
raise ValueError(f'ValueObject title <<<{title}>>> contains leading or trailing whitespaces. Only trimmed values are allowed.') # noqa: E501 # fmt: skip
|
78
88
|
|
89
|
+
if parameter is None:
|
90
|
+
parameter = 'value'
|
91
|
+
|
92
|
+
if type(parameter) is not str:
|
93
|
+
raise TypeError(f'ValueObject parameter <<<{parameter}>>> must be a string. Got <<<{type(parameter).__name__}>>> instead.') # noqa: E501 # fmt: skip
|
94
|
+
|
95
|
+
if parameter == '':
|
96
|
+
raise ValueError(f'ValueObject parameter <<<{parameter}>>> must not be an empty string.') # noqa: E501 # fmt: skip
|
97
|
+
|
98
|
+
if parameter.strip() != parameter:
|
99
|
+
raise ValueError(f'ValueObject parameter <<<{parameter}>>> contains leading or trailing whitespaces. Only trimmed values are allowed.') # noqa: E501 # fmt: skip
|
100
|
+
|
79
101
|
object.__setattr__(self, '_title', title)
|
102
|
+
object.__setattr__(self, '_parameter', parameter)
|
80
103
|
|
81
104
|
self._validate(value=value)
|
82
105
|
value = self._process(value=value)
|
@@ -247,6 +270,8 @@ class ValueObject(ABC, Generic[T]):
|
|
247
270
|
for class_name in {cls.__name__ for cls in classes}:
|
248
271
|
error.args = (str(object=error.args[0]).replace(class_name, self.title),)
|
249
272
|
|
273
|
+
error.args = (str(object=error.args[0]).replace('value', self.parameter, 1),)
|
274
|
+
|
250
275
|
raise error
|
251
276
|
|
252
277
|
def _post_order_dfs_mro(self, cls: type, visited: set[type] | None = None, cut_off: type = object) -> list[type]:
|
@@ -381,3 +406,27 @@ class ValueObject(ABC, Generic[T]):
|
|
381
406
|
```
|
382
407
|
"""
|
383
408
|
return self._title
|
409
|
+
|
410
|
+
@property
|
411
|
+
def parameter(self) -> str:
|
412
|
+
"""
|
413
|
+
Returns the value object parameter name.
|
414
|
+
|
415
|
+
Returns:
|
416
|
+
str: The value object parameter name.
|
417
|
+
|
418
|
+
Example:
|
419
|
+
```python
|
420
|
+
from value_object_pattern import ValueObject
|
421
|
+
|
422
|
+
|
423
|
+
class IntegerValueObject(ValueObject[int]):
|
424
|
+
pass
|
425
|
+
|
426
|
+
|
427
|
+
integer = IntegerValueObject(value=10)
|
428
|
+
print(integer.parameter)
|
429
|
+
# >>> value
|
430
|
+
```
|
431
|
+
"""
|
432
|
+
return self._parameter
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: value-object-pattern
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: The Value Object Pattern is a Python package that streamlines the creation and management of value objects in your projects.
|
5
5
|
Project-URL: Homepage, https://github.com/adriamontoto/value-object-pattern
|
6
6
|
Project-URL: Repository, https://github.com/adriamontoto/value-object-pattern
|
@@ -1,10 +1,11 @@
|
|
1
|
-
value_object_pattern/__init__.py,sha256=
|
1
|
+
value_object_pattern/__init__.py,sha256=12VKCLbhT9Cz8m58ucw8UJ4NhO2G0YY6TZjcEWF7MWA,220
|
2
2
|
value_object_pattern/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
value_object_pattern/decorators/__init__.py,sha256=Ze0c0z3x5sdmV-KB7nljPsybpoTXAgjJDGfR8FCIxUQ,138
|
4
4
|
value_object_pattern/decorators/value_object_process.py,sha256=Q_0w26XolcfZOE6avveA5OGs1yIMNAVC_AxsBV8F5D0,2621
|
5
5
|
value_object_pattern/decorators/value_object_validation.py,sha256=ENAQuOLalc-dDzVEpRolbBg8Y0g7--4DRimHTPDD12U,2793
|
6
|
-
value_object_pattern/models/__init__.py,sha256=
|
7
|
-
value_object_pattern/models/
|
6
|
+
value_object_pattern/models/__init__.py,sha256=1BsXKFnX_0EufuHurfgdpbMUvnrjF8HLz46saTcmNr4,163
|
7
|
+
value_object_pattern/models/enumeration_value_object.py,sha256=6ih7GlohDcA3Lknk6SaIsve2Udaco8iHTPkRCrhvYJo,4886
|
8
|
+
value_object_pattern/models/value_object.py,sha256=-xXCk_qwCj_VBIzNJfUflM4vD7M0bicJdlprFYh2_Uw,13627
|
8
9
|
value_object_pattern/usables/__init__.py,sha256=LsHh5uSECizrDXPogZC3zRMzcBIBPyiWXpqSrG2qHrg,1483
|
9
10
|
value_object_pattern/usables/dates/__init__.py,sha256=x4MafCtJ1S9WN36cVGOfPdXeKgfptlg4QkcH9fPV4tA,253
|
10
11
|
value_object_pattern/usables/dates/date/__init__.py,sha256=rSifr41n8gmqW80e0Ygzoj221dpVsZy7m4TecC_VJus,174
|
@@ -61,7 +62,7 @@ value_object_pattern/usables/primitives/string/printable_string_value_object.py,
|
|
61
62
|
value_object_pattern/usables/primitives/string/string_value_object.py,sha256=6u98oMzz5qsVNEWna-UpziDmzFhaLFW7J5NBoJnbKWk,1370
|
62
63
|
value_object_pattern/usables/primitives/string/trimmed_string_value_object.py,sha256=WMp4IBA74cEli-N_9Q7qU_oMszHtk5tgCr6o2y99HMs,1435
|
63
64
|
value_object_pattern/usables/primitives/string/uppercase_string_value_object.py,sha256=yZajkdq09zb56_UHs4nc0sIeTV4K74ek26JcoHYhM64,1457
|
64
|
-
value_object_pattern-0.
|
65
|
-
value_object_pattern-0.
|
66
|
-
value_object_pattern-0.
|
67
|
-
value_object_pattern-0.
|
65
|
+
value_object_pattern-0.4.0.dist-info/METADATA,sha256=4wEChHY3T0DNwarqKoc9_gGhl4hsomvd86r1bN7W1sI,4018
|
66
|
+
value_object_pattern-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
67
|
+
value_object_pattern-0.4.0.dist-info/licenses/LICENSE.md,sha256=6E0wpVdiB-k3oJ9cvVG9BPZ07yIevD3IuOECHKveFkM,1076
|
68
|
+
value_object_pattern-0.4.0.dist-info/RECORD,,
|
File without changes
|
{value_object_pattern-0.2.0.dist-info → value_object_pattern-0.4.0.dist-info}/licenses/LICENSE.md
RENAMED
File without changes
|