super-easy-validator-python 0.1.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 (23) hide show
  1. super_easy_validator_python-0.1.0/LICENSE +21 -0
  2. super_easy_validator_python-0.1.0/PKG-INFO +280 -0
  3. super_easy_validator_python-0.1.0/README.md +255 -0
  4. super_easy_validator_python-0.1.0/pyproject.toml +43 -0
  5. super_easy_validator_python-0.1.0/setup.cfg +4 -0
  6. super_easy_validator_python-0.1.0/super_easy_validator_python/__init__.py +55 -0
  7. super_easy_validator_python-0.1.0/super_easy_validator_python/checks.py +525 -0
  8. super_easy_validator_python-0.1.0/super_easy_validator_python/codes.py +143 -0
  9. super_easy_validator_python-0.1.0/super_easy_validator_python/formats.py +316 -0
  10. super_easy_validator_python-0.1.0/super_easy_validator_python/paths.py +162 -0
  11. super_easy_validator_python-0.1.0/super_easy_validator_python/py.typed +0 -0
  12. super_easy_validator_python-0.1.0/super_easy_validator_python/rules.py +205 -0
  13. super_easy_validator_python-0.1.0/super_easy_validator_python/types.py +136 -0
  14. super_easy_validator_python-0.1.0/super_easy_validator_python/validator.py +685 -0
  15. super_easy_validator_python-0.1.0/super_easy_validator_python.egg-info/PKG-INFO +280 -0
  16. super_easy_validator_python-0.1.0/super_easy_validator_python.egg-info/SOURCES.txt +21 -0
  17. super_easy_validator_python-0.1.0/super_easy_validator_python.egg-info/dependency_links.txt +1 -0
  18. super_easy_validator_python-0.1.0/super_easy_validator_python.egg-info/top_level.txt +1 -0
  19. super_easy_validator_python-0.1.0/tests/test_details.py +418 -0
  20. super_easy_validator_python-0.1.0/tests/test_formats.py +421 -0
  21. super_easy_validator_python-0.1.0/tests/test_indexing.py +476 -0
  22. super_easy_validator_python-0.1.0/tests/test_operators.py +969 -0
  23. super_easy_validator_python-0.1.0/tests/test_validator.py +894 -0
@@ -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,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
+ [![PyPI](https://img.shields.io/pypi/v/super-easy-validator-python)](https://pypi.org/project/super-easy-validator-python/)
29
+ [![Python](https://img.shields.io/pypi/pyversions/super-easy-validator-python)](https://pypi.org/project/super-easy-validator-python/)
30
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
31
+ [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](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,255 @@
1
+ # super-easy-validator-python
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/super-easy-validator-python)](https://pypi.org/project/super-easy-validator-python/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/super-easy-validator-python)](https://pypi.org/project/super-easy-validator-python/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+ [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](pyproject.toml)
7
+
8
+ **Validate data with rules you write as plain strings.** Zero dependencies, fully typed. No schemas, no model classes — just `"optional|email"`.
9
+
10
+ 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).
11
+
12
+ ```sh
13
+ pip install super-easy-validator-python
14
+ ```
15
+
16
+ **[📖 Full documentation — guide and complete API reference](DOCS.md)**
17
+
18
+ ## Why
19
+
20
+ ```python
21
+ # super-easy-validator-python
22
+ {"age": "optional|natural|min:18"}
23
+
24
+ # the usual alternative
25
+ class User(BaseModel):
26
+ age: int | None = Field(default=None, gt=0, ge=18)
27
+ ```
28
+
29
+ - **Zero runtime dependencies**
30
+ - Rules are data, so they can be built at runtime, loaded from config, or shared
31
+ - Validates decoded JSON directly — no model class required
32
+ - Nested objects, arrays of objects, per-element array rules, custom messages
33
+
34
+ ## Quick start
35
+
36
+ ```python
37
+ from super_easy_validator_python import validate
38
+
39
+ rules = {
40
+ "name": "fullname",
41
+ "email": "email",
42
+ "password": "string|min:8",
43
+ "age": "optional|natural|min:18",
44
+ "role": "enums:admin,user,guest",
45
+ "website": "optional|url",
46
+ }
47
+
48
+ data = {
49
+ "name": "John",
50
+ "email": "not-an-email",
51
+ "password": "abc",
52
+ "age": 15,
53
+ "role": "superuser",
54
+ "website": "example.com",
55
+ }
56
+
57
+ result = validate(rules, data)
58
+ if result.errors:
59
+ for message in result.errors:
60
+ print(message)
61
+ ```
62
+
63
+ ```
64
+ name must be a valid fullname
65
+ email must be a valid email
66
+ password must have length of at least 8
67
+ age must be at least 18
68
+ role is invalid
69
+ website must be a valid url
70
+ ```
71
+
72
+ `result.errors` is `None` when everything passes, so `if result.errors:` is the idiom. `result.valid` says the same thing the other way round.
73
+
74
+ Alongside `errors`, the `details` list pairs each message with a stable code and the field it came from:
75
+
76
+ ```python
77
+ result = validate({"age": "natural|min:18"}, {"age": 15})
78
+ result.details[0]
79
+ # {'field': 'age', 'message': 'age must be at least 18', 'code': 'TOO_SMALL'}
80
+ ```
81
+
82
+ Details are plain dicts, so they serialize straight to JSON — useful when returning validation errors from an API.
83
+
84
+ Three other ways to read a result:
85
+
86
+ ```python
87
+ errors, details = validate(rules, data) # tuple unpacking
88
+ if not validate(rules, data): ... # falsy when invalid
89
+ validate(rules, data).raise_for_errors() # raises ValidationError
90
+ ```
91
+
92
+ ## Data, and the three states
93
+
94
+ Data is a plain `dict` — what `json.loads` gives you:
95
+
96
+ | State | Written as | Satisfies |
97
+ |---|---|---|
98
+ | absent | key not in the dict | `optional` |
99
+ | null | key present, value `None` | `nullable` |
100
+ | present | key present, value set | the rest of the rules |
101
+
102
+ 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)`.
103
+
104
+ ## Structure: nested objects, arrays, and indexing
105
+
106
+ ```python
107
+ rules = {
108
+ "address": {
109
+ "city": "name",
110
+ "pin": "string|natural|size:6",
111
+ "country": {"code": "alpha|upper|size:2"},
112
+ },
113
+ "tags": "array|min:2|arrayof:string|arrayof:max:10",
114
+ "users": [{"name": "name", "age": "natural"}],
115
+ "grid": [[{"label": "string"}]], # arrays of arrays of objects
116
+
117
+ "coords": "array|size:2",
118
+ "coords[0]": "number|min:-90|max:90", # latitude
119
+ "coords[1]": "number|min:-180|max:180", # longitude
120
+ "history[-1]": "date", # the most recent entry
121
+ "matrix": "arrayof:arrayof:number", # arrays of arrays
122
+ }
123
+ ```
124
+
125
+ Errors carry the full path, including array indexes:
126
+
127
+ ```
128
+ address.pin must be a valid numeric string
129
+ address.country.code must not contains lower case letters
130
+ tags[1] must have length of at most 10
131
+ users[1].name is required
132
+ coords[0] must be at most 90
133
+ history[-1] must be a valid date
134
+ matrix[1][0] must be a valid number
135
+ ```
136
+
137
+ Slices work too — `"c[0:2]"`, `"c[1:]"`, `"c[-2:]"` — applying the rule to each selected element.
138
+
139
+ ## Operators: `$or`, `$and`, `$switch`
140
+
141
+ `$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.
142
+
143
+ Operators are dict keys:
144
+
145
+ ```python
146
+ rules = {
147
+ # one field, several valid shapes
148
+ "address": {"$or": [
149
+ "string|max:60",
150
+ {"city": "name", "pin": "string|natural|size:6"},
151
+ ]},
152
+
153
+ # $and with optional makes a nested object or list optional
154
+ "billing": {"$and": [
155
+ "optional",
156
+ {"line1": "string|min:5", "city": "name"},
157
+ ]},
158
+
159
+ # one rule chosen by case; default supplies the error when nothing matches
160
+ "amount": {"$switch": [
161
+ {"case": "number|max:1000", "then": "positive", "default": True},
162
+ {"case": "number|min:1001", "then": "positive|decimalmax:2"},
163
+ ]},
164
+ }
165
+ ```
166
+
167
+ `$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`.
168
+
169
+ Because operators are plain dict keys, a whole rule tree can be loaded from JSON:
170
+
171
+ ```python
172
+ rules = json.load(open("rules.json"))
173
+ result = validate(rules, data)
174
+ ```
175
+
176
+ ## Custom rules and cross-field validation
177
+
178
+ 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.
179
+
180
+ ```python
181
+ rules = {
182
+ "password": "string|min:8",
183
+ "confirm_password": lambda value, parent: (
184
+ None if value == parent["password"]
185
+ else {"message": "passwords must match", "code": "PASSWORD_MISMATCH"}
186
+ ),
187
+ }
188
+ ```
189
+
190
+ Combine a function with built-in rules through `$and`:
191
+
192
+ ```python
193
+ {"n": {"$and": ["natural", is_even]}}
194
+ ```
195
+
196
+ 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.
197
+
198
+ ## Every rule at a glance
199
+
200
+ Combine rules with `|`, or pass a list — `["string", "min:3"]` — when a rule contains a `|` itself.
201
+
202
+ | Group | Rules |
203
+ |---|---|
204
+ | **Presence** | `optional` `nullable` `$atleast` `$atmost` |
205
+ | **Types** | `string` `number` `boolean` `array` `object` |
206
+ | **Strings** | `email` `url` `domain` `name` `fullname` `username` `alpha` `alphanumeric` `phone` `phonecode` `objectid` `uuid` `date` `dateonly` `time` `lower` `upper` `ip` |
207
+ | **Numbers** | `int` `positive` `negative` `natural` `whole` |
208
+ | **Constraints** | `equal:` `size:` `min:` `max:` `regex:` `decimalsize:` `decimalmin:` `decimalmax:` `enums:` |
209
+ | **Arrays** | `arrayof:<any rule above>` |
210
+ | **Operators** | `$or` `$and` `$switch` |
211
+ | **Messages** | `field:` `error:` and a `quotes` option |
212
+
213
+ 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"`.
214
+
215
+ An unknown rule raises `InvalidRuleError` rather than being ignored, so a typo surfaces at first run.
216
+
217
+ ## Numbers
218
+
219
+ `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.
220
+
221
+ Python integers are arbitrary precision, so large values keep their digits with no special rule:
222
+
223
+ ```python
224
+ validate({"f": "natural"}, json.loads('{"f": 12345678901234567890}')) # exact
225
+ ```
226
+
227
+ ## Regular expressions
228
+
229
+ Patterns may be written in literal form and are translated automatically:
230
+
231
+ ```python
232
+ {"hash": r"regex:/^[A-Z0-9]{128}$/i"} # or plain: r"regex:(?i)^[A-Z0-9]{128}$"
233
+ ```
234
+
235
+ Flags `i`, `m` and `s` are honoured; `g`, `y` and `u` are accepted and ignored. Lookahead, lookbehind and backreferences all work.
236
+
237
+ ## Options
238
+
239
+ ```python
240
+ from super_easy_validator_python import Config
241
+
242
+ validate(rules, data, Config(quotes="backtick", strict=True))
243
+ ```
244
+
245
+ - **`quotes`** — `"none"` (default), `"single-quotes"`, `"double-quotes"`, `"backtick"`
246
+ - **`strict`** — reject any field in the data that has no rule, nested objects included
247
+ - **`array_indexing_check`** — `True` by default. Set `False` to treat keys like `"c[0]"` as literal names
248
+
249
+ ## Typing
250
+
251
+ 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.
252
+
253
+ ## License
254
+
255
+ MIT
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "super-easy-validator-python"
7
+ version = "0.1.0"
8
+ description = "Validate data with rules you write as plain strings, like 'optional|email'. Zero dependencies, no schemas."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Rituraj Shakti", email = "riturajshakti@gmail.com" }]
14
+ keywords = [
15
+ "validator", "validation", "validate", "schema", "schema-validation",
16
+ "data-validation", "input-validation", "form-validation",
17
+ "request-validation", "validation-rules", "zero-dependency",
18
+ "json-validation", "dict-validation",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Intended Audience :: Developers",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Topic :: Software Development :: Libraries :: Python Modules",
29
+ "Typing :: Typed",
30
+ ]
31
+ dependencies = []
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/riturajshakti/super-easy-validator-python"
35
+ Documentation = "https://github.com/riturajshakti/super-easy-validator-python/blob/main/DOCS.md"
36
+ Changelog = "https://github.com/riturajshakti/super-easy-validator-python/blob/main/CHANGELOG.md"
37
+ Issues = "https://github.com/riturajshakti/super-easy-validator-python/issues"
38
+
39
+ [tool.setuptools.packages.find]
40
+ include = ["super_easy_validator_python*"]
41
+
42
+ [tool.setuptools.package-data]
43
+ super_easy_validator_python = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,55 @@
1
+ """Validate data with rules you write as plain strings.
2
+
3
+ from super_easy_validator_python import validate
4
+
5
+ rules = {
6
+ "name": "fullname",
7
+ "email": "email",
8
+ "age": "optional|natural|min:18",
9
+ "role": "enums:admin,user,guest",
10
+ }
11
+
12
+ result = validate(rules, data)
13
+ if result.errors:
14
+ print(result.errors)
15
+
16
+ Rules are a plain dict, so a whole rule tree can be loaded from JSON or built
17
+ at runtime. See DOCS.md for the full reference.
18
+ """
19
+
20
+ from .codes import ErrorCodes
21
+ from .types import (
22
+ Config,
23
+ CustomRule,
24
+ Data,
25
+ Detail,
26
+ InvalidRuleError,
27
+ Result,
28
+ Rules,
29
+ ValidationError,
30
+ QUOTE_BACKTICK,
31
+ QUOTE_DOUBLE,
32
+ QUOTE_NONE,
33
+ QUOTE_SINGLE,
34
+ )
35
+ from .validator import validate
36
+
37
+ __version__ = "0.1.0"
38
+
39
+ __all__ = [
40
+ "validate",
41
+ "ErrorCodes",
42
+ "Config",
43
+ "Result",
44
+ "Rules",
45
+ "Data",
46
+ "Detail",
47
+ "CustomRule",
48
+ "InvalidRuleError",
49
+ "ValidationError",
50
+ "QUOTE_NONE",
51
+ "QUOTE_SINGLE",
52
+ "QUOTE_DOUBLE",
53
+ "QUOTE_BACKTICK",
54
+ "__version__",
55
+ ]