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