vtjson 2.1.3__tar.gz → 2.1.4__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.3/src/vtjson.egg-info → vtjson-2.1.4}/PKG-INFO +14 -4
- {vtjson-2.1.3 → vtjson-2.1.4}/README.md +13 -3
- {vtjson-2.1.3 → vtjson-2.1.4}/src/vtjson/vtjson.py +18 -7
- {vtjson-2.1.3 → vtjson-2.1.4/src/vtjson.egg-info}/PKG-INFO +14 -4
- {vtjson-2.1.3 → vtjson-2.1.4}/tests/test_vtjson.py +62 -6
- {vtjson-2.1.3 → vtjson-2.1.4}/AUTHORS +0 -0
- {vtjson-2.1.3 → vtjson-2.1.4}/LICENSE +0 -0
- {vtjson-2.1.3 → vtjson-2.1.4}/pyproject.toml +0 -0
- {vtjson-2.1.3 → vtjson-2.1.4}/setup.cfg +0 -0
- {vtjson-2.1.3 → vtjson-2.1.4}/src/vtjson/__init__.py +0 -0
- {vtjson-2.1.3 → vtjson-2.1.4}/src/vtjson.egg-info/SOURCES.txt +0 -0
- {vtjson-2.1.3 → vtjson-2.1.4}/src/vtjson.egg-info/dependency_links.txt +0 -0
- {vtjson-2.1.3 → vtjson-2.1.4}/src/vtjson.egg-info/requires.txt +0 -0
- {vtjson-2.1.3 → vtjson-2.1.4}/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.4
|
|
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
|
|
@@ -70,8 +70,8 @@ A wrapper takes one or more schemas as arguments and produces a new schema.
|
|
|
70
70
|
- An object matches the schema `complement(schema)` if it does not match `schema`.
|
|
71
71
|
- An object matches the schema `lax(schema)` if it matches `schema` when validated with `strict=False`.
|
|
72
72
|
- An object matches the schema `strict(schema)` if it matches `schema` when validated with `strict=True`.
|
|
73
|
-
- An object matches the schema `set_name(schema, name)` if it matches `schema
|
|
74
|
-
- An object matches the schema `
|
|
73
|
+
- An object matches the schema `set_name(schema, name, reason=False)` if it matches `schema`, but the `name` argument will be used in non-validation messages. Unless `reason` is `True` the original non-validation message will be suppressed.
|
|
74
|
+
- An object matches the schema `protocol(schema, dict=False)` if `schema` is a class and its fields are annotated with schemas which validate the corresponding fields in the object. If `dict` is `True` then the object is validated as a `dict`.
|
|
75
75
|
- An object matches the schema `set_label(schema, label1, ..., labelN, debug=False)` if it matches `schema`, unless the schema is replaced by a different one via the `subs` argument to `validate`. If the optional argument `debug` is `True` then a message will be printed on the console if the schema was changed.
|
|
76
76
|
|
|
77
77
|
## Built-ins
|
|
@@ -190,7 +190,7 @@ A consequence of this algorithm is that non-const keys are automatically optiona
|
|
|
190
190
|
|
|
191
191
|
```python
|
|
192
192
|
Annotated, dict[...], Dict[...], list[...], List[...], tuple[...], Tuple[...],
|
|
193
|
-
Protocol, Literal, NewType, TypedDict, Union (or the equivalent operator |).
|
|
193
|
+
Protocol, NamedTuple, Literal, NewType, TypedDict, Union (or the equivalent operator |).
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
For example `dict[str, str]` is translated internally into the schema `{str: str}`. See below for more information.
|
|
@@ -256,6 +256,16 @@ Note that Python imposes strong restrictions on what constitutes a valid type hi
|
|
|
256
256
|
|
|
257
257
|
internally becomes `fields({"title": str, "price": float})`.
|
|
258
258
|
|
|
259
|
+
- `NamedTuple`. A `NamedTuple` class is translated as the intersection of a `tuple` schema and a fields schema. E.g.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
class Movie(NamedTuple):
|
|
263
|
+
title: str
|
|
264
|
+
price: float
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
internally becomes `intersect(tuple, fields({"title": str, "price": float}))`.
|
|
268
|
+
|
|
259
269
|
- `Annotated` has already been discussed. It is translated into a suitable `intersect` schema. The handling of `Annotated` schemas can be influenced by `Apply` objects (see below).
|
|
260
270
|
|
|
261
271
|
- `NewType` is translated into a `set_name` schema. E.g. `NewType('Movie', str)` becomes `set_name(str, 'Movie')`
|
|
@@ -50,8 +50,8 @@ A wrapper takes one or more schemas as arguments and produces a new schema.
|
|
|
50
50
|
- An object matches the schema `complement(schema)` if it does not match `schema`.
|
|
51
51
|
- An object matches the schema `lax(schema)` if it matches `schema` when validated with `strict=False`.
|
|
52
52
|
- An object matches the schema `strict(schema)` if it matches `schema` when validated with `strict=True`.
|
|
53
|
-
- An object matches the schema `set_name(schema, name)` if it matches `schema
|
|
54
|
-
- An object matches the schema `
|
|
53
|
+
- An object matches the schema `set_name(schema, name, reason=False)` if it matches `schema`, but the `name` argument will be used in non-validation messages. Unless `reason` is `True` the original non-validation message will be suppressed.
|
|
54
|
+
- An object matches the schema `protocol(schema, dict=False)` if `schema` is a class and its fields are annotated with schemas which validate the corresponding fields in the object. If `dict` is `True` then the object is validated as a `dict`.
|
|
55
55
|
- An object matches the schema `set_label(schema, label1, ..., labelN, debug=False)` if it matches `schema`, unless the schema is replaced by a different one via the `subs` argument to `validate`. If the optional argument `debug` is `True` then a message will be printed on the console if the schema was changed.
|
|
56
56
|
|
|
57
57
|
## Built-ins
|
|
@@ -170,7 +170,7 @@ A consequence of this algorithm is that non-const keys are automatically optiona
|
|
|
170
170
|
|
|
171
171
|
```python
|
|
172
172
|
Annotated, dict[...], Dict[...], list[...], List[...], tuple[...], Tuple[...],
|
|
173
|
-
Protocol, Literal, NewType, TypedDict, Union (or the equivalent operator |).
|
|
173
|
+
Protocol, NamedTuple, Literal, NewType, TypedDict, Union (or the equivalent operator |).
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
For example `dict[str, str]` is translated internally into the schema `{str: str}`. See below for more information.
|
|
@@ -236,6 +236,16 @@ Note that Python imposes strong restrictions on what constitutes a valid type hi
|
|
|
236
236
|
|
|
237
237
|
internally becomes `fields({"title": str, "price": float})`.
|
|
238
238
|
|
|
239
|
+
- `NamedTuple`. A `NamedTuple` class is translated as the intersection of a `tuple` schema and a fields schema. E.g.
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
class Movie(NamedTuple):
|
|
243
|
+
title: str
|
|
244
|
+
price: float
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
internally becomes `intersect(tuple, fields({"title": str, "price": float}))`.
|
|
248
|
+
|
|
239
249
|
- `Annotated` has already been discussed. It is translated into a suitable `intersect` schema. The handling of `Annotated` schemas can be influenced by `Apply` objects (see below).
|
|
240
250
|
|
|
241
251
|
- `NewType` is translated into a `set_name` schema. E.g. `NewType('Movie', str)` becomes `set_name(str, 'Movie')`
|
|
@@ -115,7 +115,7 @@ class SchemaError(Exception):
|
|
|
115
115
|
pass
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
__version__ = "2.1.
|
|
118
|
+
__version__ = "2.1.4"
|
|
119
119
|
|
|
120
120
|
|
|
121
121
|
@dataclass
|
|
@@ -152,9 +152,10 @@ def _get_type_hints(schema: object) -> dict[str, object]:
|
|
|
152
152
|
raise SchemaError(
|
|
153
153
|
"Structural subtyping in not supported in this " "Python version"
|
|
154
154
|
)
|
|
155
|
-
type_hints = {}
|
|
156
155
|
if isinstance(schema, type) and hasattr(schema, "__annotations__"):
|
|
157
156
|
type_hints = typing.get_type_hints(schema, include_extras=True)
|
|
157
|
+
else:
|
|
158
|
+
raise SchemaError("The schema does not have type hints")
|
|
158
159
|
return type_hints
|
|
159
160
|
|
|
160
161
|
|
|
@@ -1110,11 +1111,19 @@ def _compile(
|
|
|
1110
1111
|
ret = schema
|
|
1111
1112
|
elif supports_TypedDict and typing.is_typeddict(schema):
|
|
1112
1113
|
ret = _compile(
|
|
1113
|
-
|
|
1114
|
+
protocol(schema, dict=True), _deferred_compiles=_deferred_compiles
|
|
1114
1115
|
)
|
|
1115
1116
|
elif isinstance(schema, type) and hasattr(schema, "_is_protocol"):
|
|
1116
1117
|
assert hasattr(schema, "__name__") and isinstance(schema.__name__, str)
|
|
1117
|
-
ret = _compile(
|
|
1118
|
+
ret = _compile(protocol(schema), _deferred_compiles=_deferred_compiles)
|
|
1119
|
+
elif (
|
|
1120
|
+
isinstance(schema, type)
|
|
1121
|
+
and issubclass(schema, tuple)
|
|
1122
|
+
and hasattr(schema, "_fields")
|
|
1123
|
+
):
|
|
1124
|
+
ret = _compile(
|
|
1125
|
+
intersect(tuple, protocol(schema)), _deferred_compiles=_deferred_compiles
|
|
1126
|
+
)
|
|
1118
1127
|
elif schema == Any:
|
|
1119
1128
|
ret = anything()
|
|
1120
1129
|
elif hasattr(schema, "__name__") and hasattr(schema, "__supertype__"):
|
|
@@ -2087,7 +2096,7 @@ class _set(compiled_schema):
|
|
|
2087
2096
|
return str(self.schema_)
|
|
2088
2097
|
|
|
2089
2098
|
|
|
2090
|
-
class
|
|
2099
|
+
class protocol:
|
|
2091
2100
|
type_dict: dict[object, object]
|
|
2092
2101
|
dict: bool
|
|
2093
2102
|
__name__: str
|
|
@@ -2101,8 +2110,10 @@ class structural:
|
|
|
2101
2110
|
if hasattr(schema, "__total__") and isinstance(schema.__total__, bool):
|
|
2102
2111
|
total = schema.__total__
|
|
2103
2112
|
self.type_dict = _to_dict(type_hints, total=total)
|
|
2104
|
-
|
|
2105
|
-
|
|
2113
|
+
if hasattr(schema, "__name__") and isinstance(schema.__name__, str):
|
|
2114
|
+
self.__name__ = schema.__name__
|
|
2115
|
+
else:
|
|
2116
|
+
self.__name__ = "schema"
|
|
2106
2117
|
|
|
2107
2118
|
def __compile__(
|
|
2108
2119
|
self, _deferred_compiles: _mapping | None = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vtjson
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
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
|
|
@@ -70,8 +70,8 @@ A wrapper takes one or more schemas as arguments and produces a new schema.
|
|
|
70
70
|
- An object matches the schema `complement(schema)` if it does not match `schema`.
|
|
71
71
|
- An object matches the schema `lax(schema)` if it matches `schema` when validated with `strict=False`.
|
|
72
72
|
- An object matches the schema `strict(schema)` if it matches `schema` when validated with `strict=True`.
|
|
73
|
-
- An object matches the schema `set_name(schema, name)` if it matches `schema
|
|
74
|
-
- An object matches the schema `
|
|
73
|
+
- An object matches the schema `set_name(schema, name, reason=False)` if it matches `schema`, but the `name` argument will be used in non-validation messages. Unless `reason` is `True` the original non-validation message will be suppressed.
|
|
74
|
+
- An object matches the schema `protocol(schema, dict=False)` if `schema` is a class and its fields are annotated with schemas which validate the corresponding fields in the object. If `dict` is `True` then the object is validated as a `dict`.
|
|
75
75
|
- An object matches the schema `set_label(schema, label1, ..., labelN, debug=False)` if it matches `schema`, unless the schema is replaced by a different one via the `subs` argument to `validate`. If the optional argument `debug` is `True` then a message will be printed on the console if the schema was changed.
|
|
76
76
|
|
|
77
77
|
## Built-ins
|
|
@@ -190,7 +190,7 @@ A consequence of this algorithm is that non-const keys are automatically optiona
|
|
|
190
190
|
|
|
191
191
|
```python
|
|
192
192
|
Annotated, dict[...], Dict[...], list[...], List[...], tuple[...], Tuple[...],
|
|
193
|
-
Protocol, Literal, NewType, TypedDict, Union (or the equivalent operator |).
|
|
193
|
+
Protocol, NamedTuple, Literal, NewType, TypedDict, Union (or the equivalent operator |).
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
For example `dict[str, str]` is translated internally into the schema `{str: str}`. See below for more information.
|
|
@@ -256,6 +256,16 @@ Note that Python imposes strong restrictions on what constitutes a valid type hi
|
|
|
256
256
|
|
|
257
257
|
internally becomes `fields({"title": str, "price": float})`.
|
|
258
258
|
|
|
259
|
+
- `NamedTuple`. A `NamedTuple` class is translated as the intersection of a `tuple` schema and a fields schema. E.g.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
class Movie(NamedTuple):
|
|
263
|
+
title: str
|
|
264
|
+
price: float
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
internally becomes `intersect(tuple, fields({"title": str, "price": float}))`.
|
|
268
|
+
|
|
259
269
|
- `Annotated` has already been discussed. It is translated into a suitable `intersect` schema. The handling of `Annotated` schemas can be influenced by `Apply` objects (see below).
|
|
260
270
|
|
|
261
271
|
- `NewType` is translated into a `set_name` schema. E.g. `NewType('Movie', str)` becomes `set_name(str, 'Movie')`
|
|
@@ -5,7 +5,7 @@ import re
|
|
|
5
5
|
import sys
|
|
6
6
|
import unittest
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
|
-
from typing import Any, Dict, List, NewType, Tuple, Union
|
|
8
|
+
from typing import Any, Dict, List, NamedTuple, NewType, Tuple, Union
|
|
9
9
|
from urllib.parse import urlparse
|
|
10
10
|
|
|
11
11
|
import vtjson
|
|
@@ -79,6 +79,7 @@ from vtjson import (
|
|
|
79
79
|
number,
|
|
80
80
|
one_of,
|
|
81
81
|
optional_key,
|
|
82
|
+
protocol,
|
|
82
83
|
quote,
|
|
83
84
|
regex,
|
|
84
85
|
safe_cast,
|
|
@@ -86,7 +87,6 @@ from vtjson import (
|
|
|
86
87
|
set_name,
|
|
87
88
|
size,
|
|
88
89
|
strict,
|
|
89
|
-
structural,
|
|
90
90
|
time,
|
|
91
91
|
union,
|
|
92
92
|
url,
|
|
@@ -2045,7 +2045,39 @@ class TestValidation(unittest.TestCase):
|
|
|
2045
2045
|
|
|
2046
2046
|
validate(dummy, w())
|
|
2047
2047
|
|
|
2048
|
-
def
|
|
2048
|
+
def test_NamedTuple(self) -> None:
|
|
2049
|
+
class dummy(NamedTuple):
|
|
2050
|
+
b: int = 0
|
|
2051
|
+
c: str = ""
|
|
2052
|
+
|
|
2053
|
+
def f(self, i: float) -> bool:
|
|
2054
|
+
return i == i
|
|
2055
|
+
|
|
2056
|
+
class x(NamedTuple):
|
|
2057
|
+
b: str = ""
|
|
2058
|
+
c: str = ""
|
|
2059
|
+
|
|
2060
|
+
if not vtjson.supports_structural:
|
|
2061
|
+
with self.assertRaises(SchemaError) as mc_:
|
|
2062
|
+
compile(dummy)
|
|
2063
|
+
show(mc_)
|
|
2064
|
+
return
|
|
2065
|
+
|
|
2066
|
+
with self.assertRaises(ValidationError) as mc:
|
|
2067
|
+
validate(dummy, x())
|
|
2068
|
+
show(mc)
|
|
2069
|
+
self.assertTrue("dummy" in str(mc.exception))
|
|
2070
|
+
|
|
2071
|
+
class w(NamedTuple):
|
|
2072
|
+
b: int = 1
|
|
2073
|
+
c: str = ""
|
|
2074
|
+
|
|
2075
|
+
def g(self) -> bool:
|
|
2076
|
+
return True
|
|
2077
|
+
|
|
2078
|
+
validate(dummy, w())
|
|
2079
|
+
|
|
2080
|
+
def test_protocol(self) -> None:
|
|
2049
2081
|
class dummy:
|
|
2050
2082
|
b: int = 0
|
|
2051
2083
|
c: str = ""
|
|
@@ -2055,11 +2087,19 @@ class TestValidation(unittest.TestCase):
|
|
|
2055
2087
|
|
|
2056
2088
|
if not vtjson.supports_structural:
|
|
2057
2089
|
with self.assertRaises(SchemaError) as mc_:
|
|
2058
|
-
schema =
|
|
2090
|
+
schema = protocol(dummy)
|
|
2059
2091
|
show(mc_)
|
|
2060
2092
|
return
|
|
2061
2093
|
|
|
2062
|
-
|
|
2094
|
+
with self.assertRaises(SchemaError) as mc_:
|
|
2095
|
+
schema = protocol(dummy, dict=None) # type: ignore
|
|
2096
|
+
show(mc_)
|
|
2097
|
+
|
|
2098
|
+
with self.assertRaises(SchemaError) as mc_:
|
|
2099
|
+
schema = protocol({})
|
|
2100
|
+
show(mc_)
|
|
2101
|
+
|
|
2102
|
+
schema = protocol(dummy)
|
|
2063
2103
|
|
|
2064
2104
|
class x:
|
|
2065
2105
|
b: str = ""
|
|
@@ -2079,13 +2119,29 @@ class TestValidation(unittest.TestCase):
|
|
|
2079
2119
|
|
|
2080
2120
|
validate(schema, w())
|
|
2081
2121
|
|
|
2082
|
-
schema =
|
|
2122
|
+
schema = protocol(dummy, dict=True)
|
|
2083
2123
|
validate(schema, {"b": 1, "c": ""})
|
|
2084
2124
|
|
|
2085
2125
|
with self.assertRaises(ValidationError):
|
|
2086
2126
|
validate(schema, w())
|
|
2087
2127
|
show(mc)
|
|
2088
2128
|
|
|
2129
|
+
if sys.version_info >= (3, 11):
|
|
2130
|
+
|
|
2131
|
+
class dummy2:
|
|
2132
|
+
# oh, horror
|
|
2133
|
+
a: {"b": int} # type: ignore # noqa: F821
|
|
2134
|
+
|
|
2135
|
+
class u:
|
|
2136
|
+
def __init__(self, v: object) -> None:
|
|
2137
|
+
self.a = {"b": v}
|
|
2138
|
+
|
|
2139
|
+
validate(protocol(dummy2), u(5))
|
|
2140
|
+
|
|
2141
|
+
with self.assertRaises(ValidationError) as mc:
|
|
2142
|
+
validate(protocol(dummy2), u(""))
|
|
2143
|
+
show(mc)
|
|
2144
|
+
|
|
2089
2145
|
|
|
2090
2146
|
if __name__ == "__main__":
|
|
2091
2147
|
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
|