jsonbp 0.9.2__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.
- jsonbp/__init__.py +31 -0
- jsonbp/array.py +39 -0
- jsonbp/blueprint.py +412 -0
- jsonbp/declaration.py +14 -0
- jsonbp/error.py +117 -0
- jsonbp/error_type.py +17 -0
- jsonbp/exception.py +4 -0
- jsonbp/field.py +15 -0
- jsonbp/field_type.py +5 -0
- jsonbp/parser.py +549 -0
- jsonbp/ply/__init__.py +5 -0
- jsonbp/ply/lex.py +901 -0
- jsonbp/ply/yacc.py +2482 -0
- jsonbp/types.py +42 -0
- jsonbp-0.9.2.dist-info/LICENSE.txt +21 -0
- jsonbp-0.9.2.dist-info/METADATA +205 -0
- jsonbp-0.9.2.dist-info/RECORD +19 -0
- jsonbp-0.9.2.dist-info/WHEEL +5 -0
- jsonbp-0.9.2.dist-info/top_level.txt +1 -0
jsonbp/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from . import error_type
|
|
5
|
+
from .parser import load, loads, invalidateCache
|
|
6
|
+
from .exception import SchemaViolation
|
|
7
|
+
from .error import useTranslation
|
|
8
|
+
|
|
9
|
+
def useLanguage(languageCode):
|
|
10
|
+
localizationPath = f'messages.{languageCode}.ini'
|
|
11
|
+
|
|
12
|
+
if not os.path.isfile(localizationPath):
|
|
13
|
+
localizationPath = os.path.join(
|
|
14
|
+
os.path.dirname(__file__),
|
|
15
|
+
"localization",
|
|
16
|
+
localizationPath)
|
|
17
|
+
|
|
18
|
+
useTranslation(localizationPath)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
defaultLanguage = "en_US"
|
|
22
|
+
useLanguage(defaultLanguage)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"load",
|
|
26
|
+
"loads",
|
|
27
|
+
"invalidateCache",
|
|
28
|
+
"SchemaViolation",
|
|
29
|
+
"useLanguage"
|
|
30
|
+
]
|
|
31
|
+
|
jsonbp/array.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
from sys import maxsize
|
|
3
|
+
from .exception import SchemaViolation
|
|
4
|
+
from .field import JsonField
|
|
5
|
+
|
|
6
|
+
class JsonArray(JsonField):
|
|
7
|
+
def __init__(self, baseField):
|
|
8
|
+
JsonField.__init__(self,
|
|
9
|
+
baseField.fieldKind,
|
|
10
|
+
baseField.fieldType)
|
|
11
|
+
|
|
12
|
+
self.maxLength = maxsize
|
|
13
|
+
self.minLength = 0
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def applySpec(self, spec, value):
|
|
17
|
+
if not spec in ("minLength", "maxLength"):
|
|
18
|
+
msg = f"Invalid array spec '{spec}'"
|
|
19
|
+
raise SchemaViolation(msg)
|
|
20
|
+
|
|
21
|
+
if value < 0:
|
|
22
|
+
msg = f"Invalid array length: {value}"
|
|
23
|
+
raise SchemaViolation(msg)
|
|
24
|
+
|
|
25
|
+
if "minLength" == spec: self.minLength = value
|
|
26
|
+
if "maxLength" == spec: self.maxLength = value
|
|
27
|
+
|
|
28
|
+
if not (0 <= self.minLength <= self.maxLength and 0 < self.maxLength):
|
|
29
|
+
msg = f"Invalid array length for '{spec}'"
|
|
30
|
+
raise SchemaViolation(msg)
|
|
31
|
+
|
|
32
|
+
#------------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
def makeArray(baseField):
|
|
35
|
+
return JsonArray(baseField)
|
|
36
|
+
|
|
37
|
+
def isArray(decl):
|
|
38
|
+
return isinstance(decl, JsonArray)
|
|
39
|
+
|
jsonbp/blueprint.py
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
|
|
2
|
+
import re
|
|
3
|
+
import decimal
|
|
4
|
+
import json
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from . import field_type
|
|
11
|
+
from . import error_type
|
|
12
|
+
|
|
13
|
+
from .types import primitive_types
|
|
14
|
+
from .error import createErrorForField, createErrorForNode, createErrorForRoot
|
|
15
|
+
from .array import isArray
|
|
16
|
+
|
|
17
|
+
#-------------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
class taggedNumber:
|
|
20
|
+
def __init__(self, strValue): self.strValue = strValue
|
|
21
|
+
def __str__(self): return self.strValue
|
|
22
|
+
__repr__ = __str__
|
|
23
|
+
|
|
24
|
+
#-------------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
def d_integer(fieldName, value, specs):
|
|
27
|
+
strValue = value.strValue if isinstance(value, taggedNumber) else value
|
|
28
|
+
|
|
29
|
+
if None == value:
|
|
30
|
+
return False, createErrorForField(fieldName,
|
|
31
|
+
error_type.NULL_VALUE)
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
rawValue = int(strValue)
|
|
35
|
+
if not specs['min'] <= rawValue <= specs['max']:
|
|
36
|
+
return False, createErrorForField(fieldName,
|
|
37
|
+
error_type.OUTSIDE_RANGE, value=rawValue)
|
|
38
|
+
|
|
39
|
+
except Exception as e:
|
|
40
|
+
return False, createErrorForField(fieldName,
|
|
41
|
+
error_type.INTEGER_PARSING, text=strValue)
|
|
42
|
+
|
|
43
|
+
return True, rawValue
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def d_float(fieldName, value, specs):
|
|
47
|
+
strValue = value.strValue if isinstance(value, taggedNumber) else value
|
|
48
|
+
|
|
49
|
+
if None == value:
|
|
50
|
+
return False, createErrorForField(fieldName,
|
|
51
|
+
error_type.NULL_VALUE)
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
sanedValue = strValue.replace('Infinity', 'inf')
|
|
55
|
+
rawValue = float(sanedValue)
|
|
56
|
+
|
|
57
|
+
if not specs['min'] <= rawValue <= specs['max']:
|
|
58
|
+
return False, createErrorForField(fieldName,
|
|
59
|
+
error_type.OUTSIDE_RANGE, value=rawValue)
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
return False, createErrorForField(fieldName,
|
|
63
|
+
error_type.FLOAT_PARSING, text=strValue)
|
|
64
|
+
|
|
65
|
+
return True, rawValue
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
roundingContext = decimal.Context(rounding=decimal.ROUND_DOWN)
|
|
69
|
+
specialChars = r'.^$*+?|'
|
|
70
|
+
|
|
71
|
+
def d_decimal(fieldName, value, specs):
|
|
72
|
+
strValue = value.strValue if isinstance(value, taggedNumber) else value
|
|
73
|
+
|
|
74
|
+
if None == strValue:
|
|
75
|
+
return False, createErrorForField(fieldName,
|
|
76
|
+
error_type.NULL_VALUE)
|
|
77
|
+
|
|
78
|
+
if not isinstance(strValue, str):
|
|
79
|
+
return False, createErrorForField(fieldName,
|
|
80
|
+
error_type.DECIMAL_PARSING, text=strValue)
|
|
81
|
+
|
|
82
|
+
grpSep = specs['groupSeparator']
|
|
83
|
+
decSep = specs['decimalSeparator']
|
|
84
|
+
|
|
85
|
+
if grpSep in specialChars: grpSep = f'\\{grpSep}'
|
|
86
|
+
if decSep in specialChars: decSep = f'\\{decSep}'
|
|
87
|
+
decimalPattern = f'^[+-]?\\d+({grpSep}\\d+)*({decSep}\\d+)?$'
|
|
88
|
+
|
|
89
|
+
if None == re.match(decimalPattern, strValue):
|
|
90
|
+
return False, createErrorForField(fieldName,
|
|
91
|
+
error_type.DECIMAL_PARSING, text=strValue)
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
precision = f"1e-{specs['fractionalLength']}"
|
|
95
|
+
sanedStrValue = strValue.replace(specs['groupSeparator'], '')
|
|
96
|
+
sanedStrValue = sanedStrValue.replace(specs['decimalSeparator'], '.')
|
|
97
|
+
rawValue = Decimal(sanedStrValue).quantize(Decimal(precision),
|
|
98
|
+
context=roundingContext)
|
|
99
|
+
|
|
100
|
+
if specs['min'] > rawValue or rawValue > specs['max']:
|
|
101
|
+
return False, createErrorForField(fieldName,
|
|
102
|
+
error_type.OUTSIDE_RANGE, value=rawValue)
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
return False, createErrorForField(fieldName,
|
|
106
|
+
error_type.DECIMAL_PARSING, text=strValue)
|
|
107
|
+
|
|
108
|
+
return True, rawValue
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def d_bool(fieldName, value, specs):
|
|
112
|
+
value = value.strValue if isinstance(value, taggedNumber) else value
|
|
113
|
+
|
|
114
|
+
if isinstance(value, bool):
|
|
115
|
+
return True, value
|
|
116
|
+
|
|
117
|
+
if not specs['coerce']:
|
|
118
|
+
return False, createErrorForField(fieldName,
|
|
119
|
+
error_type.INVALID_BOOLEAN, value=value)
|
|
120
|
+
|
|
121
|
+
# coercion attempt
|
|
122
|
+
# check if is 'null' or empty string
|
|
123
|
+
if None == value or 0 == len(value):
|
|
124
|
+
return True, False
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
rawValue = float(value)
|
|
128
|
+
# check if is effectively zero or NaN
|
|
129
|
+
if 0 == rawValue or rawValue != rawValue:
|
|
130
|
+
return True, False
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
# it was just an attempt, no problem
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
# if none of the above, then most likely it's a truthy value
|
|
137
|
+
return True, True
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def d_datetime(fieldName, value, specs):
|
|
141
|
+
strValue = value.strValue if isinstance(value, taggedNumber) else value
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
parsed_date = datetime.strptime(strValue, specs['format'])
|
|
145
|
+
return True, parsed_date
|
|
146
|
+
|
|
147
|
+
except ValueError as e:
|
|
148
|
+
return False, createErrorForField(fieldName,
|
|
149
|
+
error_type.INVALID_DATETIME, text=strValue)
|
|
150
|
+
|
|
151
|
+
except TypeError as e:
|
|
152
|
+
return False, createErrorForField(fieldName,
|
|
153
|
+
error_type.INVALID_STRING, text=strValue)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def d_string(fieldName, strValue, specs):
|
|
157
|
+
if not isinstance(strValue, str):
|
|
158
|
+
return False, createErrorForField(fieldName,
|
|
159
|
+
error_type.INVALID_STRING, value=strValue)
|
|
160
|
+
|
|
161
|
+
strLength = len(strValue)
|
|
162
|
+
if not specs['minLength'] <= strLength <= specs['maxLength']:
|
|
163
|
+
return False, createErrorForField(fieldName,
|
|
164
|
+
error_type.INVALID_LENGTH, length=strLength)
|
|
165
|
+
|
|
166
|
+
return True, strValue
|
|
167
|
+
|
|
168
|
+
#-------------------------------------------------------------------------------
|
|
169
|
+
#-------------------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
class JsonBlueprint:
|
|
172
|
+
def __init__(self):
|
|
173
|
+
self.uuid = uuid.uuid4()
|
|
174
|
+
self.includes = list()
|
|
175
|
+
self.derived_types = dict()
|
|
176
|
+
self.enums = dict()
|
|
177
|
+
self.nodes = dict()
|
|
178
|
+
self.root = None
|
|
179
|
+
|
|
180
|
+
def __str__(self):
|
|
181
|
+
return (
|
|
182
|
+
f"blueprint: {self.uuid}\n" +
|
|
183
|
+
f"|-> types = {self.derived_types}\n" +
|
|
184
|
+
f"|-> enums = {self.enums}\n" +
|
|
185
|
+
f"|-> nodes = {self.nodes}\n" +
|
|
186
|
+
f"'-> root = {self.root}")
|
|
187
|
+
|
|
188
|
+
#-----------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
def deserialize_field(self, fieldName, fieldType, value):
|
|
191
|
+
if fieldType in primitive_types:
|
|
192
|
+
specs = primitive_types[fieldType]
|
|
193
|
+
baseType = fieldType
|
|
194
|
+
|
|
195
|
+
else:
|
|
196
|
+
specs = self.find_element_declaration(fieldType)
|
|
197
|
+
baseType = specs['__baseType__']
|
|
198
|
+
|
|
199
|
+
deserialize_method = globals()['d_' + baseType]
|
|
200
|
+
return deserialize_method(fieldName, value, specs)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def validate_enum(self, fieldName, enumType, value):
|
|
204
|
+
if None == value:
|
|
205
|
+
return False, createErrorForField(fieldName,
|
|
206
|
+
error_type.NULL_VALUE)
|
|
207
|
+
|
|
208
|
+
if not isinstance(value, str):
|
|
209
|
+
return False, createErrorForField(fieldName,
|
|
210
|
+
error_type.INVALID_ENUM, value=value)
|
|
211
|
+
|
|
212
|
+
possibleValues = self.find_enum_declaration(enumType)
|
|
213
|
+
|
|
214
|
+
if not value in possibleValues:
|
|
215
|
+
return False, createErrorForField(fieldName,
|
|
216
|
+
error_type.UNKNOWN_LITERAL, value=value)
|
|
217
|
+
|
|
218
|
+
return True, value
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def validate_array(self, fieldName, jArray, contents):
|
|
222
|
+
if not isinstance(contents, list):
|
|
223
|
+
return False, createErrorForField(fieldName,
|
|
224
|
+
error_type.INVALID_ARRAY)
|
|
225
|
+
|
|
226
|
+
arrayLen = len(contents)
|
|
227
|
+
if not jArray.minLength <= arrayLen <= jArray.maxLength:
|
|
228
|
+
return False, createErrorForField(fieldName,
|
|
229
|
+
error_type.INVALID_LENGTH, length=arrayLen)
|
|
230
|
+
|
|
231
|
+
arrayKind = jArray.fieldKind
|
|
232
|
+
arrayType = jArray.fieldType
|
|
233
|
+
|
|
234
|
+
if arrayKind == field_type.NODE:
|
|
235
|
+
arrayNode = self.find_node_declaration(arrayType)
|
|
236
|
+
for idx, content in enumerate(contents):
|
|
237
|
+
success, processed = self.validate_node(fieldName, arrayNode, content)
|
|
238
|
+
|
|
239
|
+
if not success:
|
|
240
|
+
processed.setAsArrayElement(idx)
|
|
241
|
+
return False, processed
|
|
242
|
+
|
|
243
|
+
contents[idx] = processed
|
|
244
|
+
|
|
245
|
+
return True, contents
|
|
246
|
+
|
|
247
|
+
if arrayKind == field_type.ENUM:
|
|
248
|
+
for idx, value in enumerate(contents):
|
|
249
|
+
success, processed = self.validate_enum(fieldName, arrayType, value)
|
|
250
|
+
|
|
251
|
+
if not success:
|
|
252
|
+
processed.setAsArrayElement(idx)
|
|
253
|
+
return False, processed
|
|
254
|
+
|
|
255
|
+
contents[idx] = processed
|
|
256
|
+
|
|
257
|
+
return True, contents
|
|
258
|
+
|
|
259
|
+
for idx, value in enumerate(contents):
|
|
260
|
+
success, processed = self.deserialize_field(fieldName, arrayType, value)
|
|
261
|
+
|
|
262
|
+
if not success:
|
|
263
|
+
processed.setAsArrayElement(idx)
|
|
264
|
+
return False, processed
|
|
265
|
+
|
|
266
|
+
contents[idx] = processed
|
|
267
|
+
|
|
268
|
+
return True, contents
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def validate_node(self, nodeName, node, contents):
|
|
272
|
+
if not isinstance(contents, dict):
|
|
273
|
+
return False, createErrorForNode(nodeName,
|
|
274
|
+
error_type.INVALID_NODE)
|
|
275
|
+
|
|
276
|
+
for fieldName, fieldData in node.items():
|
|
277
|
+
if not fieldName in contents:
|
|
278
|
+
if fieldData.optional: continue
|
|
279
|
+
return False, createErrorForNode(nodeName,
|
|
280
|
+
error_type.MISSING_FIELD, field=fieldName)
|
|
281
|
+
|
|
282
|
+
retrieved = contents[fieldName]
|
|
283
|
+
if isArray(fieldData):
|
|
284
|
+
success, processed = self.validate_array(fieldName, fieldData, retrieved)
|
|
285
|
+
if success: continue
|
|
286
|
+
return success, processed
|
|
287
|
+
|
|
288
|
+
fieldKind = fieldData.fieldKind
|
|
289
|
+
fieldType = fieldData.fieldType
|
|
290
|
+
|
|
291
|
+
if fieldKind == field_type.NODE:
|
|
292
|
+
nodeSpecs = self.find_node_declaration(fieldType)
|
|
293
|
+
success, processed = self.validate_node(fieldName, nodeSpecs, retrieved)
|
|
294
|
+
if not success: return success, processed
|
|
295
|
+
contents[fieldName] = processed
|
|
296
|
+
continue
|
|
297
|
+
|
|
298
|
+
if fieldKind == field_type.ENUM:
|
|
299
|
+
success, processed = self.validate_enum(fieldName, fieldType, retrieved)
|
|
300
|
+
if not success: return success, processed
|
|
301
|
+
contents[fieldName] = processed
|
|
302
|
+
continue
|
|
303
|
+
|
|
304
|
+
success, processed = self.deserialize_field(fieldName, fieldType, retrieved)
|
|
305
|
+
if not success: return False, processed
|
|
306
|
+
contents[fieldName] = processed
|
|
307
|
+
|
|
308
|
+
return True, contents
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def validate(self, rootContents):
|
|
312
|
+
if isArray(self.root):
|
|
313
|
+
return self.validate_array(None, self.root, rootContents)
|
|
314
|
+
|
|
315
|
+
rootKind = self.root.fieldKind
|
|
316
|
+
rootType = self.root.fieldType
|
|
317
|
+
|
|
318
|
+
if rootKind == field_type.NODE:
|
|
319
|
+
rootNode = self.find_node_declaration(rootType)
|
|
320
|
+
return self.validate_node(None, rootNode, rootContents)
|
|
321
|
+
|
|
322
|
+
if rootKind == field_type.ENUM:
|
|
323
|
+
rootEnum = self.find_enum_declaration(rootType)
|
|
324
|
+
return self.validate_enum(None, rootType, rootContents)
|
|
325
|
+
|
|
326
|
+
if rootKind == field_type.SIMPLE:
|
|
327
|
+
return self.deserialize_field(None, rootType, rootContents)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def deserialize(self, contents):
|
|
331
|
+
tag_number = lambda x : taggedNumber(x)
|
|
332
|
+
ident = lambda x : x
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
loaded = json.loads(contents,
|
|
336
|
+
parse_float=tag_number, parse_int=tag_number,
|
|
337
|
+
parse_constant=ident)
|
|
338
|
+
|
|
339
|
+
except json.JSONDecodeError as e:
|
|
340
|
+
return False, createErrorForRoot(error_type.JSON_PARSING,
|
|
341
|
+
line=e.lineno, column=e.colno, message=e.msg)
|
|
342
|
+
|
|
343
|
+
return self.validate(loaded)
|
|
344
|
+
|
|
345
|
+
#----------------------------------------------------------------------------
|
|
346
|
+
|
|
347
|
+
def collectSources(self, collected=None):
|
|
348
|
+
collected = collected if None != collected else set()
|
|
349
|
+
collected.add(self)
|
|
350
|
+
|
|
351
|
+
for blueprint in self.includes:
|
|
352
|
+
blueprint.collectSources(collected)
|
|
353
|
+
|
|
354
|
+
return collected
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def collectTypes(self):
|
|
358
|
+
collected = list()
|
|
359
|
+
sources = self.collectSources()
|
|
360
|
+
|
|
361
|
+
for source in sources:
|
|
362
|
+
collected.extend(source.derived_types.keys())
|
|
363
|
+
collected.extend(source.enums.keys())
|
|
364
|
+
collected.extend(source.nodes.keys())
|
|
365
|
+
|
|
366
|
+
return collected
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def __hash__(self):
|
|
370
|
+
return hash(self.uuid)
|
|
371
|
+
|
|
372
|
+
#----------------------------------------------------------------------------
|
|
373
|
+
|
|
374
|
+
def find_element_declaration(self, typeName, checked=None):
|
|
375
|
+
if typeName in primitive_types: return primitive_types[typeName]
|
|
376
|
+
if typeName in self.derived_types: return self.derived_types[typeName]
|
|
377
|
+
checked = checked or set()
|
|
378
|
+
checked.add(self)
|
|
379
|
+
|
|
380
|
+
for blueprint in self.includes:
|
|
381
|
+
if not blueprint in checked:
|
|
382
|
+
found = blueprint.find_element_declaration(typeName, checked)
|
|
383
|
+
if None != found: return found
|
|
384
|
+
|
|
385
|
+
return None
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def find_node_declaration(self, nodeName, checked=None):
|
|
389
|
+
if nodeName in self.nodes: return self.nodes[nodeName]
|
|
390
|
+
checked = checked or set()
|
|
391
|
+
checked.add(self)
|
|
392
|
+
|
|
393
|
+
for blueprint in self.includes:
|
|
394
|
+
if not blueprint in checked:
|
|
395
|
+
found = blueprint.find_node_declaration(nodeName, checked)
|
|
396
|
+
if None != found: return found
|
|
397
|
+
|
|
398
|
+
return None
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def find_enum_declaration(self, enumName, checked=None):
|
|
402
|
+
if enumName in self.enums: return self.enums[enumName]
|
|
403
|
+
checked = checked or set()
|
|
404
|
+
checked.add(self)
|
|
405
|
+
|
|
406
|
+
for blueprint in self.includes:
|
|
407
|
+
if not blueprint in checked:
|
|
408
|
+
found = blueprint.find_enum_declaration(enumName, checked)
|
|
409
|
+
if None != found: return found
|
|
410
|
+
|
|
411
|
+
return None
|
|
412
|
+
|
jsonbp/declaration.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
class TypeDeclaration:
|
|
3
|
+
def __init__(self, typeName, specs):
|
|
4
|
+
self.typeName = typeName
|
|
5
|
+
self.specs = specs
|
|
6
|
+
|
|
7
|
+
def isCustomized(self):
|
|
8
|
+
return len(self.specs) > 0
|
|
9
|
+
|
|
10
|
+
#-------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
def createDeclaration(typeName, specs):
|
|
13
|
+
return TypeDeclaration(typeName, specs)
|
|
14
|
+
|
jsonbp/error.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
|
|
2
|
+
import configparser
|
|
3
|
+
from . import error_type
|
|
4
|
+
|
|
5
|
+
_error_names = ([ entry
|
|
6
|
+
for entry in dir(error_type)
|
|
7
|
+
if not entry.startswith('_')
|
|
8
|
+
])
|
|
9
|
+
|
|
10
|
+
_error_codes = dict()
|
|
11
|
+
for error_name in _error_names:
|
|
12
|
+
error_code = getattr(error_type, error_name)
|
|
13
|
+
_error_codes[error_name] = error_code
|
|
14
|
+
|
|
15
|
+
#-------------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
texts = dict()
|
|
18
|
+
possiblePrefixes = {'FIELD', 'NODE', 'ARRAY', 'ROOT'}
|
|
19
|
+
prefixes = dict()
|
|
20
|
+
|
|
21
|
+
#-------------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
def useTranslation(filename):
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
with open(filename) as fd:
|
|
27
|
+
contents = fd.read()
|
|
28
|
+
|
|
29
|
+
except IOError as e:
|
|
30
|
+
print(f"Unable to read localization file: {e.strerror}")
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
config = configparser.ConfigParser()
|
|
34
|
+
config.read_string(contents)
|
|
35
|
+
sections = config.sections()
|
|
36
|
+
|
|
37
|
+
if 'Messages' in sections:
|
|
38
|
+
section = config['Messages']
|
|
39
|
+
for entry in config.options('Messages'):
|
|
40
|
+
sanedEntry = entry.upper()
|
|
41
|
+
|
|
42
|
+
if sanedEntry in _error_codes:
|
|
43
|
+
index = _error_codes[sanedEntry]
|
|
44
|
+
texts[index] = section[sanedEntry]
|
|
45
|
+
|
|
46
|
+
if 'Prefixes' in sections:
|
|
47
|
+
section = config['Prefixes']
|
|
48
|
+
for entry in config.options('Prefixes'):
|
|
49
|
+
sanedEntry = entry.upper()
|
|
50
|
+
|
|
51
|
+
if sanedEntry in possiblePrefixes:
|
|
52
|
+
prefixValue = section[sanedEntry]
|
|
53
|
+
prefixes[sanedEntry] = prefixValue
|
|
54
|
+
|
|
55
|
+
#-------------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
class _instance:
|
|
58
|
+
def __init__(self, error_id, **context):
|
|
59
|
+
self.error_id = error_id
|
|
60
|
+
self.context = context
|
|
61
|
+
self.prefix = None
|
|
62
|
+
self.assignee = None
|
|
63
|
+
self.index = None
|
|
64
|
+
|
|
65
|
+
def getErrorType(self):
|
|
66
|
+
return self.error_id
|
|
67
|
+
|
|
68
|
+
def setAssignee(self, assigneeType, assigneeName=None):
|
|
69
|
+
self.prefix = assigneeType
|
|
70
|
+
self.assignee = assigneeName
|
|
71
|
+
|
|
72
|
+
def setAsArrayElement(self, arrayIndex):
|
|
73
|
+
self.prefix = "ARRAY"
|
|
74
|
+
self.index = arrayIndex
|
|
75
|
+
|
|
76
|
+
def __str__(self):
|
|
77
|
+
prefixText = ""
|
|
78
|
+
if None != self.prefix:
|
|
79
|
+
prefix = prefixes[self.prefix]
|
|
80
|
+
prefixText = prefix.format(
|
|
81
|
+
assignee=self.assignee,
|
|
82
|
+
index=self.index)
|
|
83
|
+
|
|
84
|
+
msg = texts[self.error_id]
|
|
85
|
+
errorMsg = msg.format(**self.context)
|
|
86
|
+
return " ".join([prefixText, errorMsg])
|
|
87
|
+
|
|
88
|
+
#-------------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
def createErrorForField(fieldName, error_id, **context):
|
|
91
|
+
result = _instance(error_id, **context)
|
|
92
|
+
args = ("FIELD", fieldName) if fieldName != None else ("ROOT",)
|
|
93
|
+
result.setAssignee(*args)
|
|
94
|
+
return result
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def createErrorForNode(nodeName, error_id, **context):
|
|
98
|
+
result = _instance(error_id, **context)
|
|
99
|
+
args = ("NODE", nodeName) if nodeName != None else ("ROOT",)
|
|
100
|
+
result.setAssignee(*args)
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def createErrorForRoot(error_id, **context):
|
|
105
|
+
result = _instance(error_id, **context)
|
|
106
|
+
result.setAssignee("ROOT")
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
#-------------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
__all__ = [
|
|
112
|
+
"useTranslation",
|
|
113
|
+
"createErrorForField",
|
|
114
|
+
"createErrorForNode",
|
|
115
|
+
"createErrorForRoot"
|
|
116
|
+
]
|
|
117
|
+
|
jsonbp/error_type.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
JSON_PARSING = 0 # line, column, message
|
|
3
|
+
NULL_VALUE = 1 #
|
|
4
|
+
INTEGER_PARSING = 2 # text
|
|
5
|
+
FLOAT_PARSING = 3 # text
|
|
6
|
+
DECIMAL_PARSING = 4 # text
|
|
7
|
+
OUTSIDE_RANGE = 5 # value
|
|
8
|
+
INVALID_BOOLEAN = 6 # value
|
|
9
|
+
INVALID_STRING = 7 # value
|
|
10
|
+
INVALID_LENGTH = 8 # length
|
|
11
|
+
INVALID_DATETIME = 9 # text
|
|
12
|
+
UNKNOWN_LITERAL = 10 # text
|
|
13
|
+
INVALID_ENUM = 11 # value
|
|
14
|
+
MISSING_FIELD = 12 # field
|
|
15
|
+
INVALID_ARRAY = 13 #
|
|
16
|
+
INVALID_NODE = 14 #
|
|
17
|
+
|
jsonbp/exception.py
ADDED
jsonbp/field.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
class JsonField:
|
|
3
|
+
def __init__(self, fieldKind, fieldType):
|
|
4
|
+
self.fieldKind = fieldKind
|
|
5
|
+
self.fieldType = fieldType
|
|
6
|
+
self.optional = False
|
|
7
|
+
|
|
8
|
+
def setOptional(self):
|
|
9
|
+
self.optional = True
|
|
10
|
+
|
|
11
|
+
#-------------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
def createField(fieldKind, fieldType):
|
|
14
|
+
return JsonField(fieldKind, fieldType)
|
|
15
|
+
|