super-easy-validator-python 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.
- super_easy_validator_python/__init__.py +55 -0
- super_easy_validator_python/checks.py +525 -0
- super_easy_validator_python/codes.py +143 -0
- super_easy_validator_python/formats.py +316 -0
- super_easy_validator_python/paths.py +162 -0
- super_easy_validator_python/py.typed +0 -0
- super_easy_validator_python/rules.py +205 -0
- super_easy_validator_python/types.py +136 -0
- super_easy_validator_python/validator.py +685 -0
- super_easy_validator_python-0.1.0.dist-info/METADATA +280 -0
- super_easy_validator_python-0.1.0.dist-info/RECORD +14 -0
- super_easy_validator_python-0.1.0.dist-info/WHEEL +5 -0
- super_easy_validator_python-0.1.0.dist-info/licenses/LICENSE +21 -0
- super_easy_validator_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: super-easy-validator-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Validate data with rules you write as plain strings, like 'optional|email'. Zero dependencies, no schemas.
|
|
5
|
+
Author-email: Rituraj Shakti <riturajshakti@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/riturajshakti/super-easy-validator-python
|
|
8
|
+
Project-URL: Documentation, https://github.com/riturajshakti/super-easy-validator-python/blob/main/DOCS.md
|
|
9
|
+
Project-URL: Changelog, https://github.com/riturajshakti/super-easy-validator-python/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/riturajshakti/super-easy-validator-python/issues
|
|
11
|
+
Keywords: validator,validation,validate,schema,schema-validation,data-validation,input-validation,form-validation,request-validation,validation-rules,zero-dependency,json-validation,dict-validation
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# super-easy-validator-python
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/super-easy-validator-python/)
|
|
29
|
+
[](https://pypi.org/project/super-easy-validator-python/)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
[](pyproject.toml)
|
|
32
|
+
|
|
33
|
+
**Validate data with rules you write as plain strings.** Zero dependencies, fully typed. No schemas, no model classes — just `"optional|email"`.
|
|
34
|
+
|
|
35
|
+
Also available for JavaScript and TypeScript: [super-easy-validator](https://www.npmjs.com/package/super-easy-validator) on npm, [@riturajshakti/super-easy-validator](https://jsr.io/@riturajshakti/super-easy-validator) on JSR. And for Go: [super-easy-validator-go](https://pkg.go.dev/github.com/riturajshakti/super-easy-validator-go).
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
pip install super-easy-validator-python
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**[📖 Full documentation — guide and complete API reference](DOCS.md)**
|
|
42
|
+
|
|
43
|
+
## Why
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
# super-easy-validator-python
|
|
47
|
+
{"age": "optional|natural|min:18"}
|
|
48
|
+
|
|
49
|
+
# the usual alternative
|
|
50
|
+
class User(BaseModel):
|
|
51
|
+
age: int | None = Field(default=None, gt=0, ge=18)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- **Zero runtime dependencies**
|
|
55
|
+
- Rules are data, so they can be built at runtime, loaded from config, or shared
|
|
56
|
+
- Validates decoded JSON directly — no model class required
|
|
57
|
+
- Nested objects, arrays of objects, per-element array rules, custom messages
|
|
58
|
+
|
|
59
|
+
## Quick start
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from super_easy_validator_python import validate
|
|
63
|
+
|
|
64
|
+
rules = {
|
|
65
|
+
"name": "fullname",
|
|
66
|
+
"email": "email",
|
|
67
|
+
"password": "string|min:8",
|
|
68
|
+
"age": "optional|natural|min:18",
|
|
69
|
+
"role": "enums:admin,user,guest",
|
|
70
|
+
"website": "optional|url",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
data = {
|
|
74
|
+
"name": "John",
|
|
75
|
+
"email": "not-an-email",
|
|
76
|
+
"password": "abc",
|
|
77
|
+
"age": 15,
|
|
78
|
+
"role": "superuser",
|
|
79
|
+
"website": "example.com",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
result = validate(rules, data)
|
|
83
|
+
if result.errors:
|
|
84
|
+
for message in result.errors:
|
|
85
|
+
print(message)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
name must be a valid fullname
|
|
90
|
+
email must be a valid email
|
|
91
|
+
password must have length of at least 8
|
|
92
|
+
age must be at least 18
|
|
93
|
+
role is invalid
|
|
94
|
+
website must be a valid url
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`result.errors` is `None` when everything passes, so `if result.errors:` is the idiom. `result.valid` says the same thing the other way round.
|
|
98
|
+
|
|
99
|
+
Alongside `errors`, the `details` list pairs each message with a stable code and the field it came from:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
result = validate({"age": "natural|min:18"}, {"age": 15})
|
|
103
|
+
result.details[0]
|
|
104
|
+
# {'field': 'age', 'message': 'age must be at least 18', 'code': 'TOO_SMALL'}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Details are plain dicts, so they serialize straight to JSON — useful when returning validation errors from an API.
|
|
108
|
+
|
|
109
|
+
Three other ways to read a result:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
errors, details = validate(rules, data) # tuple unpacking
|
|
113
|
+
if not validate(rules, data): ... # falsy when invalid
|
|
114
|
+
validate(rules, data).raise_for_errors() # raises ValidationError
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Data, and the three states
|
|
118
|
+
|
|
119
|
+
Data is a plain `dict` — what `json.loads` gives you:
|
|
120
|
+
|
|
121
|
+
| State | Written as | Satisfies |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| absent | key not in the dict | `optional` |
|
|
124
|
+
| null | key present, value `None` | `nullable` |
|
|
125
|
+
| present | key present, value set | the rest of the rules |
|
|
126
|
+
|
|
127
|
+
Dataclasses and model classes are not accepted: every field always exists on them, so `optional` and `nullable` would collapse into one. Convert first with `dataclasses.asdict(obj)`.
|
|
128
|
+
|
|
129
|
+
## Structure: nested objects, arrays, and indexing
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
rules = {
|
|
133
|
+
"address": {
|
|
134
|
+
"city": "name",
|
|
135
|
+
"pin": "string|natural|size:6",
|
|
136
|
+
"country": {"code": "alpha|upper|size:2"},
|
|
137
|
+
},
|
|
138
|
+
"tags": "array|min:2|arrayof:string|arrayof:max:10",
|
|
139
|
+
"users": [{"name": "name", "age": "natural"}],
|
|
140
|
+
"grid": [[{"label": "string"}]], # arrays of arrays of objects
|
|
141
|
+
|
|
142
|
+
"coords": "array|size:2",
|
|
143
|
+
"coords[0]": "number|min:-90|max:90", # latitude
|
|
144
|
+
"coords[1]": "number|min:-180|max:180", # longitude
|
|
145
|
+
"history[-1]": "date", # the most recent entry
|
|
146
|
+
"matrix": "arrayof:arrayof:number", # arrays of arrays
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Errors carry the full path, including array indexes:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
address.pin must be a valid numeric string
|
|
154
|
+
address.country.code must not contains lower case letters
|
|
155
|
+
tags[1] must have length of at most 10
|
|
156
|
+
users[1].name is required
|
|
157
|
+
coords[0] must be at most 90
|
|
158
|
+
history[-1] must be a valid date
|
|
159
|
+
matrix[1][0] must be a valid number
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Slices work too — `"c[0:2]"`, `"c[1:]"`, `"c[-2:]"` — applying the rule to each selected element.
|
|
163
|
+
|
|
164
|
+
## Operators: `$or`, `$and`, `$switch`
|
|
165
|
+
|
|
166
|
+
`$or` passes if any branch passes. `$and` requires every branch. `$switch` applies one rule, chosen by which `case` matches. Branches accept any rule value: strings, nested rules, list rules, functions, or nested operators.
|
|
167
|
+
|
|
168
|
+
Operators are dict keys:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
rules = {
|
|
172
|
+
# one field, several valid shapes
|
|
173
|
+
"address": {"$or": [
|
|
174
|
+
"string|max:60",
|
|
175
|
+
{"city": "name", "pin": "string|natural|size:6"},
|
|
176
|
+
]},
|
|
177
|
+
|
|
178
|
+
# $and with optional makes a nested object or list optional
|
|
179
|
+
"billing": {"$and": [
|
|
180
|
+
"optional",
|
|
181
|
+
{"line1": "string|min:5", "city": "name"},
|
|
182
|
+
]},
|
|
183
|
+
|
|
184
|
+
# one rule chosen by case; default supplies the error when nothing matches
|
|
185
|
+
"amount": {"$switch": [
|
|
186
|
+
{"case": "number|max:1000", "then": "positive", "default": True},
|
|
187
|
+
{"case": "number|min:1001", "then": "positive|decimalmax:2"},
|
|
188
|
+
]},
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`$or` reports the branch that best fits the value, so you get `address.pin ...` rather than a vague "address is invalid". `$and` merges its object branches, so a key declared in any branch counts as declared under `strict`.
|
|
193
|
+
|
|
194
|
+
Because operators are plain dict keys, a whole rule tree can be loaded from JSON:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
rules = json.load(open("rules.json"))
|
|
198
|
+
result = validate(rules, data)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Custom rules and cross-field validation
|
|
202
|
+
|
|
203
|
+
A rule value can be a function. It receives the value and its parent, and returns `None` to pass or a dict to fail — so you own the wording and the code, and cross-field checks need no special syntax.
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
rules = {
|
|
207
|
+
"password": "string|min:8",
|
|
208
|
+
"confirm_password": lambda value, parent: (
|
|
209
|
+
None if value == parent["password"]
|
|
210
|
+
else {"message": "passwords must match", "code": "PASSWORD_MISMATCH"}
|
|
211
|
+
),
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Combine a function with built-in rules through `$and`:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
{"n": {"$and": ["natural", is_even]}}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The function is called even when the value is absent, so it owns the decision about absence. An exception inside a custom rule is reported as a rule error naming the field, rather than crashing the caller.
|
|
222
|
+
|
|
223
|
+
## Every rule at a glance
|
|
224
|
+
|
|
225
|
+
Combine rules with `|`, or pass a list — `["string", "min:3"]` — when a rule contains a `|` itself.
|
|
226
|
+
|
|
227
|
+
| Group | Rules |
|
|
228
|
+
|---|---|
|
|
229
|
+
| **Presence** | `optional` `nullable` `$atleast` `$atmost` |
|
|
230
|
+
| **Types** | `string` `number` `boolean` `array` `object` |
|
|
231
|
+
| **Strings** | `email` `url` `domain` `name` `fullname` `username` `alpha` `alphanumeric` `phone` `phonecode` `objectid` `uuid` `date` `dateonly` `time` `lower` `upper` `ip` |
|
|
232
|
+
| **Numbers** | `int` `positive` `negative` `natural` `whole` |
|
|
233
|
+
| **Constraints** | `equal:` `size:` `min:` `max:` `regex:` `decimalsize:` `decimalmin:` `decimalmax:` `enums:` |
|
|
234
|
+
| **Arrays** | `arrayof:<any rule above>` |
|
|
235
|
+
| **Operators** | `$or` `$and` `$switch` |
|
|
236
|
+
| **Messages** | `field:` `error:` and a `quotes` option |
|
|
237
|
+
|
|
238
|
+
String rules check for a string automatically; number rules check for a number. Prefix with `string` to validate numeric or boolean strings — `"string|natural"`, `"string|boolean"`.
|
|
239
|
+
|
|
240
|
+
An unknown rule raises `InvalidRuleError` rather than being ignored, so a typo surfaces at first run.
|
|
241
|
+
|
|
242
|
+
## Numbers
|
|
243
|
+
|
|
244
|
+
`bool` is a subclass of `int` in Python, so `True` would otherwise satisfy every numeric rule. It does not here: numeric rules reject booleans, and `boolean` rejects numbers.
|
|
245
|
+
|
|
246
|
+
Python integers are arbitrary precision, so large values keep their digits with no special rule:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
validate({"f": "natural"}, json.loads('{"f": 12345678901234567890}')) # exact
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Regular expressions
|
|
253
|
+
|
|
254
|
+
Patterns may be written in literal form and are translated automatically:
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
{"hash": r"regex:/^[A-Z0-9]{128}$/i"} # or plain: r"regex:(?i)^[A-Z0-9]{128}$"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Flags `i`, `m` and `s` are honoured; `g`, `y` and `u` are accepted and ignored. Lookahead, lookbehind and backreferences all work.
|
|
261
|
+
|
|
262
|
+
## Options
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from super_easy_validator_python import Config
|
|
266
|
+
|
|
267
|
+
validate(rules, data, Config(quotes="backtick", strict=True))
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
- **`quotes`** — `"none"` (default), `"single-quotes"`, `"double-quotes"`, `"backtick"`
|
|
271
|
+
- **`strict`** — reject any field in the data that has no rule, nested objects included
|
|
272
|
+
- **`array_indexing_check`** — `True` by default. Set `False` to treat keys like `"c[0]"` as literal names
|
|
273
|
+
|
|
274
|
+
## Typing
|
|
275
|
+
|
|
276
|
+
The package ships `py.typed`, so mypy and pyright see the annotations. `Rules` is `dict[str, Any]`, which is honest about how dynamic a rule tree is.
|
|
277
|
+
|
|
278
|
+
## License
|
|
279
|
+
|
|
280
|
+
MIT
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
super_easy_validator_python/__init__.py,sha256=T-MdPHNVLv9fHcBL5WN81X9y9G3V80SqPJDMk0Vr640,1064
|
|
2
|
+
super_easy_validator_python/checks.py,sha256=uALR83LYKt4PyBJ1q5bsDCSRFNXqYIF-HM3TNpS_vfc,17598
|
|
3
|
+
super_easy_validator_python/codes.py,sha256=6TlZO6FDnTiA2CnRJk3ivhysE7EzCBmjkLOJdYhQhIw,3763
|
|
4
|
+
super_easy_validator_python/formats.py,sha256=t4MmAeq7hoxGKLbsr4uXI5vHvU_SK8zqrow4LFbSKyo,25033
|
|
5
|
+
super_easy_validator_python/paths.py,sha256=c1N3fsmnkDuV0Q10amcg4Yo9oHsoYbwnn62MenY6acA,4891
|
|
6
|
+
super_easy_validator_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
super_easy_validator_python/rules.py,sha256=fxOCVWdwhPcb0Hw32UimO5oyXnTci6XM-jEC4cDRTIc,5347
|
|
8
|
+
super_easy_validator_python/types.py,sha256=iHfJ_S2NSdgFe90GYCIPWaOCug341GC6EeuGRpzHl6M,3785
|
|
9
|
+
super_easy_validator_python/validator.py,sha256=e9OHB8Fdx30NDmPVCT4v8yvrR-vKfvsAWWBiZ68ON2o,21914
|
|
10
|
+
super_easy_validator_python-0.1.0.dist-info/licenses/LICENSE,sha256=rZPBzUNbwnxKSxAW4DzGAGIpKpel-bR6c4MzyqEjDpk,1071
|
|
11
|
+
super_easy_validator_python-0.1.0.dist-info/METADATA,sha256=MZzaPolLpPy3hqAChIJjQOR5Iz9MUznGar53cCOXcPw,10494
|
|
12
|
+
super_easy_validator_python-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
13
|
+
super_easy_validator_python-0.1.0.dist-info/top_level.txt,sha256=t22NerRLcfy0Ua9MZdvG4hRVfvEn0feIXZ-12Px9piQ,28
|
|
14
|
+
super_easy_validator_python-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rituraj Shakti
|
|
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
|
+
super_easy_validator_python
|