Source code for tic.utils.jsonpickle.unpickler

# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 John Paulett (john -at- paulett.org)
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.

from tic.utils.jsonpickle.tags import DATE_REG_EXP
from tic.utils.jsonpickle.util import is_date
import operator
import sys
from tic.utils.jsonpickle.tags import *
from tic.utils.jsonpickle.util import *
from tic.utils.jsonpickle.handlers import *
from tic.utils.jsonpickle.compat import set


[docs]class Unpickler(object): def __init__(self): ## The current recursion depth self._depth = 0 ## Maps reference names to object instances self._namedict = {} ## The namestack grows whenever we recurse into a child object self._namestack = [] def _reset(self): """Resets the object's internal state. """ self._namedict = {} self._namestack = [] def _push(self): """Steps down one level in the namespace. """ self._depth += 1 def _pop(self, value): """Step up one level in the namespace and return the value. If we're at the root, reset the unpickler's state. """ self._depth -= 1 if self._depth == 0: self._reset() return value
[docs] def restore(self, obj): """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'} """ self._push() if has_tag(obj, REF): return self._pop(self._namedict.get(obj[REF])) if has_tag(obj, TYPE): typeref = loadclass(obj[TYPE]) if not typeref: return self._pop(obj) return self._pop(typeref) if has_tag(obj, REPR): return self._pop(loadrepr(obj[REPR])) if has_tag(obj, OBJECT): cls = loadclass(obj[OBJECT]) if not cls: return self._pop(obj) # check custom handlers HandlerClass = registry.get(cls) if HandlerClass: handler = HandlerClass(self) return self._pop(handler.restore(obj)) try: if hasattr(cls, '__new__'): instance = cls.__new__(cls) else: instance = object.__new__(cls) except TypeError: # old-style classes try: instance = cls() except TypeError: # fail gracefully if the constructor requires arguments self._mkref(obj) return self._pop(obj) # keep a obj->name mapping for use in the _isobjref() case self._mkref(instance) if hasattr(instance, '__setstate__') and has_tag(obj, STATE): state = self.restore(obj[STATE]) instance.__setstate__(state) return self._pop(instance) for k, v in sorted(obj.iteritems(), key=operator.itemgetter(0)): # ignore the reserved attribute if k in RESERVED: continue self._namestack.append(k) # step into the namespace value = self.restore(v) if (is_noncomplex(instance) or is_dictionary_subclass(instance)): instance[k] = value else: setattr(instance, k, value) # step out self._namestack.pop() # Handle list and set subclasses if has_tag(obj, SEQ): if hasattr(instance, 'append'): for v in obj[SEQ]: instance.append(self.restore(v)) if hasattr(instance, 'add'): for v in obj[SEQ]: instance.add(self.restore(v)) return self._pop(instance) if is_list(obj): return self._pop([self.restore(v) for v in obj]) if has_tag(obj, TUPLE): return self._pop(tuple([self.restore(v) for v in obj[TUPLE]])) if has_tag(obj, SET): return self._pop(set([self.restore(v) for v in obj[SET]])) if is_dictionary(obj): data = {} for k, v in sorted(obj.iteritems(), key=operator.itemgetter(0)): self._namestack.append(k) data[k] = self.restore(v) self._namestack.pop() return self._pop(data) if is_date(obj): from datetime import datetime unix_time = int(DATE_REG_EXP.findall(obj)[0]) / 1000 #since js getTime is in ms return self._pop(datetime.fromtimestamp(unix_time)) return self._pop(obj)
def _refname(self): """Calculates the name of the current location in the JSON stack. This is called as jsonpickle traverses the object structure to create references to previously-traversed objects. This allows cyclical data structures such as doubly-linked lists. jsonpickle ensures that duplicate python references to the same object results in only a single JSON object definition and special reference tags to represent each reference. >>> u = Unpickler() >>> u._namestack = [] >>> u._refname() '/' >>> u._namestack = ['a'] >>> u._refname() '/a' >>> u._namestack = ['a', 'b'] >>> u._refname() '/a/b' """ return '/' + '/'.join(self._namestack) def _mkref(self, obj): name = self._refname() if name not in self._namedict: self._namedict[name] = obj return name
[docs]def loadclass(module_and_name): """Loads the module and returns the class. """ try: module, name = module_and_name.rsplit('.', 1) __import__(module) return getattr(sys.modules[module], name) except: return None
[docs]def loadrepr(reprstr): """Returns an instance of the object from the object's repr() string. It involves the dynamic specification of code. """ module, evalstr = reprstr.split('/') mylocals = locals() localname = module if '.' in localname: localname = module.split('.', 1)[0] mylocals[localname] = __import__(module) return eval(evalstr)
[docs]def has_tag(obj, tag): """Helper class that tests to see if the obj is a dictionary and contains a particular key/tag. >>> obj = {'test': 1} >>> has_tag(obj, 'test') True >>> has_tag(obj, 'fail') False >>> has_tag(42, 'fail') False """ return type(obj) is dict and tag in obj