[1104] | 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 |
---|