Example #1
0
    def _flatten_obj_instance(self, obj):
        """Recursively flatten an instance and return a json-friendly dict
        """
        data = ObjDict(obj)
        has_class = hasattr(obj, '__class__')
        has_dict = hasattr(obj, '__dict__')
        has_slots = not has_dict and hasattr(obj, '__slots__')
        has_getstate = has_dict and hasattr(obj, '__getstate__')
        has_getstate_support = has_getstate and hasattr(obj, '__setstate__')

        HandlerClass = handlers.BaseHandler._registry.get(type(obj))

        if has_class and not util.is_module(obj):
            module, name = _getclassdetail(obj)
            if self.unpicklable:
                data[tags.OBJECT] = '%s.%s' % (module, name)
            # Check for a custom handler
            if HandlerClass:
                handler = HandlerClass(self)
                flat_obj = handler.flatten(obj, data)
                self._mkref(flat_obj)
                return flat_obj

        if util.is_module(obj):
            if self.unpicklable:
                data[tags.REPR] = '%s/%s' % (obj.__name__,
                                             obj.__name__)
            else:
                data = unicode(obj)
            return data

        if util.is_dictionary_subclass(obj):
            return self._flatten_dict_obj(obj, data)

        if has_dict:
            # Support objects that subclasses list and set
            if util.is_collection_subclass(obj):
                return self._flatten_collection_obj(obj, data)

            # Support objects with __getstate__(); this ensures that
            # both __setstate__() and __getstate__() are implemented
            if has_getstate_support:
                state = self.flatten(obj.__getstate__())
                if self.unpicklable:
                    data[tags.STATE] = state
                else:
                    data = state
                return data

            # hack for zope persistent objects; this unghostifies the object
            getattr(obj, '_', None)
            return self._flatten_dict_obj(obj.__dict__, data,
                    self._is_filter_none_attr)

        if util.is_collection_subclass(obj):
            return self._flatten_collection_obj(obj, data)

        if util.is_noncomplex(obj):
            return [self.flatten(v) for v in obj]

        if has_slots:
            return self._flatten_newstyle_with_slots(obj, data)
Example #2
0
    def _flatten_obj_instance(self, obj):
        """Recursively flatten an instance and return a json-friendly dict
        """
        data = ObjDict(obj)
        has_class = hasattr(obj, '__class__')
        has_dict = hasattr(obj, '__dict__')
        has_slots = not has_dict and hasattr(obj, '__slots__')
        has_getstate = has_dict and hasattr(obj, '__getstate__')
        has_getstate_support = has_getstate and hasattr(obj, '__setstate__')

        HandlerClass = handlers.BaseHandler._registry.get(type(obj))

        if has_class and not util.is_module(obj):
            module, name = _getclassdetail(obj)
            if self.unpicklable:
                data[tags.OBJECT] = '%s.%s' % (module, name)
            # Check for a custom handler
            if HandlerClass:
                handler = HandlerClass(self)
                flat_obj = handler.flatten(obj, data)
                self._mkref(flat_obj)
                return flat_obj

        if util.is_module(obj):
            if self.unpicklable:
                data[tags.REPR] = '%s/%s' % (obj.__name__,
                                             obj.__name__)
            else:
                data = unicode(obj)
            return data

        if util.is_dictionary_subclass(obj):
            return self._flatten_dict_obj(obj, data)

        if has_dict:
            # Support objects that subclasses list and set
            if util.is_collection_subclass(obj):
                return self._flatten_collection_obj(obj, data)

            # Support objects with __getstate__(); this ensures that
            # both __setstate__() and __getstate__() are implemented
            if has_getstate_support:
                state = self.flatten(obj.__getstate__())
                if self.unpicklable:
                    data[tags.STATE] = state
                else:
                    data = state
                return data

            # hack for zope persistent objects; this unghostifies the object
            getattr(obj, '_', None)
            return self._flatten_dict_obj(obj.__dict__, data,
                    self._is_filter_none_attr)

        if util.is_collection_subclass(obj):
            return self._flatten_collection_obj(obj, data)

        if util.is_noncomplex(obj):
            return [self.flatten(v) for v in obj]

        if has_slots:
            return self._flatten_newstyle_with_slots(obj, data)
Example #3
0
    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)