vtjson 2.1.1__py3-none-any.whl → 2.1.3__py3-none-any.whl

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/vtjson.py CHANGED
@@ -12,7 +12,7 @@ import urllib.parse
12
12
  import warnings
13
13
  from collections.abc import Sequence, Sized
14
14
  from dataclasses import dataclass
15
- from typing import Any, Callable, NewType, Type, TypeVar, Union, cast
15
+ from typing import Any, Callable, Type, TypeVar, Union, cast
16
16
 
17
17
  try:
18
18
  from typing import Literal
@@ -41,15 +41,15 @@ except ImportError:
41
41
  supports_Annotated = False
42
42
 
43
43
  if hasattr(typing, "get_origin"):
44
- supports_GenericAlias = True
44
+ supports_Generics = True
45
45
  else:
46
- supports_GenericAlias = False
46
+ supports_Generics = False
47
47
 
48
48
  try:
49
49
  typing.get_type_hints(int, include_extras=True)
50
- supports_extra_type_hints = True
50
+ supports_structural = True
51
51
  except Exception:
52
- supports_extra_type_hints = False
52
+ supports_structural = False
53
53
 
54
54
  try:
55
55
  from types import UnionType
@@ -115,7 +115,7 @@ class SchemaError(Exception):
115
115
  pass
116
116
 
117
117
 
118
- __version__ = "2.1.1"
118
+ __version__ = "2.1.3"
119
119
 
120
120
 
121
121
  @dataclass
@@ -147,6 +147,35 @@ skip_first = Apply(skip_first=True)
147
147
  _dns_resolver: dns.resolver.Resolver | None = None
148
148
 
149
149
 
150
+ def _get_type_hints(schema: object) -> dict[str, object]:
151
+ if not supports_structural:
152
+ raise SchemaError(
153
+ "Structural subtyping in not supported in this " "Python version"
154
+ )
155
+ type_hints = {}
156
+ if isinstance(schema, type) and hasattr(schema, "__annotations__"):
157
+ type_hints = typing.get_type_hints(schema, include_extras=True)
158
+ return type_hints
159
+
160
+
161
+ def _to_dict(type_hints: dict[str, object], total: bool = True) -> dict[object, object]:
162
+ d: dict[object, object] = {}
163
+ if not supports_Generics:
164
+ raise SchemaError("Generic types are not supported")
165
+ for k, v in type_hints.items():
166
+ v_ = v
167
+ k_: str | optional_key = k
168
+ value_type = typing.get_origin(v)
169
+ if supports_NotRequired and value_type in (Required, NotRequired):
170
+ v_ = typing.get_args(v)[0]
171
+ if total and supports_NotRequired and value_type == NotRequired:
172
+ k_ = optional_key(k)
173
+ elif not total and (not supports_NotRequired or value_type != Required):
174
+ k_ = optional_key(k)
175
+ d[k_] = v_
176
+ return d
177
+
178
+
150
179
  def _get_dns_resolver() -> dns.resolver.Resolver:
151
180
  global _dns_resolver
152
181
  if _dns_resolver is not None:
@@ -177,9 +206,16 @@ def _c(s: object) -> str:
177
206
 
178
207
 
179
208
  def _wrong_type_message(
180
- object_: object, name: str, type_name: str, explanation: str | None = None
209
+ object_: object,
210
+ name: str,
211
+ type_name: str,
212
+ explanation: str | None = None,
213
+ skip_value: bool = False,
181
214
  ) -> str:
182
- message = f"{name} (value:{_c(object_)}) is not of type '{type_name}'"
215
+ if not skip_value:
216
+ message = f"{name} (value:{_c(object_)}) is not of type '{type_name}'"
217
+ else:
218
+ message = f"{name} is not of type '{type_name}'"
183
219
  if explanation is not None:
184
220
  message += f": {explanation}"
185
221
  return message
@@ -480,14 +516,20 @@ class quote(compiled_schema):
480
516
 
481
517
 
482
518
  class _set_name(compiled_schema):
519
+ reason: bool
483
520
  schema: compiled_schema
484
521
  __name__: str
485
522
 
486
523
  def __init__(
487
- self, schema: object, name: str, _deferred_compiles: _mapping | None = None
524
+ self,
525
+ schema: object,
526
+ name: str,
527
+ reason: bool = False,
528
+ _deferred_compiles: _mapping | None = None,
488
529
  ) -> None:
489
530
  self.schema = _compile(schema, _deferred_compiles=_deferred_compiles)
490
531
  self.__name__ = name
532
+ self.reason = reason
491
533
 
492
534
  def __validate__(
493
535
  self,
@@ -498,22 +540,34 @@ class _set_name(compiled_schema):
498
540
  ) -> str:
499
541
  message = self.schema.__validate__(object_, name=name, strict=strict, subs=subs)
500
542
  if message != "":
501
- return _wrong_type_message(object_, name, self.__name__)
543
+ if not self.reason:
544
+ return _wrong_type_message(object_, name, self.__name__)
545
+ else:
546
+ return _wrong_type_message(
547
+ object_, name, self.__name__, explanation=message, skip_value=True
548
+ )
502
549
  return ""
503
550
 
504
551
 
505
552
  class set_name:
553
+ reason: bool
506
554
  schema: object
507
555
  name: str
508
556
 
509
- def __init__(self, schema: object, name: str) -> None:
557
+ def __init__(self, schema: object, name: str, reason: bool = False) -> None:
510
558
  if not isinstance(name, str):
511
559
  raise SchemaError(f"The name {_c(name)} is not a string")
512
560
  self.schema = schema
513
561
  self.name = name
562
+ self.reason = reason
514
563
 
515
564
  def __compile__(self, _deferred_compiles: _mapping | None = None) -> _set_name:
516
- return _set_name(self.schema, self.name, _deferred_compiles=_deferred_compiles)
565
+ return _set_name(
566
+ self.schema,
567
+ self.name,
568
+ reason=self.reason,
569
+ _deferred_compiles=_deferred_compiles,
570
+ )
517
571
 
518
572
 
519
573
  class regex(compiled_schema):
@@ -1035,6 +1089,11 @@ def _compile(
1035
1089
  _deferred_compiles[schema] = _deferred(_deferred_compiles, schema)
1036
1090
 
1037
1091
  # real work starts here
1092
+ if supports_Generics:
1093
+ origin = typing.get_origin(schema)
1094
+ else:
1095
+ origin = object()
1096
+
1038
1097
  ret: compiled_schema
1039
1098
  if isinstance(schema, type) and issubclass(schema, compiled_schema):
1040
1099
  try:
@@ -1049,69 +1108,46 @@ def _compile(
1049
1108
  ret = schema.__compile__(_deferred_compiles=_deferred_compiles)
1050
1109
  elif isinstance(schema, compiled_schema):
1051
1110
  ret = schema
1111
+ elif supports_TypedDict and typing.is_typeddict(schema):
1112
+ ret = _compile(
1113
+ structural(schema, dict=True), _deferred_compiles=_deferred_compiles
1114
+ )
1115
+ elif isinstance(schema, type) and hasattr(schema, "_is_protocol"):
1116
+ assert hasattr(schema, "__name__") and isinstance(schema.__name__, str)
1117
+ ret = _compile(structural(schema), _deferred_compiles=_deferred_compiles)
1118
+ elif schema == Any:
1119
+ ret = anything()
1120
+ elif hasattr(schema, "__name__") and hasattr(schema, "__supertype__"):
1121
+ ret = _NewType(
1122
+ schema,
1123
+ _deferred_compiles=_deferred_compiles,
1124
+ )
1125
+ elif origin == list:
1126
+ ret = _List(typing.get_args(schema)[0], _deferred_compiles=_deferred_compiles)
1127
+ elif origin == tuple:
1128
+ ret = _Tuple(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
1129
+ elif origin == dict:
1130
+ ret = _Dict(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
1131
+ elif origin == Union:
1132
+ ret = _Union(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
1133
+ elif supports_Literal and origin == Literal:
1134
+ ret = _Literal(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
1135
+ elif supports_Annotated and origin == Annotated:
1136
+ ret = _Annotated(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
1137
+ elif supports_UnionType and isinstance(schema, UnionType):
1138
+ ret = _Union(schema.__args__, _deferred_compiles=_deferred_compiles)
1139
+ elif isinstance(schema, type):
1140
+ ret = _type(schema)
1141
+ elif callable(schema):
1142
+ ret = _callable(schema)
1143
+ elif isinstance(schema, tuple) or isinstance(schema, list):
1144
+ ret = _sequence(schema, _deferred_compiles=_deferred_compiles)
1145
+ elif isinstance(schema, dict):
1146
+ ret = _dict(schema, _deferred_compiles=_deferred_compiles)
1147
+ elif isinstance(schema, set):
1148
+ ret = _set(schema, _deferred_compiles=_deferred_compiles)
1052
1149
  else:
1053
- if supports_GenericAlias:
1054
- origin = typing.get_origin(schema)
1055
-
1056
- type_hints = {}
1057
- if (
1058
- supports_extra_type_hints
1059
- and isinstance(schema, type)
1060
- and hasattr(schema, "__annotations__")
1061
- ):
1062
- type_hints = typing.get_type_hints(schema, include_extras=True)
1063
- if supports_TypedDict and typing.is_typeddict(schema):
1064
- assert hasattr(schema, "__total__") and isinstance(schema.__total__, bool)
1065
- ret = _TypedDict(
1066
- type_hints,
1067
- schema.__total__,
1068
- _deferred_compiles=_deferred_compiles,
1069
- )
1070
- elif isinstance(schema, type) and hasattr(schema, "_is_protocol"):
1071
- ret = _fields(type_hints, _deferred_compiles=_deferred_compiles)
1072
- elif schema == Any:
1073
- ret = anything()
1074
- elif (sys.version_info < (3, 10) and hasattr(schema, "__supertype__")) or (
1075
- sys.version_info >= (3, 10) and isinstance(schema, NewType)
1076
- ):
1077
- assert hasattr(schema, "__name__") and hasattr(schema, "__supertype__")
1078
- ret = _NewType(
1079
- schema.__supertype__,
1080
- schema.__name__,
1081
- _deferred_compiles=_deferred_compiles,
1082
- )
1083
- elif supports_GenericAlias and origin == list:
1084
- ret = _List(
1085
- typing.get_args(schema)[0], _deferred_compiles=_deferred_compiles
1086
- )
1087
- elif supports_GenericAlias and origin == tuple:
1088
- ret = _Tuple(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
1089
- elif supports_GenericAlias and origin == dict:
1090
- ret = _Dict(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
1091
- elif supports_GenericAlias and origin == Union:
1092
- ret = _Union(typing.get_args(schema), _deferred_compiles=_deferred_compiles)
1093
- elif supports_Literal and origin == Literal:
1094
- ret = _Literal(
1095
- typing.get_args(schema), _deferred_compiles=_deferred_compiles
1096
- )
1097
- elif supports_Annotated and origin == Annotated:
1098
- ret = _Annotated(
1099
- typing.get_args(schema), _deferred_compiles=_deferred_compiles
1100
- )
1101
- elif supports_UnionType and isinstance(schema, UnionType):
1102
- ret = _Union(schema.__args__, _deferred_compiles=_deferred_compiles)
1103
- elif isinstance(schema, type):
1104
- ret = _type(schema)
1105
- elif callable(schema):
1106
- ret = _callable(schema)
1107
- elif isinstance(schema, tuple) or isinstance(schema, list):
1108
- ret = _sequence(schema, _deferred_compiles=_deferred_compiles)
1109
- elif isinstance(schema, dict):
1110
- ret = _dict(schema, _deferred_compiles=_deferred_compiles)
1111
- elif isinstance(schema, set):
1112
- ret = _set(schema, _deferred_compiles=_deferred_compiles)
1113
- else:
1114
- ret = _const(schema)
1150
+ ret = _const(schema)
1115
1151
 
1116
1152
  # back to updating the cache
1117
1153
  if _deferred_compiles.in_use(schema):
@@ -2051,6 +2087,43 @@ class _set(compiled_schema):
2051
2087
  return str(self.schema_)
2052
2088
 
2053
2089
 
2090
+ class structural:
2091
+ type_dict: dict[object, object]
2092
+ dict: bool
2093
+ __name__: str
2094
+
2095
+ def __init__(self, schema: object, dict: bool = False):
2096
+ if not isinstance(dict, bool):
2097
+ raise SchemaError("bool flag is not a bool")
2098
+ type_hints = _get_type_hints(schema)
2099
+ self.dict = dict
2100
+ total = True
2101
+ if hasattr(schema, "__total__") and isinstance(schema.__total__, bool):
2102
+ total = schema.__total__
2103
+ self.type_dict = _to_dict(type_hints, total=total)
2104
+ assert hasattr(schema, "__name__") and isinstance(schema.__name__, str)
2105
+ self.__name__ = schema.__name__
2106
+
2107
+ def __compile__(
2108
+ self, _deferred_compiles: _mapping | None = None
2109
+ ) -> compiled_schema:
2110
+ if not self.dict:
2111
+ type_dict_ = cast(dict[str, object], self.type_dict)
2112
+ return _set_name(
2113
+ fields(type_dict_),
2114
+ self.__name__,
2115
+ reason=True,
2116
+ _deferred_compiles=_deferred_compiles,
2117
+ )
2118
+ else:
2119
+ return _set_name(
2120
+ dict(self.type_dict),
2121
+ self.__name__,
2122
+ reason=True,
2123
+ _deferred_compiles=_deferred_compiles,
2124
+ )
2125
+
2126
+
2054
2127
  class _Literal(compiled_schema):
2055
2128
  def __init__(
2056
2129
  self, schema: tuple[object, ...], _deferred_compiles: _mapping | None = None
@@ -2111,9 +2184,12 @@ class _Dict(compiled_schema):
2111
2184
 
2112
2185
  class _NewType(compiled_schema):
2113
2186
  def __init__(
2114
- self, schema: object, name: str, _deferred_compiles: _mapping | None = None
2187
+ self, schema: object, _deferred_compiles: _mapping | None = None
2115
2188
  ) -> None:
2116
- c = _set_name(schema, name, _deferred_compiles=_deferred_compiles)
2189
+ assert hasattr(schema, "__name__") and hasattr(schema, "__supertype__")
2190
+ c = _set_name(
2191
+ schema.__supertype__, schema.__name__, _deferred_compiles=_deferred_compiles
2192
+ )
2117
2193
  setattr(
2118
2194
  self,
2119
2195
  "__validate__",
@@ -2144,29 +2220,3 @@ class _Annotated(compiled_schema):
2144
2220
  "__validate__",
2145
2221
  c.__validate__,
2146
2222
  )
2147
-
2148
-
2149
- class _TypedDict(compiled_schema):
2150
- def __init__(
2151
- self,
2152
- schema: dict[str, object],
2153
- total: bool,
2154
- _deferred_compiles: _mapping | None = None,
2155
- ) -> None:
2156
- d: dict[object, object] = {}
2157
- for k, v in schema.items():
2158
- v_ = v
2159
- k_: str | optional_key = k
2160
- value_type = typing.get_origin(v)
2161
- if supports_NotRequired and value_type in (Required, NotRequired):
2162
- v_ = typing.get_args(v)[0]
2163
- if total and supports_NotRequired and value_type == NotRequired:
2164
- k_ = optional_key(k)
2165
- elif not total and (not supports_NotRequired or value_type != Required):
2166
- k_ = optional_key(k)
2167
- d[k_] = v_
2168
- setattr(
2169
- self,
2170
- "__validate__",
2171
- _dict(d, _deferred_compiles=_deferred_compiles).__validate__,
2172
- )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vtjson
3
- Version: 2.1.1
3
+ Version: 2.1.3
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
@@ -24,193 +24,24 @@ A lightweight package for validating JSON like Python objects.
24
24
 
25
25
  ## Schemas
26
26
 
27
- Validation of JSON like Python objects is done according to a `schema` which is somewhat inspired by a typescript type. The format of a schema is more or less self explanatory as the following example shows.
27
+ Validation of JSON like Python objects is done according to a `schema` which is somewhat inspired by a typescript type. The format of a schema is more or less self explanatory. As an [example](https://raw.githubusercontent.com/vdbergh/vtjson/refs/heads/main/docs/example1.md) one may consult the schema of the run object in the mongodb database underlying the Fishtest web application <https://tests.stockfishchess.org/tests>.
28
28
 
29
- ### Example
29
+ The following conventions are used:
30
30
 
31
- Below is a simplified version of the schema of the run object in the mongodb database underlying the Fishtest web application <https://tests.stockfishchess.org/tests>
31
+ - As in typescript, a (string) key ending in `?` represents an optional key. The corresponding schema (the item the key points to) will only be used for validation when the key is present in the object that should be validated. A key can also be made optional by wrapping it as `optional_key(key)`.
32
+ - If in a list/tuple the last entry is `...` (ellipsis) it means that the next to last entry will be repeated zero or more times. In this way generic types can be created. For example the schema `[str, ...]` represents a list of strings.
33
+
34
+ As of version 2.1, a suitable adapted `vtjson` schema can be used as a Python type hint. Here is the above [example](https://raw.githubusercontent.com/vdbergh/vtjson/refs/heads/main/docs/example2.md) rewritten in a way that is compatible with type hints. E.g. if one wants to ensure that a run object obtained via an api has the correct type one can do
32
35
 
33
36
  ```python
34
- import math
35
- from datetime import datetime
36
- from bson.objectid import ObjectId
37
- from vtjson import glob, ip_address, regex, url
38
-
39
- net_name = regex("nn-[a-z0-9]{12}.nnue", name="net_name")
40
- tc = regex(r"([1-9]\d*/)?\d+(\.\d+)?(\+\d+(\.\d+)?)?", name="tc")
41
- str_int = regex(r"[1-9]\d*", name="str_int")
42
- sha = regex(r"[a-f0-9]{40}", name="sha")
43
- country_code = regex(r"[A-Z][A-Z]", name="country_code")
44
- run_id = regex(r"[a-f0-9]{24}", name="run_id")
45
- uuid = regex(r"[0-9a-zA-Z]{2,}(-[a-f0-9]{4}){3}-[a-f0-9]{12}", name="uuid")
46
- epd_file = glob("*.epd", name="epd_file")
47
- pgn_file = glob("*.pgn", name="pgn_file")
48
-
49
- worker_info_schema = {
50
- "uname": str,
51
- "architecture": [str, str],
52
- "concurrency": int,
53
- "max_memory": int,
54
- "min_threads": int,
55
- "username": str,
56
- "version": int,
57
- "python_version": [int, int, int],
58
- "gcc_version": [int, int, int],
59
- "compiler": union("clang++", "g++"),
60
- "unique_key": uuid,
61
- "modified": bool,
62
- "ARCH": str,
63
- "nps": float,
64
- "near_github_api_limit": bool,
65
- "remote_addr": ip_address,
66
- "country_code": union(country_code, "?"),
67
- }
68
-
69
- results_schema = {
70
- "wins": int,
71
- "losses": int,
72
- "draws": int,
73
- "crashes": int,
74
- "time_losses": int,
75
- "pentanomial": [int, int, int, int, int],
76
- }
77
-
78
- schema = {
79
- "_id?": ObjectId,
80
- "start_time": datetime,
81
- "last_updated": datetime,
82
- "tc_base": float,
83
- "base_same_as_master": bool,
84
- "rescheduled_from?": run_id,
85
- "approved": bool,
86
- "approver": str,
87
- "finished": bool,
88
- "deleted": bool,
89
- "failed": bool,
90
- "is_green": bool,
91
- "is_yellow": bool,
92
- "workers": int,
93
- "cores": int,
94
- "results": results_schema,
95
- "results_info?": {
96
- "style": str,
97
- "info": [str, ...],
98
- },
99
- "args": {
100
- "base_tag": str,
101
- "new_tag": str,
102
- "base_nets": [net_name, ...],
103
- "new_nets": [net_name, ...],
104
- "num_games": int,
105
- "tc": tc,
106
- "new_tc": tc,
107
- "book": union(epd_file, pgn_file),
108
- "book_depth": str_int,
109
- "threads": int,
110
- "resolved_base": sha,
111
- "resolved_new": sha,
112
- "msg_base": str,
113
- "msg_new": str,
114
- "base_options": str,
115
- "new_options": str,
116
- "info": str,
117
- "base_signature": str_int,
118
- "new_signature": str_int,
119
- "username": str,
120
- "tests_repo": url,
121
- "auto_purge": bool,
122
- "throughput": float,
123
- "itp": float,
124
- "priority": float,
125
- "adjudication": bool,
126
- "sprt?": {
127
- "alpha": 0.05,
128
- "beta": 0.05,
129
- "elo0": float,
130
- "elo1": float,
131
- "elo_model": "normalized",
132
- "state": union("", "accepted", "rejected"),
133
- "llr": float,
134
- "batch_size": int,
135
- "lower_bound": -math.log(19),
136
- "upper_bound": math.log(19),
137
- "lost_samples?": int,
138
- "illegal_update?": int,
139
- "overshoot?": {
140
- "last_update": int,
141
- "skipped_updates": int,
142
- "ref0": float,
143
- "m0": float,
144
- "sq0": float,
145
- "ref1": float,
146
- "m1": float,
147
- "sq1": float,
148
- },
149
- },
150
- "spsa?": {
151
- "A": float,
152
- "alpha": float,
153
- "gamma": float,
154
- "raw_params": str,
155
- "iter": int,
156
- "num_iter": int,
157
- "params": [
158
- {
159
- "name": str,
160
- "start": float,
161
- "min": float,
162
- "max": float,
163
- "c_end": float,
164
- "r_end": float,
165
- "c": float,
166
- "a_end": float,
167
- "a": float,
168
- "theta": float,
169
- },
170
- ...,
171
- ],
172
- "param_history?": [
173
- [{"theta": float, "R": float, "c": float}, ...],
174
- ...,
175
- ],
176
- },
177
- },
178
- "tasks": [
179
- {
180
- "num_games": int,
181
- "active": bool,
182
- "last_updated": datetime,
183
- "start": int,
184
- "residual?": float,
185
- "residual_color?": str,
186
- "bad?": True,
187
- "stats": results_schema,
188
- "worker_info": worker_info_schema,
189
- },
190
- ...,
191
- ],
192
- "bad_tasks?": [
193
- {
194
- "num_games": int,
195
- "active": False,
196
- "last_updated": datetime,
197
- "start": int,
198
- "residual": float,
199
- "residual_color": str,
200
- "bad": True,
201
- "task_id": int,
202
- "stats": results_schema,
203
- "worker_info": worker_info_schema,
204
- },
205
- ...,
206
- ],
207
- }
208
- ```
37
+ from typing import assert_type
209
38
 
210
- ## Conventions
39
+ def f(run_from_api: object, ...) -> ...:
40
+ run = safe_cast(runs_schema, run_from_api)
41
+ assert_type(run, runs_schema) # Confirm that run has indeed the correct type now
42
+ ```
211
43
 
212
- - As in typescript, a (string) key ending in `?` represents an optional key. The corresponding schema (the item the key points to) will only be used for validation when the key is present in the object that should be validated. A key can also be made optional by wrapping it as `optional_key(key)`.
213
- - If in a list/tuple the last entry is `...` (ellipsis) it means that the next to last entry will be repeated zero or more times. In this way generic types can be created. For example the schema `[str, ...]` represents a list of strings.
44
+ If the cast succeeds then it means that the `run_from_api` object has been validated against the `runs_schema` and its type has been changed accordingly.
214
45
 
215
46
  ## Usage
216
47
 
@@ -358,15 +189,15 @@ A consequence of this algorithm is that non-const keys are automatically optiona
358
189
  `vtjson` recognizes the following type hints as schemas.
359
190
 
360
191
  ```python
361
- Annotated, dict[...], Dict[...], list[...], List[...], tuple[...], Protocol,
362
- Tuple[...], Literal, NewType, TypedDict, Union (or the equivalent operator |).
192
+ Annotated, dict[...], Dict[...], list[...], List[...], tuple[...], Tuple[...],
193
+ Protocol, Literal, NewType, TypedDict, Union (or the equivalent operator |).
363
194
  ```
364
195
 
365
196
  For example `dict[str, str]` is translated internally into the schema `{str: str}`. See below for more information.
366
197
 
367
198
  ### Annotated
368
199
 
369
- - More general vtjson schemas can work along Python type hints by using `typing.Annotated` contruct. The most naive way to do this is via
200
+ - More general vtjson schemas can work along Python type hints by using the `typing.Annotated` contruct. The most naive way to do this is via
370
201
 
371
202
  ```python
372
203
  Annotated[type_hint, vtjson_schema, skip_first]
@@ -405,7 +236,7 @@ For example `dict[str, str]` is translated internally into the schema `{str: str
405
236
 
406
237
  Note that Python imposes strong restrictions on what constitutes a valid type hint but `vtjson` is much more lax about this. Enforcing the restrictions is left to the type checkers or the Python interpreter.
407
238
 
408
- - `TypedDict` A TypedDict type hint is translated into a `dict` schema. E.g.
239
+ - `TypedDict`. A TypedDict type hint is translated into a `dict` schema. E.g.
409
240
 
410
241
  ```python
411
242
  class Movie(TypedDict):
@@ -415,7 +246,7 @@ Note that Python imposes strong restrictions on what constitutes a valid type hi
415
246
 
416
247
  internally becomes `{"title": str, "price": float}`. `vtjson` supports the `total` option to `TypedDict` as well as the `Required` and `NotRequired` annotations of fields, if they are compatible with the Python version being used.
417
248
 
418
- - `Protocol`. A class implementing a protocol is translated into a fields schemas. E.g.
249
+ - `Protocol`. A class implementing a protocol is translated into a `fields` schema. E.g.
419
250
 
420
251
  ```python
421
252
  class Movie(Protocol):
@@ -0,0 +1,8 @@
1
+ vtjson/__init__.py,sha256=oLX4JH6_R7dYtTiGfBG3pQGR21IArspifdmZilbuGOw,68
2
+ vtjson/vtjson.py,sha256=bCndIajECAc65M4aeNcjDJRZ8cLv4Jz4H7ryt7XHwio,65935
3
+ vtjson-2.1.3.dist-info/AUTHORS,sha256=qmxaXxaIO-YPNHJAZ0dcCrnPCs1x9ocbtMksiy4i80M,21
4
+ vtjson-2.1.3.dist-info/LICENSE,sha256=n7xW-zX8xBLHzCdqWIMRuMzBD_ACLcNCwio0LEkKt1o,1077
5
+ vtjson-2.1.3.dist-info/METADATA,sha256=hoLkMLs5Kh6GVA-Xw8QX1TlCJDB7gSrzRSYrjzj9OqM,23506
6
+ vtjson-2.1.3.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
7
+ vtjson-2.1.3.dist-info/top_level.txt,sha256=9DlSF3l63igcvnYPcj117F2hzOW4Nx0N-JBoW3jjBZM,7
8
+ vtjson-2.1.3.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- vtjson/__init__.py,sha256=oLX4JH6_R7dYtTiGfBG3pQGR21IArspifdmZilbuGOw,68
2
- vtjson/vtjson.py,sha256=yUcIkc2qOxBr9ThaESForCkIR91bxvp_MS0UCl-OTfM,64659
3
- vtjson-2.1.1.dist-info/AUTHORS,sha256=qmxaXxaIO-YPNHJAZ0dcCrnPCs1x9ocbtMksiy4i80M,21
4
- vtjson-2.1.1.dist-info/LICENSE,sha256=n7xW-zX8xBLHzCdqWIMRuMzBD_ACLcNCwio0LEkKt1o,1077
5
- vtjson-2.1.1.dist-info/METADATA,sha256=3jfSDOvDaKFRPhJGO0p0tvKJYKf0Jg16JWoMQvN1usk,27476
6
- vtjson-2.1.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
7
- vtjson-2.1.1.dist-info/top_level.txt,sha256=9DlSF3l63igcvnYPcj117F2hzOW4Nx0N-JBoW3jjBZM,7
8
- vtjson-2.1.1.dist-info/RECORD,,
File without changes