pytastic 0.0.6__tar.gz → 0.0.7__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.
- {pytastic-0.0.6 → pytastic-0.0.7}/PKG-INFO +3 -2
- {pytastic-0.0.6 → pytastic-0.0.7}/README.md +1 -1
- {pytastic-0.0.6 → pytastic-0.0.7}/pyproject.toml +2 -1
- {pytastic-0.0.6 → pytastic-0.0.7}/pytastic/codegen.py +56 -2
- {pytastic-0.0.6 → pytastic-0.0.7}/LICENSE +0 -0
- {pytastic-0.0.6 → pytastic-0.0.7}/pytastic/__init__.py +0 -0
- {pytastic-0.0.6 → pytastic-0.0.7}/pytastic/compiler.py +0 -0
- {pytastic-0.0.6 → pytastic-0.0.7}/pytastic/core.py +0 -0
- {pytastic-0.0.6 → pytastic-0.0.7}/pytastic/exceptions.py +0 -0
- {pytastic-0.0.6 → pytastic-0.0.7}/pytastic/schema.py +0 -0
- {pytastic-0.0.6 → pytastic-0.0.7}/pytastic/utils.py +0 -0
- {pytastic-0.0.6 → pytastic-0.0.7}/pytastic/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytastic
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: A dependency-free JSON validation library using TypedDict and Annotated
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Tersoo
|
|
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Project-URL: Repository, https://github.com/tersoo/pytastic
|
|
16
17
|
Description-Content-Type: text/markdown
|
|
17
18
|
|
|
18
19
|
# Pytastic
|
|
@@ -37,7 +38,7 @@ Benchmark results (100,000 validation iterations):
|
|
|
37
38
|
| **Pytastic** | **0.1794** | **557,277** | **3.37x** |
|
|
38
39
|
| Pydantic | 0.2002 | 499,381 | 3.76x |
|
|
39
40
|
|
|
40
|
-
**Pytastic is faster than Pydantic** Pure Python with zero dependencies!
|
|
41
|
+
**Pytastic is faster than Pydantic** i.e. Pure Python with zero dependencies!
|
|
41
42
|
|
|
42
43
|
## Installation
|
|
43
44
|
|
|
@@ -20,7 +20,7 @@ Benchmark results (100,000 validation iterations):
|
|
|
20
20
|
| **Pytastic** | **0.1794** | **557,277** | **3.37x** |
|
|
21
21
|
| Pydantic | 0.2002 | 499,381 | 3.76x |
|
|
22
22
|
|
|
23
|
-
**Pytastic is faster than Pydantic** Pure Python with zero dependencies!
|
|
23
|
+
**Pytastic is faster than Pydantic** i.e. Pure Python with zero dependencies!
|
|
24
24
|
|
|
25
25
|
## Installation
|
|
26
26
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pytastic"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.7"
|
|
4
4
|
description = "A dependency-free JSON validation library using TypedDict and Annotated"
|
|
5
5
|
authors = ["Tersoo <tersoo@example.com>"]
|
|
6
6
|
readme = "README.md"
|
|
7
|
+
repository = "https://github.com/tersoo/pytastic"
|
|
7
8
|
packages = [{include = "pytastic"}]
|
|
8
9
|
|
|
9
10
|
[tool.poetry.dependencies]
|
|
@@ -6,7 +6,6 @@ from pytastic.utils import parse_constraints
|
|
|
6
6
|
|
|
7
7
|
class CodegenCompiler:
|
|
8
8
|
"""Generates optimized Python validation functions from type schemas."""
|
|
9
|
-
|
|
10
9
|
def __init__(self):
|
|
11
10
|
self._cache: Dict[Type, Any] = {}
|
|
12
11
|
self._counter = 0
|
|
@@ -66,14 +65,32 @@ class CodegenCompiler:
|
|
|
66
65
|
elif base_origin is list or base_origin is List:
|
|
67
66
|
inner_type = get_args(base_type)[0] if get_args(base_type) else Any
|
|
68
67
|
return self._gen_list(var_name, path_var, inner_type, constraints, indent)
|
|
68
|
+
elif base_origin is tuple or base_origin is Tuple:
|
|
69
|
+
inner_types = get_args(base_type)
|
|
70
|
+
return self._gen_tuple(var_name, path_var, inner_types, constraints, indent)
|
|
69
71
|
|
|
70
72
|
if self._is_typeddict(schema):
|
|
71
|
-
|
|
73
|
+
constraints = {}
|
|
74
|
+
# Handle metadata from `_: Annotated[...]` pattern
|
|
75
|
+
if hasattr(schema, '__annotations__') and '_' in schema.__annotations__:
|
|
76
|
+
meta_annotation = schema.__annotations__.get('_', None)
|
|
77
|
+
if meta_annotation and get_origin(meta_annotation) is Annotated:
|
|
78
|
+
meta_args = get_args(meta_annotation)
|
|
79
|
+
constraint_str = ""
|
|
80
|
+
for arg in meta_args[1:]:
|
|
81
|
+
if isinstance(arg, str):
|
|
82
|
+
constraint_str += arg + ";"
|
|
83
|
+
meta_constraints = parse_constraints(constraint_str)
|
|
84
|
+
constraints.update(meta_constraints)
|
|
85
|
+
return self._gen_object(schema, var_name, path_var, constraints, indent)
|
|
72
86
|
|
|
73
87
|
if origin is list or origin is List:
|
|
74
88
|
inner_type = args[0] if args else Any
|
|
75
89
|
return self._gen_list(var_name, path_var, inner_type, {}, indent)
|
|
76
90
|
|
|
91
|
+
if origin is tuple or origin is Tuple:
|
|
92
|
+
return self._gen_tuple(var_name, path_var, args, {}, indent)
|
|
93
|
+
|
|
77
94
|
if origin is Union:
|
|
78
95
|
return self._gen_union(var_name, path_var, args, indent)
|
|
79
96
|
|
|
@@ -178,6 +195,19 @@ class CodegenCompiler:
|
|
|
178
195
|
if not is_required:
|
|
179
196
|
indent -= 1
|
|
180
197
|
|
|
198
|
+
# Additional Properties Check
|
|
199
|
+
if constraints.get('strict') or constraints.get('additional_properties') is False:
|
|
200
|
+
lines.append(f"{ind}known_keys = set({repr(list(type_hints.keys()))}) - {{'_'}}")
|
|
201
|
+
lines.append(f"{ind}extra_keys = set({var}.keys()) - known_keys")
|
|
202
|
+
lines.append(f"{ind}if extra_keys:")
|
|
203
|
+
lines.append(f"{ind} raise ValidationError(f'Extra fields not allowed at {{{path}}}: {{extra_keys}}', [{{'path': {path}, 'message': 'Extra fields not allowed'}}])")
|
|
204
|
+
|
|
205
|
+
# Min Properties
|
|
206
|
+
min_props = constraints.get('min_properties') or constraints.get('min_props')
|
|
207
|
+
if min_props:
|
|
208
|
+
lines.append(f"{ind}if len({var}) < {min_props}:")
|
|
209
|
+
lines.append(f"{ind} raise ValidationError(f'Too few properties at {{{path}}}', [{{'path': {path}, 'message': 'Min properties {min_props}'}}])")
|
|
210
|
+
|
|
181
211
|
return lines
|
|
182
212
|
|
|
183
213
|
def _gen_list(self, var: str, path: str, item_type: Type, constraints: Dict, indent: int) -> List[str]:
|
|
@@ -228,6 +258,30 @@ class CodegenCompiler:
|
|
|
228
258
|
|
|
229
259
|
return lines
|
|
230
260
|
|
|
261
|
+
def _gen_tuple(self, var: str, path: str, item_types: Tuple[Type, ...], constraints: Dict, indent: int) -> List[str]:
|
|
262
|
+
"""Generate tuple validation code."""
|
|
263
|
+
ind = ' ' * indent
|
|
264
|
+
lines = []
|
|
265
|
+
|
|
266
|
+
lines.append(f"{ind}if not isinstance({var}, (list, tuple)):")
|
|
267
|
+
lines.append(f"{ind} raise ValidationError(f'Expected tuple at {{{path}}}', [{{'path': {path}, 'message': 'Invalid type'}}])")
|
|
268
|
+
|
|
269
|
+
expected_len = len(item_types)
|
|
270
|
+
lines.append(f"{ind}if len({var}) != {expected_len}:")
|
|
271
|
+
lines.append(f"{ind} raise ValidationError(f'Expected {expected_len} items at {{{path}}}', [{{'path': {path}, 'message': 'Expected {expected_len} items'}}])")
|
|
272
|
+
|
|
273
|
+
for i, item_type in enumerate(item_types):
|
|
274
|
+
item_path = f"{path} + f'[{i}]'"
|
|
275
|
+
# Tuple items are accessed by index
|
|
276
|
+
# We need to assign to a temp var to be safe for recursive validation
|
|
277
|
+
temp_var = f"_tuple_item_{indent}_{i}"
|
|
278
|
+
lines.append(f"{ind}{temp_var} = {var}[{i}]")
|
|
279
|
+
|
|
280
|
+
item_lines = self._generate_validator(item_type, temp_var, item_path, indent)
|
|
281
|
+
lines.extend(item_lines)
|
|
282
|
+
|
|
283
|
+
return lines
|
|
284
|
+
|
|
231
285
|
def _gen_literal(self, var: str, path: str, values: Tuple, indent: int) -> List[str]:
|
|
232
286
|
"""Generate literal validation code."""
|
|
233
287
|
ind = ' ' * indent
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|