voluptuous-openapi 0.0.1__tar.gz → 0.0.2__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.
- {voluptuous-openapi-0.0.1/voluptuous_openapi.egg-info → voluptuous_openapi-0.0.2}/PKG-INFO +1 -1
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/setup.py +1 -1
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/tests/test_lib.py +107 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi/__init__.py +83 -17
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2/voluptuous_openapi.egg-info}/PKG-INFO +1 -1
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/LICENSE +0 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/MANIFEST.in +0 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/README.md +0 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/setup.cfg +0 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi.egg-info/SOURCES.txt +0 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi.egg-info/dependency_links.txt +0 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi.egg-info/requires.txt +0 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi.egg-info/top_level.txt +0 -0
- {voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi.egg-info/zip-safe +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
3
|
import voluptuous as vol
|
|
4
|
+
from typing import Any, TypeVar
|
|
4
5
|
|
|
5
6
|
from voluptuous_openapi import UNSUPPORTED, convert
|
|
6
7
|
|
|
@@ -191,6 +192,7 @@ def test_custom_serializer():
|
|
|
191
192
|
def test_constant():
|
|
192
193
|
for value in True, False, "Hello", 1, None:
|
|
193
194
|
assert {"enum": [value]} == convert(vol.Schema(value))
|
|
195
|
+
assert {"enum": [None]} == convert(vol.Schema(type(None)))
|
|
194
196
|
|
|
195
197
|
|
|
196
198
|
def test_enum():
|
|
@@ -207,6 +209,20 @@ def test_list():
|
|
|
207
209
|
"items": {"type": "string"},
|
|
208
210
|
} == convert(vol.Schema([str]))
|
|
209
211
|
|
|
212
|
+
assert {"type": "array", "items": {"type": "string"}} == convert(vol.Schema(list))
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_any_of():
|
|
216
|
+
assert {"anyOf": [{"type": "number"}, {"type": "integer"}]} == convert(
|
|
217
|
+
vol.Any(float, int)
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_all_of():
|
|
222
|
+
assert {"allOf": [{"minimum": 5}, {"minimum": 10}]} == convert(
|
|
223
|
+
vol.All(vol.Range(min=5), vol.Range(min=10))
|
|
224
|
+
)
|
|
225
|
+
|
|
210
226
|
|
|
211
227
|
def test_key_any():
|
|
212
228
|
assert {
|
|
@@ -223,3 +239,94 @@ def test_key_any():
|
|
|
223
239
|
},
|
|
224
240
|
"required": [],
|
|
225
241
|
} == convert(vol.Schema({vol.Any("name", "area"): str}))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def test_function():
|
|
245
|
+
def validator(data):
|
|
246
|
+
return data
|
|
247
|
+
|
|
248
|
+
assert {
|
|
249
|
+
"type": "object",
|
|
250
|
+
"properties": {"test_data": {"type": "string"}},
|
|
251
|
+
"required": [],
|
|
252
|
+
} == convert(vol.Schema({"test_data": validator}))
|
|
253
|
+
|
|
254
|
+
def validator_str(data: str):
|
|
255
|
+
return data
|
|
256
|
+
|
|
257
|
+
assert {"type": "string"} == convert(vol.Schema(validator_str))
|
|
258
|
+
|
|
259
|
+
def validator_any(data: Any):
|
|
260
|
+
return data
|
|
261
|
+
|
|
262
|
+
assert {} == convert(validator_any)
|
|
263
|
+
assert {"type": "integer"} == convert(vol.All(vol.Coerce(int), lambda x: x / 100))
|
|
264
|
+
|
|
265
|
+
def validator_nullable(data: float | None):
|
|
266
|
+
return data
|
|
267
|
+
|
|
268
|
+
assert {"type": "number", "nullable": True} == convert(
|
|
269
|
+
vol.Schema(validator_nullable)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def validator_union(data: float | int):
|
|
273
|
+
return data
|
|
274
|
+
|
|
275
|
+
assert {"anyOf": [{"type": "number"}, {"type": "integer"}]} == convert(
|
|
276
|
+
vol.Schema(validator_union)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
_T = TypeVar("_T")
|
|
280
|
+
|
|
281
|
+
def validator_nullable_2(value: _T | None):
|
|
282
|
+
return value
|
|
283
|
+
|
|
284
|
+
assert {
|
|
285
|
+
"type": "object",
|
|
286
|
+
"properties": {"var": {"type": "array", "items": {"type": "string"}}},
|
|
287
|
+
"required": [],
|
|
288
|
+
} == convert(vol.Schema({"var": vol.All(validator_nullable_2, [validator_any])}))
|
|
289
|
+
|
|
290
|
+
def validator_list_int(value: list[int]):
|
|
291
|
+
return value
|
|
292
|
+
|
|
293
|
+
assert {"type": "array", "items": {"type": "integer"}} == convert(
|
|
294
|
+
validator_list_int
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
def validator_list_any(value: list[Any]):
|
|
298
|
+
return value
|
|
299
|
+
|
|
300
|
+
assert {"type": "array", "items": {"type": "string"}} == convert(validator_list_any)
|
|
301
|
+
|
|
302
|
+
def validator_list(value: list):
|
|
303
|
+
return value
|
|
304
|
+
|
|
305
|
+
assert {"type": "array", "items": {"type": "string"}} == convert(validator_list)
|
|
306
|
+
|
|
307
|
+
def validator_set_int(value: set[int]):
|
|
308
|
+
return value
|
|
309
|
+
|
|
310
|
+
assert {"type": "array", "items": {"type": "integer"}} == convert(validator_set_int)
|
|
311
|
+
|
|
312
|
+
def validator_set_any(value: set[Any]):
|
|
313
|
+
return value
|
|
314
|
+
|
|
315
|
+
assert {"type": "array", "items": {"type": "string"}} == convert(validator_set_any)
|
|
316
|
+
|
|
317
|
+
def validator_set(value: set):
|
|
318
|
+
return value
|
|
319
|
+
|
|
320
|
+
assert {"type": "array", "items": {"type": "string"}} == convert(validator_set)
|
|
321
|
+
|
|
322
|
+
def validator_dict(value: dict):
|
|
323
|
+
return value
|
|
324
|
+
|
|
325
|
+
assert {"type": "object", "additionalProperties": True} == convert(validator_dict)
|
|
326
|
+
|
|
327
|
+
def validator_dict_int(value: dict[str, int]):
|
|
328
|
+
return value
|
|
329
|
+
|
|
330
|
+
assert {"type": "object", "additionalProperties": {"type": "integer"}} == convert(
|
|
331
|
+
validator_dict_int
|
|
332
|
+
)
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from collections.abc import Callable, Mapping, Sequence
|
|
4
4
|
from enum import Enum
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any, TypeVar, Union, get_args, get_origin, get_type_hints
|
|
6
|
+
from types import NoneType, UnionType
|
|
7
|
+
from inspect import signature
|
|
6
8
|
|
|
7
9
|
import voluptuous as vol
|
|
8
10
|
|
|
@@ -20,6 +22,15 @@ UNSUPPORTED = object()
|
|
|
20
22
|
def convert(schema: Any, *, custom_serializer: Callable | None = None) -> dict:
|
|
21
23
|
"""Convert a voluptuous schema to a OpenAPI Schema object."""
|
|
22
24
|
# pylint: disable=too-many-return-statements,too-many-branches
|
|
25
|
+
|
|
26
|
+
def ensure_default(value: dict[str:Any]):
|
|
27
|
+
"""Make sure that type is set."""
|
|
28
|
+
if all(
|
|
29
|
+
x not in value for x in ("type", "enum", "anyOf", "oneOf", "allOf", "not")
|
|
30
|
+
):
|
|
31
|
+
value["type"] = "string" # Type not determined, using default
|
|
32
|
+
return value
|
|
33
|
+
|
|
23
34
|
additional_properties = None
|
|
24
35
|
if isinstance(schema, vol.Schema):
|
|
25
36
|
if schema.extra == vol.ALLOW_EXTRA:
|
|
@@ -31,6 +42,13 @@ def convert(schema: Any, *, custom_serializer: Callable | None = None) -> dict:
|
|
|
31
42
|
if val is not UNSUPPORTED:
|
|
32
43
|
return val
|
|
33
44
|
|
|
45
|
+
if isinstance(schema, vol.Object):
|
|
46
|
+
schema = schema.schema
|
|
47
|
+
if custom_serializer:
|
|
48
|
+
val = custom_serializer(schema)
|
|
49
|
+
if val is not UNSUPPORTED:
|
|
50
|
+
return val
|
|
51
|
+
|
|
34
52
|
if isinstance(schema, Mapping):
|
|
35
53
|
properties = {}
|
|
36
54
|
required = []
|
|
@@ -67,6 +85,7 @@ def convert(schema: Any, *, custom_serializer: Callable | None = None) -> dict:
|
|
|
67
85
|
if key.default is not vol.UNDEFINED:
|
|
68
86
|
pval["default"] = key.default()
|
|
69
87
|
|
|
88
|
+
pval = ensure_default(pval)
|
|
70
89
|
pkey = str(pkey)
|
|
71
90
|
properties[pkey] = pval
|
|
72
91
|
|
|
@@ -94,7 +113,7 @@ def convert(schema: Any, *, custom_serializer: Callable | None = None) -> dict:
|
|
|
94
113
|
val.update(v)
|
|
95
114
|
if fallback:
|
|
96
115
|
return {"allOf": allOf}
|
|
97
|
-
return val
|
|
116
|
+
return ensure_default(val)
|
|
98
117
|
|
|
99
118
|
if isinstance(schema, (vol.Clamp, vol.Range)):
|
|
100
119
|
val = {}
|
|
@@ -143,18 +162,23 @@ def convert(schema: Any, *, custom_serializer: Callable | None = None) -> dict:
|
|
|
143
162
|
}
|
|
144
163
|
|
|
145
164
|
if isinstance(schema, vol.Any):
|
|
146
|
-
|
|
147
|
-
if
|
|
148
|
-
|
|
165
|
+
schema = schema.validators
|
|
166
|
+
if None in schema or NoneType in schema:
|
|
167
|
+
schema = [val for val in schema if val is not None and val is not NoneType]
|
|
168
|
+
nullable = True
|
|
169
|
+
else:
|
|
170
|
+
nullable = False
|
|
171
|
+
if len(schema) == 1:
|
|
172
|
+
result = convert(schema[0], custom_serializer=custom_serializer)
|
|
173
|
+
else:
|
|
174
|
+
result = {
|
|
175
|
+
"anyOf": [
|
|
176
|
+
convert(val, custom_serializer=custom_serializer) for val in schema
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
if nullable:
|
|
149
180
|
result["nullable"] = True
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
"anyOf": [
|
|
154
|
-
convert(val, custom_serializer=custom_serializer)
|
|
155
|
-
for val in schema.validators
|
|
156
|
-
]
|
|
157
|
-
}
|
|
181
|
+
return result
|
|
158
182
|
|
|
159
183
|
if isinstance(schema, vol.Coerce):
|
|
160
184
|
schema = schema.type
|
|
@@ -166,19 +190,61 @@ def convert(schema: Any, *, custom_serializer: Callable | None = None) -> dict:
|
|
|
166
190
|
if len(schema) == 1:
|
|
167
191
|
return {
|
|
168
192
|
"type": "array",
|
|
169
|
-
"items":
|
|
193
|
+
"items": ensure_default(
|
|
194
|
+
convert(schema[0], custom_serializer=custom_serializer)
|
|
195
|
+
),
|
|
170
196
|
}
|
|
171
197
|
return {
|
|
172
198
|
"type": "array",
|
|
173
199
|
"items": [
|
|
174
|
-
convert(s, custom_serializer=custom_serializer)
|
|
200
|
+
ensure_default(convert(s, custom_serializer=custom_serializer))
|
|
201
|
+
for s in schema.items()
|
|
175
202
|
],
|
|
176
203
|
}
|
|
177
204
|
|
|
178
205
|
if schema in TYPES_MAP:
|
|
179
206
|
return {"type": TYPES_MAP[schema]}
|
|
180
207
|
|
|
181
|
-
if
|
|
182
|
-
return {"
|
|
208
|
+
if schema is list or schema is set:
|
|
209
|
+
return {"type": "array", "items": ensure_default({})}
|
|
210
|
+
|
|
211
|
+
if schema is dict:
|
|
212
|
+
return {"type": "object", "additionalProperties": True}
|
|
213
|
+
|
|
214
|
+
if isinstance(schema, type):
|
|
215
|
+
if issubclass(schema, Enum):
|
|
216
|
+
return {"enum": [item.value for item in schema]}
|
|
217
|
+
elif schema is NoneType:
|
|
218
|
+
return {"enum": [None]}
|
|
219
|
+
|
|
220
|
+
if callable(schema):
|
|
221
|
+
schema = get_type_hints(schema).get(
|
|
222
|
+
list(signature(schema).parameters.keys())[0], Any
|
|
223
|
+
)
|
|
224
|
+
if schema is Any or isinstance(schema, TypeVar):
|
|
225
|
+
return {}
|
|
226
|
+
if isinstance(schema, UnionType) or get_origin(schema) is Union:
|
|
227
|
+
schema = [t for t in get_args(schema) if not isinstance(t, TypeVar)]
|
|
228
|
+
if len(schema) > 1:
|
|
229
|
+
schema = vol.Any(*schema)
|
|
230
|
+
elif len(schema) == 1 and schema[0] is not NoneType:
|
|
231
|
+
schema = schema[0]
|
|
232
|
+
else:
|
|
233
|
+
return {}
|
|
234
|
+
if get_origin(schema) is dict:
|
|
235
|
+
schema = get_args(schema)[1]
|
|
236
|
+
if schema is Any or isinstance(schema, TypeVar):
|
|
237
|
+
schema = dict
|
|
238
|
+
else:
|
|
239
|
+
return {
|
|
240
|
+
"type": "object",
|
|
241
|
+
"additionalProperties": convert(
|
|
242
|
+
schema, custom_serializer=custom_serializer
|
|
243
|
+
),
|
|
244
|
+
}
|
|
245
|
+
if get_origin(schema) is list or get_origin(schema) is set:
|
|
246
|
+
schema = [get_args(schema)[0]]
|
|
247
|
+
|
|
248
|
+
return convert(schema, custom_serializer=custom_serializer)
|
|
183
249
|
|
|
184
250
|
raise ValueError("Unable to convert schema: {}".format(schema))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi.egg-info/requires.txt
RENAMED
|
File without changes
|
{voluptuous-openapi-0.0.1 → voluptuous_openapi-0.0.2}/voluptuous_openapi.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|