sqlobjects 0.1.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.
@@ -0,0 +1,294 @@
1
+ """SQLObjects Validators Module - Field validation system"""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any
5
+
6
+ from .exceptions import ValidationError
7
+
8
+
9
+ __all__ = [
10
+ "FieldValidator",
11
+ "LengthValidator",
12
+ "RangeValidator",
13
+ "EmailValidator",
14
+ "validate_field_value",
15
+ ]
16
+
17
+
18
+ class FieldValidator(ABC):
19
+ """Base class for field validators.
20
+
21
+ This abstract base class defines the interface for all field validators
22
+ in the SQLObjects system. Validators are used to ensure data integrity
23
+ and enforce business rules on model fields.
24
+
25
+ Examples:
26
+ >>> class CustomValidator(FieldValidator):
27
+ ... def validate(self, value: Any, field_name: str) -> Any:
28
+ ... if value and len(str(value)) < 3:
29
+ ... raise ValidationError(self.get_error_message(field_name, value))
30
+ ... return value
31
+ ...
32
+ ... def get_error_message(self, field_name: str, value: Any) -> str:
33
+ ... return f"Field '{field_name}' must be at least 3 characters long"
34
+ """
35
+
36
+ @abstractmethod
37
+ def validate(self, value: Any, field_name: str) -> Any:
38
+ """Validate and return the processed value.
39
+
40
+ Args:
41
+ value: The value to validate
42
+ field_name: Name of the field being validated
43
+
44
+ Returns:
45
+ The validated (and potentially transformed) value
46
+
47
+ Raises:
48
+ ValidationError: If validation fails
49
+ """
50
+ pass
51
+
52
+ @abstractmethod
53
+ def get_error_message(self, field_name: str, value: Any) -> str:
54
+ """Get the error message for validation failure.
55
+
56
+ Args:
57
+ field_name: Name of the field that failed validation
58
+ value: The value that failed validation
59
+
60
+ Returns:
61
+ Human-readable error message
62
+ """
63
+ pass
64
+
65
+
66
+ class LengthValidator(FieldValidator):
67
+ """Validator for string/value length constraints.
68
+
69
+ This validator ensures that the string representation of a value
70
+ meets minimum and/or maximum length requirements.
71
+
72
+ Args:
73
+ min_length: Minimum required length (default: 0)
74
+ max_length: Maximum allowed length (optional)
75
+
76
+ Examples:
77
+ >>> # Username must be 3-20 characters
78
+ >>> username_validator = LengthValidator(min_length=3, max_length=20)
79
+ >>> username_validator.validate("john", "username") # Valid
80
+ >>> username_validator.validate("jo", "username") # Raises ValidationError
81
+
82
+ >>> # Description must be at least 10 characters
83
+ >>> desc_validator = LengthValidator(min_length=10)
84
+ >>> desc_validator.validate("Short desc", "description") # Valid
85
+ """
86
+
87
+ def __init__(self, min_length: int = 0, max_length: int | None = None):
88
+ self.min_length = min_length
89
+ self.max_length = max_length
90
+
91
+ def validate(self, value: Any, field_name: str) -> Any:
92
+ """Validate the length of the value.
93
+
94
+ Args:
95
+ value: The value to validate
96
+ field_name: Name of the field being validated
97
+
98
+ Returns:
99
+ The original value if validation passes
100
+
101
+ Raises:
102
+ ValidationError: If the value length is outside the allowed range
103
+ """
104
+ if value is None:
105
+ return value
106
+
107
+ length = len(str(value))
108
+ if length < self.min_length:
109
+ raise ValidationError(self.get_error_message(field_name, value))
110
+ if self.max_length and length > self.max_length:
111
+ raise ValidationError(self.get_error_message(field_name, value))
112
+
113
+ return value
114
+
115
+ def get_error_message(self, field_name: str, value: Any) -> str:
116
+ """Get the error message for length validation failure.
117
+
118
+ Args:
119
+ field_name: Name of the field that failed validation
120
+ value: The value that failed validation
121
+
122
+ Returns:
123
+ Human-readable error message describing the length requirement
124
+ """
125
+ if self.max_length:
126
+ return f"Field '{field_name}' length must be between {self.min_length} and {self.max_length}"
127
+ return f"Field '{field_name}' length must be at least {self.min_length}"
128
+
129
+
130
+ class RangeValidator(FieldValidator):
131
+ """Validator for numeric range constraints.
132
+
133
+ This validator ensures that numeric values fall within specified
134
+ minimum and/or maximum bounds. The value is converted to float
135
+ for comparison.
136
+
137
+ Args:
138
+ min_value: Minimum allowed value (optional)
139
+ max_value: Maximum allowed value (optional)
140
+
141
+ Examples:
142
+ >>> # Age must be between 0 and 150
143
+ >>> age_validator = RangeValidator(min_value=0, max_value=150)
144
+ >>> age_validator.validate(25, "age") # Valid
145
+ >>> age_validator.validate(-5, "age") # Raises ValidationError
146
+
147
+ >>> # Price must be at least 0
148
+ >>> price_validator = RangeValidator(min_value=0)
149
+ >>> price_validator.validate(19.99, "price") # Valid
150
+ """
151
+
152
+ def __init__(self, min_value: float | None = None, max_value: float | None = None):
153
+ self.min_value = min_value
154
+ self.max_value = max_value
155
+
156
+ def validate(self, value: Any, field_name: str) -> Any:
157
+ """Validate the numeric range of the value.
158
+
159
+ Args:
160
+ value: The value to validate
161
+ field_name: Name of the field being validated
162
+
163
+ Returns:
164
+ The original value if validation passes
165
+
166
+ Raises:
167
+ ValidationError: If the value is not numeric or outside the allowed range
168
+ """
169
+ if value is None:
170
+ return value
171
+
172
+ try:
173
+ num_value = float(value)
174
+ except (ValueError, TypeError) as e:
175
+ raise ValidationError(f"Field '{field_name}' must be a number") from e
176
+
177
+ if self.min_value is not None and num_value < self.min_value:
178
+ raise ValidationError(self.get_error_message(field_name, value))
179
+ if self.max_value is not None and num_value > self.max_value:
180
+ raise ValidationError(self.get_error_message(field_name, value))
181
+
182
+ return value
183
+
184
+ def get_error_message(self, field_name: str, value: Any) -> str:
185
+ """Get the error message for range validation failure.
186
+
187
+ Args:
188
+ field_name: Name of the field that failed validation
189
+ value: The value that failed validation
190
+
191
+ Returns:
192
+ Human-readable error message describing the range requirement
193
+ """
194
+ if self.min_value is not None and self.max_value is not None:
195
+ return f"Field '{field_name}' must be between {self.min_value} and {self.max_value}"
196
+ elif self.min_value is not None:
197
+ return f"Field '{field_name}' must be at least {self.min_value}"
198
+ else:
199
+ return f"Field '{field_name}' must be at most {self.max_value}"
200
+
201
+
202
+ class EmailValidator(FieldValidator):
203
+ """Validator for email address format.
204
+
205
+ This validator performs basic email format validation by checking
206
+ for the presence of '@' symbol and a dot in the domain part.
207
+ For production use, consider using more comprehensive email
208
+ validation libraries.
209
+
210
+ Examples:
211
+ >>> email_validator = EmailValidator()
212
+ >>> email_validator.validate("user@example.com", "email") # Valid
213
+ >>> email_validator.validate("invalid-email", "email") # Raises ValidationError
214
+ >>> email_validator.validate("user@domain", "email") # Raises ValidationError
215
+ """
216
+
217
+ def validate(self, value: Any, field_name: str) -> Any:
218
+ """Validate the email address format.
219
+
220
+ Args:
221
+ value: The value to validate
222
+ field_name: Name of the field being validated
223
+
224
+ Returns:
225
+ The original value if validation passes
226
+
227
+ Raises:
228
+ ValidationError: If the value is not a valid email format
229
+ """
230
+ if value is None:
231
+ return value
232
+
233
+ email_str = str(value)
234
+ if "@" not in email_str or "." not in email_str.split("@")[-1]:
235
+ raise ValidationError(self.get_error_message(field_name, value))
236
+
237
+ return value
238
+
239
+ def get_error_message(self, field_name: str, value: Any) -> str:
240
+ """Get the error message for email validation failure.
241
+
242
+ Args:
243
+ field_name: Name of the field that failed validation
244
+ value: The value that failed validation
245
+
246
+ Returns:
247
+ Human-readable error message for invalid email format
248
+ """
249
+ return f"Field '{field_name}' must be a valid email address"
250
+
251
+
252
+ def validate_field_value(validators: list[Any], value: Any, field_name: str) -> Any:
253
+ """Validate a field value using a list of validators.
254
+
255
+ This function applies multiple validators to a field value in sequence.
256
+ It supports both FieldValidator instances and simple callable functions.
257
+
258
+ Args:
259
+ validators: List of validators to apply
260
+ value: The value to validate
261
+ field_name: Name of the field being validated
262
+
263
+ Returns:
264
+ The validated (and potentially transformed) value
265
+
266
+ Raises:
267
+ ValidationError: If any validator fails
268
+
269
+ Examples:
270
+ >>> validators = [LengthValidator(min_length=3, max_length=50), EmailValidator()]
271
+ >>> validate_field_value(validators, "user@example.com", "email")
272
+ 'user@example.com'
273
+
274
+ >>> # Using simple function validator
275
+ >>> def no_spaces(value):
276
+ ... if " " in str(value):
277
+ ... raise ValueError("No spaces allowed")
278
+ ... return value
279
+ >>> validate_field_value([no_spaces], "username", "username")
280
+ 'username'
281
+ """
282
+ for validator in validators:
283
+ if isinstance(validator, FieldValidator):
284
+ value = validator.validate(value, field_name)
285
+ elif callable(validator):
286
+ # Support for simple function validators
287
+ try:
288
+ result = validator(value)
289
+ if result is not None:
290
+ value = result
291
+ except Exception as e:
292
+ raise ValidationError(f"Validation failed for {field_name}: {e}") from e
293
+
294
+ return value
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlobjects
3
+ Version: 0.1.0
4
+ Summary: Django-style async ORM library based on SQLAlchemy with chainable queries, Q objects, and relationship loading
5
+ Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
6
+ Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/XtraVisionsAI/sqlobjects
9
+ Project-URL: Repository, https://github.com/XtraVisionsAI/sqlobjects.git
10
+ Project-URL: Documentation, https://github.com/XtraVisionsAI/sqlobjects#readme
11
+ Project-URL: Bug Tracker, https://github.com/XtraVisionsAI/sqlobjects/issues
12
+ Project-URL: Changelog, https://github.com/XtraVisionsAI/sqlobjects/blob/main/CHANGELOG.md
13
+ Keywords: python,orm,async,django-style,database,query
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Topic :: Database
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Framework :: AsyncIO
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.12
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.43
29
+ Dynamic: license-file
@@ -0,0 +1,23 @@
1
+ sqlobjects/__init__.py,sha256=ptVLnrTySBdIHFl9Nj3oVDpY_2ssu8riX3pg-_h3T50,1165
2
+ sqlobjects/config.py,sha256=WW8Dzf-8IY0j3i0kwzVt19rELXENSkBi1fpRl6Us5l0,19295
3
+ sqlobjects/database.py,sha256=Vciywb0FWb1ChGYEfbqtBjopyiIR3KDHtH4p7bePc8g,19848
4
+ sqlobjects/exceptions.py,sha256=J0mKk77JhJmJ28Gv80DckstB9rM_ky61Cq1ZwO4kbkE,18212
5
+ sqlobjects/expressions.py,sha256=QyK3k_q4UaA5OLKu-fDkdiAc9dKHdplJd49OABCDzjk,38218
6
+ sqlobjects/fields.py,sha256=5_vi31Bix7OSH0zp16WGjcbeT0f8x7iR7EA-gM8kj6k,56822
7
+ sqlobjects/history.py,sha256=hcO-P3XcgdUF2zTL9IBv-2rfo0LKIZqdDAuIqSN6TA8,3742
8
+ sqlobjects/metadata.py,sha256=PL1ow7xptGzVaXUQ6aqv2lS1MFCf1eP_bK3GnUqMQ_8,40459
9
+ sqlobjects/model.py,sha256=-qnDZm9DMtE23tzcKNrVjHMJBO2dqIH11FtnD0tBO78,35014
10
+ sqlobjects/objects.py,sha256=DkjdIzlkO2JFhY5C34U0wAXPoy9gk_k2GFIkfJ6GBbg,29546
11
+ sqlobjects/queries.py,sha256=UysAA3Zmi0_sm0DTfHLotkKe7vXNJuTiQ64VyFnYMIQ,39992
12
+ sqlobjects/relations.py,sha256=oY4Wt3jxCJokATvQ5ukF79-f8yKU46Y7JrzbwxxFF-Y,27684
13
+ sqlobjects/session.py,sha256=-F6ia0DMWL2DPRw7aizBvzt7XNrz9aPZJYunvdkj2G4,13743
14
+ sqlobjects/signals.py,sha256=ybUFi5KD7kve8jHtr8QJuqLxg-opCoLDsDl5QJtkqT8,17680
15
+ sqlobjects/validators.py,sha256=cWZOVYw__No5YetGAFe4RBpXY14EKFxb7OkZoERyeAk,10114
16
+ sqlobjects/utils/__init__.py,sha256=NsJ52Fl_PaB-2WlOIJVGNPmgOnqPmvYRmp7uaq5zfYs,192
17
+ sqlobjects/utils/naming.py,sha256=PmfNuEz-Twly2qREjKfMNulJt8TmOUJaxzFTuts3COg,1411
18
+ sqlobjects/utils/pattern.py,sha256=FGCgobTpnmKjdAtQidHIg4Xa2CQLeP35F0VuX_1T1dk,14740
19
+ sqlobjects-0.1.0.dist-info/licenses/LICENSE,sha256=ScFR7nvWIhar0d32Y-LA4vqp9zNmy1HSJia7UX7jU6c,1068
20
+ sqlobjects-0.1.0.dist-info/METADATA,sha256=DzDRI2_FR9FtvYD-9b7m_JydZC54TKwu1IchBXNGKks,1466
21
+ sqlobjects-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ sqlobjects-0.1.0.dist-info/top_level.txt,sha256=brO5vDtCpn4L9dpX0EHpehbu4kcHaK_2Bbnwze35YKc,11
23
+ sqlobjects-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 XtraVisions
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ sqlobjects