import json from common.global_symbols import NoFirstToken, NotFound, NotInit, Removed from common.utils import decode_enum, get_class, unstr_concept from sheerkapickle import tags, utils, handlers def decode(sheerka, obj): return SheerkaUnpickler(sheerka).restore(json.loads(obj)) class SheerkaUnpickler: def __init__(self, sheerka): self.sheerka = sheerka self.objs = [] def restore(self, obj): if has_tag(obj, tags.ID): return self._restore_id(obj) if has_tag(obj, tags.TUPLE): return self._restore_tuple(obj) if has_tag(obj, tags.CUSTOM): return self._restore_custom(obj) if has_tag(obj, tags.SET): return self._restore_set(obj) if has_tag(obj, tags.ENUM): return self._restore_enum(obj) if has_tag(obj, tags.OBJECT): return self._restore_obj(obj) if utils.is_list(obj): return self._restore_list(obj) if utils.is_dictionary(obj): return self._restore_dict(obj) return obj def _restore_list(self, obj): return [self.restore(v) for v in obj] def _restore_tuple(self, obj): return tuple([self.restore(v) for v in obj[tags.TUPLE]]) def _restore_custom(self, obj): if obj[tags.CUSTOM] == NotInit.value: instance = NotInit elif obj[tags.CUSTOM] == NotFound.value: instance = NotFound elif obj[tags.CUSTOM] == Removed.value: instance = Removed elif obj[tags.CUSTOM] == NoFirstToken.value: instance = NoFirstToken else: raise KeyError(f"unknown {obj[tags.CUSTOM]}") self.objs.append(instance) return instance def _restore_set(self, obj): return set([self.restore(v) for v in obj[tags.SET]]) def _restore_enum(self, obj): instance = decode_enum(obj[tags.ENUM]) self.objs.append(instance) return instance def _restore_dict(self, obj): data = {} for k, v in obj.items(): resolved_key = self._resolve_key(k) data[resolved_key] = self.restore(v) return data def _restore_id(self, obj): try: return self.objs[obj[tags.ID]] except IndexError: pass def _restore_obj(self, obj): handler = handlers.get(obj[tags.OBJECT]) if handler: handler = handler(self.sheerka, self) instance = handler.new(obj) self.objs.append(instance) instance = handler.restore(obj, instance) else: # KSI 202011: Hack because Property is removed # To suppress asap if obj[tags.OBJECT] == "core.concept.Property": return self.restore(obj["value"]) cls = get_class(obj[tags.OBJECT]) instance = cls.__new__(cls) self.objs.append(instance) for k, v in obj.items(): if k == tags.OBJECT: continue value = self.restore(v) setattr(instance, k, value) return instance def _resolve_key(self, key): if key == "null": return None concept_key, concept_id = unstr_concept(key) if concept_key is not None: return self.sheerka.new((concept_key, concept_id)) if concept_id else self.sheerka.new(concept_key) as_enum = decode_enum(key) if as_enum is not None: return as_enum return key def has_tag(obj, tag): return type(obj) is dict and tag in obj