1 | import warnings |
---|
2 | |
---|
3 | from framsfiles._context import _specs |
---|
4 | from ._serializer import _serialize_value |
---|
5 | |
---|
6 | _EXPECTED_OBJECT_WARNING = 'Encountered item of type {} in list of objects. Skipping.' |
---|
7 | _MISSING_CLASSNAME_WARNING = 'Object defined without classname. Resulting file might be invalid.' |
---|
8 | _INVALID_TYPE_WARINING = 'Field {} in class {} has type {}, {} expected.' |
---|
9 | _LOWER_LIMIT_WARINING = 'Value {} of field {} in class {} is less than {}.' |
---|
10 | _UPPER_LIMIT_WARINING = 'Value {} of field {} in class {} is more than {}.' |
---|
11 | |
---|
12 | _CAST_ERROR = 'Cannot cast {} to {}, aborting.' |
---|
13 | |
---|
14 | |
---|
15 | def _parse_object_list(object_list, context=None): |
---|
16 | fram_objects = [] |
---|
17 | for obj in object_list: |
---|
18 | if isinstance(obj, dict): |
---|
19 | fram_objects.append(_parse_object(obj, context)) |
---|
20 | else: |
---|
21 | warnings.warn(_EXPECTED_OBJECT_WARNING.format(type(obj))) |
---|
22 | return '\n'.join(fram_objects) |
---|
23 | |
---|
24 | |
---|
25 | def _parse_object(obj, context=None): |
---|
26 | line_list = [] |
---|
27 | spec = None |
---|
28 | classname = obj.get('_classname') |
---|
29 | |
---|
30 | if classname is None: |
---|
31 | warnings.warn(_MISSING_CLASSNAME_WARNING) |
---|
32 | else: |
---|
33 | line_list = [classname + ':'] |
---|
34 | spec = _specs.get((context, classname)) |
---|
35 | |
---|
36 | for key, value in obj.items(): |
---|
37 | if not _is_classname(key): |
---|
38 | if spec is not None: |
---|
39 | _validate_field(key, value, classname, spec) |
---|
40 | if isinstance(value, str): |
---|
41 | if _is_multiline(value): |
---|
42 | value = _parse_multiline(value) |
---|
43 | elif _contains_serialized_keyword(value) or _contains_tab(value): |
---|
44 | value = _serialize_value(value) |
---|
45 | elif isinstance(value, (list, dict)): |
---|
46 | value = _serialize_value(value) |
---|
47 | line = _to_fram_field_string(key, value) |
---|
48 | line_list.append(line) |
---|
49 | |
---|
50 | return '\n'.join(line_list) + '\n' |
---|
51 | |
---|
52 | |
---|
53 | def _validate_field(key, value, classname, spec): |
---|
54 | _validate_type(key, value, classname, spec) |
---|
55 | _validate_min(key, value, classname, spec) |
---|
56 | _validate_max(key, value, classname, spec) |
---|
57 | |
---|
58 | |
---|
59 | def _validate_type(key, value, classname, spec): |
---|
60 | if not isinstance(value, spec['dtype']): |
---|
61 | warnings.warn(_INVALID_TYPE_WARINING.format(key, classname, type(value), spec['type'])) |
---|
62 | |
---|
63 | |
---|
64 | def _validate_min(key, value, classname, spec): |
---|
65 | if 'min' in spec: |
---|
66 | casted = spec['dtype'](value) |
---|
67 | if casted < spec['min']: |
---|
68 | warnings.warn(_LOWER_LIMIT_WARINING.format(value, key, classname, spec['min'])) |
---|
69 | |
---|
70 | |
---|
71 | def _validate_max(key, value, classname, spec): |
---|
72 | if 'max' in spec: |
---|
73 | casted = spec['dtype'](value) |
---|
74 | if casted > spec['max']: |
---|
75 | warnings.warn(_UPPER_LIMIT_WARINING.format(value, key, classname, spec['max'])) |
---|
76 | |
---|
77 | |
---|
78 | def _is_classname(key): |
---|
79 | return key == '_classname' |
---|
80 | |
---|
81 | |
---|
82 | def _parse_multiline(value): |
---|
83 | value = _escape_tildes(value) |
---|
84 | return _tilde_wrap(value) |
---|
85 | |
---|
86 | |
---|
87 | def _escape_tildes(value): |
---|
88 | return value.replace('~', '\\~') |
---|
89 | |
---|
90 | |
---|
91 | def _tilde_wrap(value): |
---|
92 | return '~\n{}~'.format(value) |
---|
93 | |
---|
94 | |
---|
95 | def _to_fram_field_string(key, value): |
---|
96 | return '{}:{}'.format(key, value) |
---|
97 | |
---|
98 | |
---|
99 | def _contains_serialized_keyword(value): |
---|
100 | return '@Serialized:' in value |
---|
101 | |
---|
102 | |
---|
103 | def _contains_tab(value): |
---|
104 | return '\t' in value |
---|
105 | |
---|
106 | |
---|
107 | def _is_multiline(value): |
---|
108 | return '\n' in value |
---|