vtjson 2.1.4__tar.gz → 2.1.6__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.
- {vtjson-2.1.4/src/vtjson.egg-info → vtjson-2.1.6}/PKG-INFO +6 -6
- {vtjson-2.1.4 → vtjson-2.1.6}/README.md +5 -5
- vtjson-2.1.6/src/vtjson/py.typed +0 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/src/vtjson/vtjson.py +203 -108
- {vtjson-2.1.4 → vtjson-2.1.6/src/vtjson.egg-info}/PKG-INFO +6 -6
- {vtjson-2.1.4 → vtjson-2.1.6}/src/vtjson.egg-info/SOURCES.txt +1 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/tests/test_vtjson.py +214 -2
- {vtjson-2.1.4 → vtjson-2.1.6}/AUTHORS +0 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/LICENSE +0 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/pyproject.toml +0 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/setup.cfg +0 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/src/vtjson/__init__.py +0 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/src/vtjson.egg-info/dependency_links.txt +0 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/src/vtjson.egg-info/requires.txt +0 -0
- {vtjson-2.1.4 → vtjson-2.1.6}/src/vtjson.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vtjson
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.6
|
|
4
4
|
Summary: A lightweight package for validating JSON like Python objects
|
|
5
5
|
Author-email: Michel Van den Bergh <michel.vandenbergh@uhasselt.be>
|
|
6
6
|
Project-URL: Homepage, https://github.com/vdbergh/vtjson
|
|
@@ -163,16 +163,16 @@ A schema can be, in order of precedence:
|
|
|
163
163
|
- A Python type hint such as `list[str]`. This is discussed further below.
|
|
164
164
|
- A Python type. In that case validation is done by checking membership. By convention the schema `float` matches both ints and floats. Similarly the schema `complex` matches ints and floats besides of course complex numbers.
|
|
165
165
|
- A callable. Validation is done by applying the callable to the object. If applying the callable throws an exception then the corresponding message will be part of the non-validation message.
|
|
166
|
-
-
|
|
167
|
-
-
|
|
166
|
+
- An instance of `Sequence` that is not an instance of `str` (e.g a `list` or a `tuple`). Validation is done by first checking membership of the schema type, and then performing validation for each of the entries of the object being validated against the corresponding entries of the schema.
|
|
167
|
+
- An instance of `Mapping`. Validation is done by first checking membership of the schema type, and then performing validation for each of the values of the object being validated against the corresponding values of the schema. Keys are themselves considered as schemas. E.g. `{str: str}` represents a dictionary whose keys and values are both strings. A more elaborate discussion of validation of dictionaries is given below.
|
|
168
168
|
- A `set`. A set validates an object if the object is a set and the elements of the object are validated by an element of the schema.
|
|
169
169
|
- An arbitrary Python object. Validation is done by checking equality of the schema and the object, except when the schema is `float`, in which case `math.isclose` is used. Below we call such an object a `const schema`.
|
|
170
170
|
|
|
171
|
-
## Validating
|
|
171
|
+
## Validating Mapping
|
|
172
172
|
|
|
173
|
-
For a
|
|
173
|
+
For a Mapping schema containing only `const keys` (i.e. keys corresponding to a `const schema`) the interpretation is obvious (see the introductory example above). Below we discuss the validation of an object against a Mapping schema in the general case.
|
|
174
174
|
|
|
175
|
-
- First we verify that the object is
|
|
175
|
+
- First we verify that the type of the object is a subtype of the type of the schema. If not then validation fails.
|
|
176
176
|
- We verify that all non-optional const keys of the schema are also keys of the object. If this is not the case then validation fails.
|
|
177
177
|
- Now we make a list of all the keys of the schema (both optional and non-optional). The result will be called the `key list` below.
|
|
178
178
|
- The object will pass validation if all its keys pass validation. We next discuss how to validate a particular key of the object.
|
|
@@ -143,16 +143,16 @@ A schema can be, in order of precedence:
|
|
|
143
143
|
- A Python type hint such as `list[str]`. This is discussed further below.
|
|
144
144
|
- A Python type. In that case validation is done by checking membership. By convention the schema `float` matches both ints and floats. Similarly the schema `complex` matches ints and floats besides of course complex numbers.
|
|
145
145
|
- A callable. Validation is done by applying the callable to the object. If applying the callable throws an exception then the corresponding message will be part of the non-validation message.
|
|
146
|
-
-
|
|
147
|
-
-
|
|
146
|
+
- An instance of `Sequence` that is not an instance of `str` (e.g a `list` or a `tuple`). Validation is done by first checking membership of the schema type, and then performing validation for each of the entries of the object being validated against the corresponding entries of the schema.
|
|
147
|
+
- An instance of `Mapping`. Validation is done by first checking membership of the schema type, and then performing validation for each of the values of the object being validated against the corresponding values of the schema. Keys are themselves considered as schemas. E.g. `{str: str}` represents a dictionary whose keys and values are both strings. A more elaborate discussion of validation of dictionaries is given below.
|
|
148
148
|
- A `set`. A set validates an object if the object is a set and the elements of the object are validated by an element of the schema.
|
|
149
149
|
- An arbitrary Python object. Validation is done by checking equality of the schema and the object, except when the schema is `float`, in which case `math.isclose` is used. Below we call such an object a `const schema`.
|
|
150
150
|
|
|
151
|
-
## Validating
|
|
151
|
+
## Validating Mapping
|
|
152
152
|
|
|
153
|
-
For a
|
|
153
|
+
For a Mapping schema containing only `const keys` (i.e. keys corresponding to a `const schema`) the interpretation is obvious (see the introductory example above). Below we discuss the validation of an object against a Mapping schema in the general case.
|
|
154
154
|
|
|
155
|
-
- First we verify that the object is
|
|
155
|
+
- First we verify that the type of the object is a subtype of the type of the schema. If not then validation fails.
|
|
156
156
|
- We verify that all non-optional const keys of the schema are also keys of the object. If this is not the case then validation fails.
|
|
157
157
|
- Now we make a list of all the keys of the schema (both optional and non-optional). The result will be called the `key list` below.
|
|
158
158
|
- The object will pass validation if all its keys pass validation. We next discuss how to validate a particular key of the object.
|
|
File without changes
|
|
@@ -10,9 +10,9 @@ import types
|
|
|
10
10
|
import typing
|
|
11
11
|
import urllib.parse
|
|
12
12
|
import warnings
|
|
13
|
-
from collections.abc import Sequence, Sized
|
|
13
|
+
from collections.abc import Sequence, Set, Sized
|
|
14
14
|
from dataclasses import dataclass
|
|
15
|
-
from typing import Any, Callable, Type, TypeVar, Union, cast
|
|
15
|
+
from typing import Any, Callable, Container, Mapping, Type, TypeVar, Union, cast
|
|
16
16
|
|
|
17
17
|
try:
|
|
18
18
|
from typing import Literal
|
|
@@ -45,6 +45,12 @@ if hasattr(typing, "get_origin"):
|
|
|
45
45
|
else:
|
|
46
46
|
supports_Generics = False
|
|
47
47
|
|
|
48
|
+
try:
|
|
49
|
+
Sequence[str]
|
|
50
|
+
supports_Generic_ABC = True
|
|
51
|
+
except Exception:
|
|
52
|
+
supports_Generic_ABC = False
|
|
53
|
+
|
|
48
54
|
try:
|
|
49
55
|
typing.get_type_hints(int, include_extras=True)
|
|
50
56
|
supports_structural = True
|
|
@@ -83,7 +89,7 @@ class compiled_schema:
|
|
|
83
89
|
object_: object,
|
|
84
90
|
name: str,
|
|
85
91
|
strict: bool,
|
|
86
|
-
subs:
|
|
92
|
+
subs: Mapping[str, object],
|
|
87
93
|
) -> str:
|
|
88
94
|
return ""
|
|
89
95
|
|
|
@@ -115,14 +121,14 @@ class SchemaError(Exception):
|
|
|
115
121
|
pass
|
|
116
122
|
|
|
117
123
|
|
|
118
|
-
__version__ = "2.1.
|
|
124
|
+
__version__ = "2.1.6"
|
|
119
125
|
|
|
120
126
|
|
|
121
127
|
@dataclass
|
|
122
128
|
class Apply:
|
|
123
129
|
skip_first: bool | None = None
|
|
124
130
|
name: str | None = None
|
|
125
|
-
labels:
|
|
131
|
+
labels: Sequence[str] | None = None
|
|
126
132
|
|
|
127
133
|
def __call__(self, schemas: tuple[object, ...]) -> object:
|
|
128
134
|
if len(schemas) == 0:
|
|
@@ -147,6 +153,16 @@ skip_first = Apply(skip_first=True)
|
|
|
147
153
|
_dns_resolver: dns.resolver.Resolver | None = None
|
|
148
154
|
|
|
149
155
|
|
|
156
|
+
def _generic_name(origin: type, args: tuple[object, ...]) -> str:
|
|
157
|
+
def to_name(c: object) -> str:
|
|
158
|
+
if hasattr(c, "__name__"):
|
|
159
|
+
return str(c.__name__)
|
|
160
|
+
else:
|
|
161
|
+
return str(c)
|
|
162
|
+
|
|
163
|
+
return to_name(origin) + "[" + ",".join([to_name(arg) for arg in args]) + "]"
|
|
164
|
+
|
|
165
|
+
|
|
150
166
|
def _get_type_hints(schema: object) -> dict[str, object]:
|
|
151
167
|
if not supports_structural:
|
|
152
168
|
raise SchemaError(
|
|
@@ -159,7 +175,9 @@ def _get_type_hints(schema: object) -> dict[str, object]:
|
|
|
159
175
|
return type_hints
|
|
160
176
|
|
|
161
177
|
|
|
162
|
-
def _to_dict(
|
|
178
|
+
def _to_dict(
|
|
179
|
+
type_hints: Mapping[str, object], total: bool = True
|
|
180
|
+
) -> dict[object, object]:
|
|
163
181
|
d: dict[object, object] = {}
|
|
164
182
|
if not supports_Generics:
|
|
165
183
|
raise SchemaError("Generic types are not supported")
|
|
@@ -225,7 +243,7 @@ def _wrong_type_message(
|
|
|
225
243
|
class _validate_meta(type):
|
|
226
244
|
__schema__: object
|
|
227
245
|
__strict__: bool
|
|
228
|
-
__subs__:
|
|
246
|
+
__subs__: Mapping[str, object]
|
|
229
247
|
__dbg__: bool
|
|
230
248
|
|
|
231
249
|
def __instancecheck__(cls, object_: object) -> bool:
|
|
@@ -242,7 +260,7 @@ def make_type(
|
|
|
242
260
|
name: str | None = None,
|
|
243
261
|
strict: bool = True,
|
|
244
262
|
debug: bool = False,
|
|
245
|
-
subs:
|
|
263
|
+
subs: Mapping[str, object] = {},
|
|
246
264
|
) -> _validate_meta:
|
|
247
265
|
if name is None:
|
|
248
266
|
if hasattr(schema, "__name__"):
|
|
@@ -293,7 +311,7 @@ class _union(compiled_schema):
|
|
|
293
311
|
object_: object,
|
|
294
312
|
name: str = "object",
|
|
295
313
|
strict: bool = True,
|
|
296
|
-
subs:
|
|
314
|
+
subs: Mapping[str, object] = {},
|
|
297
315
|
) -> str:
|
|
298
316
|
messages = []
|
|
299
317
|
for schema in self.schemas:
|
|
@@ -332,7 +350,7 @@ class _intersect(compiled_schema):
|
|
|
332
350
|
object_: object,
|
|
333
351
|
name: str = "object",
|
|
334
352
|
strict: bool = True,
|
|
335
|
-
subs:
|
|
353
|
+
subs: Mapping[str, object] = {},
|
|
336
354
|
) -> str:
|
|
337
355
|
for schema in self.schemas:
|
|
338
356
|
message = schema.__validate__(object_, name=name, strict=strict, subs=subs)
|
|
@@ -364,7 +382,7 @@ class _complement(compiled_schema):
|
|
|
364
382
|
object_: object,
|
|
365
383
|
name: str = "object",
|
|
366
384
|
strict: bool = True,
|
|
367
|
-
subs:
|
|
385
|
+
subs: Mapping[str, object] = {},
|
|
368
386
|
) -> str:
|
|
369
387
|
message = self.schema.__validate__(object_, name=name, strict=strict, subs=subs)
|
|
370
388
|
if message != "":
|
|
@@ -396,7 +414,7 @@ class _lax(compiled_schema):
|
|
|
396
414
|
object_: object,
|
|
397
415
|
name: str = "object",
|
|
398
416
|
strict: bool = True,
|
|
399
|
-
subs:
|
|
417
|
+
subs: Mapping[str, object] = {},
|
|
400
418
|
) -> str:
|
|
401
419
|
return self.schema.__validate__(object_, name=name, strict=False, subs=subs)
|
|
402
420
|
|
|
@@ -424,7 +442,7 @@ class _strict(compiled_schema):
|
|
|
424
442
|
object_: object,
|
|
425
443
|
name: str = "object",
|
|
426
444
|
strict: bool = True,
|
|
427
|
-
subs:
|
|
445
|
+
subs: Mapping[str, object] = {},
|
|
428
446
|
) -> str:
|
|
429
447
|
return self.schema.__validate__(object_, name=name, strict=True, subs=subs)
|
|
430
448
|
|
|
@@ -458,7 +476,7 @@ class _set_label(compiled_schema):
|
|
|
458
476
|
object_: object,
|
|
459
477
|
name: str = "object",
|
|
460
478
|
strict: bool = True,
|
|
461
|
-
subs:
|
|
479
|
+
subs: Mapping[str, object] = {},
|
|
462
480
|
) -> str:
|
|
463
481
|
common_labels = tuple(set(subs.keys()).intersection(self.labels))
|
|
464
482
|
if len(common_labels) >= 2:
|
|
@@ -511,7 +529,7 @@ class quote(compiled_schema):
|
|
|
511
529
|
object_: object,
|
|
512
530
|
name: str = "object",
|
|
513
531
|
strict: bool = True,
|
|
514
|
-
subs:
|
|
532
|
+
subs: Mapping[str, object] = {},
|
|
515
533
|
) -> str:
|
|
516
534
|
return self.schema.__validate__(object_, name=name, strict=strict, subs=subs)
|
|
517
535
|
|
|
@@ -537,7 +555,7 @@ class _set_name(compiled_schema):
|
|
|
537
555
|
object_: object,
|
|
538
556
|
name: str = "object",
|
|
539
557
|
strict: bool = True,
|
|
540
|
-
subs:
|
|
558
|
+
subs: Mapping[str, object] = {},
|
|
541
559
|
) -> str:
|
|
542
560
|
message = self.schema.__validate__(object_, name=name, strict=strict, subs=subs)
|
|
543
561
|
if message != "":
|
|
@@ -608,7 +626,7 @@ class regex(compiled_schema):
|
|
|
608
626
|
object_: object,
|
|
609
627
|
name: str = "object",
|
|
610
628
|
strict: bool = True,
|
|
611
|
-
subs:
|
|
629
|
+
subs: Mapping[str, object] = {},
|
|
612
630
|
) -> str:
|
|
613
631
|
if not isinstance(object_, str):
|
|
614
632
|
return _wrong_type_message(object_, name, self.__name__)
|
|
@@ -647,7 +665,7 @@ class glob(compiled_schema):
|
|
|
647
665
|
object_: object,
|
|
648
666
|
name: str = "object",
|
|
649
667
|
strict: bool = True,
|
|
650
|
-
subs:
|
|
668
|
+
subs: Mapping[str, object] = {},
|
|
651
669
|
) -> str:
|
|
652
670
|
if not isinstance(object_, str):
|
|
653
671
|
return _wrong_type_message(object_, name, self.__name__)
|
|
@@ -683,7 +701,7 @@ class magic(compiled_schema):
|
|
|
683
701
|
object_: object,
|
|
684
702
|
name: str = "object",
|
|
685
703
|
strict: bool = True,
|
|
686
|
-
subs:
|
|
704
|
+
subs: Mapping[str, object] = {},
|
|
687
705
|
) -> str:
|
|
688
706
|
if not isinstance(object_, (str, bytes)):
|
|
689
707
|
return _wrong_type_message(object_, name, self.__name__)
|
|
@@ -732,7 +750,7 @@ class div(compiled_schema):
|
|
|
732
750
|
object_: object,
|
|
733
751
|
name: str = "object",
|
|
734
752
|
strict: bool = True,
|
|
735
|
-
subs:
|
|
753
|
+
subs: Mapping[str, object] = {},
|
|
736
754
|
) -> str:
|
|
737
755
|
if not isinstance(object_, int):
|
|
738
756
|
return _wrong_type_message(object_, name, "int")
|
|
@@ -779,7 +797,7 @@ class close_to(compiled_schema):
|
|
|
779
797
|
object_: object,
|
|
780
798
|
name: str = "object",
|
|
781
799
|
strict: bool = True,
|
|
782
|
-
subs:
|
|
800
|
+
subs: Mapping[str, object] = {},
|
|
783
801
|
) -> str:
|
|
784
802
|
if not isinstance(object_, (float, int)):
|
|
785
803
|
return _wrong_type_message(object_, name, "number")
|
|
@@ -809,7 +827,7 @@ class gt(compiled_schema):
|
|
|
809
827
|
object_: object,
|
|
810
828
|
name: str = "object",
|
|
811
829
|
strict: bool = True,
|
|
812
|
-
subs:
|
|
830
|
+
subs: Mapping[str, object] = {},
|
|
813
831
|
) -> str:
|
|
814
832
|
try:
|
|
815
833
|
if self.lb < object_:
|
|
@@ -840,7 +858,7 @@ class ge(compiled_schema):
|
|
|
840
858
|
object_: object,
|
|
841
859
|
name: str = "object",
|
|
842
860
|
strict: bool = True,
|
|
843
|
-
subs:
|
|
861
|
+
subs: Mapping[str, object] = {},
|
|
844
862
|
) -> str:
|
|
845
863
|
try:
|
|
846
864
|
if self.lb <= object_:
|
|
@@ -871,7 +889,7 @@ class lt(compiled_schema):
|
|
|
871
889
|
object_: object,
|
|
872
890
|
name: str = "object",
|
|
873
891
|
strict: bool = True,
|
|
874
|
-
subs:
|
|
892
|
+
subs: Mapping[str, object] = {},
|
|
875
893
|
) -> str:
|
|
876
894
|
try:
|
|
877
895
|
if self.ub > object_:
|
|
@@ -902,7 +920,7 @@ class le(compiled_schema):
|
|
|
902
920
|
object_: object,
|
|
903
921
|
name: str = "object",
|
|
904
922
|
strict: bool = True,
|
|
905
|
-
subs:
|
|
923
|
+
subs: Mapping[str, object] = {},
|
|
906
924
|
) -> str:
|
|
907
925
|
try:
|
|
908
926
|
if self.ub >= object_:
|
|
@@ -1006,7 +1024,7 @@ class size(compiled_schema):
|
|
|
1006
1024
|
object_: object,
|
|
1007
1025
|
name: str = "object",
|
|
1008
1026
|
strict: bool = True,
|
|
1009
|
-
subs:
|
|
1027
|
+
subs: Mapping[str, object] = {},
|
|
1010
1028
|
) -> str:
|
|
1011
1029
|
if not isinstance(object_, Sized):
|
|
1012
1030
|
return f"{name} (value:{_c(object_)}) has no len()"
|
|
@@ -1029,7 +1047,7 @@ class _deferred(compiled_schema):
|
|
|
1029
1047
|
object_: object,
|
|
1030
1048
|
name: str = "object",
|
|
1031
1049
|
strict: bool = True,
|
|
1032
|
-
subs:
|
|
1050
|
+
subs: Mapping[str, object] = {},
|
|
1033
1051
|
) -> str:
|
|
1034
1052
|
if self.key not in self.collection:
|
|
1035
1053
|
raise ValidationError(f"{name}: key {self.key} is unknown")
|
|
@@ -1131,12 +1149,20 @@ def _compile(
|
|
|
1131
1149
|
schema,
|
|
1132
1150
|
_deferred_compiles=_deferred_compiles,
|
|
1133
1151
|
)
|
|
1134
|
-
elif origin == list:
|
|
1135
|
-
ret = _List(typing.get_args(schema)[0], _deferred_compiles=_deferred_compiles)
|
|
1136
1152
|
elif origin == tuple:
|
|
1137
1153
|
ret = _Tuple(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
|
|
1138
|
-
elif origin
|
|
1139
|
-
ret =
|
|
1154
|
+
elif isinstance(origin, type) and issubclass(origin, Mapping):
|
|
1155
|
+
ret = _Mapping(
|
|
1156
|
+
typing.get_args(schema),
|
|
1157
|
+
type_schema=origin,
|
|
1158
|
+
_deferred_compiles=_deferred_compiles,
|
|
1159
|
+
)
|
|
1160
|
+
elif isinstance(origin, type) and issubclass(origin, Container):
|
|
1161
|
+
ret = _Container(
|
|
1162
|
+
typing.get_args(schema),
|
|
1163
|
+
type_schema=origin,
|
|
1164
|
+
_deferred_compiles=_deferred_compiles,
|
|
1165
|
+
)
|
|
1140
1166
|
elif origin == Union:
|
|
1141
1167
|
ret = _Union(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
|
|
1142
1168
|
elif supports_Literal and origin == Literal:
|
|
@@ -1149,11 +1175,11 @@ def _compile(
|
|
|
1149
1175
|
ret = _type(schema)
|
|
1150
1176
|
elif callable(schema):
|
|
1151
1177
|
ret = _callable(schema)
|
|
1152
|
-
elif isinstance(schema,
|
|
1178
|
+
elif isinstance(schema, Sequence) and not isinstance(schema, str):
|
|
1153
1179
|
ret = _sequence(schema, _deferred_compiles=_deferred_compiles)
|
|
1154
|
-
elif isinstance(schema,
|
|
1180
|
+
elif isinstance(schema, Mapping):
|
|
1155
1181
|
ret = _dict(schema, _deferred_compiles=_deferred_compiles)
|
|
1156
|
-
elif isinstance(schema,
|
|
1182
|
+
elif isinstance(schema, Set):
|
|
1157
1183
|
ret = _set(schema, _deferred_compiles=_deferred_compiles)
|
|
1158
1184
|
else:
|
|
1159
1185
|
ret = _const(schema)
|
|
@@ -1171,7 +1197,7 @@ def _validate(
|
|
|
1171
1197
|
object_: object,
|
|
1172
1198
|
name: str = "object",
|
|
1173
1199
|
strict: bool = True,
|
|
1174
|
-
subs:
|
|
1200
|
+
subs: Mapping[str, object] = {},
|
|
1175
1201
|
) -> str:
|
|
1176
1202
|
return compile(schema).__validate__(object_, name=name, strict=strict, subs=subs)
|
|
1177
1203
|
|
|
@@ -1181,7 +1207,7 @@ def validate(
|
|
|
1181
1207
|
object_: object,
|
|
1182
1208
|
name: str = "object",
|
|
1183
1209
|
strict: bool = True,
|
|
1184
|
-
subs:
|
|
1210
|
+
subs: Mapping[str, object] = {},
|
|
1185
1211
|
) -> None:
|
|
1186
1212
|
message = _validate(
|
|
1187
1213
|
schema,
|
|
@@ -1210,7 +1236,7 @@ class number(compiled_schema):
|
|
|
1210
1236
|
object_: object,
|
|
1211
1237
|
name: str = "object",
|
|
1212
1238
|
strict: bool = True,
|
|
1213
|
-
subs:
|
|
1239
|
+
subs: Mapping[str, object] = {},
|
|
1214
1240
|
) -> str:
|
|
1215
1241
|
if isinstance(object_, (int, float)):
|
|
1216
1242
|
return ""
|
|
@@ -1233,7 +1259,7 @@ class email(compiled_schema):
|
|
|
1233
1259
|
object_: object,
|
|
1234
1260
|
name: str = "object",
|
|
1235
1261
|
strict: bool = True,
|
|
1236
|
-
subs:
|
|
1262
|
+
subs: Mapping[str, object] = {},
|
|
1237
1263
|
) -> str:
|
|
1238
1264
|
if not isinstance(object_, str):
|
|
1239
1265
|
return _wrong_type_message(
|
|
@@ -1270,7 +1296,7 @@ class ip_address(compiled_schema):
|
|
|
1270
1296
|
object_: object,
|
|
1271
1297
|
name: str = "object",
|
|
1272
1298
|
strict: bool = True,
|
|
1273
|
-
subs:
|
|
1299
|
+
subs: Mapping[str, object] = {},
|
|
1274
1300
|
) -> str:
|
|
1275
1301
|
if not isinstance(object_, (int, str, bytes)):
|
|
1276
1302
|
return _wrong_type_message(object_, name, self.__name__)
|
|
@@ -1287,7 +1313,7 @@ class url(compiled_schema):
|
|
|
1287
1313
|
object_: object,
|
|
1288
1314
|
name: str = "object",
|
|
1289
1315
|
strict: bool = True,
|
|
1290
|
-
subs:
|
|
1316
|
+
subs: Mapping[str, object] = {},
|
|
1291
1317
|
) -> str:
|
|
1292
1318
|
if not isinstance(object_, str):
|
|
1293
1319
|
return _wrong_type_message(object_, name, "url")
|
|
@@ -1313,7 +1339,7 @@ class date_time(compiled_schema):
|
|
|
1313
1339
|
object_: object,
|
|
1314
1340
|
name: str = "object",
|
|
1315
1341
|
strict: bool = True,
|
|
1316
|
-
subs:
|
|
1342
|
+
subs: Mapping[str, object] = {},
|
|
1317
1343
|
) -> str:
|
|
1318
1344
|
if not isinstance(object_, str):
|
|
1319
1345
|
return _wrong_type_message(object_, name, self.__name__)
|
|
@@ -1336,7 +1362,7 @@ class date(compiled_schema):
|
|
|
1336
1362
|
object_: object,
|
|
1337
1363
|
name: str = "object",
|
|
1338
1364
|
strict: bool = True,
|
|
1339
|
-
subs:
|
|
1365
|
+
subs: Mapping[str, object] = {},
|
|
1340
1366
|
) -> str:
|
|
1341
1367
|
if not isinstance(object_, str):
|
|
1342
1368
|
return _wrong_type_message(object_, name, "date")
|
|
@@ -1353,7 +1379,7 @@ class time(compiled_schema):
|
|
|
1353
1379
|
object_: object,
|
|
1354
1380
|
name: str = "object",
|
|
1355
1381
|
strict: bool = True,
|
|
1356
|
-
subs:
|
|
1382
|
+
subs: Mapping[str, object] = {},
|
|
1357
1383
|
) -> str:
|
|
1358
1384
|
if not isinstance(object_, str):
|
|
1359
1385
|
return _wrong_type_message(object_, name, "date")
|
|
@@ -1370,7 +1396,7 @@ class nothing(compiled_schema):
|
|
|
1370
1396
|
object_: object,
|
|
1371
1397
|
name: str = "object",
|
|
1372
1398
|
strict: bool = True,
|
|
1373
|
-
subs:
|
|
1399
|
+
subs: Mapping[str, object] = {},
|
|
1374
1400
|
) -> str:
|
|
1375
1401
|
return _wrong_type_message(object_, name, "nothing")
|
|
1376
1402
|
|
|
@@ -1381,7 +1407,7 @@ class anything(compiled_schema):
|
|
|
1381
1407
|
object_: object,
|
|
1382
1408
|
name: str = "object",
|
|
1383
1409
|
strict: bool = True,
|
|
1384
|
-
subs:
|
|
1410
|
+
subs: Mapping[str, object] = {},
|
|
1385
1411
|
) -> str:
|
|
1386
1412
|
return ""
|
|
1387
1413
|
|
|
@@ -1412,7 +1438,7 @@ class domain_name(compiled_schema):
|
|
|
1412
1438
|
object_: object,
|
|
1413
1439
|
name: str = "object",
|
|
1414
1440
|
strict: bool = True,
|
|
1415
|
-
subs:
|
|
1441
|
+
subs: Mapping[str, object] = {},
|
|
1416
1442
|
) -> str:
|
|
1417
1443
|
if not isinstance(object_, str):
|
|
1418
1444
|
return _wrong_type_message(object_, name, self.__name__)
|
|
@@ -1448,9 +1474,9 @@ class at_least_one_of(compiled_schema):
|
|
|
1448
1474
|
object_: object,
|
|
1449
1475
|
name: str = "object",
|
|
1450
1476
|
strict: bool = True,
|
|
1451
|
-
subs:
|
|
1477
|
+
subs: Mapping[str, object] = {},
|
|
1452
1478
|
) -> str:
|
|
1453
|
-
if not isinstance(object_,
|
|
1479
|
+
if not isinstance(object_, Mapping):
|
|
1454
1480
|
return _wrong_type_message(object_, name, self.__name__)
|
|
1455
1481
|
try:
|
|
1456
1482
|
if any([a in object_ for a in self.args]):
|
|
@@ -1475,9 +1501,9 @@ class at_most_one_of(compiled_schema):
|
|
|
1475
1501
|
object_: object,
|
|
1476
1502
|
name: str = "object",
|
|
1477
1503
|
strict: bool = True,
|
|
1478
|
-
subs:
|
|
1504
|
+
subs: Mapping[str, object] = {},
|
|
1479
1505
|
) -> str:
|
|
1480
|
-
if not isinstance(object_,
|
|
1506
|
+
if not isinstance(object_, Mapping):
|
|
1481
1507
|
return _wrong_type_message(object_, name, self.__name__)
|
|
1482
1508
|
try:
|
|
1483
1509
|
if sum([a in object_ for a in self.args]) <= 1:
|
|
@@ -1502,9 +1528,9 @@ class one_of(compiled_schema):
|
|
|
1502
1528
|
object_: object,
|
|
1503
1529
|
name: str = "object",
|
|
1504
1530
|
strict: bool = True,
|
|
1505
|
-
subs:
|
|
1531
|
+
subs: Mapping[str, object] = {},
|
|
1506
1532
|
) -> str:
|
|
1507
|
-
if not isinstance(object_,
|
|
1533
|
+
if not isinstance(object_, Mapping):
|
|
1508
1534
|
return _wrong_type_message(object_, name, self.__name__)
|
|
1509
1535
|
try:
|
|
1510
1536
|
if sum([a in object_ for a in self.args]) == 1:
|
|
@@ -1526,10 +1552,10 @@ class keys(compiled_schema):
|
|
|
1526
1552
|
object_: object,
|
|
1527
1553
|
name: str = "object",
|
|
1528
1554
|
strict: bool = True,
|
|
1529
|
-
subs:
|
|
1555
|
+
subs: Mapping[str, object] = {},
|
|
1530
1556
|
) -> str:
|
|
1531
|
-
if not isinstance(object_,
|
|
1532
|
-
return _wrong_type_message(object_, name, "
|
|
1557
|
+
if not isinstance(object_, Mapping):
|
|
1558
|
+
return _wrong_type_message(object_, name, "Mapping") # TODO: __name__
|
|
1533
1559
|
for k in self.args:
|
|
1534
1560
|
if k not in object_:
|
|
1535
1561
|
return f"{name}[{repr(k)}] is missing"
|
|
@@ -1562,7 +1588,7 @@ class _ifthen(compiled_schema):
|
|
|
1562
1588
|
object_: object,
|
|
1563
1589
|
name: str = "object",
|
|
1564
1590
|
strict: bool = True,
|
|
1565
|
-
subs:
|
|
1591
|
+
subs: Mapping[str, object] = {},
|
|
1566
1592
|
) -> str:
|
|
1567
1593
|
if (
|
|
1568
1594
|
self.if_schema.__validate__(object_, name=name, strict=strict, subs=subs)
|
|
@@ -1624,7 +1650,7 @@ class _cond(compiled_schema):
|
|
|
1624
1650
|
object_: object,
|
|
1625
1651
|
name: str = "object",
|
|
1626
1652
|
strict: bool = True,
|
|
1627
|
-
subs:
|
|
1653
|
+
subs: Mapping[str, object] = {},
|
|
1628
1654
|
) -> str:
|
|
1629
1655
|
for c in self.conditions:
|
|
1630
1656
|
if c[0].__validate__(object_, name=name, strict=strict, subs=subs) == "":
|
|
@@ -1649,7 +1675,7 @@ class _fields(compiled_schema):
|
|
|
1649
1675
|
d: dict[str, compiled_schema]
|
|
1650
1676
|
|
|
1651
1677
|
def __init__(
|
|
1652
|
-
self, d:
|
|
1678
|
+
self, d: Mapping[str, object], _deferred_compiles: _mapping | None = None
|
|
1653
1679
|
) -> None:
|
|
1654
1680
|
self.d = {}
|
|
1655
1681
|
for k, v in d.items():
|
|
@@ -1660,7 +1686,7 @@ class _fields(compiled_schema):
|
|
|
1660
1686
|
object_: object,
|
|
1661
1687
|
name: str = "object",
|
|
1662
1688
|
strict: bool = True,
|
|
1663
|
-
subs:
|
|
1689
|
+
subs: Mapping[str, object] = {},
|
|
1664
1690
|
) -> str:
|
|
1665
1691
|
for k, v in self.d.items():
|
|
1666
1692
|
name_ = f"{name}.{k}"
|
|
@@ -1676,8 +1702,8 @@ class _fields(compiled_schema):
|
|
|
1676
1702
|
|
|
1677
1703
|
class fields:
|
|
1678
1704
|
def __init__(self, d: object) -> None:
|
|
1679
|
-
if not isinstance(d,
|
|
1680
|
-
raise SchemaError(f"{repr(d)} is not a
|
|
1705
|
+
if not isinstance(d, Mapping):
|
|
1706
|
+
raise SchemaError(f"{repr(d)} is not a Mapping")
|
|
1681
1707
|
for k in d:
|
|
1682
1708
|
if not isinstance(k, str):
|
|
1683
1709
|
raise SchemaError(f"key {repr(k)} in {repr(d)} is not a string")
|
|
@@ -1716,7 +1742,7 @@ class _filter(compiled_schema):
|
|
|
1716
1742
|
object_: object,
|
|
1717
1743
|
name: str = "object",
|
|
1718
1744
|
strict: bool = True,
|
|
1719
|
-
subs:
|
|
1745
|
+
subs: Mapping[str, object] = {},
|
|
1720
1746
|
) -> str:
|
|
1721
1747
|
try:
|
|
1722
1748
|
object_ = self.filter(object_)
|
|
@@ -1775,7 +1801,7 @@ class _type(compiled_schema):
|
|
|
1775
1801
|
object_: object,
|
|
1776
1802
|
name: str = "object",
|
|
1777
1803
|
strict: bool = True,
|
|
1778
|
-
subs:
|
|
1804
|
+
subs: Mapping[str, object] = {},
|
|
1779
1805
|
) -> str:
|
|
1780
1806
|
try:
|
|
1781
1807
|
if self.schema == float and isinstance(object_, int):
|
|
@@ -1792,7 +1818,7 @@ class _type(compiled_schema):
|
|
|
1792
1818
|
object_: object,
|
|
1793
1819
|
name: str = "object",
|
|
1794
1820
|
strict: bool = True,
|
|
1795
|
-
subs:
|
|
1821
|
+
subs: Mapping[str, object] = {},
|
|
1796
1822
|
) -> str:
|
|
1797
1823
|
# consider int as a subtype of float
|
|
1798
1824
|
if isinstance(object_, (int, float)):
|
|
@@ -1805,7 +1831,7 @@ class _type(compiled_schema):
|
|
|
1805
1831
|
object_: object,
|
|
1806
1832
|
name: str = "object",
|
|
1807
1833
|
strict: bool = True,
|
|
1808
|
-
subs:
|
|
1834
|
+
subs: Mapping[str, object] = {},
|
|
1809
1835
|
) -> str:
|
|
1810
1836
|
# consider int, float as subtypes of complex
|
|
1811
1837
|
if isinstance(object_, (int, float, complex)):
|
|
@@ -1824,7 +1850,8 @@ class _sequence(compiled_schema):
|
|
|
1824
1850
|
|
|
1825
1851
|
def __init__(
|
|
1826
1852
|
self,
|
|
1827
|
-
schema:
|
|
1853
|
+
schema: Sequence[object],
|
|
1854
|
+
# type_schema: type | None = None,
|
|
1828
1855
|
_deferred_compiles: _mapping | None = None,
|
|
1829
1856
|
) -> None:
|
|
1830
1857
|
self.type_schema = type(schema)
|
|
@@ -1847,10 +1874,10 @@ class _sequence(compiled_schema):
|
|
|
1847
1874
|
object_: object,
|
|
1848
1875
|
name: str = "object",
|
|
1849
1876
|
strict: bool = True,
|
|
1850
|
-
subs:
|
|
1877
|
+
subs: Mapping[str, object] = {},
|
|
1851
1878
|
) -> str:
|
|
1852
1879
|
if not isinstance(object_, self.type_schema):
|
|
1853
|
-
return _wrong_type_message(object_, name,
|
|
1880
|
+
return _wrong_type_message(object_, name, self.type_schema.__name__)
|
|
1854
1881
|
ls = len(self.schema)
|
|
1855
1882
|
lo = len(object_)
|
|
1856
1883
|
if strict:
|
|
@@ -1870,10 +1897,10 @@ class _sequence(compiled_schema):
|
|
|
1870
1897
|
object_: object,
|
|
1871
1898
|
name: str = "object",
|
|
1872
1899
|
strict: bool = True,
|
|
1873
|
-
subs:
|
|
1900
|
+
subs: Mapping[str, object] = {},
|
|
1874
1901
|
) -> str:
|
|
1875
1902
|
if not isinstance(object_, self.type_schema):
|
|
1876
|
-
return _wrong_type_message(object_, name,
|
|
1903
|
+
return _wrong_type_message(object_, name, self.type_schema.__name__)
|
|
1877
1904
|
ls = len(self.schema)
|
|
1878
1905
|
lo = len(object_)
|
|
1879
1906
|
if ls > lo:
|
|
@@ -1910,7 +1937,7 @@ class _const(compiled_schema):
|
|
|
1910
1937
|
object_: object,
|
|
1911
1938
|
name: str = "object",
|
|
1912
1939
|
strict: bool = True,
|
|
1913
|
-
subs:
|
|
1940
|
+
subs: Mapping[str, object] = {},
|
|
1914
1941
|
) -> str:
|
|
1915
1942
|
if object_ != self.schema:
|
|
1916
1943
|
return self.message(name, object_)
|
|
@@ -1936,7 +1963,7 @@ class _callable(compiled_schema):
|
|
|
1936
1963
|
object_: object,
|
|
1937
1964
|
name: str = "object",
|
|
1938
1965
|
strict: bool = True,
|
|
1939
|
-
subs:
|
|
1966
|
+
subs: Mapping[str, object] = {},
|
|
1940
1967
|
) -> str:
|
|
1941
1968
|
try:
|
|
1942
1969
|
if self.schema(object_):
|
|
@@ -1955,12 +1982,14 @@ class _dict(compiled_schema):
|
|
|
1955
1982
|
const_keys: set[object]
|
|
1956
1983
|
other_keys: set[compiled_schema]
|
|
1957
1984
|
schema: dict[object, compiled_schema]
|
|
1985
|
+
type_schema: Type[Mapping[object, object]]
|
|
1958
1986
|
|
|
1959
1987
|
def __init__(
|
|
1960
1988
|
self,
|
|
1961
|
-
schema:
|
|
1989
|
+
schema: Mapping[object, object],
|
|
1962
1990
|
_deferred_compiles: _mapping | None = None,
|
|
1963
1991
|
) -> None:
|
|
1992
|
+
self.type_schema = type(schema)
|
|
1964
1993
|
self.min_keys = set()
|
|
1965
1994
|
self.const_keys = set()
|
|
1966
1995
|
self.other_keys = set()
|
|
@@ -1990,10 +2019,10 @@ class _dict(compiled_schema):
|
|
|
1990
2019
|
object_: object,
|
|
1991
2020
|
name: str = "object",
|
|
1992
2021
|
strict: bool = True,
|
|
1993
|
-
subs:
|
|
2022
|
+
subs: Mapping[str, object] = {},
|
|
1994
2023
|
) -> str:
|
|
1995
|
-
if not isinstance(object_,
|
|
1996
|
-
return _wrong_type_message(object_, name,
|
|
2024
|
+
if not isinstance(object_, self.type_schema):
|
|
2025
|
+
return _wrong_type_message(object_, name, self.type_schema.__name__)
|
|
1997
2026
|
|
|
1998
2027
|
for k in self.min_keys:
|
|
1999
2028
|
if k not in object_:
|
|
@@ -2033,15 +2062,18 @@ class _dict(compiled_schema):
|
|
|
2033
2062
|
|
|
2034
2063
|
|
|
2035
2064
|
class _set(compiled_schema):
|
|
2065
|
+
type_schema: Type[Set[object]]
|
|
2036
2066
|
schema: compiled_schema
|
|
2037
|
-
schema_:
|
|
2067
|
+
schema_: Set[object]
|
|
2038
2068
|
|
|
2039
2069
|
def __init__(
|
|
2040
|
-
self,
|
|
2070
|
+
self,
|
|
2071
|
+
schema: Set[object],
|
|
2072
|
+
_deferred_compiles: _mapping | None = None,
|
|
2041
2073
|
) -> None:
|
|
2074
|
+
self.type_schema = type(schema)
|
|
2042
2075
|
self.schema_ = schema
|
|
2043
2076
|
if len(schema) == 0:
|
|
2044
|
-
self.schema = _const(set())
|
|
2045
2077
|
setattr(self, "__validate__", self.__validate_empty_set__)
|
|
2046
2078
|
elif len(schema) == 1:
|
|
2047
2079
|
self.schema = _compile(
|
|
@@ -2056,19 +2088,23 @@ class _set(compiled_schema):
|
|
|
2056
2088
|
object_: object,
|
|
2057
2089
|
name: str = "object",
|
|
2058
2090
|
strict: bool = True,
|
|
2059
|
-
subs:
|
|
2091
|
+
subs: Mapping[str, object] = {},
|
|
2060
2092
|
) -> str:
|
|
2061
|
-
|
|
2093
|
+
if not isinstance(object_, self.type_schema):
|
|
2094
|
+
return _wrong_type_message(object_, name, self.type_schema.__name__)
|
|
2095
|
+
if len(object_) != 0:
|
|
2096
|
+
return f"{name} (value:{_c(object_)}) is not empty"
|
|
2097
|
+
return ""
|
|
2062
2098
|
|
|
2063
2099
|
def __validate_singleton__(
|
|
2064
2100
|
self,
|
|
2065
2101
|
object_: object,
|
|
2066
2102
|
name: str = "object",
|
|
2067
2103
|
strict: bool = True,
|
|
2068
|
-
subs:
|
|
2104
|
+
subs: Mapping[str, object] = {},
|
|
2069
2105
|
) -> str:
|
|
2070
2106
|
if not isinstance(object_, set):
|
|
2071
|
-
return _wrong_type_message(object_, name,
|
|
2107
|
+
return _wrong_type_message(object_, name, self.type_schema.__name__)
|
|
2072
2108
|
for i, o in enumerate(object_):
|
|
2073
2109
|
name_ = f"{name}{{{i}}}"
|
|
2074
2110
|
v = self.schema.__validate__(o, name=name_, strict=True, subs=subs)
|
|
@@ -2081,10 +2117,10 @@ class _set(compiled_schema):
|
|
|
2081
2117
|
object_: object,
|
|
2082
2118
|
name: str = "object",
|
|
2083
2119
|
strict: bool = True,
|
|
2084
|
-
subs:
|
|
2120
|
+
subs: Mapping[str, object] = {},
|
|
2085
2121
|
) -> str:
|
|
2086
|
-
if not isinstance(object_,
|
|
2087
|
-
return _wrong_type_message(object_, name,
|
|
2122
|
+
if not isinstance(object_, self.type_schema):
|
|
2123
|
+
return _wrong_type_message(object_, name, self.type_schema.__name__)
|
|
2088
2124
|
for i, o in enumerate(object_):
|
|
2089
2125
|
name_ = f"{name}{{{i}}}"
|
|
2090
2126
|
v = self.schema.__validate__(o, name=name_, strict=True, subs=subs)
|
|
@@ -2119,9 +2155,8 @@ class protocol:
|
|
|
2119
2155
|
self, _deferred_compiles: _mapping | None = None
|
|
2120
2156
|
) -> compiled_schema:
|
|
2121
2157
|
if not self.dict:
|
|
2122
|
-
type_dict_ = cast(dict[str, object], self.type_dict)
|
|
2123
2158
|
return _set_name(
|
|
2124
|
-
fields(
|
|
2159
|
+
fields(self.type_dict),
|
|
2125
2160
|
self.__name__,
|
|
2126
2161
|
reason=True,
|
|
2127
2162
|
_deferred_compiles=_deferred_compiles,
|
|
@@ -2157,19 +2192,6 @@ class _Union(compiled_schema):
|
|
|
2157
2192
|
)
|
|
2158
2193
|
|
|
2159
2194
|
|
|
2160
|
-
class _List(compiled_schema):
|
|
2161
|
-
def __init__(
|
|
2162
|
-
self, schema: object, _deferred_compiles: _mapping | None = None
|
|
2163
|
-
) -> None:
|
|
2164
|
-
setattr(
|
|
2165
|
-
self,
|
|
2166
|
-
"__validate__",
|
|
2167
|
-
_sequence(
|
|
2168
|
-
[schema, ...], _deferred_compiles=_deferred_compiles
|
|
2169
|
-
).__validate__,
|
|
2170
|
-
)
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
2195
|
class _Tuple(compiled_schema):
|
|
2174
2196
|
def __init__(
|
|
2175
2197
|
self, schema: tuple[object, ...], _deferred_compiles: _mapping | None = None
|
|
@@ -2181,16 +2203,89 @@ class _Tuple(compiled_schema):
|
|
|
2181
2203
|
)
|
|
2182
2204
|
|
|
2183
2205
|
|
|
2184
|
-
class
|
|
2206
|
+
class _Mapping(compiled_schema):
|
|
2207
|
+
type_schema: Type[Mapping[object, object]]
|
|
2208
|
+
key: compiled_schema
|
|
2209
|
+
value: compiled_schema
|
|
2210
|
+
__name__: str
|
|
2211
|
+
|
|
2185
2212
|
def __init__(
|
|
2186
|
-
self,
|
|
2213
|
+
self,
|
|
2214
|
+
schema: tuple[object, ...],
|
|
2215
|
+
type_schema: type,
|
|
2216
|
+
_deferred_compiles: _mapping | None = None,
|
|
2187
2217
|
) -> None:
|
|
2218
|
+
if len(schema) != 2:
|
|
2219
|
+
raise SchemaError("Number of arguments of mapping is not two")
|
|
2188
2220
|
k, v = schema
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2221
|
+
self.key = _compile(k, _deferred_compiles=_deferred_compiles)
|
|
2222
|
+
self.value = _compile(v, _deferred_compiles=_deferred_compiles)
|
|
2223
|
+
self.type_schema = type_schema
|
|
2224
|
+
self.__name__ = _generic_name(type_schema, schema)
|
|
2225
|
+
|
|
2226
|
+
def __validate__(
|
|
2227
|
+
self,
|
|
2228
|
+
object_: object,
|
|
2229
|
+
name: str = "object",
|
|
2230
|
+
strict: bool = True,
|
|
2231
|
+
subs: Mapping[str, object] = {},
|
|
2232
|
+
) -> str:
|
|
2233
|
+
|
|
2234
|
+
if not isinstance(object_, self.type_schema):
|
|
2235
|
+
return _wrong_type_message(object_, name, self.__name__)
|
|
2236
|
+
|
|
2237
|
+
for k, v in object_.items():
|
|
2238
|
+
_name = f"{name}[{repr(k)}]"
|
|
2239
|
+
message = self.key.__validate__(k, name=str(k), strict=strict, subs=subs)
|
|
2240
|
+
if message != "":
|
|
2241
|
+
return f"{_name} is not in the schema"
|
|
2242
|
+
message = self.value.__validate__(v, name=_name, strict=strict, subs=subs)
|
|
2243
|
+
if message != "":
|
|
2244
|
+
return message
|
|
2245
|
+
|
|
2246
|
+
return ""
|
|
2247
|
+
|
|
2248
|
+
|
|
2249
|
+
class _Container(compiled_schema):
|
|
2250
|
+
type_schema: Type[Mapping[object, object]]
|
|
2251
|
+
schema: compiled_schema
|
|
2252
|
+
__name__: str
|
|
2253
|
+
|
|
2254
|
+
def __init__(
|
|
2255
|
+
self,
|
|
2256
|
+
schema: tuple[object, ...],
|
|
2257
|
+
type_schema: type,
|
|
2258
|
+
_deferred_compiles: _mapping | None = None,
|
|
2259
|
+
) -> None:
|
|
2260
|
+
if len(schema) != 1:
|
|
2261
|
+
raise SchemaError("Number of arguments of Generic type is not one")
|
|
2262
|
+
self.schema = _compile(schema[0], _deferred_compiles=_deferred_compiles)
|
|
2263
|
+
self.type_schema = type_schema
|
|
2264
|
+
self.__name__ = _generic_name(type_schema, schema)
|
|
2265
|
+
|
|
2266
|
+
def __validate__(
|
|
2267
|
+
self,
|
|
2268
|
+
object_: object,
|
|
2269
|
+
name: str = "object",
|
|
2270
|
+
strict: bool = True,
|
|
2271
|
+
subs: Mapping[str, object] = {},
|
|
2272
|
+
) -> str:
|
|
2273
|
+
|
|
2274
|
+
if not isinstance(object_, self.type_schema):
|
|
2275
|
+
return _wrong_type_message(object_, name, self.type_schema.__name__)
|
|
2276
|
+
|
|
2277
|
+
try:
|
|
2278
|
+
for i, o in enumerate(object_):
|
|
2279
|
+
_name = f"{name}[{i}]"
|
|
2280
|
+
message = self.schema.__validate__(
|
|
2281
|
+
o, name=_name, strict=strict, subs=subs
|
|
2282
|
+
)
|
|
2283
|
+
if message != "":
|
|
2284
|
+
return message
|
|
2285
|
+
except Exception as e:
|
|
2286
|
+
return _wrong_type_message(object_, name, self.__name__, explanation=str(e))
|
|
2287
|
+
|
|
2288
|
+
return ""
|
|
2194
2289
|
|
|
2195
2290
|
|
|
2196
2291
|
class _NewType(compiled_schema):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vtjson
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.6
|
|
4
4
|
Summary: A lightweight package for validating JSON like Python objects
|
|
5
5
|
Author-email: Michel Van den Bergh <michel.vandenbergh@uhasselt.be>
|
|
6
6
|
Project-URL: Homepage, https://github.com/vdbergh/vtjson
|
|
@@ -163,16 +163,16 @@ A schema can be, in order of precedence:
|
|
|
163
163
|
- A Python type hint such as `list[str]`. This is discussed further below.
|
|
164
164
|
- A Python type. In that case validation is done by checking membership. By convention the schema `float` matches both ints and floats. Similarly the schema `complex` matches ints and floats besides of course complex numbers.
|
|
165
165
|
- A callable. Validation is done by applying the callable to the object. If applying the callable throws an exception then the corresponding message will be part of the non-validation message.
|
|
166
|
-
-
|
|
167
|
-
-
|
|
166
|
+
- An instance of `Sequence` that is not an instance of `str` (e.g a `list` or a `tuple`). Validation is done by first checking membership of the schema type, and then performing validation for each of the entries of the object being validated against the corresponding entries of the schema.
|
|
167
|
+
- An instance of `Mapping`. Validation is done by first checking membership of the schema type, and then performing validation for each of the values of the object being validated against the corresponding values of the schema. Keys are themselves considered as schemas. E.g. `{str: str}` represents a dictionary whose keys and values are both strings. A more elaborate discussion of validation of dictionaries is given below.
|
|
168
168
|
- A `set`. A set validates an object if the object is a set and the elements of the object are validated by an element of the schema.
|
|
169
169
|
- An arbitrary Python object. Validation is done by checking equality of the schema and the object, except when the schema is `float`, in which case `math.isclose` is used. Below we call such an object a `const schema`.
|
|
170
170
|
|
|
171
|
-
## Validating
|
|
171
|
+
## Validating Mapping
|
|
172
172
|
|
|
173
|
-
For a
|
|
173
|
+
For a Mapping schema containing only `const keys` (i.e. keys corresponding to a `const schema`) the interpretation is obvious (see the introductory example above). Below we discuss the validation of an object against a Mapping schema in the general case.
|
|
174
174
|
|
|
175
|
-
- First we verify that the object is
|
|
175
|
+
- First we verify that the type of the object is a subtype of the type of the schema. If not then validation fails.
|
|
176
176
|
- We verify that all non-optional const keys of the schema are also keys of the object. If this is not the case then validation fails.
|
|
177
177
|
- Now we make a list of all the keys of the schema (both optional and non-optional). The result will be called the `key list` below.
|
|
178
178
|
- The object will pass validation if all its keys pass validation. We next discuss how to validate a particular key of the object.
|
|
@@ -4,8 +4,21 @@ import json
|
|
|
4
4
|
import re
|
|
5
5
|
import sys
|
|
6
6
|
import unittest
|
|
7
|
+
from collections.abc import Mapping, Sequence
|
|
7
8
|
from datetime import datetime, timezone
|
|
8
|
-
from typing import
|
|
9
|
+
from typing import (
|
|
10
|
+
Any,
|
|
11
|
+
Container,
|
|
12
|
+
Dict,
|
|
13
|
+
Generator,
|
|
14
|
+
List,
|
|
15
|
+
NamedTuple,
|
|
16
|
+
NewType,
|
|
17
|
+
Tuple,
|
|
18
|
+
TypeVar,
|
|
19
|
+
Union,
|
|
20
|
+
overload,
|
|
21
|
+
)
|
|
9
22
|
from urllib.parse import urlparse
|
|
10
23
|
|
|
11
24
|
import vtjson
|
|
@@ -1066,6 +1079,200 @@ class TestValidation(unittest.TestCase):
|
|
|
1066
1079
|
validate(schema, object_)
|
|
1067
1080
|
show(mc)
|
|
1068
1081
|
|
|
1082
|
+
@unittest.skipUnless(
|
|
1083
|
+
vtjson.supports_Generic_ABC,
|
|
1084
|
+
"Generic base classes were introduced in Pythin 3.9",
|
|
1085
|
+
)
|
|
1086
|
+
def test_Container(self) -> None:
|
|
1087
|
+
|
|
1088
|
+
T = TypeVar("T")
|
|
1089
|
+
|
|
1090
|
+
class dumb(Container): # type: ignore
|
|
1091
|
+
def __contains__(a): # type: ignore
|
|
1092
|
+
return True
|
|
1093
|
+
|
|
1094
|
+
def __str__(b): # type: ignore
|
|
1095
|
+
return "dumb Container"
|
|
1096
|
+
|
|
1097
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1098
|
+
validate(Container[str], dumb())
|
|
1099
|
+
show(mc)
|
|
1100
|
+
|
|
1101
|
+
if vtjson.supports_UnionType:
|
|
1102
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1103
|
+
validate(Container[int | str], dumb())
|
|
1104
|
+
show(mc)
|
|
1105
|
+
|
|
1106
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1107
|
+
validate(Container[Union[int, str]], dumb())
|
|
1108
|
+
show(mc)
|
|
1109
|
+
|
|
1110
|
+
class dummy(Sequence[T]):
|
|
1111
|
+
L: Sequence[T]
|
|
1112
|
+
|
|
1113
|
+
def __init__(self, L: Sequence[T] = ()) -> None:
|
|
1114
|
+
self.L = L
|
|
1115
|
+
|
|
1116
|
+
@overload
|
|
1117
|
+
def __getitem__(self, index: int) -> T: ...
|
|
1118
|
+
|
|
1119
|
+
@overload
|
|
1120
|
+
def __getitem__(self, index: slice) -> dummy[T]: ...
|
|
1121
|
+
|
|
1122
|
+
def __getitem__(self, index: int | slice) -> T | dummy[T]:
|
|
1123
|
+
if isinstance(index, int):
|
|
1124
|
+
return self.L[index]
|
|
1125
|
+
return dummy(self.L[index])
|
|
1126
|
+
|
|
1127
|
+
def __len__(self) -> int:
|
|
1128
|
+
return len(self.L)
|
|
1129
|
+
|
|
1130
|
+
class dummy_ex(dummy[T]):
|
|
1131
|
+
pass
|
|
1132
|
+
|
|
1133
|
+
schema: object
|
|
1134
|
+
object_: object
|
|
1135
|
+
|
|
1136
|
+
object_ = dummy((1, 2))
|
|
1137
|
+
schema = dummy((1, 2))
|
|
1138
|
+
validate(schema, object_)
|
|
1139
|
+
|
|
1140
|
+
schema = dummy([1, 2])
|
|
1141
|
+
validate(schema, object_)
|
|
1142
|
+
|
|
1143
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1144
|
+
object_ = [1, 2]
|
|
1145
|
+
validate(schema, object_)
|
|
1146
|
+
show(mc)
|
|
1147
|
+
|
|
1148
|
+
validate(schema, dummy_ex([1, 2]))
|
|
1149
|
+
|
|
1150
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1151
|
+
object_ = dummy(["a", "b"])
|
|
1152
|
+
validate(schema, object_)
|
|
1153
|
+
show(mc)
|
|
1154
|
+
|
|
1155
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1156
|
+
object_ = dummy(["a", "b", None, "c"])
|
|
1157
|
+
validate(schema, object_)
|
|
1158
|
+
show(mc)
|
|
1159
|
+
|
|
1160
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1161
|
+
schema = dummy(["a", int, ...])
|
|
1162
|
+
object_ = dummy(["a", "c", 1])
|
|
1163
|
+
validate(schema, object_)
|
|
1164
|
+
show(mc)
|
|
1165
|
+
|
|
1166
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1167
|
+
schema = dummy_ex((1, 2))
|
|
1168
|
+
object_ = dummy((1, 2))
|
|
1169
|
+
validate(schema, object_)
|
|
1170
|
+
show(mc)
|
|
1171
|
+
self.assertTrue("dummy_ex" in str(mc.exception))
|
|
1172
|
+
|
|
1173
|
+
schema = dummy[str]
|
|
1174
|
+
object_ = dummy(("a", "b"))
|
|
1175
|
+
validate(schema, object_)
|
|
1176
|
+
|
|
1177
|
+
object_ = dummy_ex(("a", "b"))
|
|
1178
|
+
validate(schema, object_)
|
|
1179
|
+
|
|
1180
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1181
|
+
object_ = dummy((1, 2))
|
|
1182
|
+
validate(schema, object_)
|
|
1183
|
+
show(mc)
|
|
1184
|
+
|
|
1185
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1186
|
+
schema = dummy_ex[str]
|
|
1187
|
+
object_ = dummy(("a", "b"))
|
|
1188
|
+
validate(schema, object_)
|
|
1189
|
+
show(mc)
|
|
1190
|
+
self.assertTrue("dummy_ex" in str(mc.exception))
|
|
1191
|
+
|
|
1192
|
+
@unittest.skipUnless(
|
|
1193
|
+
vtjson.supports_Generic_ABC,
|
|
1194
|
+
"Generic base classes were introduced in Pythin 3.9",
|
|
1195
|
+
)
|
|
1196
|
+
def test_Mapping(self) -> None:
|
|
1197
|
+
|
|
1198
|
+
S = TypeVar("S")
|
|
1199
|
+
T = TypeVar("T")
|
|
1200
|
+
|
|
1201
|
+
class dummy(Mapping[S, T]):
|
|
1202
|
+
L: Mapping[S, T]
|
|
1203
|
+
|
|
1204
|
+
def __init__(self, L: Mapping[S, T] = {}) -> None:
|
|
1205
|
+
self.L = L
|
|
1206
|
+
|
|
1207
|
+
def __getitem__(self, key: S) -> T:
|
|
1208
|
+
return self.L[key]
|
|
1209
|
+
|
|
1210
|
+
def __iter__(self) -> Generator[S]:
|
|
1211
|
+
for key in self.L:
|
|
1212
|
+
yield key
|
|
1213
|
+
|
|
1214
|
+
def __len__(self) -> int:
|
|
1215
|
+
return len(self.L)
|
|
1216
|
+
|
|
1217
|
+
class dummy_ex(dummy[S, T]):
|
|
1218
|
+
pass
|
|
1219
|
+
|
|
1220
|
+
schema: object
|
|
1221
|
+
object_: object
|
|
1222
|
+
|
|
1223
|
+
schema = dummy({1: 2})
|
|
1224
|
+
object_ = dummy({1: 2})
|
|
1225
|
+
validate(schema, object_)
|
|
1226
|
+
|
|
1227
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1228
|
+
object_ = [1, 2]
|
|
1229
|
+
validate(schema, object_)
|
|
1230
|
+
show(mc)
|
|
1231
|
+
|
|
1232
|
+
validate(schema, dummy_ex({1: 2}))
|
|
1233
|
+
|
|
1234
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1235
|
+
object_ = dummy(["a", "b"]) # type: ignore
|
|
1236
|
+
validate(schema, object_)
|
|
1237
|
+
show(mc)
|
|
1238
|
+
|
|
1239
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1240
|
+
object_ = dummy({1: "b"})
|
|
1241
|
+
validate(schema, object_)
|
|
1242
|
+
show(mc)
|
|
1243
|
+
|
|
1244
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1245
|
+
schema = dummy({"a": int})
|
|
1246
|
+
object_ = dummy({"a": "c"})
|
|
1247
|
+
validate(schema, object_)
|
|
1248
|
+
show(mc)
|
|
1249
|
+
|
|
1250
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1251
|
+
schema = dummy_ex({1: 2})
|
|
1252
|
+
object_ = dummy({1: 2})
|
|
1253
|
+
validate(schema, object_)
|
|
1254
|
+
show(mc)
|
|
1255
|
+
self.assertTrue("dummy_ex" in str(mc.exception))
|
|
1256
|
+
|
|
1257
|
+
schema = dummy[str, str]
|
|
1258
|
+
object_ = dummy({"a": "b"})
|
|
1259
|
+
validate(schema, object_)
|
|
1260
|
+
|
|
1261
|
+
object_ = dummy_ex({"a": "b"})
|
|
1262
|
+
validate(schema, object_)
|
|
1263
|
+
|
|
1264
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1265
|
+
object_ = dummy({1: 2})
|
|
1266
|
+
validate(schema, object_)
|
|
1267
|
+
show(mc)
|
|
1268
|
+
|
|
1269
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1270
|
+
schema = dummy_ex[str, str]
|
|
1271
|
+
object_ = dummy({"a": "b"})
|
|
1272
|
+
validate(schema, object_)
|
|
1273
|
+
show(mc)
|
|
1274
|
+
self.assertTrue("dummy_ex" in str(mc.exception))
|
|
1275
|
+
|
|
1069
1276
|
def test_validate(self) -> None:
|
|
1070
1277
|
schema: object
|
|
1071
1278
|
object_: object
|
|
@@ -1114,7 +1321,7 @@ class TestValidation(unittest.TestCase):
|
|
|
1114
1321
|
object_: object,
|
|
1115
1322
|
name: str,
|
|
1116
1323
|
strict: bool,
|
|
1117
|
-
subs:
|
|
1324
|
+
subs: Mapping[str, object],
|
|
1118
1325
|
) -> str:
|
|
1119
1326
|
if not isinstance(object_, str):
|
|
1120
1327
|
return f"{name} (value:{object_}) is not of type str"
|
|
@@ -1788,6 +1995,11 @@ class TestValidation(unittest.TestCase):
|
|
|
1788
1995
|
"Parametrized types were introduced in Python 3.9",
|
|
1789
1996
|
)
|
|
1790
1997
|
def test_generic_list(self) -> None:
|
|
1998
|
+
schema: object
|
|
1999
|
+
with self.assertRaises(SchemaError) as mc_:
|
|
2000
|
+
schema = list[str, str] # type: ignore
|
|
2001
|
+
validate(schema, "")
|
|
2002
|
+
show(mc_)
|
|
1791
2003
|
schema = list[str]
|
|
1792
2004
|
validate(schema, ["a", "b"])
|
|
1793
2005
|
with self.assertRaises(ValidationError) as mc:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|