1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008 John Paulett (john -at- 7oars.com)
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution.
10 import jsonpickle
.util
as util
11 import jsonpickle
.tags
as tags
14 class Unpickler(object):
16 ## The current recursion depth
18 ## Maps reference names to object instances
20 ## The namestack grows whenever we recurse into a child object
24 """Resets the object's internal state.
30 """Steps down one level in the namespace.
34 def _pop(self
, value
):
35 """Step up one level in the namespace and return the value.
36 If we're at the root, reset the unpickler's state.
43 def restore(self
, obj
):
44 """Restores a flattened object to its original python state.
46 Simply returns any of the basic builtin types
49 >>> u.restore('hello world')
51 >>> u.restore({'key': 'value'})
56 if has_tag(obj
, tags
.REF
):
57 return self
._pop
(self
._namedict
.get(obj
[tags
.REF
]))
59 if has_tag(obj
, tags
.TYPE
):
60 typeref
= loadclass(obj
[tags
.TYPE
])
63 return self
._pop
(typeref
)
65 if has_tag(obj
, tags
.REPR
):
66 return self
._pop
(loadrepr(obj
[tags
.REPR
]))
68 if has_tag(obj
, tags
.OBJECT
):
70 cls
= loadclass(obj
[tags
.OBJECT
])
74 instance
= object.__new
__(cls
)
80 # fail gracefully if the constructor requires arguments
84 # keep a obj->name mapping for use in the _isobjref() case
87 for k
, v
in obj
.iteritems():
88 # ignore the reserved attribute
89 if k
in tags
.RESERVED
:
91 self
._namestack
.append(k
)
92 # step into the namespace
93 value
= self
.restore(v
)
94 if (util
.is_noncomplex(instance
) or
95 util
.is_dictionary_subclass(instance
)):
98 instance
.__dict
__[k
] = value
100 self
._namestack
.pop()
101 return self
._pop
(instance
)
103 if util
.is_list(obj
):
104 return self
._pop
([self
.restore(v
) for v
in obj
])
106 if has_tag(obj
, tags
.TUPLE
):
107 return self
._pop
(tuple([self
.restore(v
) for v
in obj
[tags
.TUPLE
]]))
109 if has_tag(obj
, tags
.SET
):
110 return self
._pop
(set([self
.restore(v
) for v
in obj
[tags
.SET
]]))
112 if util
.is_dictionary(obj
):
114 for k
, v
in obj
.iteritems():
115 self
._namestack
.append(k
)
116 data
[k
] = self
.restore(v
)
117 self
._namestack
.pop()
118 return self
._pop
(data
)
120 return self
._pop
(obj
)
123 """Calculates the name of the current location in the JSON stack.
125 This is called as jsonpickle traverses the object structure to
126 create references to previously-traversed objects. This allows
127 cyclical data structures such as doubly-linked lists.
128 jsonpickle ensures that duplicate python references to the same
129 object results in only a single JSON object definition and
130 special reference tags to represent each reference.
133 >>> u._namestack = []
137 >>> u._namestack = ['a']
141 >>> u._namestack = ['a', 'b']
146 return '/' + '/'.join(self
._namestack
)
148 def _mkref(self
, obj
):
150 >>> from jsonpickle.tests.classes import Thing
151 >>> thing = Thing('referenced-thing')
156 jsonpickle.tests.classes.Thing("referenced-thing")
159 name
= self
._refname
()
160 if name
not in self
._namedict
:
161 self
._namedict
[name
] = obj
164 def loadclass(module_and_name
):
165 """Loads the module and returns the class.
167 >>> loadclass('jsonpickle.tests.classes.Thing')
168 <class 'jsonpickle.tests.classes.Thing'>
170 >>> loadclass('example.module.does.not.exist.Missing')
173 >>> loadclass('jsonpickle.tests.classes.MissingThing')
178 module
, name
= module_and_name
.rsplit('.', 1)
180 return getattr(sys
.modules
[module
], name
)
184 def loadrepr(reprstr
):
185 """Returns an instance of the object from the object's repr() string.
186 It involves the dynamic specification of code.
188 >>> from jsonpickle import tags
189 >>> loadrepr('jsonpickle.tests.classes/jsonpickle.tests.classes.Thing("json")')
190 jsonpickle.tests.classes.Thing("json")
193 module
, evalstr
= reprstr
.split('/')
197 localname
= module
.split('.', 1)[0]
198 mylocals
[localname
] = __import__(module
)
201 def has_tag(obj
, tag
):
202 """Helper class that tests to see if the obj is a dictionary
203 and contains a particular key/tag.
205 >>> obj = {'test': 1}
206 >>> has_tag(obj, 'test')
208 >>> has_tag(obj, 'fail')
211 >>> has_tag(42, 'fail')
215 return type(obj
) is dict and tag
in obj