def _get_flattener(self, obj): if util.is_primitive(obj): return lambda obj: obj list_recurse = lambda obj: [self.flatten(v) for v in obj] if util.is_list(obj): if self._mkref(obj): return list_recurse else: self._push() return self._getref # We handle tuples and sets by encoding them in a "(tuple|set)dict" if util.is_tuple(obj): if not self.unpicklable: return list_recurse return lambda obj: {tags.TUPLE: [self.flatten(v) for v in obj]} if util.is_set(obj): if not self.unpicklable: return list_recurse return lambda obj: {tags.SET: [self.flatten(v) for v in obj]} if util.is_dictionary(obj): return self._flatten_dict_obj if util.is_type(obj): return _mktyperef if util.is_object(obj): return self._ref_obj_instance # else, what else? (methods, functions, old style classes...) return None
def restore(self, obj, cls_def = None): """Restores a flattened object to its original python state. Simply returns any of the basic builtin types >>> u = Unpickler() >>> u.restore('hello world') 'hello world' >>> u.restore({'key': 'value'}) {'key': 'value'} cls_def could be either a type or an instance. In case of instance, it will be used to define types inside a list or dict. eg. [Test()] means a list of Test. """ self._push() if has_tag(obj, tags.ID): return self._pop(self._objs[obj[tags.ID]]) # Backwards compatibility if has_tag(obj, tags.REF): return self._pop(self._namedict.get(obj[tags.REF])) if has_tag(obj, tags.TYPE): typeref = loadclass(obj[tags.TYPE]) if not typeref: return self._pop(obj) return self._pop(typeref) # Backwards compatibility if has_tag(obj, tags.REPR): obj = loadrepr(obj[tags.REPR]) return self._pop(self._mkref(obj)) if util.is_type(cls_def): if not util.is_dictionary(obj): # Type mismatch. cls_def is a type but we didn't get a dict # from JSON. Return None. return self._pop(None) # check custom handlers HandlerClass = handlers.BaseHandler._registry.get(cls_def) if HandlerClass: handler = HandlerClass(self) instance = handler.restore(obj) return self._pop(self._mkref(instance)) factory = loadfactory(obj) args = getargs(obj, cls_def) if args: args = self.restore(args) try: if hasattr(cls_def, '__new__'): # new style classes if factory: instance = cls_def.__new__(cls_def, factory, *args) instance.default_factory = factory else: instance = cls_def.__new__(cls_def, *args) else: instance = object.__new__(cls_def) except TypeError: # old-style classes try: instance = cls_def() except TypeError: # fail gracefully if the constructor requires arguments return self._pop(self._mkref(obj)) # Add to the instance table to allow being referenced by a # downstream object self._mkref(instance) if isinstance(instance, tuple): return self._pop(instance) if hasattr(instance, '__setstate__') and has_tag(obj, tags.STATE): state = self.restore(obj[tags.STATE]) instance.__setstate__(state) return self._pop(instance) for k in util.get_public_variables(cls_def): if k in obj: v = obj[k] # ignore the reserved attribute if k in tags.RESERVED: continue self._namestack.append(k) # step into the namespace value = self.restore(v, get_attr_cls_def(cls_def, k)) if (util.is_noncomplex(instance) or util.is_dictionary(instance)): instance[k] = value else: setattr(instance, k, value) # step out self._namestack.pop() else: # Attribute in cls_def but not given in JSON. Assign it to # None so that user could tell that it wasn't given know. setattr(instance, k, None) # Handle list and set subclasses if has_tag(obj, tags.SEQ): if hasattr(instance, 'append'): for v in obj[tags.SEQ]: instance.append(self.restore(v)) if hasattr(instance, 'add'): for v in obj[tags.SEQ]: instance.add(self.restore(v)) return self._pop(instance) if util.is_list(obj): if util.is_collection(cls_def): parent = type(cls_def)() else: parent = [] self._mkref(parent) item_type = get_collection_item_type(cls_def) is_set = type(parent) is set for v in obj: restored_v = self.restore(v, item_type) if is_set: parent.add(restored_v) else: parent.append(restored_v) return self._pop(parent) if has_tag(obj, tags.TUPLE): return self._pop(tuple([self.restore(v) for v in obj[tags.TUPLE]])) if has_tag(obj, tags.SET): return self._pop(set([self.restore(v) for v in obj[tags.SET]])) if util.is_dictionary(obj): if util.is_dictionary(cls_def): data = type(cls_def)() else: data = {} k_type, v_type = get_dictionary_item_type(cls_def) for k, v in sorted(obj.items(), key=operator.itemgetter(0)): self._namestack.append(k) data[self.restore(k, k_type)] = self.restore(v, v_type) self._namestack.pop() return self._pop(data) return self._pop(obj)