vtjson 2.2.6__tar.gz → 2.2.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.
- {vtjson-2.2.6/src/vtjson.egg-info → vtjson-2.2.7}/PKG-INFO +1 -1
- {vtjson-2.2.6 → vtjson-2.2.7}/src/vtjson/vtjson.py +265 -77
- {vtjson-2.2.6 → vtjson-2.2.7/src/vtjson.egg-info}/PKG-INFO +1 -1
- {vtjson-2.2.6 → vtjson-2.2.7}/tests/test_vtjson.py +195 -1
- {vtjson-2.2.6 → vtjson-2.2.7}/AUTHORS +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/LICENSE +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/README.md +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/pyproject.toml +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/setup.cfg +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/src/vtjson/__init__.py +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/src/vtjson/py.typed +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/src/vtjson.egg-info/SOURCES.txt +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/src/vtjson.egg-info/dependency_links.txt +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/src/vtjson.egg-info/requires.txt +0 -0
- {vtjson-2.2.6 → vtjson-2.2.7}/src/vtjson.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vtjson
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.7
|
|
4
4
|
Summary: An easy to use validation library compatible with Python type annotations
|
|
5
5
|
Author-email: Michel Van den Bergh <michel.vandenbergh@uhasselt.be>
|
|
6
6
|
Project-URL: Homepage, https://github.com/vdbergh/vtjson
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
|
+
import functools
|
|
5
|
+
import inspect
|
|
4
6
|
import ipaddress
|
|
5
7
|
import math
|
|
6
8
|
import pathlib
|
|
@@ -10,7 +12,7 @@ import types
|
|
|
10
12
|
import typing
|
|
11
13
|
import urllib.parse
|
|
12
14
|
import warnings
|
|
13
|
-
from collections.abc import Sequence, Set, Sized
|
|
15
|
+
from collections.abc import Iterable, Sequence, Set, Sized
|
|
14
16
|
from dataclasses import dataclass
|
|
15
17
|
from typing import (
|
|
16
18
|
Any,
|
|
@@ -204,7 +206,7 @@ class SchemaError(Exception):
|
|
|
204
206
|
pass
|
|
205
207
|
|
|
206
208
|
|
|
207
|
-
__version__ = "2.2.
|
|
209
|
+
__version__ = "2.2.7"
|
|
208
210
|
|
|
209
211
|
|
|
210
212
|
@dataclass
|
|
@@ -248,14 +250,79 @@ skip_first = Apply(skip_first=True)
|
|
|
248
250
|
_dns_resolver: dns.resolver.Resolver | None = None
|
|
249
251
|
|
|
250
252
|
|
|
251
|
-
def
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
253
|
+
def _to_name(s: object) -> str:
|
|
254
|
+
if hasattr(s, "__name__"):
|
|
255
|
+
return str(s.__name__)
|
|
256
|
+
else:
|
|
257
|
+
if isinstance(s, str):
|
|
258
|
+
return repr(s)
|
|
259
|
+
elif s == Ellipsis:
|
|
260
|
+
return "..."
|
|
255
261
|
else:
|
|
256
|
-
return str(
|
|
262
|
+
return str(s)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _generic_name(origin: type, args: tuple[object, ...]) -> str:
|
|
266
|
+
return _to_name(origin) + "[" + ",".join([_to_name(arg) for arg in args]) + "]"
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _make_name(
|
|
270
|
+
type: type,
|
|
271
|
+
args: tuple[object, ...],
|
|
272
|
+
kw: dict[str, object],
|
|
273
|
+
defaults: dict[str, object] = {},
|
|
274
|
+
) -> str:
|
|
275
|
+
arg_list = []
|
|
276
|
+
for a in args:
|
|
277
|
+
arg_list.append(_to_name(a))
|
|
278
|
+
for k, v in kw.items():
|
|
279
|
+
if k not in defaults or v != defaults[k]:
|
|
280
|
+
arg_list.append(f"{k}={_to_name(v)}")
|
|
281
|
+
if len(arg_list) == 0:
|
|
282
|
+
return _to_name(type)
|
|
283
|
+
else:
|
|
284
|
+
return f"{_to_name(type)}({','.join(arg_list)})"
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
C = TypeVar("C")
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _set__name__(c: type[C]) -> type[C]:
|
|
291
|
+
defaults = {}
|
|
292
|
+
a = inspect.getfullargspec(c.__init__)
|
|
293
|
+
a_defaults: tuple[object, ...] = ()
|
|
294
|
+
if a.defaults is not None:
|
|
295
|
+
a_defaults = a.defaults
|
|
296
|
+
|
|
297
|
+
if a is None or a.args is None:
|
|
298
|
+
raise Exception(f"Could not get signature of {c.__name__}")
|
|
299
|
+
start = len(a.args) - len(a_defaults)
|
|
300
|
+
for i in range(start, len(a.args)):
|
|
301
|
+
defaults[a.args[i]] = a_defaults[i - start]
|
|
302
|
+
__init__org = c.__init__
|
|
303
|
+
|
|
304
|
+
@functools.wraps(c.__init__)
|
|
305
|
+
def __init__wrapper(self: C, *args: object, **kw: object) -> None:
|
|
306
|
+
setattr(
|
|
307
|
+
self, "__name__", _make_name(self.__class__, args, kw, defaults=defaults)
|
|
308
|
+
)
|
|
309
|
+
return __init__org(self, *args, **kw)
|
|
310
|
+
|
|
311
|
+
@functools.wraps(c.__str__)
|
|
312
|
+
def __str__(self: C) -> str:
|
|
313
|
+
assert hasattr(self, "__name__")
|
|
314
|
+
return str(self.__name__)
|
|
315
|
+
|
|
316
|
+
@functools.wraps(c.__repr__)
|
|
317
|
+
def __repr__(self: C) -> str:
|
|
318
|
+
assert hasattr(self, "__name__")
|
|
319
|
+
return str(self.__name__)
|
|
257
320
|
|
|
258
|
-
|
|
321
|
+
setattr(c, "__init__", __init__wrapper)
|
|
322
|
+
setattr(c, "__str__", __str__)
|
|
323
|
+
setattr(c, "__repr__", __repr__)
|
|
324
|
+
|
|
325
|
+
return c
|
|
259
326
|
|
|
260
327
|
|
|
261
328
|
def _get_type_hints(schema: object) -> dict[str, object]:
|
|
@@ -414,6 +481,7 @@ def make_type(
|
|
|
414
481
|
K = TypeVar("K")
|
|
415
482
|
|
|
416
483
|
|
|
484
|
+
@_set__name__
|
|
417
485
|
class optional_key(Generic[K]):
|
|
418
486
|
"""
|
|
419
487
|
Make a key in a Mapping schema optional. In the common case that the key
|
|
@@ -424,6 +492,7 @@ class optional_key(Generic[K]):
|
|
|
424
492
|
|
|
425
493
|
key: K
|
|
426
494
|
optional: bool
|
|
495
|
+
__name__: str
|
|
427
496
|
|
|
428
497
|
def __init__(self, key: K, _optional: bool = True) -> None:
|
|
429
498
|
"""
|
|
@@ -445,6 +514,13 @@ class optional_key(Generic[K]):
|
|
|
445
514
|
return hash(self.key)
|
|
446
515
|
|
|
447
516
|
|
|
517
|
+
# def __str__(self) -> str:
|
|
518
|
+
# return self.__name__
|
|
519
|
+
#
|
|
520
|
+
# def __repr__(self) -> str:
|
|
521
|
+
# return self.__name__
|
|
522
|
+
|
|
523
|
+
|
|
448
524
|
StringKeyType = TypeVar("StringKeyType", bound=Union[str, optional_key[str]])
|
|
449
525
|
|
|
450
526
|
|
|
@@ -823,6 +899,7 @@ class set_name(wrapper):
|
|
|
823
899
|
)
|
|
824
900
|
|
|
825
901
|
|
|
902
|
+
@_set__name__
|
|
826
903
|
class regex(compiled_schema):
|
|
827
904
|
"""
|
|
828
905
|
This matches the strings which match the given pattern.
|
|
@@ -857,10 +934,6 @@ class regex(compiled_schema):
|
|
|
857
934
|
if not isinstance(name, str):
|
|
858
935
|
raise SchemaError(f"The regex name {_c(name)} is not a string")
|
|
859
936
|
self.__name__ = name
|
|
860
|
-
else:
|
|
861
|
-
_flags = "" if flags == 0 else f", flags={flags}"
|
|
862
|
-
_fullmatch = "" if fullmatch else ", fullmatch=False"
|
|
863
|
-
self.__name__ = f"regex({repr(regex)}{_fullmatch}{_flags})"
|
|
864
937
|
|
|
865
938
|
try:
|
|
866
939
|
self.pattern = re.compile(regex, flags)
|
|
@@ -878,7 +951,9 @@ class regex(compiled_schema):
|
|
|
878
951
|
subs: Mapping[str, object] = {},
|
|
879
952
|
) -> str:
|
|
880
953
|
if not isinstance(obj, str):
|
|
881
|
-
return _wrong_type_message(
|
|
954
|
+
return _wrong_type_message(
|
|
955
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a string"
|
|
956
|
+
)
|
|
882
957
|
try:
|
|
883
958
|
if self.fullmatch and self.pattern.fullmatch(obj):
|
|
884
959
|
return ""
|
|
@@ -889,6 +964,7 @@ class regex(compiled_schema):
|
|
|
889
964
|
return _wrong_type_message(obj, name, self.__name__)
|
|
890
965
|
|
|
891
966
|
|
|
967
|
+
@_set__name__
|
|
892
968
|
class glob(compiled_schema):
|
|
893
969
|
"""
|
|
894
970
|
Unix style filename matching. This is implemented using
|
|
@@ -910,9 +986,7 @@ class glob(compiled_schema):
|
|
|
910
986
|
|
|
911
987
|
self.pattern = pattern
|
|
912
988
|
|
|
913
|
-
if name is None:
|
|
914
|
-
self.__name__ = f"glob({repr(pattern)})"
|
|
915
|
-
else:
|
|
989
|
+
if name is not None:
|
|
916
990
|
self.__name__ = name
|
|
917
991
|
|
|
918
992
|
try:
|
|
@@ -931,7 +1005,9 @@ class glob(compiled_schema):
|
|
|
931
1005
|
subs: Mapping[str, object] = {},
|
|
932
1006
|
) -> str:
|
|
933
1007
|
if not isinstance(obj, str):
|
|
934
|
-
return _wrong_type_message(
|
|
1008
|
+
return _wrong_type_message(
|
|
1009
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a string"
|
|
1010
|
+
)
|
|
935
1011
|
try:
|
|
936
1012
|
if pathlib.PurePath(obj).match(self.pattern):
|
|
937
1013
|
return ""
|
|
@@ -941,6 +1017,7 @@ class glob(compiled_schema):
|
|
|
941
1017
|
return _wrong_type_message(obj, name, self.__name__, str(e))
|
|
942
1018
|
|
|
943
1019
|
|
|
1020
|
+
@_set__name__
|
|
944
1021
|
class magic(compiled_schema):
|
|
945
1022
|
"""
|
|
946
1023
|
Checks if a buffer (for example a string or a byte array) has the given
|
|
@@ -966,9 +1043,7 @@ class magic(compiled_schema):
|
|
|
966
1043
|
|
|
967
1044
|
self.mime_type = mime_type
|
|
968
1045
|
|
|
969
|
-
if name is None:
|
|
970
|
-
self.__name__ = f"magic({repr(mime_type)})"
|
|
971
|
-
else:
|
|
1046
|
+
if name is not None:
|
|
972
1047
|
self.__name__ = name
|
|
973
1048
|
|
|
974
1049
|
def __validate__(
|
|
@@ -979,7 +1054,12 @@ class magic(compiled_schema):
|
|
|
979
1054
|
subs: Mapping[str, object] = {},
|
|
980
1055
|
) -> str:
|
|
981
1056
|
if not isinstance(obj, (str, bytes)):
|
|
982
|
-
return _wrong_type_message(
|
|
1057
|
+
return _wrong_type_message(
|
|
1058
|
+
obj,
|
|
1059
|
+
name,
|
|
1060
|
+
self.__name__,
|
|
1061
|
+
explanation=f"{_c(obj)} is not a string nor bytes",
|
|
1062
|
+
)
|
|
983
1063
|
try:
|
|
984
1064
|
objmime_type = magic_.from_buffer(obj, mime=True)
|
|
985
1065
|
except Exception as e:
|
|
@@ -994,6 +1074,7 @@ class magic(compiled_schema):
|
|
|
994
1074
|
return ""
|
|
995
1075
|
|
|
996
1076
|
|
|
1077
|
+
@_set__name__
|
|
997
1078
|
class div(compiled_schema):
|
|
998
1079
|
"""
|
|
999
1080
|
This matches the integers `x` such that `(x - remainder) % divisor` == 0.
|
|
@@ -1024,13 +1105,7 @@ class div(compiled_schema):
|
|
|
1024
1105
|
self.divisor = divisor
|
|
1025
1106
|
self.remainder = remainder
|
|
1026
1107
|
|
|
1027
|
-
if name is None:
|
|
1028
|
-
_divisor = str(divisor)
|
|
1029
|
-
_remainder = ""
|
|
1030
|
-
if remainder != 0:
|
|
1031
|
-
_remainder = "," + str(remainder)
|
|
1032
|
-
self.__name__ = f"div({_divisor+_remainder})"
|
|
1033
|
-
else:
|
|
1108
|
+
if name is not None:
|
|
1034
1109
|
self.__name__ = name
|
|
1035
1110
|
|
|
1036
1111
|
def __validate__(
|
|
@@ -1041,13 +1116,16 @@ class div(compiled_schema):
|
|
|
1041
1116
|
subs: Mapping[str, object] = {},
|
|
1042
1117
|
) -> str:
|
|
1043
1118
|
if not isinstance(obj, int):
|
|
1044
|
-
return _wrong_type_message(
|
|
1119
|
+
return _wrong_type_message(
|
|
1120
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not an integer"
|
|
1121
|
+
)
|
|
1045
1122
|
elif (obj - self.remainder) % self.divisor == 0:
|
|
1046
1123
|
return ""
|
|
1047
1124
|
else:
|
|
1048
1125
|
return _wrong_type_message(obj, name, self.__name__)
|
|
1049
1126
|
|
|
1050
1127
|
|
|
1128
|
+
@_set__name__
|
|
1051
1129
|
class close_to(compiled_schema):
|
|
1052
1130
|
"""
|
|
1053
1131
|
This matches the real numbers that are close to `x` in the sense of
|
|
@@ -1087,9 +1165,6 @@ class close_to(compiled_schema):
|
|
|
1087
1165
|
)
|
|
1088
1166
|
self.kw["abs_tol"] = abs_tol
|
|
1089
1167
|
|
|
1090
|
-
kwl = [str(x)] + [f"{k}={v}" for (k, v) in self.kw.items()]
|
|
1091
|
-
kwl_ = ",".join(kwl)
|
|
1092
|
-
self.__name__ = f"close_to({kwl_})"
|
|
1093
1168
|
self.x = x
|
|
1094
1169
|
|
|
1095
1170
|
def __validate__(
|
|
@@ -1100,19 +1175,23 @@ class close_to(compiled_schema):
|
|
|
1100
1175
|
subs: Mapping[str, object] = {},
|
|
1101
1176
|
) -> str:
|
|
1102
1177
|
if not isinstance(obj, (float, int)):
|
|
1103
|
-
return _wrong_type_message(
|
|
1178
|
+
return _wrong_type_message(
|
|
1179
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not an integer"
|
|
1180
|
+
)
|
|
1104
1181
|
elif math.isclose(obj, self.x, **self.kw):
|
|
1105
1182
|
return ""
|
|
1106
1183
|
else:
|
|
1107
1184
|
return _wrong_type_message(obj, name, self.__name__)
|
|
1108
1185
|
|
|
1109
1186
|
|
|
1187
|
+
@_set__name__
|
|
1110
1188
|
class gt(compiled_schema):
|
|
1111
1189
|
"""
|
|
1112
1190
|
This checks if `object > lb`.
|
|
1113
1191
|
"""
|
|
1114
1192
|
|
|
1115
1193
|
lb: comparable
|
|
1194
|
+
__name__: str
|
|
1116
1195
|
|
|
1117
1196
|
def __init__(self, lb: comparable) -> None:
|
|
1118
1197
|
"""
|
|
@@ -1148,12 +1227,14 @@ class gt(compiled_schema):
|
|
|
1148
1227
|
return f"{self.message(name, obj)}: {str(e)}"
|
|
1149
1228
|
|
|
1150
1229
|
|
|
1230
|
+
@_set__name__
|
|
1151
1231
|
class ge(compiled_schema):
|
|
1152
1232
|
"""
|
|
1153
1233
|
This checks if `object >= lb`.
|
|
1154
1234
|
"""
|
|
1155
1235
|
|
|
1156
1236
|
lb: comparable
|
|
1237
|
+
__name__: str
|
|
1157
1238
|
|
|
1158
1239
|
def __init__(self, lb: comparable) -> None:
|
|
1159
1240
|
"""
|
|
@@ -1189,12 +1270,14 @@ class ge(compiled_schema):
|
|
|
1189
1270
|
return f"{self.message(name, obj)}: {str(e)}"
|
|
1190
1271
|
|
|
1191
1272
|
|
|
1273
|
+
@_set__name__
|
|
1192
1274
|
class lt(compiled_schema):
|
|
1193
1275
|
"""
|
|
1194
1276
|
This checks if `object < ub`.
|
|
1195
1277
|
"""
|
|
1196
1278
|
|
|
1197
1279
|
ub: comparable
|
|
1280
|
+
__name__: str
|
|
1198
1281
|
|
|
1199
1282
|
def __init__(self, ub: comparable) -> None:
|
|
1200
1283
|
"""
|
|
@@ -1230,12 +1313,14 @@ class lt(compiled_schema):
|
|
|
1230
1313
|
return f"{self.message(name, obj)}: {str(e)}"
|
|
1231
1314
|
|
|
1232
1315
|
|
|
1316
|
+
@_set__name__
|
|
1233
1317
|
class le(compiled_schema):
|
|
1234
1318
|
"""
|
|
1235
1319
|
This checks if `object <= ub`.
|
|
1236
1320
|
"""
|
|
1237
1321
|
|
|
1238
1322
|
ub: comparable
|
|
1323
|
+
__name__: str
|
|
1239
1324
|
|
|
1240
1325
|
def __init__(self, ub: comparable) -> None:
|
|
1241
1326
|
"""
|
|
@@ -1271,6 +1356,7 @@ class le(compiled_schema):
|
|
|
1271
1356
|
return f"{self.message(name, obj)}: {str(e)}"
|
|
1272
1357
|
|
|
1273
1358
|
|
|
1359
|
+
@_set__name__
|
|
1274
1360
|
class interval(compiled_schema):
|
|
1275
1361
|
"""
|
|
1276
1362
|
This checks if `lb <= object <= ub`, provided the comparisons make sense.
|
|
@@ -1278,6 +1364,7 @@ class interval(compiled_schema):
|
|
|
1278
1364
|
|
|
1279
1365
|
lb_s: str
|
|
1280
1366
|
ub_s: str
|
|
1367
|
+
__name__: str
|
|
1281
1368
|
|
|
1282
1369
|
def __init__(
|
|
1283
1370
|
self,
|
|
@@ -1346,6 +1433,7 @@ class interval(compiled_schema):
|
|
|
1346
1433
|
setattr(self, "__validate__", anything().__validate__)
|
|
1347
1434
|
|
|
1348
1435
|
|
|
1436
|
+
@_set__name__
|
|
1349
1437
|
class size(compiled_schema):
|
|
1350
1438
|
"""
|
|
1351
1439
|
Matches the objects (which support `len()` such as strings or lists) whose
|
|
@@ -1353,6 +1441,7 @@ class size(compiled_schema):
|
|
|
1353
1441
|
"""
|
|
1354
1442
|
|
|
1355
1443
|
interval_: interval
|
|
1444
|
+
__name__: str
|
|
1356
1445
|
|
|
1357
1446
|
def __init__(self, lb: int, ub: int | types.EllipsisType | None = None) -> None:
|
|
1358
1447
|
"""
|
|
@@ -1625,11 +1714,14 @@ def validate(
|
|
|
1625
1714
|
# Some predefined schemas
|
|
1626
1715
|
|
|
1627
1716
|
|
|
1717
|
+
@_set__name__
|
|
1628
1718
|
class number(compiled_schema):
|
|
1629
1719
|
"""
|
|
1630
1720
|
A deprecated alias for `float`.
|
|
1631
1721
|
"""
|
|
1632
1722
|
|
|
1723
|
+
__name__: str
|
|
1724
|
+
|
|
1633
1725
|
def __init__(self) -> None:
|
|
1634
1726
|
warnings.warn(
|
|
1635
1727
|
"The schema 'number' is deprecated. Use 'float' instead.",
|
|
@@ -1646,14 +1738,20 @@ class number(compiled_schema):
|
|
|
1646
1738
|
if isinstance(obj, (int, float)):
|
|
1647
1739
|
return ""
|
|
1648
1740
|
else:
|
|
1649
|
-
return _wrong_type_message(obj, name,
|
|
1741
|
+
return _wrong_type_message(obj, name, self.__name__)
|
|
1650
1742
|
|
|
1651
1743
|
|
|
1744
|
+
@_set__name__
|
|
1652
1745
|
class float_(compiled_schema):
|
|
1653
1746
|
"""
|
|
1654
1747
|
Schema that only matches floats. Not ints.
|
|
1655
1748
|
"""
|
|
1656
1749
|
|
|
1750
|
+
__name__: str
|
|
1751
|
+
|
|
1752
|
+
def __init__(self) -> None:
|
|
1753
|
+
pass
|
|
1754
|
+
|
|
1657
1755
|
def __validate__(
|
|
1658
1756
|
self,
|
|
1659
1757
|
obj: object,
|
|
@@ -1664,9 +1762,10 @@ class float_(compiled_schema):
|
|
|
1664
1762
|
if isinstance(obj, float):
|
|
1665
1763
|
return ""
|
|
1666
1764
|
else:
|
|
1667
|
-
return _wrong_type_message(obj, name,
|
|
1765
|
+
return _wrong_type_message(obj, name, self.__name__)
|
|
1668
1766
|
|
|
1669
1767
|
|
|
1768
|
+
@_set__name__
|
|
1670
1769
|
class email(compiled_schema):
|
|
1671
1770
|
"""
|
|
1672
1771
|
Checks if the object is a valid email address. This uses the package
|
|
@@ -1675,6 +1774,7 @@ class email(compiled_schema):
|
|
|
1675
1774
|
"""
|
|
1676
1775
|
|
|
1677
1776
|
kw: dict[str, Any]
|
|
1777
|
+
__name__: str
|
|
1678
1778
|
|
|
1679
1779
|
def __init__(self, **kw: Any) -> None:
|
|
1680
1780
|
"""
|
|
@@ -1695,14 +1795,17 @@ class email(compiled_schema):
|
|
|
1695
1795
|
subs: Mapping[str, object] = {},
|
|
1696
1796
|
) -> str:
|
|
1697
1797
|
if not isinstance(obj, str):
|
|
1698
|
-
return _wrong_type_message(
|
|
1798
|
+
return _wrong_type_message(
|
|
1799
|
+
obj, name, self.__name__, f"{_c(obj)} is not a string"
|
|
1800
|
+
)
|
|
1699
1801
|
try:
|
|
1700
1802
|
email_validator.validate_email(obj, **self.kw)
|
|
1701
1803
|
return ""
|
|
1702
1804
|
except Exception as e:
|
|
1703
|
-
return _wrong_type_message(obj, name,
|
|
1805
|
+
return _wrong_type_message(obj, name, self.__name__, str(e))
|
|
1704
1806
|
|
|
1705
1807
|
|
|
1808
|
+
@_set__name__
|
|
1706
1809
|
class ip_address(compiled_schema):
|
|
1707
1810
|
"""
|
|
1708
1811
|
Matches ip addresses of the specified version which can be 4, 6 or None.
|
|
@@ -1719,10 +1822,7 @@ class ip_address(compiled_schema):
|
|
|
1719
1822
|
"""
|
|
1720
1823
|
if version is not None and version not in (4, 6):
|
|
1721
1824
|
raise SchemaError("version is not 4 or 6")
|
|
1722
|
-
|
|
1723
|
-
self.__name__ = "ip_address"
|
|
1724
|
-
else:
|
|
1725
|
-
self.__name__ = f"ip_address(version={version})"
|
|
1825
|
+
|
|
1726
1826
|
if version == 4:
|
|
1727
1827
|
self.method = ipaddress.IPv4Address
|
|
1728
1828
|
elif version == 6:
|
|
@@ -1738,7 +1838,12 @@ class ip_address(compiled_schema):
|
|
|
1738
1838
|
subs: Mapping[str, object] = {},
|
|
1739
1839
|
) -> str:
|
|
1740
1840
|
if not isinstance(obj, (int, str, bytes)):
|
|
1741
|
-
return _wrong_type_message(
|
|
1841
|
+
return _wrong_type_message(
|
|
1842
|
+
obj,
|
|
1843
|
+
name,
|
|
1844
|
+
self.__name__,
|
|
1845
|
+
explanation=f"{_c(obj)} is not a string, an int or bytes",
|
|
1846
|
+
)
|
|
1742
1847
|
try:
|
|
1743
1848
|
self.method(obj)
|
|
1744
1849
|
except ValueError as e:
|
|
@@ -1746,6 +1851,7 @@ class ip_address(compiled_schema):
|
|
|
1746
1851
|
return ""
|
|
1747
1852
|
|
|
1748
1853
|
|
|
1854
|
+
@_set__name__
|
|
1749
1855
|
class regex_pattern(compiled_schema):
|
|
1750
1856
|
"""
|
|
1751
1857
|
Matches valid regular expression patterns
|
|
@@ -1754,7 +1860,7 @@ class regex_pattern(compiled_schema):
|
|
|
1754
1860
|
__name__: str
|
|
1755
1861
|
|
|
1756
1862
|
def __init__(self) -> None:
|
|
1757
|
-
|
|
1863
|
+
pass
|
|
1758
1864
|
|
|
1759
1865
|
def __validate__(
|
|
1760
1866
|
self,
|
|
@@ -1764,7 +1870,9 @@ class regex_pattern(compiled_schema):
|
|
|
1764
1870
|
subs: Mapping[str, object] = {},
|
|
1765
1871
|
) -> str:
|
|
1766
1872
|
if not isinstance(obj, str):
|
|
1767
|
-
return _wrong_type_message(
|
|
1873
|
+
return _wrong_type_message(
|
|
1874
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a string"
|
|
1875
|
+
)
|
|
1768
1876
|
try:
|
|
1769
1877
|
re.compile(obj)
|
|
1770
1878
|
except re.error as e:
|
|
@@ -1772,11 +1880,60 @@ class regex_pattern(compiled_schema):
|
|
|
1772
1880
|
return ""
|
|
1773
1881
|
|
|
1774
1882
|
|
|
1883
|
+
@_set__name__
|
|
1884
|
+
class unique(compiled_schema):
|
|
1885
|
+
"""
|
|
1886
|
+
Matches containers whose entries do not repeat. We first attempt to convert the
|
|
1887
|
+
object to a set and check its size. If this does not work then we check the
|
|
1888
|
+
entries of the object one by one (this is a quadratic algorithm).
|
|
1889
|
+
"""
|
|
1890
|
+
|
|
1891
|
+
__name__: str
|
|
1892
|
+
|
|
1893
|
+
def __init__(self) -> None:
|
|
1894
|
+
pass
|
|
1895
|
+
|
|
1896
|
+
def __validate__(
|
|
1897
|
+
self,
|
|
1898
|
+
obj: object,
|
|
1899
|
+
name: str = "object",
|
|
1900
|
+
strict: bool = True,
|
|
1901
|
+
subs: Mapping[str, object] = {},
|
|
1902
|
+
) -> str:
|
|
1903
|
+
if not isinstance(obj, Iterable):
|
|
1904
|
+
return _wrong_type_message(
|
|
1905
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not iterable"
|
|
1906
|
+
)
|
|
1907
|
+
try:
|
|
1908
|
+
if isinstance(obj, Sized):
|
|
1909
|
+
if len(set(obj)) == len(obj):
|
|
1910
|
+
return ""
|
|
1911
|
+
except Exception:
|
|
1912
|
+
pass
|
|
1913
|
+
try:
|
|
1914
|
+
object_list = []
|
|
1915
|
+
for o in obj:
|
|
1916
|
+
if o in object_list:
|
|
1917
|
+
return _wrong_type_message(
|
|
1918
|
+
obj, name, self.__name__, explanation=f"{_c(o)} is repeated"
|
|
1919
|
+
)
|
|
1920
|
+
object_list.append(o)
|
|
1921
|
+
except Exception as e:
|
|
1922
|
+
return _wrong_type_message(obj, name, self.__name__, explanation=str(e))
|
|
1923
|
+
return ""
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
@_set__name__
|
|
1775
1927
|
class url(compiled_schema):
|
|
1776
1928
|
"""
|
|
1777
1929
|
Matches valid urls.
|
|
1778
1930
|
"""
|
|
1779
1931
|
|
|
1932
|
+
__name__: str
|
|
1933
|
+
|
|
1934
|
+
def __init__(self) -> None:
|
|
1935
|
+
pass
|
|
1936
|
+
|
|
1780
1937
|
def __validate__(
|
|
1781
1938
|
self,
|
|
1782
1939
|
obj: object,
|
|
@@ -1785,13 +1942,16 @@ class url(compiled_schema):
|
|
|
1785
1942
|
subs: Mapping[str, object] = {},
|
|
1786
1943
|
) -> str:
|
|
1787
1944
|
if not isinstance(obj, str):
|
|
1788
|
-
return _wrong_type_message(
|
|
1945
|
+
return _wrong_type_message(
|
|
1946
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a string"
|
|
1947
|
+
)
|
|
1789
1948
|
result = urllib.parse.urlparse(obj)
|
|
1790
1949
|
if all([result.scheme, result.netloc]):
|
|
1791
1950
|
return ""
|
|
1792
|
-
return _wrong_type_message(obj, name,
|
|
1951
|
+
return _wrong_type_message(obj, name, self.__name__)
|
|
1793
1952
|
|
|
1794
1953
|
|
|
1954
|
+
@_set__name__
|
|
1795
1955
|
class date_time(compiled_schema):
|
|
1796
1956
|
"""
|
|
1797
1957
|
Without argument this represents an ISO 8601 date-time. The `format`
|
|
@@ -1806,10 +1966,6 @@ class date_time(compiled_schema):
|
|
|
1806
1966
|
:param format: format string for `strftime`
|
|
1807
1967
|
"""
|
|
1808
1968
|
self.format = format
|
|
1809
|
-
if format is not None:
|
|
1810
|
-
self.__name__ = f"date_time({repr(format)})"
|
|
1811
|
-
else:
|
|
1812
|
-
self.__name__ = "date_time"
|
|
1813
1969
|
|
|
1814
1970
|
def __validate__(
|
|
1815
1971
|
self,
|
|
@@ -1819,7 +1975,9 @@ class date_time(compiled_schema):
|
|
|
1819
1975
|
subs: Mapping[str, object] = {},
|
|
1820
1976
|
) -> str:
|
|
1821
1977
|
if not isinstance(obj, str):
|
|
1822
|
-
return _wrong_type_message(
|
|
1978
|
+
return _wrong_type_message(
|
|
1979
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a string"
|
|
1980
|
+
)
|
|
1823
1981
|
if self.format is not None:
|
|
1824
1982
|
try:
|
|
1825
1983
|
datetime.datetime.strptime(obj, self.format)
|
|
@@ -1833,11 +1991,17 @@ class date_time(compiled_schema):
|
|
|
1833
1991
|
return ""
|
|
1834
1992
|
|
|
1835
1993
|
|
|
1994
|
+
@_set__name__
|
|
1836
1995
|
class date(compiled_schema):
|
|
1837
1996
|
"""
|
|
1838
1997
|
Matches an ISO 8601 date.
|
|
1839
1998
|
"""
|
|
1840
1999
|
|
|
2000
|
+
__name__: str
|
|
2001
|
+
|
|
2002
|
+
def __init__(self) -> None:
|
|
2003
|
+
pass
|
|
2004
|
+
|
|
1841
2005
|
def __validate__(
|
|
1842
2006
|
self,
|
|
1843
2007
|
obj: object,
|
|
@@ -1846,19 +2010,27 @@ class date(compiled_schema):
|
|
|
1846
2010
|
subs: Mapping[str, object] = {},
|
|
1847
2011
|
) -> str:
|
|
1848
2012
|
if not isinstance(obj, str):
|
|
1849
|
-
return _wrong_type_message(
|
|
2013
|
+
return _wrong_type_message(
|
|
2014
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a string"
|
|
2015
|
+
)
|
|
1850
2016
|
try:
|
|
1851
2017
|
datetime.date.fromisoformat(obj)
|
|
1852
2018
|
except Exception as e:
|
|
1853
|
-
return _wrong_type_message(obj, name,
|
|
2019
|
+
return _wrong_type_message(obj, name, self.__name__, str(e))
|
|
1854
2020
|
return ""
|
|
1855
2021
|
|
|
1856
2022
|
|
|
2023
|
+
@_set__name__
|
|
1857
2024
|
class time(compiled_schema):
|
|
1858
2025
|
"""
|
|
1859
2026
|
Matches an ISO 8601 time.
|
|
1860
2027
|
"""
|
|
1861
2028
|
|
|
2029
|
+
__name__: str
|
|
2030
|
+
|
|
2031
|
+
def __init__(self) -> None:
|
|
2032
|
+
pass
|
|
2033
|
+
|
|
1862
2034
|
def __validate__(
|
|
1863
2035
|
self,
|
|
1864
2036
|
obj: object,
|
|
@@ -1867,19 +2039,27 @@ class time(compiled_schema):
|
|
|
1867
2039
|
subs: Mapping[str, object] = {},
|
|
1868
2040
|
) -> str:
|
|
1869
2041
|
if not isinstance(obj, str):
|
|
1870
|
-
return _wrong_type_message(
|
|
2042
|
+
return _wrong_type_message(
|
|
2043
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a string"
|
|
2044
|
+
)
|
|
1871
2045
|
try:
|
|
1872
2046
|
datetime.time.fromisoformat(obj)
|
|
1873
2047
|
except Exception as e:
|
|
1874
|
-
return _wrong_type_message(obj, name,
|
|
2048
|
+
return _wrong_type_message(obj, name, self.__name__, str(e))
|
|
1875
2049
|
return ""
|
|
1876
2050
|
|
|
1877
2051
|
|
|
2052
|
+
@_set__name__
|
|
1878
2053
|
class nothing(compiled_schema):
|
|
1879
2054
|
"""
|
|
1880
2055
|
Matches nothing.
|
|
1881
2056
|
"""
|
|
1882
2057
|
|
|
2058
|
+
__name__: str
|
|
2059
|
+
|
|
2060
|
+
def __init__(self) -> None:
|
|
2061
|
+
pass
|
|
2062
|
+
|
|
1883
2063
|
def __validate__(
|
|
1884
2064
|
self,
|
|
1885
2065
|
obj: object,
|
|
@@ -1887,14 +2067,20 @@ class nothing(compiled_schema):
|
|
|
1887
2067
|
strict: bool = True,
|
|
1888
2068
|
subs: Mapping[str, object] = {},
|
|
1889
2069
|
) -> str:
|
|
1890
|
-
return _wrong_type_message(obj, name,
|
|
2070
|
+
return _wrong_type_message(obj, name, self.__name__)
|
|
1891
2071
|
|
|
1892
2072
|
|
|
2073
|
+
@_set__name__
|
|
1893
2074
|
class anything(compiled_schema):
|
|
1894
2075
|
"""
|
|
1895
2076
|
Matchess anything.
|
|
1896
2077
|
"""
|
|
1897
2078
|
|
|
2079
|
+
__name__: str
|
|
2080
|
+
|
|
2081
|
+
def __init__(self) -> None:
|
|
2082
|
+
pass
|
|
2083
|
+
|
|
1898
2084
|
def __validate__(
|
|
1899
2085
|
self,
|
|
1900
2086
|
obj: object,
|
|
@@ -1905,6 +2091,7 @@ class anything(compiled_schema):
|
|
|
1905
2091
|
return ""
|
|
1906
2092
|
|
|
1907
2093
|
|
|
2094
|
+
@_set__name__
|
|
1908
2095
|
class domain_name(compiled_schema):
|
|
1909
2096
|
"""
|
|
1910
2097
|
Checks if the object is a valid domain name.
|
|
@@ -1923,16 +2110,6 @@ class domain_name(compiled_schema):
|
|
|
1923
2110
|
self.re_ascii = re.compile(r"[\x00-\x7F]*")
|
|
1924
2111
|
self.ascii_only = ascii_only
|
|
1925
2112
|
self.resolve = resolve
|
|
1926
|
-
arg_string = ""
|
|
1927
|
-
if not ascii_only:
|
|
1928
|
-
arg_string += ", ascii_only=False"
|
|
1929
|
-
if resolve:
|
|
1930
|
-
arg_string += ", resolve=True"
|
|
1931
|
-
if arg_string != "":
|
|
1932
|
-
arg_string = arg_string[2:]
|
|
1933
|
-
self.__name__ = (
|
|
1934
|
-
"domain_name" if not arg_string else f"domain_name({arg_string})"
|
|
1935
|
-
)
|
|
1936
2113
|
|
|
1937
2114
|
def __validate__(
|
|
1938
2115
|
self,
|
|
@@ -1942,7 +2119,9 @@ class domain_name(compiled_schema):
|
|
|
1942
2119
|
subs: Mapping[str, object] = {},
|
|
1943
2120
|
) -> str:
|
|
1944
2121
|
if not isinstance(obj, str):
|
|
1945
|
-
return _wrong_type_message(
|
|
2122
|
+
return _wrong_type_message(
|
|
2123
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a string"
|
|
2124
|
+
)
|
|
1946
2125
|
if self.ascii_only:
|
|
1947
2126
|
if not self.re_ascii.fullmatch(obj):
|
|
1948
2127
|
return _wrong_type_message(
|
|
@@ -1961,6 +2140,7 @@ class domain_name(compiled_schema):
|
|
|
1961
2140
|
return ""
|
|
1962
2141
|
|
|
1963
2142
|
|
|
2143
|
+
@_set__name__
|
|
1964
2144
|
class at_least_one_of(compiled_schema):
|
|
1965
2145
|
"""
|
|
1966
2146
|
This represents a dictionary with a least one key among a collection of
|
|
@@ -1975,8 +2155,6 @@ class at_least_one_of(compiled_schema):
|
|
|
1975
2155
|
:param args: a collection of keys
|
|
1976
2156
|
"""
|
|
1977
2157
|
self.args = args
|
|
1978
|
-
args_s = [repr(a) for a in args]
|
|
1979
|
-
self.__name__ = f"{self.__class__.__name__}({','.join(args_s)})"
|
|
1980
2158
|
|
|
1981
2159
|
def __validate__(
|
|
1982
2160
|
self,
|
|
@@ -1986,7 +2164,9 @@ class at_least_one_of(compiled_schema):
|
|
|
1986
2164
|
subs: Mapping[str, object] = {},
|
|
1987
2165
|
) -> str:
|
|
1988
2166
|
if not isinstance(obj, Mapping):
|
|
1989
|
-
return _wrong_type_message(
|
|
2167
|
+
return _wrong_type_message(
|
|
2168
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a Mapping"
|
|
2169
|
+
)
|
|
1990
2170
|
try:
|
|
1991
2171
|
if any([a in obj for a in self.args]):
|
|
1992
2172
|
return ""
|
|
@@ -1996,6 +2176,7 @@ class at_least_one_of(compiled_schema):
|
|
|
1996
2176
|
return _wrong_type_message(obj, name, self.__name__, str(e))
|
|
1997
2177
|
|
|
1998
2178
|
|
|
2179
|
+
@_set__name__
|
|
1999
2180
|
class at_most_one_of(compiled_schema):
|
|
2000
2181
|
"""
|
|
2001
2182
|
This represents an dictionary with at most one key among a collection of
|
|
@@ -2010,8 +2191,6 @@ class at_most_one_of(compiled_schema):
|
|
|
2010
2191
|
:param args: a collection of keys
|
|
2011
2192
|
"""
|
|
2012
2193
|
self.args = args
|
|
2013
|
-
args_s = [repr(a) for a in args]
|
|
2014
|
-
self.__name__ = f"{self.__class__.__name__}({','.join(args_s)})"
|
|
2015
2194
|
|
|
2016
2195
|
def __validate__(
|
|
2017
2196
|
self,
|
|
@@ -2021,7 +2200,9 @@ class at_most_one_of(compiled_schema):
|
|
|
2021
2200
|
subs: Mapping[str, object] = {},
|
|
2022
2201
|
) -> str:
|
|
2023
2202
|
if not isinstance(obj, Mapping):
|
|
2024
|
-
return _wrong_type_message(
|
|
2203
|
+
return _wrong_type_message(
|
|
2204
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a Mapping"
|
|
2205
|
+
)
|
|
2025
2206
|
try:
|
|
2026
2207
|
if sum([a in obj for a in self.args]) <= 1:
|
|
2027
2208
|
return ""
|
|
@@ -2031,6 +2212,7 @@ class at_most_one_of(compiled_schema):
|
|
|
2031
2212
|
return _wrong_type_message(obj, name, self.__name__, str(e))
|
|
2032
2213
|
|
|
2033
2214
|
|
|
2215
|
+
@_set__name__
|
|
2034
2216
|
class one_of(compiled_schema):
|
|
2035
2217
|
"""
|
|
2036
2218
|
This represents a dictionary with exactly one key among a collection of
|
|
@@ -2045,8 +2227,6 @@ class one_of(compiled_schema):
|
|
|
2045
2227
|
:param args: a collection of keys
|
|
2046
2228
|
"""
|
|
2047
2229
|
self.args = args
|
|
2048
|
-
args_s = [repr(a) for a in args]
|
|
2049
|
-
self.__name__ = f"{self.__class__.__name__}({','.join(args_s)})"
|
|
2050
2230
|
|
|
2051
2231
|
def __validate__(
|
|
2052
2232
|
self,
|
|
@@ -2056,7 +2236,9 @@ class one_of(compiled_schema):
|
|
|
2056
2236
|
subs: Mapping[str, object] = {},
|
|
2057
2237
|
) -> str:
|
|
2058
2238
|
if not isinstance(obj, Mapping):
|
|
2059
|
-
return _wrong_type_message(
|
|
2239
|
+
return _wrong_type_message(
|
|
2240
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a Mapping"
|
|
2241
|
+
)
|
|
2060
2242
|
try:
|
|
2061
2243
|
if sum([a in obj for a in self.args]) == 1:
|
|
2062
2244
|
return ""
|
|
@@ -2066,6 +2248,7 @@ class one_of(compiled_schema):
|
|
|
2066
2248
|
return _wrong_type_message(obj, name, self.__name__, str(e))
|
|
2067
2249
|
|
|
2068
2250
|
|
|
2251
|
+
@_set__name__
|
|
2069
2252
|
class keys(compiled_schema):
|
|
2070
2253
|
"""
|
|
2071
2254
|
This represents a dictionary containing all the keys in a collection of
|
|
@@ -2073,6 +2256,7 @@ class keys(compiled_schema):
|
|
|
2073
2256
|
"""
|
|
2074
2257
|
|
|
2075
2258
|
args: tuple[object, ...]
|
|
2259
|
+
__name__: str
|
|
2076
2260
|
|
|
2077
2261
|
def __init__(self, *args: object) -> None:
|
|
2078
2262
|
"""
|
|
@@ -2088,7 +2272,9 @@ class keys(compiled_schema):
|
|
|
2088
2272
|
subs: Mapping[str, object] = {},
|
|
2089
2273
|
) -> str:
|
|
2090
2274
|
if not isinstance(obj, Mapping):
|
|
2091
|
-
return _wrong_type_message(
|
|
2275
|
+
return _wrong_type_message(
|
|
2276
|
+
obj, name, self.__name__, explanation=f"{_c(obj)} is not a Mapping"
|
|
2277
|
+
)
|
|
2092
2278
|
for k in self.args:
|
|
2093
2279
|
if k not in obj:
|
|
2094
2280
|
return f"{name}[{repr(k)}] is missing"
|
|
@@ -2268,6 +2454,7 @@ class _fields(compiled_schema):
|
|
|
2268
2454
|
return ""
|
|
2269
2455
|
|
|
2270
2456
|
|
|
2457
|
+
@_set__name__
|
|
2271
2458
|
class fields(wrapper, Generic[StringKeyType]):
|
|
2272
2459
|
"""
|
|
2273
2460
|
`d` is a dictionary `{"field1": schema1, ...}`.
|
|
@@ -2277,6 +2464,7 @@ class fields(wrapper, Generic[StringKeyType]):
|
|
|
2277
2464
|
"""
|
|
2278
2465
|
|
|
2279
2466
|
d: Mapping[StringKeyType, object]
|
|
2467
|
+
__name__: str
|
|
2280
2468
|
|
|
2281
2469
|
def __init__(self, d: Mapping[StringKeyType, object]) -> None:
|
|
2282
2470
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vtjson
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.7
|
|
4
4
|
Summary: An easy to use validation library compatible with Python type annotations
|
|
5
5
|
Author-email: Michel Van den Bergh <michel.vandenbergh@uhasselt.be>
|
|
6
6
|
Project-URL: Homepage, https://github.com/vdbergh/vtjson
|
|
@@ -104,6 +104,7 @@ from vtjson import (
|
|
|
104
104
|
strict,
|
|
105
105
|
time,
|
|
106
106
|
union,
|
|
107
|
+
unique,
|
|
107
108
|
url,
|
|
108
109
|
validate,
|
|
109
110
|
)
|
|
@@ -111,7 +112,7 @@ from vtjson import (
|
|
|
111
112
|
|
|
112
113
|
def show(mc: Any) -> None:
|
|
113
114
|
exception = mc.exception
|
|
114
|
-
print(f"{exception.__class__.__name__}: {str(mc.exception)}")
|
|
115
|
+
print(f"{exception.__class__.__name__}: {str(mc.exception)}", flush=True)
|
|
115
116
|
|
|
116
117
|
|
|
117
118
|
class TestValidation(unittest.TestCase):
|
|
@@ -212,6 +213,10 @@ class TestValidation(unittest.TestCase):
|
|
|
212
213
|
object_ = "hello.doc"
|
|
213
214
|
validate(schema, object_)
|
|
214
215
|
show(mc)
|
|
216
|
+
with self.assertRaises(ValidationError) as mc:
|
|
217
|
+
object_ = 123
|
|
218
|
+
validate(schema, object_)
|
|
219
|
+
show(mc)
|
|
215
220
|
with self.assertRaises(SchemaError) as mc_:
|
|
216
221
|
schema = glob({}) # type: ignore
|
|
217
222
|
show(mc_)
|
|
@@ -404,6 +409,10 @@ class TestValidation(unittest.TestCase):
|
|
|
404
409
|
schema = close_to(1.0)
|
|
405
410
|
validate(schema, 1.0)
|
|
406
411
|
|
|
412
|
+
with self.assertRaises(ValidationError) as mc:
|
|
413
|
+
validate(schema, "1.0")
|
|
414
|
+
show(mc)
|
|
415
|
+
|
|
407
416
|
with self.assertRaises(ValidationError) as mc:
|
|
408
417
|
validate(schema, 1.1)
|
|
409
418
|
show(mc)
|
|
@@ -423,6 +432,10 @@ class TestValidation(unittest.TestCase):
|
|
|
423
432
|
validate(schema, object_)
|
|
424
433
|
object_ = {"cat": None}
|
|
425
434
|
validate(schema, object_)
|
|
435
|
+
with self.assertRaises(ValidationError) as mc:
|
|
436
|
+
object_ = 123
|
|
437
|
+
validate(schema, object_)
|
|
438
|
+
show(mc)
|
|
426
439
|
with self.assertRaises(ValidationError) as mc:
|
|
427
440
|
object_ = {"cat": None, "dog": None}
|
|
428
441
|
validate(schema, object_)
|
|
@@ -474,6 +487,10 @@ class TestValidation(unittest.TestCase):
|
|
|
474
487
|
schema = keys("a", "b")
|
|
475
488
|
object_ = {"a": 1, "b": 2}
|
|
476
489
|
validate(schema, object_)
|
|
490
|
+
with self.assertRaises(ValidationError) as mc:
|
|
491
|
+
object_ = 123
|
|
492
|
+
validate(schema, object_)
|
|
493
|
+
show(mc)
|
|
477
494
|
with self.assertRaises(ValidationError) as mc:
|
|
478
495
|
object_ = {"a": 1}
|
|
479
496
|
validate(schema, object_)
|
|
@@ -761,6 +778,11 @@ class TestValidation(unittest.TestCase):
|
|
|
761
778
|
def test_date_time(self) -> None:
|
|
762
779
|
schema: object
|
|
763
780
|
object_: object
|
|
781
|
+
with self.assertRaises(ValidationError) as mc:
|
|
782
|
+
schema = date_time
|
|
783
|
+
object_ = 123
|
|
784
|
+
validate(schema, object_)
|
|
785
|
+
show(mc)
|
|
764
786
|
with self.assertRaises(ValidationError) as mc:
|
|
765
787
|
schema = date_time
|
|
766
788
|
object_ = "2000-30-30"
|
|
@@ -796,6 +818,10 @@ class TestValidation(unittest.TestCase):
|
|
|
796
818
|
object_ = "2023-10-10"
|
|
797
819
|
validate(schema, object_)
|
|
798
820
|
|
|
821
|
+
with self.assertRaises(ValidationError) as mc:
|
|
822
|
+
object_ = 123
|
|
823
|
+
validate(schema, object_)
|
|
824
|
+
show(mc)
|
|
799
825
|
with self.assertRaises(ValidationError) as mc:
|
|
800
826
|
object_ = "2023-10-10T01:01:01"
|
|
801
827
|
validate(schema, object_)
|
|
@@ -808,6 +834,10 @@ class TestValidation(unittest.TestCase):
|
|
|
808
834
|
object_ = "01:01:01"
|
|
809
835
|
validate(schema, object_)
|
|
810
836
|
|
|
837
|
+
with self.assertRaises(ValidationError) as mc:
|
|
838
|
+
object_ = 123
|
|
839
|
+
validate(schema, object_)
|
|
840
|
+
show(mc)
|
|
811
841
|
with self.assertRaises(ValidationError) as mc:
|
|
812
842
|
object_ = "2023-10-10T01:01:01"
|
|
813
843
|
validate(schema, object_)
|
|
@@ -1386,6 +1416,12 @@ class TestValidation(unittest.TestCase):
|
|
|
1386
1416
|
compile(schema)
|
|
1387
1417
|
show(cm_)
|
|
1388
1418
|
|
|
1419
|
+
ip_address = regex(r"(?:[\d]+\.){3}(?:[\d]+)")
|
|
1420
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1421
|
+
object_ = 123
|
|
1422
|
+
validate(ip_address, object_)
|
|
1423
|
+
show(mc)
|
|
1424
|
+
|
|
1389
1425
|
ip_address = regex(r"(?:[\d]+\.){3}(?:[\d]+)", name="ip_address")
|
|
1390
1426
|
with self.assertRaises(ValidationError) as mc:
|
|
1391
1427
|
object_ = 123
|
|
@@ -1788,6 +1824,11 @@ class TestValidation(unittest.TestCase):
|
|
|
1788
1824
|
object_ = {"url": "https://user:pass@google.com?search=chatgpt"}
|
|
1789
1825
|
validate(schema, object_)
|
|
1790
1826
|
|
|
1827
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1828
|
+
object_ = {"url": 123}
|
|
1829
|
+
validate(schema, object_)
|
|
1830
|
+
show(mc)
|
|
1831
|
+
|
|
1791
1832
|
with self.assertRaises(ValidationError) as mc:
|
|
1792
1833
|
object_ = {"url": "google.com"}
|
|
1793
1834
|
validate(schema, object_)
|
|
@@ -1800,6 +1841,10 @@ class TestValidation(unittest.TestCase):
|
|
|
1800
1841
|
object_ = "www.example.com"
|
|
1801
1842
|
validate(schema, object_)
|
|
1802
1843
|
|
|
1844
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1845
|
+
object_ = 123
|
|
1846
|
+
validate(schema, object_)
|
|
1847
|
+
show(mc)
|
|
1803
1848
|
with self.assertRaises(ValidationError) as mc:
|
|
1804
1849
|
object_ = "www.éxample.com"
|
|
1805
1850
|
validate(schema, object_)
|
|
@@ -1849,6 +1894,10 @@ class TestValidation(unittest.TestCase):
|
|
|
1849
1894
|
schema: object
|
|
1850
1895
|
object_: object
|
|
1851
1896
|
schema = regex_pattern
|
|
1897
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1898
|
+
object_ = 123
|
|
1899
|
+
validate(schema, object_)
|
|
1900
|
+
show(mc)
|
|
1852
1901
|
with self.assertRaises(ValidationError) as mc:
|
|
1853
1902
|
object_ = "(("
|
|
1854
1903
|
validate(schema, object_)
|
|
@@ -1856,6 +1905,26 @@ class TestValidation(unittest.TestCase):
|
|
|
1856
1905
|
object_ = ".*"
|
|
1857
1906
|
validate(schema, object_)
|
|
1858
1907
|
|
|
1908
|
+
def test_unique(self) -> None:
|
|
1909
|
+
schema = unique
|
|
1910
|
+
object_: object
|
|
1911
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1912
|
+
object_ = 123
|
|
1913
|
+
validate(schema, object_)
|
|
1914
|
+
show(mc)
|
|
1915
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1916
|
+
object_ = [1, 2, 3, 2]
|
|
1917
|
+
validate(schema, object_)
|
|
1918
|
+
show(mc)
|
|
1919
|
+
with self.assertRaises(ValidationError) as mc:
|
|
1920
|
+
object_ = [[1], [2], [3], [2]]
|
|
1921
|
+
validate(schema, object_)
|
|
1922
|
+
show(mc)
|
|
1923
|
+
object_ = [1, 2, 3]
|
|
1924
|
+
validate(schema, object_)
|
|
1925
|
+
object_ = [[1], [2], [3]]
|
|
1926
|
+
validate(schema, object_)
|
|
1927
|
+
|
|
1859
1928
|
def test_truncation(self) -> None:
|
|
1860
1929
|
schema: object
|
|
1861
1930
|
object_: object
|
|
@@ -2380,6 +2449,131 @@ class TestValidation(unittest.TestCase):
|
|
|
2380
2449
|
validate(protocol(dummy2), u(""))
|
|
2381
2450
|
show(mc)
|
|
2382
2451
|
|
|
2452
|
+
def test_name(self) -> None:
|
|
2453
|
+
schema: object
|
|
2454
|
+
|
|
2455
|
+
schema = regex("abc", name="cba")
|
|
2456
|
+
self.assertEqual(schema.__name__, "cba")
|
|
2457
|
+
schema = regex("abc")
|
|
2458
|
+
self.assertEqual(schema.__name__, "regex('abc')")
|
|
2459
|
+
schema = regex("abc", fullmatch=False)
|
|
2460
|
+
self.assertEqual(schema.__name__, "regex('abc',fullmatch=False)")
|
|
2461
|
+
schema = regex("abc", fullmatch=True)
|
|
2462
|
+
self.assertEqual(schema.__name__, "regex('abc')")
|
|
2463
|
+
schema = regex("abc", fullmatch=False, flags=re.ASCII | re.MULTILINE)
|
|
2464
|
+
self.assertEqual(
|
|
2465
|
+
schema.__name__, "regex('abc',fullmatch=False,flags=re.ASCII|re.MULTILINE)"
|
|
2466
|
+
)
|
|
2467
|
+
schema = regex("abc", fullmatch=False, flags=0)
|
|
2468
|
+
self.assertEqual(schema.__name__, "regex('abc',fullmatch=False)")
|
|
2469
|
+
|
|
2470
|
+
schema = glob("*.txt", "text_file")
|
|
2471
|
+
self.assertEqual(schema.__name__, "text_file")
|
|
2472
|
+
schema = glob("*.txt")
|
|
2473
|
+
self.assertEqual(schema.__name__, "glob('*.txt')")
|
|
2474
|
+
|
|
2475
|
+
schema = div(2, name="even")
|
|
2476
|
+
self.assertEqual(schema.__name__, "even")
|
|
2477
|
+
schema = div(2)
|
|
2478
|
+
self.assertEqual(schema.__name__, "div(2)")
|
|
2479
|
+
schema = div(2, 1)
|
|
2480
|
+
self.assertEqual(schema.__name__, "div(2,1)")
|
|
2481
|
+
|
|
2482
|
+
schema = close_to(0.75)
|
|
2483
|
+
self.assertEqual(schema.__name__, "close_to(0.75)")
|
|
2484
|
+
|
|
2485
|
+
schema = email
|
|
2486
|
+
self.assertEqual(schema.__name__, "email")
|
|
2487
|
+
|
|
2488
|
+
schema = ip_address
|
|
2489
|
+
self.assertEqual(schema.__name__, "ip_address")
|
|
2490
|
+
schema = ip_address(version=4)
|
|
2491
|
+
self.assertEqual(schema.__name__, "ip_address(version=4)")
|
|
2492
|
+
|
|
2493
|
+
schema = regex_pattern
|
|
2494
|
+
self.assertEqual(schema.__name__, "regex_pattern")
|
|
2495
|
+
|
|
2496
|
+
schema = url
|
|
2497
|
+
self.assertEqual(schema.__name__, "url")
|
|
2498
|
+
|
|
2499
|
+
schema = domain_name
|
|
2500
|
+
self.assertEqual(schema.__name__, "domain_name")
|
|
2501
|
+
schema = domain_name(ascii_only=False)
|
|
2502
|
+
self.assertEqual(schema.__name__, "domain_name(ascii_only=False)")
|
|
2503
|
+
schema = domain_name(ascii_only=False, resolve=True)
|
|
2504
|
+
self.assertEqual(schema.__name__, "domain_name(ascii_only=False,resolve=True)")
|
|
2505
|
+
|
|
2506
|
+
schema = date_time
|
|
2507
|
+
self.assertEqual(schema.__name__, "date_time")
|
|
2508
|
+
schema = date_time("%Y^%m^%d")
|
|
2509
|
+
self.assertEqual(schema.__name__, "date_time('%Y^%m^%d')")
|
|
2510
|
+
|
|
2511
|
+
schema = date
|
|
2512
|
+
self.assertEqual(schema.__name__, "date")
|
|
2513
|
+
|
|
2514
|
+
schema = time
|
|
2515
|
+
self.assertEqual(schema.__name__, "time")
|
|
2516
|
+
|
|
2517
|
+
schema = nothing
|
|
2518
|
+
self.assertEqual(schema.__name__, "nothing")
|
|
2519
|
+
|
|
2520
|
+
schema = anything
|
|
2521
|
+
self.assertEqual(schema.__name__, "anything")
|
|
2522
|
+
|
|
2523
|
+
schema = float_
|
|
2524
|
+
self.assertEqual(schema.__name__, "float_")
|
|
2525
|
+
|
|
2526
|
+
schema = one_of("a", "b", 1)
|
|
2527
|
+
self.assertEqual(schema.__name__, "one_of('a','b',1)")
|
|
2528
|
+
|
|
2529
|
+
schema = at_least_one_of("a", "b", 1)
|
|
2530
|
+
self.assertEqual(schema.__name__, "at_least_one_of('a','b',1)")
|
|
2531
|
+
|
|
2532
|
+
schema = at_most_one_of("a", "b", 1)
|
|
2533
|
+
self.assertEqual(schema.__name__, "at_most_one_of('a','b',1)")
|
|
2534
|
+
|
|
2535
|
+
schema = keys("a", "b", 1)
|
|
2536
|
+
self.assertEqual(schema.__name__, "keys('a','b',1)")
|
|
2537
|
+
|
|
2538
|
+
schema = interval(1, 10)
|
|
2539
|
+
self.assertEqual(schema.__name__, "interval(1,10)")
|
|
2540
|
+
schema = interval(1, ...)
|
|
2541
|
+
self.assertEqual(schema.__name__, "interval(1,...)")
|
|
2542
|
+
schema = interval(..., 10)
|
|
2543
|
+
self.assertEqual(schema.__name__, "interval(...,10)")
|
|
2544
|
+
schema = interval(..., 10, strict_ub=True)
|
|
2545
|
+
self.assertEqual(schema.__name__, "interval(...,10,strict_ub=True)")
|
|
2546
|
+
|
|
2547
|
+
schema = gt(10)
|
|
2548
|
+
self.assertEqual(schema.__name__, "gt(10)")
|
|
2549
|
+
|
|
2550
|
+
schema = ge(10)
|
|
2551
|
+
self.assertEqual(schema.__name__, "ge(10)")
|
|
2552
|
+
|
|
2553
|
+
schema = lt(10)
|
|
2554
|
+
self.assertEqual(schema.__name__, "lt(10)")
|
|
2555
|
+
|
|
2556
|
+
schema = le(10)
|
|
2557
|
+
self.assertEqual(schema.__name__, "le(10)")
|
|
2558
|
+
|
|
2559
|
+
schema = size(1)
|
|
2560
|
+
self.assertEqual(schema.__name__, "size(1)")
|
|
2561
|
+
schema = size(1, 10)
|
|
2562
|
+
self.assertEqual(schema.__name__, "size(1,10)")
|
|
2563
|
+
|
|
2564
|
+
schema = fields({"a": "b"})
|
|
2565
|
+
self.assertEqual(schema.__name__, "fields({'a': 'b'})")
|
|
2566
|
+
schema = fields({optional_key("a"): "b"})
|
|
2567
|
+
self.assertEqual(schema.__name__, "fields({optional_key('a'): 'b'})")
|
|
2568
|
+
|
|
2569
|
+
schema = magic("application/html", name="html")
|
|
2570
|
+
self.assertEqual(schema.__name__, "html")
|
|
2571
|
+
schema = magic("application/html")
|
|
2572
|
+
self.assertEqual(schema.__name__, "magic('application/html')")
|
|
2573
|
+
|
|
2574
|
+
schema = unique
|
|
2575
|
+
self.assertEqual(schema.__name__, "unique")
|
|
2576
|
+
|
|
2383
2577
|
|
|
2384
2578
|
if __name__ == "__main__":
|
|
2385
2579
|
unittest.main(verbosity=2)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|