dhi 1.1.1__cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.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.
dhi/fields.py ADDED
@@ -0,0 +1,187 @@
1
+ """
2
+ Field definition for dhi - Pydantic v2 compatible.
3
+
4
+ Provides the Field() function for defining field-level constraints
5
+ and metadata, matching Pydantic's API.
6
+
7
+ Example:
8
+ from typing import Annotated
9
+ from dhi import BaseModel, Field
10
+
11
+ class User(BaseModel):
12
+ name: Annotated[str, Field(min_length=1, max_length=100)]
13
+ age: Annotated[int, Field(gt=0, le=120)]
14
+ """
15
+
16
+ from typing import Any, Optional, Union, List
17
+
18
+ _MISSING = object() # Sentinel for unset defaults
19
+
20
+
21
+ class FieldInfo:
22
+ """Stores field constraints and metadata.
23
+
24
+ This is the object returned by Field() and can be used in Annotated types.
25
+ """
26
+ __slots__ = (
27
+ 'default', 'default_factory', 'alias', 'title', 'description',
28
+ 'examples', 'gt', 'ge', 'lt', 'le', 'multiple_of', 'strict',
29
+ 'min_length', 'max_length', 'pattern', 'strip_whitespace',
30
+ 'to_lower', 'to_upper', 'allow_inf_nan', 'max_digits',
31
+ 'decimal_places', 'unique_items',
32
+ )
33
+
34
+ def __init__(
35
+ self,
36
+ default: Any = _MISSING,
37
+ *,
38
+ default_factory: Any = None,
39
+ alias: Optional[str] = None,
40
+ title: Optional[str] = None,
41
+ description: Optional[str] = None,
42
+ examples: Optional[List[Any]] = None,
43
+ gt: Optional[Union[int, float]] = None,
44
+ ge: Optional[Union[int, float]] = None,
45
+ lt: Optional[Union[int, float]] = None,
46
+ le: Optional[Union[int, float]] = None,
47
+ multiple_of: Optional[Union[int, float]] = None,
48
+ strict: Optional[bool] = None,
49
+ min_length: Optional[int] = None,
50
+ max_length: Optional[int] = None,
51
+ pattern: Optional[str] = None,
52
+ strip_whitespace: Optional[bool] = None,
53
+ to_lower: Optional[bool] = None,
54
+ to_upper: Optional[bool] = None,
55
+ allow_inf_nan: Optional[bool] = None,
56
+ max_digits: Optional[int] = None,
57
+ decimal_places: Optional[int] = None,
58
+ unique_items: Optional[bool] = None,
59
+ ):
60
+ self.default = default
61
+ self.default_factory = default_factory
62
+ self.alias = alias
63
+ self.title = title
64
+ self.description = description
65
+ self.examples = examples
66
+ self.gt = gt
67
+ self.ge = ge
68
+ self.lt = lt
69
+ self.le = le
70
+ self.multiple_of = multiple_of
71
+ self.strict = strict
72
+ self.min_length = min_length
73
+ self.max_length = max_length
74
+ self.pattern = pattern
75
+ self.strip_whitespace = strip_whitespace
76
+ self.to_lower = to_lower
77
+ self.to_upper = to_upper
78
+ self.allow_inf_nan = allow_inf_nan
79
+ self.max_digits = max_digits
80
+ self.decimal_places = decimal_places
81
+ self.unique_items = unique_items
82
+
83
+ @property
84
+ def is_required(self) -> bool:
85
+ return self.default is _MISSING and self.default_factory is None
86
+
87
+ def get_default(self) -> Any:
88
+ if self.default_factory is not None:
89
+ return self.default_factory()
90
+ if self.default is _MISSING:
91
+ raise ValueError("Field is required")
92
+ return self.default
93
+
94
+ def __repr__(self) -> str:
95
+ parts = []
96
+ if self.default is not _MISSING:
97
+ parts.append(f"default={self.default!r}")
98
+ if self.gt is not None:
99
+ parts.append(f"gt={self.gt}")
100
+ if self.ge is not None:
101
+ parts.append(f"ge={self.ge}")
102
+ if self.lt is not None:
103
+ parts.append(f"lt={self.lt}")
104
+ if self.le is not None:
105
+ parts.append(f"le={self.le}")
106
+ if self.multiple_of is not None:
107
+ parts.append(f"multiple_of={self.multiple_of}")
108
+ if self.min_length is not None:
109
+ parts.append(f"min_length={self.min_length}")
110
+ if self.max_length is not None:
111
+ parts.append(f"max_length={self.max_length}")
112
+ if self.pattern is not None:
113
+ parts.append(f"pattern={self.pattern!r}")
114
+ if self.strict:
115
+ parts.append("strict=True")
116
+ return f"FieldInfo({', '.join(parts)})"
117
+
118
+
119
+ def Field(
120
+ default: Any = _MISSING,
121
+ *,
122
+ default_factory: Any = None,
123
+ alias: Optional[str] = None,
124
+ title: Optional[str] = None,
125
+ description: Optional[str] = None,
126
+ examples: Optional[List[Any]] = None,
127
+ gt: Optional[Union[int, float]] = None,
128
+ ge: Optional[Union[int, float]] = None,
129
+ lt: Optional[Union[int, float]] = None,
130
+ le: Optional[Union[int, float]] = None,
131
+ multiple_of: Optional[Union[int, float]] = None,
132
+ strict: Optional[bool] = None,
133
+ min_length: Optional[int] = None,
134
+ max_length: Optional[int] = None,
135
+ pattern: Optional[str] = None,
136
+ strip_whitespace: Optional[bool] = None,
137
+ to_lower: Optional[bool] = None,
138
+ to_upper: Optional[bool] = None,
139
+ allow_inf_nan: Optional[bool] = None,
140
+ max_digits: Optional[int] = None,
141
+ decimal_places: Optional[int] = None,
142
+ unique_items: Optional[bool] = None,
143
+ ) -> FieldInfo:
144
+ """Create a FieldInfo with constraints and metadata.
145
+
146
+ Matches Pydantic v2's Field() function signature.
147
+
148
+ Example:
149
+ from typing import Annotated
150
+ from dhi import Field
151
+
152
+ # Numeric constraints
153
+ age: Annotated[int, Field(gt=0, le=120)]
154
+
155
+ # String constraints
156
+ name: Annotated[str, Field(min_length=1, max_length=50)]
157
+
158
+ # With default
159
+ score: Annotated[float, Field(default=0.0, ge=0, le=100)]
160
+ """
161
+ return FieldInfo(
162
+ default=default,
163
+ default_factory=default_factory,
164
+ alias=alias,
165
+ title=title,
166
+ description=description,
167
+ examples=examples,
168
+ gt=gt,
169
+ ge=ge,
170
+ lt=lt,
171
+ le=le,
172
+ multiple_of=multiple_of,
173
+ strict=strict,
174
+ min_length=min_length,
175
+ max_length=max_length,
176
+ pattern=pattern,
177
+ strip_whitespace=strip_whitespace,
178
+ to_lower=to_lower,
179
+ to_upper=to_upper,
180
+ allow_inf_nan=allow_inf_nan,
181
+ max_digits=max_digits,
182
+ decimal_places=decimal_places,
183
+ unique_items=unique_items,
184
+ )
185
+
186
+
187
+ __all__ = ["Field", "FieldInfo"]
@@ -0,0 +1,108 @@
1
+ """
2
+ Functional validators for dhi - Pydantic v2 compatible.
3
+
4
+ Provides decorator-based validators that can be applied to BaseModel fields
5
+ and models, matching Pydantic's @field_validator and @model_validator.
6
+
7
+ Example:
8
+ from dhi import BaseModel, field_validator, model_validator
9
+
10
+ class User(BaseModel):
11
+ name: str
12
+ password: str
13
+ confirm_password: str
14
+
15
+ @field_validator('name')
16
+ @classmethod
17
+ def name_must_be_alpha(cls, v):
18
+ if not v.isalpha():
19
+ raise ValueError('Name must be alphabetic')
20
+ return v
21
+
22
+ @model_validator(mode='after')
23
+ def passwords_match(self):
24
+ if self.password != self.confirm_password:
25
+ raise ValueError('Passwords do not match')
26
+ return self
27
+ """
28
+
29
+ from typing import Any, Callable, Optional, Sequence, Union
30
+
31
+
32
+ def field_validator(
33
+ *fields: str,
34
+ mode: str = 'after',
35
+ ) -> Callable:
36
+ """Decorator for field-level custom validation.
37
+
38
+ Matches Pydantic v2's @field_validator decorator.
39
+
40
+ Args:
41
+ *fields: Field names this validator applies to.
42
+ mode: 'before' for pre-validation, 'after' for post-validation.
43
+
44
+ Example:
45
+ class Model(BaseModel):
46
+ name: str
47
+
48
+ @field_validator('name')
49
+ @classmethod
50
+ def validate_name(cls, v):
51
+ if len(v) < 2:
52
+ raise ValueError('Name too short')
53
+ return v.title()
54
+ """
55
+ def decorator(func: Callable) -> Callable:
56
+ func.__validator_fields__ = fields
57
+ func.__validator_mode__ = mode
58
+ return func
59
+ return decorator
60
+
61
+
62
+ def model_validator(
63
+ *,
64
+ mode: str = 'after',
65
+ ) -> Callable:
66
+ """Decorator for model-level custom validation.
67
+
68
+ Matches Pydantic v2's @model_validator decorator.
69
+
70
+ Args:
71
+ mode: 'before' runs before field validation (receives raw data dict),
72
+ 'after' runs after field validation (receives model instance).
73
+
74
+ Example:
75
+ class Model(BaseModel):
76
+ start: int
77
+ end: int
78
+
79
+ @model_validator(mode='after')
80
+ def check_order(self):
81
+ if self.start >= self.end:
82
+ raise ValueError('start must be less than end')
83
+ return self
84
+ """
85
+ def decorator(func: Callable) -> Callable:
86
+ func.__model_validator__ = True
87
+ func.__validator_mode__ = mode
88
+ return func
89
+ return decorator
90
+
91
+
92
+ def validator(
93
+ *fields: str,
94
+ pre: bool = False,
95
+ always: bool = False,
96
+ ) -> Callable:
97
+ """Legacy Pydantic v1 style validator (for backward compatibility).
98
+
99
+ Prefer @field_validator for new code.
100
+ """
101
+ def decorator(func: Callable) -> Callable:
102
+ func.__validator_fields__ = fields
103
+ func.__validator_mode__ = 'before' if pre else 'after'
104
+ return func
105
+ return decorator
106
+
107
+
108
+ __all__ = ["field_validator", "model_validator", "validator"]
dhi/libsatya.so ADDED
Binary file