sindripy 0.1.1__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.
Potentially problematic release.
This version of sindripy might be problematic. Click here for more details.
- sindripy/__init__.py +11 -0
- sindripy/_compat.py +15 -0
- sindripy/mothers/__init__.py +24 -0
- sindripy/mothers/identifiers/__init__.py +0 -0
- sindripy/mothers/identifiers/string_uuid_primitives_mother.py +16 -0
- sindripy/mothers/object_mother.py +10 -0
- sindripy/mothers/primitives/__init__.py +0 -0
- sindripy/mothers/primitives/boolean_primitives_mother.py +20 -0
- sindripy/mothers/primitives/float_primitives_mother.py +30 -0
- sindripy/mothers/primitives/integer_primitives_mother.py +36 -0
- sindripy/mothers/primitives/list_primitives_mother.py +10 -0
- sindripy/mothers/primitives/string_primitives_mother.py +48 -0
- sindripy/py.typed +0 -0
- sindripy/value_objects/__init__.py +29 -0
- sindripy/value_objects/aggregate.py +312 -0
- sindripy/value_objects/decorators/__init__.py +0 -0
- sindripy/value_objects/decorators/validation.py +28 -0
- sindripy/value_objects/errors/__init__.py +0 -0
- sindripy/value_objects/errors/incorrect_value_type_error.py +12 -0
- sindripy/value_objects/errors/invalid_id_format_error.py +8 -0
- sindripy/value_objects/errors/required_value_error.py +8 -0
- sindripy/value_objects/errors/sindri_validation_error.py +10 -0
- sindripy/value_objects/identifiers/__init__.py +0 -0
- sindripy/value_objects/identifiers/string_uuid.py +55 -0
- sindripy/value_objects/primitives/__init__.py +0 -0
- sindripy/value_objects/primitives/boolean.py +44 -0
- sindripy/value_objects/primitives/float.py +44 -0
- sindripy/value_objects/primitives/integer.py +44 -0
- sindripy/value_objects/primitives/list.py +307 -0
- sindripy/value_objects/primitives/string.py +43 -0
- sindripy/value_objects/value_object.py +269 -0
- sindripy-0.1.1.dist-info/METADATA +144 -0
- sindripy-0.1.1.dist-info/RECORD +34 -0
- sindripy-0.1.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from uuid import UUID
|
|
2
|
+
|
|
3
|
+
from src.sindripy.value_objects.decorators.validation import validate
|
|
4
|
+
from src.sindripy.value_objects.errors.incorrect_value_type_error import IncorrectValueTypeError
|
|
5
|
+
from src.sindripy.value_objects.errors.invalid_id_format_error import InvalidIdFormatError
|
|
6
|
+
from src.sindripy.value_objects.errors.required_value_error import RequiredValueError
|
|
7
|
+
from src.sindripy.value_objects.value_object import ValueObject
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StringUuid(ValueObject[str]):
|
|
11
|
+
"""
|
|
12
|
+
A value object that wraps UUID (Universally Unique Identifier) string values with validation.
|
|
13
|
+
|
|
14
|
+
This class provides a specialized implementation for creating value objects that
|
|
15
|
+
represent UUID values in the domain. It ensures that the wrapped value is a
|
|
16
|
+
valid UUID string format and not None.
|
|
17
|
+
|
|
18
|
+
The class includes built-in validation for:
|
|
19
|
+
- Required value (not None)
|
|
20
|
+
- Type checking (must be a string)
|
|
21
|
+
- UUID format validation (must be a valid UUID format)
|
|
22
|
+
|
|
23
|
+
Inherits all functionality from ValueObject including immutability,
|
|
24
|
+
equality comparison, string representation, and hashing.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
```python
|
|
28
|
+
class UserId(StringUuid):
|
|
29
|
+
@validate
|
|
30
|
+
def _validate_version(self, value: str) -> None:
|
|
31
|
+
parsed_uuid = UUID(value)
|
|
32
|
+
if parsed_uuid.version != 4:
|
|
33
|
+
raise ValueError("Only UUID version 4 allowed")
|
|
34
|
+
|
|
35
|
+
user_id = UserId("123e4567-e89b-12d3-a456-426614174000")
|
|
36
|
+
user_id.value # '123e4567-e89b-12d3-a456-426614174000'
|
|
37
|
+
```
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@validate
|
|
41
|
+
def _ensure_has_value(self, value: str) -> None:
|
|
42
|
+
if value is None:
|
|
43
|
+
raise RequiredValueError
|
|
44
|
+
|
|
45
|
+
@validate
|
|
46
|
+
def _ensure_value_is_string(self, value: str) -> None:
|
|
47
|
+
if not isinstance(value, str):
|
|
48
|
+
raise IncorrectValueTypeError(value, str)
|
|
49
|
+
|
|
50
|
+
@validate
|
|
51
|
+
def _ensure_value_has_valid_uuid_format(self, value: str) -> None:
|
|
52
|
+
try:
|
|
53
|
+
UUID(value)
|
|
54
|
+
except ValueError as error:
|
|
55
|
+
raise InvalidIdFormatError from error
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from src.sindripy.value_objects.decorators.validation import validate
|
|
2
|
+
from src.sindripy.value_objects.errors.incorrect_value_type_error import IncorrectValueTypeError
|
|
3
|
+
from src.sindripy.value_objects.errors.required_value_error import RequiredValueError
|
|
4
|
+
from src.sindripy.value_objects.value_object import ValueObject
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Boolean(ValueObject[bool]):
|
|
8
|
+
"""
|
|
9
|
+
A value object that wraps boolean values with validation.
|
|
10
|
+
|
|
11
|
+
This class provides a base implementation for creating value objects that
|
|
12
|
+
represent boolean values in the domain. It ensures that the wrapped value
|
|
13
|
+
is a valid boolean and not None.
|
|
14
|
+
|
|
15
|
+
The class includes built-in validation for:
|
|
16
|
+
- Required value (not None)
|
|
17
|
+
- Type checking (must be a boolean)
|
|
18
|
+
|
|
19
|
+
Inherits all functionality from ValueObject including immutability,
|
|
20
|
+
equality comparison, string representation, and hashing.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```python
|
|
24
|
+
class IsActive(Boolean):
|
|
25
|
+
@validate
|
|
26
|
+
def _validate_true_for_premium(self, value: bool) -> None:
|
|
27
|
+
# Custom business logic can be added here
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
is_active = IsActive(True)
|
|
31
|
+
is_active.value # True
|
|
32
|
+
str(is_active) # 'True'
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@validate
|
|
37
|
+
def _ensure_has_value(self, value: bool) -> None:
|
|
38
|
+
if value is None:
|
|
39
|
+
raise RequiredValueError
|
|
40
|
+
|
|
41
|
+
@validate
|
|
42
|
+
def _ensure_value_is_boolean(self, value: bool) -> None:
|
|
43
|
+
if not isinstance(value, bool):
|
|
44
|
+
raise IncorrectValueTypeError(value, bool)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from src.sindripy.value_objects.decorators.validation import validate
|
|
2
|
+
from src.sindripy.value_objects.errors.incorrect_value_type_error import IncorrectValueTypeError
|
|
3
|
+
from src.sindripy.value_objects.errors.required_value_error import RequiredValueError
|
|
4
|
+
from src.sindripy.value_objects.value_object import ValueObject
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Float(ValueObject[float]):
|
|
8
|
+
"""
|
|
9
|
+
A value object that wraps float values with validation.
|
|
10
|
+
|
|
11
|
+
This class provides a base implementation for creating value objects that
|
|
12
|
+
represent float values in the domain. It ensures that the wrapped value
|
|
13
|
+
is a valid float and not None. It accepts both positive and negative values.
|
|
14
|
+
|
|
15
|
+
The class includes built-in validation for:
|
|
16
|
+
- Required value (not None)
|
|
17
|
+
- Type checking (must be a float)
|
|
18
|
+
|
|
19
|
+
Inherits all functionality from ValueObject including immutability,
|
|
20
|
+
equality comparison, string representation, and hashing.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```python
|
|
24
|
+
class Price(Float):
|
|
25
|
+
@validate
|
|
26
|
+
def _validate_positive(self, value: float) -> None:
|
|
27
|
+
if value < 0:
|
|
28
|
+
raise ValueError("Price cannot be negative")
|
|
29
|
+
|
|
30
|
+
price = Price(29.99)
|
|
31
|
+
price.value # 29.99
|
|
32
|
+
str(price) # '29.99'
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@validate
|
|
37
|
+
def _ensure_has_value(self, value: float) -> None:
|
|
38
|
+
if value is None:
|
|
39
|
+
raise RequiredValueError
|
|
40
|
+
|
|
41
|
+
@validate
|
|
42
|
+
def _ensure_value_is_float(self, value: float) -> None:
|
|
43
|
+
if not isinstance(value, float):
|
|
44
|
+
raise IncorrectValueTypeError(value, float)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from src.sindripy.value_objects.decorators.validation import validate
|
|
2
|
+
from src.sindripy.value_objects.errors.incorrect_value_type_error import IncorrectValueTypeError
|
|
3
|
+
from src.sindripy.value_objects.errors.required_value_error import RequiredValueError
|
|
4
|
+
from src.sindripy.value_objects.value_object import ValueObject
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Integer(ValueObject[int]):
|
|
8
|
+
"""
|
|
9
|
+
A value object that wraps integer values with validation.
|
|
10
|
+
|
|
11
|
+
This class provides a base implementation for creating value objects that
|
|
12
|
+
represent integer values in the domain. It ensures that the wrapped value
|
|
13
|
+
is a valid integer and not None.
|
|
14
|
+
|
|
15
|
+
The class includes built-in validation for:
|
|
16
|
+
- Required value (not None)
|
|
17
|
+
- Type checking (must be an integer)
|
|
18
|
+
|
|
19
|
+
Inherits all functionality from ValueObject including immutability,
|
|
20
|
+
equality comparison, string representation, and hashing.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```python
|
|
24
|
+
class Age(Integer):
|
|
25
|
+
@validate
|
|
26
|
+
def _validate_positive(self, value: int) -> None:
|
|
27
|
+
if value < 0:
|
|
28
|
+
raise ValueError("Age cannot be negative")
|
|
29
|
+
|
|
30
|
+
age = Age(25)
|
|
31
|
+
age.value # 25
|
|
32
|
+
str(age) # '25'
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@validate
|
|
37
|
+
def _ensure_has_value(self, value: int) -> None:
|
|
38
|
+
if value is None:
|
|
39
|
+
raise RequiredValueError
|
|
40
|
+
|
|
41
|
+
@validate
|
|
42
|
+
def _ensure_value_is_integer(self, value: int) -> None:
|
|
43
|
+
if not isinstance(value, int):
|
|
44
|
+
raise IncorrectValueTypeError(value, int)
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
from collections.abc import Iterator
|
|
2
|
+
from typing import Any, Generic, TypeVar, get_args, get_origin
|
|
3
|
+
|
|
4
|
+
from src.sindripy._compat import Self, override
|
|
5
|
+
from src.sindripy.value_objects.decorators.validation import validate
|
|
6
|
+
from src.sindripy.value_objects.errors.incorrect_value_type_error import IncorrectValueTypeError
|
|
7
|
+
from src.sindripy.value_objects.errors.required_value_error import RequiredValueError
|
|
8
|
+
from src.sindripy.value_objects.value_object import ValueObject
|
|
9
|
+
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class List(ValueObject[list[T]], Generic[T]):
|
|
14
|
+
@override
|
|
15
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Initialize subclass with proper type parameter validation.
|
|
18
|
+
|
|
19
|
+
This method ensures that any subclass of List is properly parameterized
|
|
20
|
+
with a type argument and extracts the element type for validation purposes. It
|
|
21
|
+
is run automatically when a subclass is created, at definition time.
|
|
22
|
+
"""
|
|
23
|
+
super().__init_subclass__(**kwargs)
|
|
24
|
+
|
|
25
|
+
cls._validate_class_has_type_parameters()
|
|
26
|
+
list_base = cls._find_parameterized_list_base()
|
|
27
|
+
element_type = cls._extract_element_type_from_base(list_base)
|
|
28
|
+
cls._validate_and_store_element_type(element_type)
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def from_primitives(cls, value: list[Any]) -> Self:
|
|
32
|
+
elements = []
|
|
33
|
+
|
|
34
|
+
for primitive in value:
|
|
35
|
+
if cls._element_is_an_aggregate_instance():
|
|
36
|
+
elements.append(cls._element_type.from_primitives(primitive))
|
|
37
|
+
elif cls._element_is_a_value_object_instance():
|
|
38
|
+
elements.append(cls._element_type(primitive))
|
|
39
|
+
else:
|
|
40
|
+
elements.append(primitive)
|
|
41
|
+
return cls(elements)
|
|
42
|
+
|
|
43
|
+
@validate
|
|
44
|
+
def _ensure_has_value(self, value: list[T]) -> None:
|
|
45
|
+
if value is None:
|
|
46
|
+
raise RequiredValueError
|
|
47
|
+
|
|
48
|
+
@validate
|
|
49
|
+
def _ensure_is_list(self, value: list[T]) -> None:
|
|
50
|
+
if not isinstance(value, list):
|
|
51
|
+
raise IncorrectValueTypeError(value, type[Any])
|
|
52
|
+
|
|
53
|
+
@validate
|
|
54
|
+
def _ensure_list_elements_have_expected_type(self, value: list[T]) -> None:
|
|
55
|
+
cls = self.__class__
|
|
56
|
+
|
|
57
|
+
if not hasattr(cls, "_element_type"):
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
element_type = cls._element_type
|
|
61
|
+
|
|
62
|
+
if isinstance(element_type, TypeVar):
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
if not isinstance(element_type, type):
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
if cls._element_is_a_value_object_instance() or cls._element_is_a_primitive_type():
|
|
69
|
+
for item in value:
|
|
70
|
+
if not isinstance(item, element_type):
|
|
71
|
+
raise IncorrectValueTypeError(item, list)
|
|
72
|
+
|
|
73
|
+
def __contains__(self, item: Any) -> bool:
|
|
74
|
+
"""
|
|
75
|
+
Check if an item is present in the list.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
item: The item to check for membership in the list.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
True if the item is in the list, False otherwise.
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
```python
|
|
85
|
+
numbers = IntList([1, 2, 3, 4, 5])
|
|
86
|
+
3 in numbers # True
|
|
87
|
+
6 in numbers # False
|
|
88
|
+
```
|
|
89
|
+
"""
|
|
90
|
+
return item in self._value
|
|
91
|
+
|
|
92
|
+
def __iter__(self) -> Iterator[T]:
|
|
93
|
+
"""
|
|
94
|
+
Return an iterator over the list elements.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
An iterator that yields each element in the list.
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
```python
|
|
101
|
+
numbers = IntList([1, 2, 3])
|
|
102
|
+
list(numbers) # [1, 2, 3]
|
|
103
|
+
for num in numbers:
|
|
104
|
+
print(num)
|
|
105
|
+
# 1
|
|
106
|
+
# 2
|
|
107
|
+
# 3
|
|
108
|
+
```
|
|
109
|
+
"""
|
|
110
|
+
return iter(self._value)
|
|
111
|
+
|
|
112
|
+
def __len__(self) -> int:
|
|
113
|
+
"""
|
|
114
|
+
Return the number of elements in the list.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
The length of the wrapped list.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
```python
|
|
121
|
+
numbers = IntList([1, 2, 3, 4, 5])
|
|
122
|
+
len(numbers) # 5
|
|
123
|
+
```
|
|
124
|
+
"""
|
|
125
|
+
return len(self._value)
|
|
126
|
+
|
|
127
|
+
def __reversed__(self) -> Iterator[T]:
|
|
128
|
+
"""
|
|
129
|
+
Return a reverse iterator over the list elements.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
A reverse iterator that yields elements from the end to the beginning.
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
```python
|
|
136
|
+
numbers = IntList([1, 2, 3])
|
|
137
|
+
list(reversed(numbers)) # [3, 2, 1]
|
|
138
|
+
```
|
|
139
|
+
"""
|
|
140
|
+
return reversed(self._value)
|
|
141
|
+
|
|
142
|
+
@override
|
|
143
|
+
def __hash__(self) -> int:
|
|
144
|
+
"""
|
|
145
|
+
Return the hash value of this value object.
|
|
146
|
+
|
|
147
|
+
Since lists are unhashable in Python, we convert the list to a tuple
|
|
148
|
+
for hashing purposes while maintaining the original list value.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
The hash value based on the tuple representation of the list.
|
|
152
|
+
"""
|
|
153
|
+
return hash(tuple(self._value))
|
|
154
|
+
|
|
155
|
+
@override
|
|
156
|
+
def __eq__(self, other: Any) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
Check equality with another List value object.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
other: The other object to compare against.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
True if both are List instances with the same element type and values, False otherwise.
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
```python
|
|
168
|
+
list1 = IntList([1, 2, 3])
|
|
169
|
+
list2 = IntList([1, 2, 3])
|
|
170
|
+
list3 = IntList([4, 5, 6])
|
|
171
|
+
list1 == list2 # True
|
|
172
|
+
list1 == list3 # False
|
|
173
|
+
```
|
|
174
|
+
"""
|
|
175
|
+
if not isinstance(other, List):
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
if self._element_type != other._element_type:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
return self._value == other._value
|
|
182
|
+
|
|
183
|
+
@override
|
|
184
|
+
def __repr__(self) -> str:
|
|
185
|
+
"""
|
|
186
|
+
Return a string representation suitable for debugging.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
A string showing the class name and the wrapped list value.
|
|
190
|
+
|
|
191
|
+
Example:
|
|
192
|
+
```python
|
|
193
|
+
numbers = IntList([1, 2, 3])
|
|
194
|
+
repr(numbers) # 'IntList(_value=[1, 2, 3])'
|
|
195
|
+
```
|
|
196
|
+
"""
|
|
197
|
+
return f"{self.__class__.__name__}(_value={self._value!r})"
|
|
198
|
+
|
|
199
|
+
@override
|
|
200
|
+
def __str__(self) -> str:
|
|
201
|
+
"""
|
|
202
|
+
Return a human-readable string representation.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
The string representation of the wrapped list.
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
```python
|
|
209
|
+
numbers = IntList([1, 2, 3])
|
|
210
|
+
str(numbers) # '[1, 2, 3]'
|
|
211
|
+
```
|
|
212
|
+
"""
|
|
213
|
+
return str(self._value)
|
|
214
|
+
|
|
215
|
+
@classmethod
|
|
216
|
+
def _validate_class_has_type_parameters(cls) -> None:
|
|
217
|
+
"""
|
|
218
|
+
Validate that the class has type parameters defined.
|
|
219
|
+
|
|
220
|
+
Raises:
|
|
221
|
+
TypeError: If the class doesn't have __orig_bases__ or it's empty.
|
|
222
|
+
"""
|
|
223
|
+
if not hasattr(cls, "__orig_bases__") or not getattr(cls, "__orig_bases__", None):
|
|
224
|
+
raise TypeError(f"Class {cls.__name__} must be parameterized with a type argument")
|
|
225
|
+
|
|
226
|
+
@classmethod
|
|
227
|
+
def _find_parameterized_list_base(cls) -> Any:
|
|
228
|
+
"""
|
|
229
|
+
Find the parameterized List base class in the inheritance chain.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
The parameterized List base class.
|
|
233
|
+
|
|
234
|
+
Raises:
|
|
235
|
+
TypeError: If no parameterized List base is found.
|
|
236
|
+
"""
|
|
237
|
+
orig_bases = getattr(cls, "__orig_bases__", ())
|
|
238
|
+
for base in orig_bases:
|
|
239
|
+
if get_origin(base) is List:
|
|
240
|
+
return base
|
|
241
|
+
|
|
242
|
+
raise TypeError(f"Class {cls.__name__} must inherit from List[T] with a type parameter")
|
|
243
|
+
|
|
244
|
+
@classmethod
|
|
245
|
+
def _extract_element_type_from_base(cls, list_base: Any) -> Any:
|
|
246
|
+
"""
|
|
247
|
+
Extract the element type from the parameterized List base.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
list_base: The parameterized List base class.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
The element type parameter.
|
|
254
|
+
"""
|
|
255
|
+
element_type, *_ = get_args(list_base)
|
|
256
|
+
return element_type
|
|
257
|
+
|
|
258
|
+
@classmethod
|
|
259
|
+
def _validate_and_store_element_type(cls, element_type: Any) -> None:
|
|
260
|
+
"""
|
|
261
|
+
Validate the element type and store it in the class.
|
|
262
|
+
|
|
263
|
+
This method handles different types of type parameters:
|
|
264
|
+
- TypeVar instances for generic types
|
|
265
|
+
- Concrete types like int, str, etc.
|
|
266
|
+
- Generic types like list[int], dict[str, int], etc.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
element_type: The element type to validate and store.
|
|
270
|
+
|
|
271
|
+
Raises:
|
|
272
|
+
TypeError: If the element type is not a valid type parameter.
|
|
273
|
+
"""
|
|
274
|
+
# Handle TypeVar cases: If the type is a generic type, store it directly
|
|
275
|
+
if isinstance(element_type, TypeVar):
|
|
276
|
+
cls._element_type = element_type
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
# Validate concrete types: Ensure the type parameter is actually a valid type
|
|
280
|
+
if isinstance(element_type, type):
|
|
281
|
+
cls._element_type = element_type
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
# Handle generic types like list[int], dict[str, int], etc.
|
|
285
|
+
if hasattr(element_type, "__origin__") or get_origin(element_type) is not None:
|
|
286
|
+
cls._element_type = element_type
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
raise TypeError(f"Type parameter must be a valid type, not a primitive value: {element_type}")
|
|
290
|
+
|
|
291
|
+
@classmethod
|
|
292
|
+
def _element_is_an_aggregate_instance(cls) -> bool:
|
|
293
|
+
return hasattr(cls._element_type, "from_primitives")
|
|
294
|
+
|
|
295
|
+
@classmethod
|
|
296
|
+
def _element_is_a_value_object_instance(cls) -> bool:
|
|
297
|
+
try:
|
|
298
|
+
return isinstance(cls._element_type, type) and issubclass(cls._element_type, ValueObject)
|
|
299
|
+
except TypeError:
|
|
300
|
+
return False
|
|
301
|
+
|
|
302
|
+
@classmethod
|
|
303
|
+
def _element_is_a_primitive_type(cls) -> bool:
|
|
304
|
+
try:
|
|
305
|
+
return isinstance(cls._element_type, type) and not issubclass(cls._element_type, ValueObject)
|
|
306
|
+
except TypeError:
|
|
307
|
+
return False
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from src.sindripy.value_objects.decorators.validation import validate
|
|
2
|
+
from src.sindripy.value_objects.errors.incorrect_value_type_error import IncorrectValueTypeError
|
|
3
|
+
from src.sindripy.value_objects.errors.required_value_error import RequiredValueError
|
|
4
|
+
from src.sindripy.value_objects.value_object import ValueObject
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class String(ValueObject[str]):
|
|
8
|
+
"""
|
|
9
|
+
A value object that wraps string values with validation.
|
|
10
|
+
|
|
11
|
+
This class provides a base implementation for creating value objects that
|
|
12
|
+
represent string values in the domain. It ensures that the wrapped value
|
|
13
|
+
is a valid string and not None.
|
|
14
|
+
|
|
15
|
+
The class includes built-in validation for:
|
|
16
|
+
- Required value (not None)
|
|
17
|
+
- Type checking (must be a string)
|
|
18
|
+
|
|
19
|
+
Inherits all functionality from ValueObject including immutability,
|
|
20
|
+
equality comparison, string representation, and hashing.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```python
|
|
24
|
+
class Email(String):
|
|
25
|
+
@validate
|
|
26
|
+
def _validate_email_format(self, value: str) -> None:
|
|
27
|
+
if "@" not in value:
|
|
28
|
+
raise ValueError("Invalid email format")
|
|
29
|
+
|
|
30
|
+
email = Email("user@example.com")
|
|
31
|
+
email.value # 'user@example.com'
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@validate
|
|
36
|
+
def _ensure_has_value(self, value: str) -> None:
|
|
37
|
+
if value is None:
|
|
38
|
+
raise RequiredValueError
|
|
39
|
+
|
|
40
|
+
@validate
|
|
41
|
+
def _ensure_is_string(self, value: str) -> None:
|
|
42
|
+
if not isinstance(value, str):
|
|
43
|
+
raise IncorrectValueTypeError(value, str)
|