value-object-pattern 0.9.2__tar.gz → 0.10.0__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.
Files changed (83) hide show
  1. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/PKG-INFO +1 -1
  2. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/__init__.py +1 -1
  3. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/models/base_model.py +35 -1
  4. value_object_pattern-0.10.0/value_object_pattern/models/collections/__init__.py +3 -0
  5. value_object_pattern-0.10.0/value_object_pattern/models/collections/list_value_object.py +376 -0
  6. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/models/enumeration_value_object.py +16 -72
  7. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/models/value_object.py +2 -2
  8. value_object_pattern-0.9.2/value_object_pattern/models/collections/__init__.py +0 -11
  9. value_object_pattern-0.9.2/value_object_pattern/models/collections/collection_value_object.py +0 -107
  10. value_object_pattern-0.9.2/value_object_pattern/models/collections/iterable_value_object.py +0 -71
  11. value_object_pattern-0.9.2/value_object_pattern/models/collections/not_empty_sequence_value_object.py +0 -51
  12. value_object_pattern-0.9.2/value_object_pattern/models/collections/sequence_value_object.py +0 -143
  13. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/.gitignore +0 -0
  14. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/LICENSE.md +0 -0
  15. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/README.md +0 -0
  16. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/pyproject.toml +0 -0
  17. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/decorators/__init__.py +0 -0
  18. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/decorators/value_object_process.py +0 -0
  19. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/decorators/value_object_validation.py +0 -0
  20. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/models/__init__.py +0 -0
  21. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/py.typed +0 -0
  22. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/__init__.py +0 -0
  23. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/__init__.py +0 -0
  24. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/date/__init__.py +0 -0
  25. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/date/date_value_object.py +0 -0
  26. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/date/string_date_value_object.py +0 -0
  27. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/datetime/__init__.py +0 -0
  28. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/datetime/datetime_value_object.py +0 -0
  29. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/datetime/string_datetime_value_object.py +0 -0
  30. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/timezone/__init__.py +0 -0
  31. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/timezone/string_timezone_value_object.py +0 -0
  32. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/dates/timezone/timezone_value_object.py +0 -0
  33. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/identifiers/__init__.py +0 -0
  34. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/identifiers/countries/__init__.py +0 -0
  35. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/identifiers/countries/spain/__init__.py +0 -0
  36. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/identifiers/countries/spain/dni_value_object.py +0 -0
  37. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/identifiers/string_uuid_value_object.py +0 -0
  38. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/identifiers/uuid_value_object.py +0 -0
  39. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/__init__.py +0 -0
  40. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/aws_cloud_region_value_object.py +0 -0
  41. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/domain_value_object.py +0 -0
  42. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/host_value_object.py +0 -0
  43. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/ipv4_address_value_object.py +0 -0
  44. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/ipv4_network_value_object.py +0 -0
  45. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/ipv6_address_value_object.py +0 -0
  46. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/ipv6_network_value_object.py +0 -0
  47. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/mac_address_value_object.py +0 -0
  48. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/port_value_object.py +0 -0
  49. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/uri/__init__.py +0 -0
  50. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/uri/http_https_url_value_object.py +0 -0
  51. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/uri/http_url_value_object.py +0 -0
  52. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/uri/https_url_value_object.py +0 -0
  53. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/internet/uri/url_value_object.py +0 -0
  54. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/__init__.py +0 -0
  55. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/boolean/__init__.py +0 -0
  56. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/boolean/boolean_value_object.py +0 -0
  57. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/boolean/false_value_object.py +0 -0
  58. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/boolean/true_value_object.py +0 -0
  59. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/bytes/__init__.py +0 -0
  60. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/bytes/bytes_value_object.py +0 -0
  61. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/float/__init__.py +0 -0
  62. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/float/float_value_object.py +0 -0
  63. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/float/negative_float_value_object.py +0 -0
  64. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/float/positive_float_value_object.py +0 -0
  65. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/integer/__init__.py +0 -0
  66. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/integer/even_integer_value_object.py +0 -0
  67. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/integer/integer_value_object.py +0 -0
  68. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/integer/negative_integer_value_object.py +0 -0
  69. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/integer/odd_integer_value_object.py +0 -0
  70. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/integer/positive_integer_value_object.py +0 -0
  71. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/none/__init__.py +0 -0
  72. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/none/none_value_object.py +0 -0
  73. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/none/not_none_value_object.py +0 -0
  74. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/__init__.py +0 -0
  75. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/alpha_value_object.py +0 -0
  76. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/alphanumeric_value_object.py +0 -0
  77. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/digit_value_object.py +0 -0
  78. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/lowercase_string_value_object.py +0 -0
  79. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/non_empty_string_value_object.py +0 -0
  80. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/printable_string_value_object.py +0 -0
  81. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/string_value_object.py +0 -0
  82. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/trimmed_string_value_object.py +0 -0
  83. {value_object_pattern-0.9.2 → value_object_pattern-0.10.0}/value_object_pattern/usables/primitives/string/uppercase_string_value_object.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: value-object-pattern
3
- Version: 0.9.2
3
+ Version: 0.10.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,4 +1,4 @@
1
- __version__ = '0.9.2'
1
+ __version__ = '0.10.0'
2
2
 
3
3
  from .decorators import process, validation
4
4
  from .models import BaseModel, EnumerationValueObject, ValueObject
@@ -11,6 +11,7 @@ else:
11
11
 
12
12
  from abc import ABC, abstractmethod
13
13
  from enum import Enum
14
+ from inspect import Parameter, _empty, signature
14
15
  from typing import Any, NoReturn, Self
15
16
 
16
17
  from .value_object import ValueObject
@@ -267,6 +268,7 @@ class BaseModel(ABC):
267
268
 
268
269
  Raises:
269
270
  TypeError: If the `primitives` is not a dictionary of strings.
271
+ ValueError: If the `primitives` does not have all the required attributes.
270
272
 
271
273
  Returns:
272
274
  Self: Instance of the class.
@@ -297,6 +299,14 @@ class BaseModel(ABC):
297
299
  if not isinstance(primitives, dict) or not all(isinstance(key, str) for key in primitives): # type: ignore[redundant-expr]
298
300
  cls._raise_value_is_not_dict_of_strings(value=primitives)
299
301
 
302
+ constructor_signature = signature(obj=cls.__init__)
303
+ parameters: dict[str, Parameter] = {parameter.name: parameter for parameter in constructor_signature.parameters.values() if parameter.name != 'self'} # noqa: E501 # fmt: skip
304
+ missing = {name for name, parameter in parameters.items() if parameter.default is _empty and name not in primitives} # noqa: E501 # fmt: skip
305
+ extra = set(primitives) - parameters.keys()
306
+
307
+ if missing or extra:
308
+ cls._raise_value_constructor_parameters_mismatch(primitives=set(primitives), missing=missing, extra=extra)
309
+
300
310
  return cls(**primitives)
301
311
 
302
312
  @classmethod
@@ -312,6 +322,30 @@ class BaseModel(ABC):
312
322
  """
313
323
  raise TypeError(f'{cls.__name__} primitives <<<{value}>>> must be a dictionary of strings. Got <<<{type(value).__name__}>>> type.') # noqa: E501 # fmt: skip
314
324
 
325
+ @classmethod
326
+ def _raise_value_constructor_parameters_mismatch(
327
+ cls,
328
+ primitives: set[str],
329
+ missing: set[str],
330
+ extra: set[str],
331
+ ) -> NoReturn:
332
+ """
333
+ Raises a ValueError if the value object constructor parameters do not match the provided primitives.
334
+
335
+ Args:
336
+ primitives (set[str]): Set of primitives keys. Only the keys are used to not expose private attributes.
337
+ missing (set[str]): Set of missing parameters.
338
+ extra (set[str]): Set of extra parameters.
339
+
340
+ Raises:
341
+ ValueError: If the constructor parameters do not match the provided primitives.
342
+ """
343
+ primitives_names = ', '.join(sorted(primitives))
344
+ missing_names = ', '.join(sorted(missing))
345
+ extra_names = ', '.join(sorted(extra))
346
+
347
+ raise ValueError(f'{cls.__name__} primitives <<<{primitives_names}>>> must contain all constructor parameters. Missing parameters: <<<{missing_names}>> and extra parameters: <<<{extra_names}>>>.') # noqa: E501 # fmt: skip
348
+
315
349
  def to_primitives(self) -> dict[str, Any]:
316
350
  """
317
351
  Returns the class as a dictionary of its primitives. Private attributes that start with "__" are not included,
@@ -367,7 +401,7 @@ class BaseModel(ABC):
367
401
  pass
368
402
 
369
403
  else:
370
- value = str(value)
404
+ value = str(object=value)
371
405
 
372
406
  dictionary[key] = value
373
407
 
@@ -0,0 +1,3 @@
1
+ from .list_value_object import ListValueObject
2
+
3
+ __all__ = ('ListValueObject',)
@@ -0,0 +1,376 @@
1
+ """
2
+ ListValueObject module.
3
+ """
4
+
5
+ from sys import version_info
6
+
7
+ if version_info >= (3, 12):
8
+ from typing import override # pragma: no cover
9
+ else:
10
+ from typing_extensions import override # pragma: no cover
11
+
12
+ from collections.abc import Iterator
13
+ from enum import Enum
14
+ from inspect import isclass
15
+ from typing import Any, Generic, NoReturn, Self, TypeVar, get_args, get_origin
16
+
17
+ from value_object_pattern.decorators import validation
18
+ from value_object_pattern.models import BaseModel, ValueObject
19
+
20
+ T = TypeVar('T', bound=Any)
21
+
22
+
23
+ class ListValueObject(ValueObject[list[T]], Generic[T]): # noqa: UP046
24
+ """
25
+ ListValueObject is a value object that ensures the provided value is from a list.
26
+
27
+ Example:
28
+ ```python
29
+ from value_object_pattern.models.collections import ListValueObject
30
+
31
+
32
+ class IntListValueObject(ListValueObject[int]):
33
+ pass
34
+
35
+
36
+ sequence = IntListValueObject(value=[1, 2, 3])
37
+ print(sequence)
38
+ # >>> [1, 2, 3]
39
+ ```
40
+ """
41
+
42
+ _type: T
43
+
44
+ @override
45
+ def __init_subclass__(cls, **kwargs: Any) -> None:
46
+ """
47
+ Initializes the class.
48
+
49
+ Args:
50
+ **kwargs (Any): Keyword arguments.
51
+
52
+ Raises:
53
+ TypeError: If the class parameter is not a type.
54
+ TypeError: If the class is not parameterized.
55
+ """
56
+ super().__init_subclass__(**kwargs)
57
+
58
+ for base in getattr(cls, '__orig_bases__', ()):
59
+ if get_origin(tp=base) is ListValueObject:
60
+ _type, *_ = get_args(tp=base)
61
+
62
+ if isinstance(_type, TypeVar):
63
+ cls._type = _type
64
+ return
65
+
66
+ if type(_type) is not type and not isclass(object=_type) and get_origin(tp=_type) is None:
67
+ raise TypeError(f'ListValueObject[...] <<<{_type}>>> must be a type. Got <<<{type(_type).__name__}>>> type.') # noqa: E501 # fmt: skip
68
+
69
+ cls._type = _type
70
+ return
71
+
72
+ raise TypeError('ListValueObject must be parameterized, e.g. "class InIntListValueObject(ListValueObject[int])".') # noqa: E501 # fmt: skip
73
+
74
+ def __contains__(self, item: Any) -> bool:
75
+ """
76
+ Returns True if the value object value contains the item, otherwise False.
77
+
78
+ Args:
79
+ item (Any): The item to check.
80
+
81
+ Returns:
82
+ bool: True if the value object value contains the item, otherwise False.
83
+
84
+ Example:
85
+ ```python
86
+ from value_object_pattern.models.collections import ListValueObject
87
+
88
+
89
+ class IntListValueObject(ListValueObject[int]):
90
+ pass
91
+
92
+
93
+ sequence = IntListValueObject(value=[1, 2, 3])
94
+ print(1 in sequence)
95
+ # >>> True
96
+ ```
97
+ """
98
+ return item in self._value
99
+
100
+ def __iter__(self) -> Iterator[T]:
101
+ """
102
+ Returns an iterator over the value object value.
103
+
104
+ Returns:
105
+ Iterator[T]: An iterator over the value object value.
106
+
107
+ Example:
108
+ ```python
109
+ from value_object_pattern.models.collections import ListValueObject
110
+
111
+
112
+ class IntListValueObject(ListValueObject[int]):
113
+ pass
114
+
115
+
116
+ sequence = IntListValueObject(value=[1, 2, 3])
117
+ print(list(sequence))
118
+ # >>> [1, 2, 3]
119
+ ```
120
+ """
121
+ return iter(self._value)
122
+
123
+ def __len__(self) -> int:
124
+ """
125
+ Returns the length of the value object value.
126
+
127
+ Returns:
128
+ int: The length of the value object value.
129
+
130
+ Example:
131
+ ```python
132
+ from value_object_pattern.models.collections import ListValueObject
133
+
134
+
135
+ class IntListValueObject(ListValueObject[int]):
136
+ pass
137
+
138
+
139
+ sequence = IntListValueObject(value=[1, 2, 3])
140
+ print(len(sequence))
141
+ # >>> 3
142
+ ```
143
+ """
144
+ return len(self._value)
145
+
146
+ def __reversed__(self) -> Iterator[T]:
147
+ """
148
+ Returns a reversed iterator over the value object value.
149
+
150
+ Returns:
151
+ Iterator[T]: A reversed iterator over the value object value.
152
+
153
+ Example:
154
+ ```python
155
+ from value_object_pattern.models.collections import ListValueObject
156
+
157
+
158
+ class IntListValueObject(ListValueObject[int]):
159
+ pass
160
+
161
+
162
+ sequence = IntListValueObject(value=[1, 2, 3])
163
+ print(list(reversed(sequence)))
164
+ # >>> [3, 2, 1]
165
+ ```
166
+ """
167
+ return reversed(self._value)
168
+
169
+ @override
170
+ def __repr__(self) -> str:
171
+ """
172
+ Returns the string representation of the value object value.
173
+
174
+ Returns:
175
+ str: The string representation of the value object value.
176
+
177
+ Example:
178
+ ```python
179
+ from value_object_pattern.models.collections import ListValueObject
180
+
181
+
182
+ class IntListValueObject(ListValueObject[int]):
183
+ pass
184
+
185
+
186
+ sequence = IntListValueObject(value=[1, 2, 3])
187
+ print(repr(sequence))
188
+ # >>> [1, 2, 3]
189
+ ```
190
+ """
191
+ primitive_types: tuple[type, ...] = (int, float, str, bool, bytes, bytearray, memoryview, type(None))
192
+ collection_types: tuple[type, ...] = (list, dict, tuple, set, frozenset)
193
+
194
+ list_to_return: list[Any] = []
195
+ for item in self._value:
196
+ if isinstance(item, BaseModel):
197
+ list_to_return.append(repr(item))
198
+
199
+ elif isinstance(item, Enum):
200
+ list_to_return.append(item.value)
201
+
202
+ elif isinstance(item, ValueObject) or hasattr(item, 'value'):
203
+ value = item.value
204
+
205
+ if isinstance(value, Enum):
206
+ value = value.value
207
+
208
+ list_to_return.append(repr(value))
209
+
210
+ elif isinstance(item, primitive_types): # noqa: SIM114
211
+ list_to_return.append(item)
212
+
213
+ elif isinstance(item, collection_types):
214
+ list_to_return.append(repr(item))
215
+
216
+ else:
217
+ list_to_return.append(repr(item))
218
+
219
+ return repr(list_to_return)
220
+
221
+ @override
222
+ def __str__(self) -> str:
223
+ """
224
+ Returns the string representation of the value object value.
225
+
226
+ Returns:
227
+ str: The string representation of the value object value.
228
+
229
+ Example:
230
+ ```python
231
+ from value_object_pattern.models.collections import ListValueObject
232
+
233
+
234
+ class IntListValueObject(ListValueObject[int]):
235
+ pass
236
+
237
+
238
+ sequence = IntListValueObject(value=[1, 2, 3])
239
+ print(str(sequence))
240
+ # >>> [1, 2, 3]
241
+ ```
242
+ """
243
+ primitive_types: tuple[type, ...] = (int, float, str, bool, bytes, bytearray, memoryview, type(None))
244
+ collection_types: tuple[type, ...] = (list, dict, tuple, set, frozenset)
245
+
246
+ list_to_return: list[Any] = []
247
+ for item in self._value:
248
+ if isinstance(item, BaseModel):
249
+ list_to_return.append(str(object=item))
250
+
251
+ elif isinstance(item, Enum):
252
+ list_to_return.append(item.value)
253
+
254
+ elif isinstance(item, ValueObject) or hasattr(item, 'value'):
255
+ value = item.value
256
+
257
+ if isinstance(value, Enum):
258
+ value = value.value
259
+
260
+ list_to_return.append(str(object=value))
261
+
262
+ elif isinstance(item, primitive_types): # noqa: SIM114
263
+ list_to_return.append(item)
264
+
265
+ elif isinstance(item, collection_types):
266
+ list_to_return.append(str(object=item))
267
+
268
+ else:
269
+ list_to_return.append(str(object=item))
270
+
271
+ return str(object=list_to_return)
272
+
273
+ @validation(order=0)
274
+ def _ensure_value_is_from_list(self, value: list[Any]) -> None:
275
+ """
276
+ Ensures the value object `value` is a list.
277
+
278
+ Args:
279
+ value (list[Any]): The provided value.
280
+
281
+ Raises:
282
+ TypeError: If the `value` is not a list.
283
+ """
284
+ if not isinstance(value, list):
285
+ self._raise_value_is_not_list(value=value)
286
+
287
+ def _raise_value_is_not_list(self, value: Any) -> NoReturn:
288
+ """
289
+ Raises a TypeError if the value object `value` is not a list.
290
+
291
+ Args:
292
+ value (Any): The provided value.
293
+
294
+ Raises:
295
+ TypeError: If the `value` is not a list.
296
+ """
297
+ raise TypeError(f'ListValueObject value <<<{value}>>> must be a list. Got <<<{type(value).__name__}>>> type.') # noqa: E501 # fmt: skip
298
+
299
+ @validation(order=1)
300
+ def _ensure_value_is_of_type(self, value: list[T]) -> None:
301
+ """
302
+ Ensures the value object `value` is of type `T`.
303
+
304
+ Args:
305
+ value (list[T]): The provided value.
306
+
307
+ Raises:
308
+ TypeError: If the `value` is not of type `T`.
309
+ """
310
+ if self._type is Any:
311
+ return
312
+
313
+ expected_type = get_origin(tp=self._type) or self._type
314
+ for item in value:
315
+ if not isinstance(item, expected_type):
316
+ self._raise_value_is_not_of_type(value=item)
317
+
318
+ def _raise_value_is_not_of_type(self, value: Any) -> NoReturn:
319
+ """
320
+ Raises a TypeError if the value object `value` is not of type `T`.
321
+
322
+ Args:
323
+ value (Any): The provided value.
324
+
325
+ Raises:
326
+ TypeError: If the `value` is not of type `T`.
327
+ """
328
+ raise TypeError(f'ListValueObject value <<<{value}>>> must be of type <<<{self._type.__name__}>>> type. Got <<<{type(value).__name__}>>> type.') # noqa: E501 # fmt: skip
329
+
330
+ def is_empty(self) -> bool:
331
+ """
332
+ Returns True if the value object value is empty, otherwise False.
333
+
334
+ Returns:
335
+ bool: True if the value object value is empty, otherwise False.
336
+
337
+ Example:
338
+ ```python
339
+ from value_object_pattern.models.collections import ListValueObject
340
+
341
+
342
+ class IntListValueObject(ListValueObject[int]):
343
+ pass
344
+
345
+
346
+ sequence = IntListValueObject(value=[1, 2, 3])
347
+ print(sequence.is_empty())
348
+ # >>> False
349
+ ```
350
+ """
351
+ return not self._value
352
+
353
+ @classmethod
354
+ def from_primitives(cls, value: list[Any]) -> Self:
355
+ """
356
+ Creates a ListValueObject from a list of primitives.
357
+
358
+ Args:
359
+ value (list[Any]): The list of primitives.
360
+
361
+ Returns:
362
+ Self: The created ListValueObject.
363
+ """
364
+ items: list[Any] = []
365
+
366
+ for primitive in value:
367
+ if hasattr(cls._type, 'from_primitives'):
368
+ items.append(cls._type.from_primitives(primitive)) # BaseModel
369
+
370
+ elif hasattr(cls._type, 'value'):
371
+ items.append(cls._type(value=primitive)) # ValueObject
372
+
373
+ else:
374
+ items.append(primitive)
375
+
376
+ return cls(value=items)
@@ -14,7 +14,8 @@ from inspect import isclass
14
14
  from typing import Any, Generic, NoReturn, TypeVar, get_args, get_origin
15
15
 
16
16
  from value_object_pattern.decorators import process, validation
17
- from value_object_pattern.models.value_object import ValueObject
17
+
18
+ from .value_object import ValueObject
18
19
 
19
20
  E = TypeVar('E', bound=Enum)
20
21
 
@@ -78,72 +79,6 @@ class EnumerationValueObject(ValueObject[Any | E], Generic[E]): # noqa: UP046
78
79
 
79
80
  raise TypeError('EnumerationValueObject must be parameterized, e.g. "class ColorValueObject(EnumerationValueObject[ColorEnumeration])".') # noqa: E501 # fmt: skip
80
81
 
81
- @override
82
- def __repr__(self) -> str:
83
- """
84
- Returns a detailed string representation of the value object.
85
-
86
- Returns:
87
- str: A string representation of the value object in the format 'ClassName(value=value)'.
88
-
89
- Example:
90
- ```python
91
- from enum import Enum, unique
92
-
93
- from value_object_pattern import EnumerationValueObject
94
-
95
-
96
- @unique
97
- class ColorEnumeration(Enum):
98
- RED = 1
99
- GREEN = 2
100
- BLUE = 3
101
-
102
-
103
- class ColorValueObject(EnumerationValueObject[ColorEnumeration]):
104
- pass
105
-
106
-
107
- red = ColorValueObject(value=ColorEnumeration.RED)
108
- print(repr(red))
109
- # >>> ColorValueObject(value=ColorEnumeration.RED)
110
- ```
111
- """
112
- return f'{self.__class__.__name__}(value={self.value!r})'
113
-
114
- @override
115
- def __str__(self) -> str:
116
- """
117
- Returns a simple string representation of the value object.
118
-
119
- Returns:
120
- str: The string representation of the value object value.
121
-
122
- Example:
123
- ```python
124
- from enum import Enum, unique
125
-
126
- from value_object_pattern import EnumerationValueObject
127
-
128
-
129
- @unique
130
- class ColorEnumeration(Enum):
131
- RED = 1
132
- GREEN = 2
133
- BLUE = 3
134
-
135
-
136
- class ColorValueObject(EnumerationValueObject[ColorEnumeration]):
137
- pass
138
-
139
-
140
- red = ColorValueObject(value=ColorEnumeration.RED)
141
- print(str(red))
142
- # >>> 1
143
- ```
144
- """
145
- return str(object=self._value.value)
146
-
147
82
  @process(order=0)
148
83
  def _ensure_value_is_stored_as_enumeration(self, value: Any | E) -> E:
149
84
  """
@@ -209,16 +144,25 @@ class EnumerationValueObject(ValueObject[Any | E], Generic[E]): # noqa: UP046
209
144
 
210
145
  Example:
211
146
  ```python
212
- from value_object_pattern import ValueObject
147
+ from enum import Enum, unique
148
+
149
+ from value_object_pattern import EnumerationValueObject
150
+
151
+
152
+ @unique
153
+ class ColorEnumeration(Enum):
154
+ RED = 1
155
+ GREEN = 2
156
+ BLUE = 3
213
157
 
214
158
 
215
- class IntegerValueObject(ValueObject[int]):
159
+ class ColorValueObject(EnumerationValueObject[ColorEnumeration]):
216
160
  pass
217
161
 
218
162
 
219
- integer = IntegerValueObject(value=10)
220
- print(integer.value)
221
- # >>> 10
163
+ red = ColorValueObject(value=ColorEnumeration.RED)
164
+ print(red.value)
165
+ # >>> ColorEnumeration.RED
222
166
  ```
223
167
  """
224
168
  return self._value
@@ -131,7 +131,7 @@ class ValueObject(ABC, Generic[T]): # noqa: UP046
131
131
  # >>> IntegerValueObject(value=10)
132
132
  ```
133
133
  """
134
- return f'{self.__class__.__name__}(value={self._value!r})'
134
+ return f'{self.__class__.__name__}(value={self.value!r})'
135
135
 
136
136
  @override
137
137
  def __str__(self) -> str:
@@ -155,7 +155,7 @@ class ValueObject(ABC, Generic[T]): # noqa: UP046
155
155
  # >>> 10
156
156
  ```
157
157
  """
158
- return str(object=self._value)
158
+ return str(object=self.value)
159
159
 
160
160
  @override
161
161
  def __hash__(self) -> int:
@@ -1,11 +0,0 @@
1
- from .collection_value_object import CollectionValueObject
2
- from .iterable_value_object import IterableValueObject
3
- from .not_empty_sequence_value_object import NotEmptySequenceValueObject
4
- from .sequence_value_object import SequenceValueObject
5
-
6
- __all__ = (
7
- 'CollectionValueObject',
8
- 'IterableValueObject',
9
- 'NotEmptySequenceValueObject',
10
- 'SequenceValueObject',
11
- )
@@ -1,107 +0,0 @@
1
- """
2
- CollectionValueObject module.
3
- """
4
-
5
- from collections.abc import Collection
6
- from typing import Any, NoReturn
7
-
8
- from value_object_pattern.decorators import validation
9
- from value_object_pattern.models.value_object import ValueObject
10
-
11
-
12
- class CollectionValueObject(ValueObject[Collection[Any]]):
13
- """
14
- CollectionValueObject is a value object that ensures the provided value is from a collection.
15
-
16
- Example:
17
- ```python
18
- from value_object_pattern.models.collections import CollectionValueObject
19
-
20
- collection = CollectionValueObject(value=[1, 2, 3])
21
- print(collection)
22
- # >>> [1, 2, 3]
23
- ```
24
- """
25
-
26
- def __contains__(self, item: Any) -> bool:
27
- """
28
- Returns True if the value object value contains the item, otherwise False.
29
-
30
- Args:
31
- item (Any): The item to check.
32
-
33
- Returns:
34
- bool: True if the value object value contains the item, otherwise False.
35
-
36
- Example:
37
- ```python
38
- from value_object_pattern.models.collections import CollectionValueObject
39
-
40
- collection = CollectionValueObject(value=[1, 2, 3])
41
- print(1 in collection)
42
- # >>> True
43
- ```
44
- """
45
- return item in self._value
46
-
47
- def __len__(self) -> int:
48
- """
49
- Returns the length of the value object value.
50
-
51
- Returns:
52
- int: The length of the value object value.
53
-
54
- Example:
55
- ```python
56
- from value_object_pattern.models.collections import CollectionValueObject
57
-
58
- collection = CollectionValueObject(value=[1, 2, 3])
59
- print(len(collection))
60
- # >>> 3
61
- ```
62
- """
63
- return len(self._value)
64
-
65
- @validation(order=0)
66
- def _ensure_value_is_from_collection(self, value: Collection[Any]) -> None:
67
- """
68
- Ensures the value object `value` is a collection.
69
-
70
- Args:
71
- value (Collection[Any]): The provided value.
72
-
73
- Raises:
74
- TypeError: If the `value` is not a collection.
75
- """
76
- if not isinstance(value, Collection):
77
- self._raise_value_is_not_collection(value=value)
78
-
79
- def _raise_value_is_not_collection(self, value: Any) -> NoReturn:
80
- """
81
- Raises a TypeError if the value object `value` is not a collection.
82
-
83
- Args:
84
- value (Any): The provided value.
85
-
86
- Raises:
87
- TypeError: If the `value` is not a collection.
88
- """
89
- raise TypeError(f'CollectionValueObject value <<<{value}>>> must be a collection. Got <<<{type(value).__name__}>>> type.') # noqa: E501 # fmt: skip
90
-
91
- def is_empty(self) -> bool:
92
- """
93
- Returns True if the value object value is empty, otherwise False.
94
-
95
- Returns:
96
- bool: True if the value object value is empty, otherwise False.
97
-
98
- Example:
99
- ```python
100
- from value_object_pattern.models.collections import CollectionValueObject
101
-
102
- collection = CollectionValueObject(value=[])
103
- print(collection.is_empty())
104
- # >>> True
105
- ```
106
- """
107
- return not self._value
@@ -1,71 +0,0 @@
1
- """
2
- IterableValueObject module.
3
- """
4
-
5
- from collections.abc import Iterable
6
- from typing import Any, NoReturn
7
-
8
- from value_object_pattern.decorators import validation
9
- from value_object_pattern.models.value_object import ValueObject
10
-
11
-
12
- class IterableValueObject(ValueObject[Iterable[Any]]):
13
- """
14
- IterableValueObject is a value object that ensures the provided value is from an iterable.
15
-
16
- Example:
17
- ```python
18
- from value_object_pattern.models.collections import IterableValueObject
19
-
20
- iterable = IterableValueObject(value=[1, 2, 3])
21
- print(iterable)
22
- # >>> [1, 2, 3]
23
- ```
24
- """
25
-
26
- def __contains__(self, item: Any) -> bool:
27
- """
28
- Returns True if the value object value contains the item, otherwise False.
29
-
30
- Args:
31
- item (Any): The item to check.
32
-
33
- Returns:
34
- bool: True if the value object value contains the item, otherwise False.
35
-
36
- Example:
37
- ```python
38
- from value_object_pattern.models.collections import IterableValueObject
39
-
40
- iterable = IterableValueObject(value=[1, 2, 3])
41
- print(1 in iterable)
42
- # >>> True
43
- ```
44
- """
45
- return item in self._value
46
-
47
- @validation(order=0)
48
- def _ensure_value_is_from_iterable(self, value: Iterable[Any]) -> None:
49
- """
50
- Ensures the value object `value` is an iterable.
51
-
52
- Args:
53
- value (Iterable[Any]): The provided value.
54
-
55
- Raises:
56
- TypeError: If the `value` is not an iterable.
57
- """
58
- if not isinstance(value, Iterable):
59
- self._raise_value_is_not_iterable(value=value)
60
-
61
- def _raise_value_is_not_iterable(self, value: Any) -> NoReturn:
62
- """
63
- Raises a TypeError if the value object `value` is not an iterable.
64
-
65
- Args:
66
- value (Any): The provided value.
67
-
68
- Raises:
69
- TypeError: If the `value` is not an iterable.
70
- """
71
- raise TypeError(f'IterableValueObject value <<<{value}>>> must be an iterable. Got <<<{type(value).__name__}>>> type.') # noqa: E501 # fmt: skip
@@ -1,51 +0,0 @@
1
- """
2
- NotEmptySequenceValueObject module.
3
- """
4
-
5
- from collections.abc import Sequence
6
- from typing import Any, NoReturn
7
-
8
- from value_object_pattern.decorators import validation
9
-
10
- from .sequence_value_object import SequenceValueObject
11
-
12
-
13
- class NotEmptySequenceValueObject(SequenceValueObject):
14
- """
15
- NotEmptySequenceValueObject is a value object that ensures the provided value is from a sequence.
16
-
17
- Example:
18
- ```python
19
- from value_object_pattern.models.collections import NotEmptySequenceValueObject
20
-
21
- sequence = NotEmptySequenceValueObject(value=[1, 2, 3])
22
- print(sequence)
23
- # >>> [1, 2, 3]
24
- ```
25
- """
26
-
27
- @validation(order=0)
28
- def _ensure_value_is_not_empty_sequence(self, value: Sequence[Any]) -> None:
29
- """
30
- Ensures the value object `value` is not an empty sequence.
31
-
32
- Args:
33
- value (Sequence[Any]): The provided value.
34
-
35
- Raises:
36
- TypeError: If the `value` is an empty sequence.
37
- """
38
- if not value:
39
- self._raise_value_is_empty_sequence(value=value)
40
-
41
- def _raise_value_is_empty_sequence(self, value: Any) -> NoReturn:
42
- """
43
- Raises a TypeError if the value object `value` is an empty sequence.
44
-
45
- Args:
46
- value (Any): The provided value.
47
-
48
- Raises:
49
- TypeError: If the `value` is an empty sequence.
50
- """
51
- raise TypeError(f'NotEmptySequenceValueObject value <<<{value}>>> must not be an empty sequence.')
@@ -1,143 +0,0 @@
1
- """
2
- SequenceValueObject module.
3
- """
4
-
5
- from collections.abc import Iterator, Sequence
6
- from typing import Any, NoReturn
7
-
8
- from value_object_pattern.decorators import validation
9
- from value_object_pattern.models.value_object import ValueObject
10
-
11
-
12
- class SequenceValueObject(ValueObject[Sequence[Any]]):
13
- """
14
- SequenceValueObject is a value object that ensures the provided value is from a sequence.
15
-
16
- Example:
17
- ```python
18
- from value_object_pattern.models.collections import SequenceValueObject
19
-
20
- sequence = SequenceValueObject(value=[1, 2, 3])
21
- print(sequence)
22
- # >>> [1, 2, 3]
23
- ```
24
- """
25
-
26
- def __contains__(self, item: Any) -> bool:
27
- """
28
- Returns True if the value object value contains the item, otherwise False.
29
-
30
- Args:
31
- item (Any): The item to check.
32
-
33
- Returns:
34
- bool: True if the value object value contains the item, otherwise False.
35
-
36
- Example:
37
- ```python
38
- from value_object_pattern.models.collections import SequenceValueObject
39
-
40
- sequence = SequenceValueObject(value=[1, 2, 3])
41
- print(1 in sequence)
42
- # >>> True
43
- ```
44
- """
45
- return item in self._value
46
-
47
- def __iter__(self) -> Iterator[Any]:
48
- """
49
- Returns an iterator over the value object value.
50
-
51
- Returns:
52
- Iterator[Any]: An iterator over the value object value.
53
-
54
- Example:
55
- ```python
56
- from value_object_pattern.models.collections import SequenceValueObject
57
-
58
- sequence = SequenceValueObject(value=[1, 2, 3])
59
- print(list(sequence))
60
- # >>> [1, 2, 3]
61
- ```
62
- """
63
- return iter(self._value)
64
-
65
- def __len__(self) -> int:
66
- """
67
- Returns the length of the value object value.
68
-
69
- Returns:
70
- int: The length of the value object value.
71
-
72
- Example:
73
- ```python
74
- from value_object_pattern.models.collections import SequenceValueObject
75
-
76
- sequence = SequenceValueObject(value=[1, 2, 3])
77
- print(len(sequence))
78
- # >>> 3
79
- ```
80
- """
81
- return len(self._value)
82
-
83
- def __reversed__(self) -> Iterator[Any]:
84
- """
85
- Returns a reversed iterator over the value object value.
86
-
87
- Returns:
88
- Iterator[Any]: A reversed iterator over the value object value.
89
-
90
- Example:
91
- ```python
92
- from value_object_pattern.models.collections import SequenceValueObject
93
-
94
- sequence = SequenceValueObject(value=[1, 2, 3])
95
- print(list(reversed(sequence)))
96
- # >>> [3, 2, 1]
97
- ```
98
- """
99
- return reversed(self._value)
100
-
101
- @validation(order=0)
102
- def _ensure_value_is_from_sequence(self, value: Sequence[Any]) -> None:
103
- """
104
- Ensures the value object `value` is a sequence.
105
-
106
- Args:
107
- value (Sequence[Any]): The provided value.
108
-
109
- Raises:
110
- TypeError: If the `value` is not a sequence.
111
- """
112
- if not isinstance(value, Sequence):
113
- self._raise_value_is_not_sequence(value=value)
114
-
115
- def _raise_value_is_not_sequence(self, value: Any) -> NoReturn:
116
- """
117
- Raises a TypeError if the value object `value` is not a sequence.
118
-
119
- Args:
120
- value (Any): The provided value.
121
-
122
- Raises:
123
- TypeError: If the `value` is not a sequence.
124
- """
125
- raise TypeError(f'SequenceValueObject value <<<{value}>>> must be a sequence. Got <<<{type(value).__name__}>>> type.') # noqa: E501 # fmt: skip
126
-
127
- def is_empty(self) -> bool:
128
- """
129
- Returns True if the value object value is empty, otherwise False.
130
-
131
- Returns:
132
- bool: True if the value object value is empty, otherwise False.
133
-
134
- Example:
135
- ```python
136
- from value_object_pattern.models.collections import SequenceValueObject
137
-
138
- sequence = SequenceValueObject(value=[])
139
- print(sequence.is_empty())
140
- # >>> True
141
- ```
142
- """
143
- return not self._value