DefEssential 0.1.8__24-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.
Files changed (104) hide show
  1. DefEssential/Adapter/Implements/Gunicorn/Server.py +152 -0
  2. DefEssential/Adapter/Implements/Gunicorn/Transport.py +171 -0
  3. DefEssential/Adapter/Implements/Gunicorn/__init__.py +5 -0
  4. DefEssential/Adapter/Implements/Python/Server.py +273 -0
  5. DefEssential/Adapter/Implements/Python/__init__.py +5 -0
  6. DefEssential/Adapter/Implements/Uvicorn/HTTP.py +256 -0
  7. DefEssential/Adapter/Implements/Uvicorn/Portal.py +65 -0
  8. DefEssential/Adapter/Implements/Uvicorn/Server.py +137 -0
  9. DefEssential/Adapter/Implements/Uvicorn/WebSocket.py +222 -0
  10. DefEssential/Adapter/Implements/Uvicorn/__init__.py +5 -0
  11. DefEssential/Adapter/Implements/__init__.py +3 -0
  12. DefEssential/Adapter/RequestHandler.py +109 -0
  13. DefEssential/Adapter/Server.py +54 -0
  14. DefEssential/Adapter/WSGI.py +219 -0
  15. DefEssential/Adapter/__init__.py +5 -0
  16. DefEssential/Application.py +657 -0
  17. DefEssential/Body.py +50 -0
  18. DefEssential/Content.py +18 -0
  19. DefEssential/Credentials.py +245 -0
  20. DefEssential/CrossOriginResourceSharing.py +107 -0
  21. DefEssential/Descriptor/DataObject.py +409 -0
  22. DefEssential/Descriptor/Types.py +248 -0
  23. DefEssential/Descriptor/Value.py +188 -0
  24. DefEssential/Descriptor/__init__.py +172 -0
  25. DefEssential/Error.py +66 -0
  26. DefEssential/Extension.py +191 -0
  27. DefEssential/Extensions/File.py +95 -0
  28. DefEssential/Extensions/FileSystem.py +195 -0
  29. DefEssential/Extensions/Metrics.py +511 -0
  30. DefEssential/Extensions/Ping.py +93 -0
  31. DefEssential/Extensions/Specifications.py +342 -0
  32. DefEssential/Extensions/Status.html +207 -0
  33. DefEssential/Extensions/Status.py +110 -0
  34. DefEssential/Extensions/Swagger.html +77 -0
  35. DefEssential/Extensions/Swagger.py +117 -0
  36. DefEssential/Extensions/Utils/Routes.py +42 -0
  37. DefEssential/Extensions/Utils/__init__.py +43 -0
  38. DefEssential/Extensions/__init__.py +21 -0
  39. DefEssential/IO/Readers/ChunkedStream.py +106 -0
  40. DefEssential/IO/Readers/ContentReader.py +36 -0
  41. DefEssential/IO/Readers/ContentReaders.py +241 -0
  42. DefEssential/IO/Readers/__init__.py +25 -0
  43. DefEssential/IO/RequestReader.py +125 -0
  44. DefEssential/IO/ResponseWriter.py +242 -0
  45. DefEssential/IO/WebSocket/WebSocket.py +288 -0
  46. DefEssential/IO/WebSocket/WebSocketError.py +17 -0
  47. DefEssential/IO/WebSocket/WebSocketFrame.py +68 -0
  48. DefEssential/IO/WebSocket/WebSocketFrameReader.py +98 -0
  49. DefEssential/IO/WebSocket/WebSocketHandshake.py +94 -0
  50. DefEssential/IO/WebSocket/__init__.py +13 -0
  51. DefEssential/IO/Writers/ChunkedStreamWriter.py +19 -0
  52. DefEssential/IO/Writers/ServerSentEvents.py +41 -0
  53. DefEssential/IO/Writers/ServerSentEventsWriter.py +67 -0
  54. DefEssential/IO/Writers/StreamWriter.py +57 -0
  55. DefEssential/IO/Writers/__init__.py +11 -0
  56. DefEssential/IO/__init__.py +7 -0
  57. DefEssential/Request.py +110 -0
  58. DefEssential/RequestRunner.py +261 -0
  59. DefEssential/Response.py +89 -0
  60. DefEssential/ResponseSpecification.py +23 -0
  61. DefEssential/Responses/ResponseFile.py +108 -0
  62. DefEssential/Responses/__init__.py +63 -0
  63. DefEssential/Route.py +55 -0
  64. DefEssential/Router.py +48 -0
  65. DefEssential/Runners/RunExtension.py +116 -0
  66. DefEssential/Runners/RunFactory.py +26 -0
  67. DefEssential/Runners/RunOptions.py +77 -0
  68. DefEssential/Runners/RunRequest.py +198 -0
  69. DefEssential/Runners/__init__.py +5 -0
  70. DefEssential/Startup.py +104 -0
  71. DefEssential/Utils/FileSystemObject/Configuration.py +15 -0
  72. DefEssential/Utils/FileSystemObject/Connection.py +57 -0
  73. DefEssential/Utils/FileSystemObject/File.py +41 -0
  74. DefEssential/Utils/FileSystemObject/Implements/FileSystem/Configuration.py +15 -0
  75. DefEssential/Utils/FileSystemObject/Implements/FileSystem/Connection.py +115 -0
  76. DefEssential/Utils/FileSystemObject/Implements/FileSystem/File.py +77 -0
  77. DefEssential/Utils/FileSystemObject/Implements/FileSystem/__init__.py +11 -0
  78. DefEssential/Utils/FileSystemObject/Implements/__init__.py +3 -0
  79. DefEssential/Utils/FileSystemObject/__init__.py +11 -0
  80. DefEssential/Utils/Loader.py +81 -0
  81. DefEssential/Utils/__init__.py +3 -0
  82. DefEssential/Validator/Pattern.py +36 -0
  83. DefEssential/Validator/Patterns/Array.py +30 -0
  84. DefEssential/Validator/Patterns/Common.py +92 -0
  85. DefEssential/Validator/Patterns/Compare.py +180 -0
  86. DefEssential/Validator/Patterns/Condition.py +60 -0
  87. DefEssential/Validator/Patterns/DataObject.py +85 -0
  88. DefEssential/Validator/Patterns/DateTime.py +111 -0
  89. DefEssential/Validator/Patterns/Number.py +43 -0
  90. DefEssential/Validator/Patterns/Object.py +150 -0
  91. DefEssential/Validator/Patterns/Size.py +110 -0
  92. DefEssential/Validator/Patterns/String.py +85 -0
  93. DefEssential/Validator/Patterns/Type.py +350 -0
  94. DefEssential/Validator/Patterns/__init__.py +137 -0
  95. DefEssential/Validator/Validate.py +29 -0
  96. DefEssential/Validator/Validator.py +30 -0
  97. DefEssential/Validator/__init__.py +11 -0
  98. DefEssential/Validators.py +255 -0
  99. DefEssential/__init__.py +83 -0
  100. defessential-0.1.8.dist-info/METADATA +86 -0
  101. defessential-0.1.8.dist-info/RECORD +104 -0
  102. defessential-0.1.8.dist-info/WHEEL +6 -0
  103. defessential-0.1.8.dist-info/licenses/LICENSE +674 -0
  104. defessential-0.1.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,409 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Value import (
4
+ Descriptor,
5
+ Condition,
6
+ Value,
7
+ Schema,
8
+ OneOf,
9
+ TypeMapper,
10
+ TypeEncoder,
11
+ )
12
+
13
+ from .Types import (
14
+ Boolean,
15
+ Integer,
16
+ Number,
17
+ String,
18
+ Binary,
19
+ Array,
20
+ Object,
21
+ Properties,
22
+ )
23
+
24
+ from dataclasses import (
25
+ is_dataclass,
26
+ MISSING,
27
+ asdict,
28
+ )
29
+ from json import (
30
+ dumps,
31
+ loads,
32
+ )
33
+ from typing import (
34
+ Type,
35
+ Any,
36
+ Union,
37
+ Optional,
38
+ Dict,
39
+ Iterable,
40
+ Annotated,
41
+ get_origin,
42
+ get_args,
43
+ get_type_hints,
44
+ ForwardRef,
45
+ )
46
+
47
+ __all__ = (
48
+ 'SchemaToDataObject',
49
+ 'DataObjectToSchema',
50
+ 'DataObjectToObject',
51
+ )
52
+
53
+
54
+ def SchemaToDataObject(
55
+ o: Schema,
56
+ name: Optional[str] = None,
57
+ ) -> object:
58
+ # TODO: Implement dynamic dataclass generation from the schema.
59
+ raise NotImplementedError('SchemaToDataObject is not implemented yet')
60
+
61
+
62
+ class DataObjectToSchema(object):
63
+ def __init__(
64
+ self,
65
+ mapper: Optional[TypeMapper] = None,
66
+ ) -> None:
67
+ self.mapper = mapper
68
+ return
69
+ def __value__(
70
+ self,
71
+ o: object,
72
+ format: Optional[object] = None,
73
+ description: Optional[str] = None,
74
+ required: bool = True,
75
+ default: Any = MISSING,
76
+ metadata: Any = None,
77
+ ) -> Union[Descriptor, Condition, Schema]:
78
+ args = get_args(o)
79
+ origin = get_origin(o)
80
+ if origin is not None and not args:
81
+ return self.__value__(
82
+ origin,
83
+ format=format,
84
+ description=description,
85
+ required=required,
86
+ default=default,
87
+ metadata=metadata,
88
+ )
89
+ if args:
90
+ if len(args) == 1:
91
+ if origin is list:
92
+ return self.__value__(
93
+ list,
94
+ format=args[0],
95
+ description=description,
96
+ required=required,
97
+ default=default,
98
+ metadata=metadata,
99
+ )
100
+ if origin is tuple:
101
+ return self.__value__(
102
+ tuple,
103
+ format=args[0],
104
+ description=description,
105
+ required=required,
106
+ default=default,
107
+ metadata=metadata,
108
+ )
109
+ if origin is set:
110
+ return self.__value__(
111
+ set,
112
+ format=args[0],
113
+ description=description,
114
+ required=required,
115
+ default=default,
116
+ metadata=metadata,
117
+ )
118
+ if origin is Union:
119
+ return self.__value__(
120
+ Union,
121
+ format=args[0],
122
+ description=description,
123
+ required=required,
124
+ default=default,
125
+ metadata=metadata,
126
+ )
127
+ raise TypeError('{} is not a supported type'.format(o))
128
+ if len(args) == 2:
129
+ if origin is Union:
130
+ if args[0] is type(None): # Union[type, None]
131
+ return self.__value__(
132
+ args[1],
133
+ description=description,
134
+ required=False,
135
+ default=None,
136
+ metadata=metadata,
137
+ )
138
+ if args[1] is type(None): # Union[None, type]
139
+ return self.__value__(
140
+ args[0],
141
+ description=description,
142
+ required=False,
143
+ default=None,
144
+ metadata=metadata,
145
+ )
146
+ # Union[type, type]
147
+ return self.__value__(
148
+ origin,
149
+ format=(args[0], args[1]),
150
+ description=description,
151
+ required=required,
152
+ default=default,
153
+ metadata=metadata,
154
+ )
155
+ if origin is dict: # Dict[type, type]
156
+ return self.__value__(
157
+ dict,
158
+ description=description,
159
+ required=required,
160
+ default=default,
161
+ metadata=metadata,
162
+ )
163
+ if origin is Annotated:
164
+ if isinstance(args[1], str):
165
+ return self.__value__(
166
+ args[0],
167
+ description=args[1],
168
+ required=required,
169
+ default=default,
170
+ )
171
+ return self.__value__(
172
+ args[0],
173
+ description=description,
174
+ required=required,
175
+ default=default,
176
+ metadata=args[1],
177
+ )
178
+ raise TypeError('{} is not a supported type'.format(o))
179
+ if origin is Union:
180
+ fs= [self.__value__(f) for f in args]
181
+ return OneOf(*fs, required=required)
182
+ if origin is Annotated:
183
+ if isinstance(args[1], str):
184
+ return self.__value__(
185
+ args[0],
186
+ description=args[1],
187
+ required=required,
188
+ default=default,
189
+ metadata=args[2] if isinstance(args[2], dict) else args[2:],
190
+ )
191
+ if isinstance(args[2], str):
192
+ return self.__value__(
193
+ args[0],
194
+ description=args[2],
195
+ required=required,
196
+ default=default,
197
+ metadata=args[1],
198
+ )
199
+ return self.__value__(
200
+ args[0],
201
+ description=description,
202
+ required=required,
203
+ default=default,
204
+ metadata=args[1:],
205
+ )
206
+ raise TypeError('{} is not a supported type'.format(o))
207
+ if o is bool:
208
+ kwargs: Dict[str, Any] = {
209
+ 'description': description,
210
+ 'required': required,
211
+ }
212
+ if default is not MISSING:
213
+ kwargs['default'] = default
214
+ if metadata and isinstance(metadata, dict):
215
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
216
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
217
+ return Boolean(**kwargs)
218
+ if o is int:
219
+ kwargs = {
220
+ 'description': description,
221
+ 'required': required,
222
+ }
223
+ if default is not MISSING:
224
+ kwargs['default'] = default
225
+ if metadata and isinstance(metadata, dict):
226
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
227
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
228
+ if 'format' in metadata.keys(): kwargs['format'] = metadata['format']
229
+ if 'min' in metadata.keys(): kwargs['min'] = metadata['min']
230
+ if 'max' in metadata.keys(): kwargs['max'] = metadata['max']
231
+ if 'enum' in metadata.keys(): kwargs['enum'] = metadata['enum']
232
+ return Integer(**kwargs)
233
+ if o is float:
234
+ kwargs = {
235
+ 'description': description,
236
+ 'required': required,
237
+ }
238
+ if default is not MISSING:
239
+ kwargs['default'] = default
240
+ if metadata and isinstance(metadata, dict):
241
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
242
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
243
+ if 'format' in metadata.keys(): kwargs['format'] = metadata['format']
244
+ if 'min' in metadata.keys(): kwargs['min'] = metadata['min']
245
+ if 'max' in metadata.keys(): kwargs['max'] = metadata['max']
246
+ if 'enum' in metadata.keys(): kwargs['enum'] = metadata['enum']
247
+ return Number(**kwargs)
248
+ if o is str:
249
+ kwargs = {
250
+ 'description': description,
251
+ 'required': required,
252
+ }
253
+ if default is not MISSING:
254
+ kwargs['default'] = default
255
+ if metadata and isinstance(metadata, dict):
256
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
257
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
258
+ if 'format' in metadata.keys(): kwargs['format'] = metadata['format']
259
+ if 'min' in metadata.keys(): kwargs['min'] = metadata['min']
260
+ if 'max' in metadata.keys(): kwargs['max'] = metadata['max']
261
+ if 'enum' in metadata.keys(): kwargs['enum'] = metadata['enum']
262
+ return String(**kwargs)
263
+ if o is bytes:
264
+ kwargs = {
265
+ 'description': description,
266
+ 'required': required,
267
+ }
268
+ if default is not MISSING:
269
+ kwargs['default'] = default
270
+ if metadata and isinstance(metadata, dict):
271
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
272
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
273
+ return Binary(**kwargs)
274
+ if o is list:
275
+ kwargs = {
276
+ 'description': description,
277
+ 'required': required,
278
+ }
279
+ if default is not MISSING:
280
+ kwargs['default'] = default
281
+ if metadata and isinstance(metadata, dict):
282
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
283
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
284
+ if format is None and 'format' in metadata:
285
+ format = metadata['format']
286
+ if format is not None:
287
+ candidates = tuple(format) if isinstance(format, Iterable) else (format,)
288
+ nodes = [self.__value__(candidate) for candidate in candidates]
289
+ kwargs['format'] = nodes[0] if len(nodes) == 1 else OneOf(*nodes)
290
+ return Array(**kwargs)
291
+ if o is tuple:
292
+ kwargs = {
293
+ 'description': description,
294
+ 'required': required,
295
+ }
296
+ if default is not MISSING:
297
+ kwargs['default'] = default
298
+ if metadata and isinstance(metadata, dict):
299
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
300
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
301
+ return Array(**kwargs)
302
+ if o is set:
303
+ kwargs = {
304
+ 'description': description,
305
+ 'required': required,
306
+ }
307
+ if default is not MISSING:
308
+ kwargs['default'] = default
309
+ if metadata and isinstance(metadata, dict):
310
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
311
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
312
+ return Array(**kwargs)
313
+ if o is dict:
314
+ kwargs = {
315
+ 'description': description,
316
+ 'required': required,
317
+ }
318
+ if default is not MISSING:
319
+ kwargs['default'] = default
320
+ if metadata and isinstance(metadata, dict):
321
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
322
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
323
+ return Object(**kwargs)
324
+ if o is Union:
325
+ kwargs = {}
326
+ if default is not MISSING:
327
+ kwargs['default'] = default
328
+ if not format:
329
+ raise TypeError('Union type must have format')
330
+ candidates = tuple(format) if isinstance(format, Iterable) else (format,)
331
+ return OneOf(*[self.__value__(f) for f in candidates], **kwargs)
332
+ if isinstance(o, type) and is_dataclass(o):
333
+ return self.__call__(o, required=required)
334
+ if isinstance(o, ForwardRef):
335
+ if o.__forward_arg__ in globals():
336
+ return self.__value__(globals()[o.__forward_arg__], description=description, required=required, default=default, metadata=metadata)
337
+ if o.__forward_arg__ in locals():
338
+ return self.__value__(locals()[o.__forward_arg__], description=description, required=required, default=default, metadata=metadata)
339
+ raise TypeError('Cannot resolve schema forward reference: ' + o.__forward_arg__)
340
+ if self.mapper and isinstance(o, type):
341
+ V, kws = self.mapper(o)
342
+ if V:
343
+ if not issubclass(V, Value):
344
+ raise TypeError('{} is not a Value'.format(V))
345
+ kwargs = {
346
+ 'description': description,
347
+ 'required': required,
348
+ }
349
+ if default is not MISSING:
350
+ kwargs['default'] = default
351
+ if metadata and isinstance(metadata, dict):
352
+ if 'description' in metadata.keys(): kwargs['description'] = metadata['description']
353
+ if 'required' in metadata.keys(): kwargs['required'] = metadata['required']
354
+ if V in (Integer, Number, String):
355
+ if 'format' in metadata.keys(): kwargs['format'] = metadata['format']
356
+ if 'min' in metadata.keys(): kwargs['min'] = metadata['min']
357
+ if 'max' in metadata.keys(): kwargs['max'] = metadata['max']
358
+ if 'enum' in metadata.keys(): kwargs['enum'] = metadata['enum']
359
+ kwargs.update(kws)
360
+ return V(**kwargs)
361
+ raise TypeError('{} is not a supported type'.format(o))
362
+
363
+ def __call__(
364
+ self,
365
+ o: Type[object],
366
+ description: Optional[str] = None,
367
+ required: bool = True,
368
+ ) -> Schema:
369
+ if not is_dataclass(o):
370
+ raise TypeError('{} is not a DataObject(dataclass).'.format(o.__name__))
371
+ hints = get_type_hints(o, include_extras=True)
372
+ return Schema(
373
+ name=description or o.__name__,
374
+ format=Object(
375
+ description=description or o.__name__,
376
+ properties=Properties(
377
+ **{
378
+ f.name: self.__value__(
379
+ hints[f.name],
380
+ required=True if f.default is MISSING and f.default_factory is MISSING else False,
381
+ default=f.default_factory() if f.default_factory is not MISSING else f.default,
382
+ ) for f in o.__dataclass_fields__.values()
383
+ }
384
+ ),
385
+ required=required,
386
+ )
387
+ )
388
+
389
+
390
+ class DataObjectToObject(object):
391
+ def __init__(
392
+ self,
393
+ encoder: Optional[TypeEncoder] = None,
394
+ ) -> None:
395
+ self.encoder = encoder
396
+ return
397
+
398
+ def __call__(
399
+ self,
400
+ o: object,
401
+ ) -> Dict[str, Any]:
402
+ if not is_dataclass(o) or isinstance(o, type):
403
+ raise TypeError('{} is not a DataObject(dataclass) instance.'.format(type(o).__name__))
404
+ _ = dumps(
405
+ asdict(o),
406
+ default=self.encoder,
407
+ ensure_ascii=False,
408
+ )
409
+ return loads(_)
@@ -0,0 +1,248 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Value import (
4
+ Descriptor,
5
+ Type,
6
+ Value,
7
+ Condition,
8
+ Schema,
9
+ )
10
+ from typing import (
11
+ Optional,
12
+ Union,
13
+ Sequence,
14
+ Any,
15
+ Dict,
16
+ Iterable,
17
+ )
18
+ from enum import Enum
19
+
20
+ __all__ = (
21
+ 'Boolean',
22
+ 'IntegerFormat',
23
+ 'Integer',
24
+ 'NumberFormat',
25
+ 'Number',
26
+ 'StringFormat',
27
+ 'String',
28
+ 'Binary',
29
+ 'BinaryBase64Encoded',
30
+ 'Array',
31
+ 'Object',
32
+ 'Properties',
33
+ )
34
+
35
+
36
+ class Boolean(Value):
37
+ def __init__(
38
+ self,
39
+ description: Optional[str] = None,
40
+ default: Any = None,
41
+ required: bool = True,
42
+ ) -> None:
43
+ super().__init__(
44
+ type=Type.Boolean,
45
+ description=description,
46
+ default=default,
47
+ required=required,
48
+ )
49
+ return
50
+
51
+
52
+ class IntegerFormat(str, Enum):
53
+ int32 = 'int32'
54
+ int64 = 'int64'
55
+ uint32 = 'uint32'
56
+ uint64 = 'uint64'
57
+ byte = 'byte'
58
+ uuid = 'uuid'
59
+ datetime = 'date-time'
60
+ def __str__(self): return self.value
61
+
62
+
63
+ class Integer(Value):
64
+ def __init__(
65
+ self,
66
+ description: Optional[str] = None,
67
+ format: Optional[Union[str, IntegerFormat]] = None,
68
+ min: Any = None,
69
+ max: Any = None,
70
+ default: Any = None,
71
+ enum: Optional[Sequence[str]] = None,
72
+ required: bool = True,
73
+ ) -> None:
74
+ super().__init__(
75
+ type=Type.Integer,
76
+ description=description,
77
+ format=str(format) if format and isinstance(format, IntegerFormat) else format,
78
+ min=min,
79
+ max=max,
80
+ default=default,
81
+ enum=enum,
82
+ required=required,
83
+ )
84
+ return
85
+
86
+
87
+ class NumberFormat(str, Enum):
88
+ float = 'float'
89
+ double = 'double'
90
+ decimal = 'decimal'
91
+ def __str__(self): return self.value
92
+
93
+
94
+ class Number(Value):
95
+ def __init__(
96
+ self,
97
+ description: Optional[str] = None,
98
+ format: Optional[Union[str, NumberFormat]] = None,
99
+ min: Any = None,
100
+ max: Any = None,
101
+ default: Any = None,
102
+ enum: Optional[Sequence[str]] = None,
103
+ required: bool = True,
104
+ ) -> None:
105
+ super().__init__(
106
+ type=Type.Number,
107
+ description=description,
108
+ format=str(format) if format and isinstance(format, NumberFormat) else format,
109
+ min=min,
110
+ max=max,
111
+ default=default,
112
+ enum=enum,
113
+ required=required,
114
+ )
115
+ return
116
+
117
+
118
+ class StringFormat(str, Enum):
119
+ datetime = 'date-time' # date-time, ISO 8601
120
+ date = 'date' # date, ISO 8601
121
+ time = 'time' # time, ISO 8601
122
+ password = 'password' # password
123
+ byte = 'byte' # byte, base64 encoded
124
+ binary = 'binary' # binary
125
+ email = 'email' # email
126
+ uuid = 'uuid' # uuid
127
+ uri = 'uri' # uri
128
+ hostname = 'hostname' # hostname
129
+ ipv4 = 'ipv4' # ipv4
130
+ ipv6 = 'ipv6' # ipv6
131
+ regex = 'regex' # regex
132
+ xml = 'xml' # xml
133
+ def __str__(self): return self.value
134
+
135
+
136
+ class String(Value):
137
+ def __init__(
138
+ self,
139
+ description: Optional[str] = None,
140
+ format: Optional[Union[str, StringFormat]] = None,
141
+ min: Any = None,
142
+ max: Any = None,
143
+ default: Any = None,
144
+ enum: Optional[Sequence[str]] = None,
145
+ required: bool = True,
146
+ ) -> None:
147
+ super().__init__(
148
+ type=Type.String,
149
+ description=description,
150
+ format=str(format) if format and isinstance(format, StringFormat) else format,
151
+ min=min,
152
+ max=max,
153
+ default=default,
154
+ enum=enum,
155
+ required=required,
156
+ )
157
+ if min is not None:
158
+ del self['minimum']
159
+ self['minLength'] = min
160
+ if max is not None:
161
+ del self['maximum']
162
+ self['maxLength'] = max
163
+ return
164
+
165
+
166
+ class BinaryBase64Encoded(String):
167
+ def __init__(
168
+ self,
169
+ description: Optional[str] = None,
170
+ default: Optional[str] = None,
171
+ required: bool = True,
172
+ ) -> None:
173
+ super().__init__(
174
+ description=description,
175
+ format=StringFormat.byte,
176
+ default=default,
177
+ required=required,
178
+ )
179
+ return
180
+
181
+
182
+ class Binary(String):
183
+ def __init__(
184
+ self,
185
+ description: Optional[str] = None,
186
+ default: Optional[bytes] = None,
187
+ required: bool = True,
188
+ ) -> None:
189
+ super().__init__(
190
+ description=description,
191
+ format=StringFormat.binary,
192
+ default=default,
193
+ required=required,
194
+ )
195
+ return
196
+
197
+
198
+ class Properties(Descriptor):
199
+ def __init__(
200
+ self,
201
+ **kwargs: Union[Descriptor, Condition, Schema],
202
+ ) -> None:
203
+ super().__init__(**{
204
+ k: v.format if isinstance(v, Schema) else v
205
+ for k, v in kwargs.items()
206
+ })
207
+ return
208
+
209
+
210
+ class Object(Descriptor):
211
+ def __init__(
212
+ self,
213
+ description: Optional[str] = None,
214
+ properties: Optional[Properties] = None,
215
+ default: Optional[Dict[str, Any]] = None,
216
+ required: bool = True,
217
+ ) -> None:
218
+ properties = properties if properties is not None else Properties()
219
+ super().__init__(
220
+ type=Type.Object,
221
+ properties=properties,
222
+ )
223
+ if description: self['description'] = description
224
+ self['required'] = []
225
+ for k, v in properties.items():
226
+ if hasattr(v, 'required') and getattr(v, 'required'):
227
+ self['required'].append(k)
228
+ if default is not None: self['default'] = default
229
+ self.required = required
230
+ return
231
+
232
+
233
+ class Array(Descriptor):
234
+ def __init__(
235
+ self,
236
+ description: Optional[str] = None,
237
+ format: Optional[Union[Descriptor, Condition, Schema]] = None,
238
+ default: Optional[Iterable[Any]] = None,
239
+ required: bool = True,
240
+ ) -> None:
241
+ super().__init__(
242
+ type=Type.Array,
243
+ )
244
+ if description: self['description'] = description
245
+ if format: self['items'] = format.format if isinstance(format, Schema) else format
246
+ if default is not None: self['default'] = default
247
+ self.required = required
248
+ return