~certify-web-dev/twisted/certify-staging

« back to all changes in this revision

Viewing changes to twisted/persisted/marmalade.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2010-01-02 19:38:17 UTC
  • mfrom: (2.2.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100102193817-jphp464ppwh7dulg
Tags: 9.0.0-1
* python-twisted: Depend on the python-twisted-* 9.0 packages.
* python-twisted: Depend on python-zope.interface only. Closes: #557781.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- test-case-name: twisted.test.test_persisted -*-
2
 
 
3
 
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4
 
# See LICENSE for details.
5
 
 
6
 
 
7
 
"""Marmalade: jelly, with just a hint of bitterness.
8
 
 
9
 
I can serialize a Python object to an XML DOM tree (twisted.web.microdom), and
10
 
therefore to XML data, similarly to twisted.spread.jelly.  Because both Python
11
 
lists and DOM trees are tree data-structures, many of the idioms used here are
12
 
identical.
13
 
 
14
 
"""
15
 
 
16
 
import warnings
17
 
warnings.warn("twisted.persisted.marmalade is deprecated", DeprecationWarning, stacklevel=2)
18
 
 
19
 
import new
20
 
 
21
 
from twisted.python.reflect import namedModule, namedClass, namedObject, fullFuncName, qual
22
 
from twisted.persisted.crefutil import NotKnown, _Tuple, _InstanceMethod, _DictKeyAndValue, _Dereference, _Defer
23
 
 
24
 
try:
25
 
    from new import instancemethod
26
 
except:
27
 
    from org.python.core import PyMethod
28
 
    instancemethod = PyMethod
29
 
 
30
 
import types
31
 
import copy_reg
32
 
 
33
 
#for some reason, __builtins__ == __builtin__.__dict__ in the context where this is used.
34
 
#Can someone tell me why?
35
 
import __builtin__ 
36
 
 
37
 
 
38
 
def instance(klass, d):
39
 
    if isinstance(klass, types.ClassType):
40
 
        return new.instance(klass, d)
41
 
    elif isinstance(klass, type):
42
 
        o = object.__new__(klass)
43
 
        o.__dict__ = d
44
 
        return o
45
 
    else:
46
 
        raise TypeError, "%s is not a class" % klass
47
 
 
48
 
 
49
 
def getValueElement(node):
50
 
    """Get the one child element of a given element.
51
 
 
52
 
    If there is more than one child element, raises ValueError.  Otherwise,
53
 
    returns the value element.
54
 
    """
55
 
    valueNode = None
56
 
    for subnode in node.childNodes:
57
 
        if isinstance(subnode, Element):
58
 
            if valueNode is None:
59
 
                valueNode = subnode
60
 
            else:
61
 
                raise ValueError("Only one value node allowed per instance!")
62
 
    return valueNode
63
 
 
64
 
 
65
 
class DOMJellyable:
66
 
 
67
 
    jellyDOMVersion = 1
68
 
 
69
 
    def jellyToDOM(self, jellier, element):
70
 
        element.setAttribute("marmalade:version", str(self.jellyDOMVersion))
71
 
        method = getattr(self, "jellyToDOM_%s" % self.jellyDOMVersion, None)
72
 
        if method:
73
 
            method(jellier, element)
74
 
        else:
75
 
            element.appendChild(jellier.jellyToNode(self.__dict__))
76
 
 
77
 
    def unjellyFromDOM(self, unjellier, element):
78
 
        pDOMVersion = element.getAttribute("marmalade:version") or "0"
79
 
        method = getattr(self, "unjellyFromDOM_%s" % pDOMVersion, None)
80
 
        if method:
81
 
            method(unjellier, element)
82
 
        else:
83
 
            # XXX: DOMJellyable.unjellyNode does not exist
84
 
            # XXX: 'node' is undefined - did you mean 'self', 'element', or 'Node'?
85
 
            state = self.unjellyNode(getValueElement(node))
86
 
            if hasattr(self.__class__, "__setstate__"):
87
 
                self.__setstate__(state)
88
 
            else:
89
 
                self.__dict__ = state
90
 
 
91
 
 
92
 
 
93
 
class DOMUnjellier:
94
 
    def __init__(self):
95
 
        self.references = {}
96
 
        self._savedLater = []
97
 
 
98
 
    def unjellyLater(self, node):
99
 
        """Unjelly a node, later.
100
 
        """
101
 
        d = _Defer()
102
 
        self.unjellyInto(d, 0, node)
103
 
        self._savedLater.append(d)
104
 
        return d
105
 
 
106
 
    def unjellyInto(self, obj, loc, node):
107
 
        """Utility method for unjellying one object into another.
108
 
 
109
 
        This automates the handling of backreferences.
110
 
        """
111
 
        o = self.unjellyNode(node)
112
 
        obj[loc] = o
113
 
        if isinstance(o, NotKnown):
114
 
            o.addDependant(obj, loc)
115
 
        return o
116
 
 
117
 
    def unjellyAttribute(self, instance, attrName, valueNode):
118
 
        """Utility method for unjellying into instances of attributes.
119
 
 
120
 
        Use this rather than unjellyNode unless you like surprising bugs!
121
 
        Alternatively, you can use unjellyInto on your instance's __dict__.
122
 
        """
123
 
        self.unjellyInto(instance.__dict__, attrName, valueNode)
124
 
 
125
 
    def unjellyNode(self, node):
126
 
        if node.tagName.lower() == "none":
127
 
            retval = None
128
 
        elif node.tagName == "string":
129
 
            # XXX FIXME this is obviously insecure
130
 
            # if you doubt:
131
 
            # >>> unjellyFromXML('''<string value="h&quot;+str(__import__(&quot;sys&quot;))+&quot;i" />''')
132
 
            # "h<module 'sys' (built-in)>i"
133
 
            retval = str(eval('"%s"' % node.getAttribute("value")))
134
 
        elif node.tagName == "int":
135
 
            retval = int(node.getAttribute("value"))
136
 
        elif node.tagName == "float":
137
 
            retval = float(node.getAttribute("value"))
138
 
        elif node.tagName == "longint":
139
 
            retval = long(node.getAttribute("value"))
140
 
        elif node.tagName == "bool":
141
 
            retval = int(node.getAttribute("value"))
142
 
            if retval:
143
 
                retval = True
144
 
            else:
145
 
                retval = False
146
 
        elif node.tagName == "module":
147
 
            retval = namedModule(str(node.getAttribute("name")))
148
 
        elif node.tagName == "class":
149
 
            retval = namedClass(str(node.getAttribute("name")))
150
 
        elif node.tagName == "unicode":
151
 
            retval = unicode(str(node.getAttribute("value")).replace("\\n", "\n").replace("\\t", "\t"), "raw_unicode_escape")
152
 
        elif node.tagName == "function":
153
 
            retval = namedObject(str(node.getAttribute("name")))
154
 
        elif node.tagName == "method":
155
 
            im_name = node.getAttribute("name")
156
 
            im_class = namedClass(node.getAttribute("class"))
157
 
            im_self = self.unjellyNode(getValueElement(node))
158
 
            if im_class.__dict__.has_key(im_name):
159
 
                if im_self is None:
160
 
                    retval = getattr(im_class, im_name)
161
 
                elif isinstance(im_self, NotKnown):
162
 
                    retval = _InstanceMethod(im_name, im_self, im_class)
163
 
                else:
164
 
                    retval = instancemethod(im_class.__dict__[im_name],
165
 
                                            im_self,
166
 
                                            im_class)
167
 
            else:
168
 
                raise TypeError("instance method changed")
169
 
        elif node.tagName == "tuple":
170
 
            l = []
171
 
            tupFunc = tuple
172
 
            for subnode in node.childNodes:
173
 
                if isinstance(subnode, Element):
174
 
                    l.append(None)
175
 
                    if isinstance(self.unjellyInto(l, len(l)-1, subnode), NotKnown):
176
 
                        tupFunc = _Tuple
177
 
            retval = tupFunc(l)
178
 
        elif node.tagName == "list":
179
 
            l = []
180
 
            finished = 1
181
 
            for subnode in node.childNodes:
182
 
                if isinstance(subnode, Element):
183
 
                    l.append(None)
184
 
                    self.unjellyInto(l, len(l)-1, subnode)
185
 
            retval = l
186
 
        elif node.tagName == "dictionary":
187
 
            d = {}
188
 
            keyMode = 1
189
 
            for subnode in node.childNodes:
190
 
                if isinstance(subnode, Element):
191
 
                    if keyMode:
192
 
                        kvd = _DictKeyAndValue(d)
193
 
                        if not subnode.getAttribute("role") == "key":
194
 
                            raise TypeError("Unjellying Error: key role not set")
195
 
                        self.unjellyInto(kvd, 0, subnode)
196
 
                    else:
197
 
                        self.unjellyInto(kvd, 1, subnode)
198
 
                    keyMode = not keyMode
199
 
            retval = d
200
 
        elif node.tagName == "instance":
201
 
            className = node.getAttribute("class")
202
 
            clasz = namedClass(className)
203
 
            if issubclass(clasz, DOMJellyable):
204
 
                retval = instance(clasz, {})
205
 
                retval.unjellyFromDOM(self, node)
206
 
            else:
207
 
                state = self.unjellyNode(getValueElement(node))
208
 
                if hasattr(clasz, "__setstate__"):
209
 
                    inst = instance(clasz, {})
210
 
                    inst.__setstate__(state)
211
 
                else:
212
 
                    inst = instance(clasz, state)
213
 
                retval = inst
214
 
        elif node.tagName == "reference":
215
 
            refkey = node.getAttribute("key")
216
 
            retval = self.references.get(refkey)
217
 
            if retval is None:
218
 
                der = _Dereference(refkey)
219
 
                self.references[refkey] = der
220
 
                retval = der
221
 
        elif node.tagName == "copyreg":
222
 
            nodefunc = namedObject(node.getAttribute("loadfunc"))
223
 
            loaddef = self.unjellyLater(getValueElement(node)).addCallback(
224
 
                lambda result, _l: apply(_l, result), nodefunc)
225
 
            retval = loaddef
226
 
        else:
227
 
            raise TypeError("Unsupported Node Type: %s" % (node.tagName,))
228
 
        if node.hasAttribute("reference"):
229
 
            refkey = node.getAttribute("reference")
230
 
            ref = self.references.get(refkey)
231
 
            if ref is None:
232
 
                self.references[refkey] = retval
233
 
            elif isinstance(ref, NotKnown):
234
 
                ref.resolveDependants(retval)
235
 
                self.references[refkey] = retval
236
 
            else:
237
 
                assert 0, "Multiple references with the same ID!"
238
 
        return retval
239
 
 
240
 
    def unjelly(self, doc):
241
 
        l = [None]
242
 
        self.unjellyInto(l, 0, doc.childNodes[0])
243
 
        for svd in self._savedLater:
244
 
            svd.unpause()
245
 
        return l[0]
246
 
 
247
 
 
248
 
class DOMJellier:
249
 
    def __init__(self):
250
 
        # dict of {id(obj): (obj, node)}
251
 
        self.prepared = {}
252
 
        self.document = Document()
253
 
        self._ref_id = 0
254
 
 
255
 
    def prepareElement(self, element, object):
256
 
        self.prepared[id(object)] = (object, element)
257
 
 
258
 
    def jellyToNode(self, obj):
259
 
        """Create a node representing the given object and return it.
260
 
        """
261
 
        objType = type(obj)
262
 
        #immutable (We don't care if these have multiple refs)
263
 
        if objType is types.NoneType:
264
 
            node = self.document.createElement("None")
265
 
        elif objType is types.StringType:
266
 
            node = self.document.createElement("string")
267
 
            r = repr(obj)
268
 
            if r[0] == '"':
269
 
                r = r.replace("'", "\\'")
270
 
            else:
271
 
                r = r.replace('"', '\\"')
272
 
            node.setAttribute("value", r[1:-1])
273
 
            # node.appendChild(CDATASection(obj))
274
 
        elif objType is types.IntType:
275
 
            node = self.document.createElement("int")
276
 
            node.setAttribute("value", str(obj))
277
 
        elif objType is types.LongType:
278
 
            node = self.document.createElement("longint")
279
 
            s = str(obj)
280
 
            if s[-1] == 'L':
281
 
                s = s[:-1]
282
 
            node.setAttribute("value", s)
283
 
        elif objType is types.FloatType:
284
 
            node = self.document.createElement("float")
285
 
            node.setAttribute("value", repr(obj))
286
 
        elif objType is types.MethodType:
287
 
            node = self.document.createElement("method")
288
 
            node.setAttribute("name", obj.im_func.__name__)
289
 
            node.setAttribute("class", qual(obj.im_class))
290
 
            # TODO: make methods 'prefer' not to jelly the object internally,
291
 
            # so that the object will show up where it's referenced first NOT
292
 
            # by a method.
293
 
            node.appendChild(self.jellyToNode(obj.im_self))
294
 
        elif hasattr(types, 'BooleanType') and objType is types.BooleanType:
295
 
            node = self.document.createElement("bool")
296
 
            node.setAttribute("value", str(int(obj)))
297
 
        elif objType is types.ModuleType:
298
 
            node = self.document.createElement("module")
299
 
            node.setAttribute("name", obj.__name__)
300
 
        elif objType==types.ClassType or issubclass(objType, type):
301
 
            node = self.document.createElement("class")
302
 
            node.setAttribute("name", qual(obj))
303
 
        elif objType is types.UnicodeType:
304
 
            node = self.document.createElement("unicode")
305
 
            obj = obj.encode('raw_unicode_escape')
306
 
            s = obj.replace("\n", "\\n").replace("\t", "\\t")
307
 
            node.setAttribute("value", s)
308
 
        elif objType in (types.FunctionType, types.BuiltinFunctionType):
309
 
            # TODO: beat pickle at its own game, and do BuiltinFunctionType
310
 
            # separately, looking for __self__ attribute and unpickling methods
311
 
            # of C objects when possible.
312
 
            node = self.document.createElement("function")
313
 
            node.setAttribute("name", fullFuncName(obj))
314
 
        else:
315
 
            #mutable!
316
 
            if self.prepared.has_key(id(obj)):
317
 
                oldNode = self.prepared[id(obj)][1]
318
 
                if oldNode.hasAttribute("reference"):
319
 
                    # it's been referenced already
320
 
                    key = oldNode.getAttribute("reference")
321
 
                else:
322
 
                    # it hasn't been referenced yet
323
 
                    self._ref_id = self._ref_id + 1
324
 
                    key = str(self._ref_id)
325
 
                    oldNode.setAttribute("reference", key)
326
 
                node = self.document.createElement("reference")
327
 
                node.setAttribute("key", key)
328
 
                return node
329
 
            node = self.document.createElement("UNNAMED")
330
 
            self.prepareElement(node, obj)
331
 
            if objType is types.ListType or __builtin__.__dict__.has_key('object') and isinstance(obj, NodeList):
332
 
                node.tagName = "list"
333
 
                for subobj in obj:
334
 
                    node.appendChild(self.jellyToNode(subobj))
335
 
            elif objType is types.TupleType:
336
 
                node.tagName = "tuple"
337
 
                for subobj in obj:
338
 
                    node.appendChild(self.jellyToNode(subobj))
339
 
            elif objType is types.DictionaryType:
340
 
                node.tagName = "dictionary"
341
 
                for k, v in obj.items():
342
 
                    n = self.jellyToNode(k)
343
 
                    n.setAttribute("role", "key")
344
 
                    n2 = self.jellyToNode(v)
345
 
                    node.appendChild(n)
346
 
                    node.appendChild(n2)
347
 
            elif copy_reg.dispatch_table.has_key(objType):
348
 
                unpickleFunc, state = copy_reg.dispatch_table[objType](obj)
349
 
                node = self.document.createElement("copyreg")
350
 
                # node.setAttribute("type", objType.__name__)
351
 
                node.setAttribute("loadfunc", fullFuncName(unpickleFunc))
352
 
                node.appendChild(self.jellyToNode(state))
353
 
            elif objType is types.InstanceType or hasattr(objType, "__module__"):
354
 
                className = qual(obj.__class__)
355
 
                node.tagName = "instance"
356
 
                node.setAttribute("class", className)
357
 
                if isinstance(obj, DOMJellyable):
358
 
                    obj.jellyToDOM(self, node)
359
 
                else:
360
 
                    if hasattr(obj, "__getstate__"):
361
 
                        state = obj.__getstate__()
362
 
                    else:
363
 
                        state = obj.__dict__
364
 
                    n = self.jellyToNode(state)
365
 
                    node.appendChild(n)
366
 
            else:
367
 
                raise TypeError("Unsupported type: %s" % (objType.__name__,))
368
 
        return node
369
 
 
370
 
    def jelly(self, obj):
371
 
        """Create a document representing the current object, and return it.
372
 
        """
373
 
        node = self.jellyToNode(obj)
374
 
        self.document.appendChild(node)
375
 
        return self.document
376
 
 
377
 
 
378
 
def jellyToDOM(object):
379
 
    """Convert an Object into an twisted.web.microdom.Document.
380
 
    """
381
 
    dj = DOMJellier()
382
 
    document = dj.jelly(object)
383
 
    return document
384
 
 
385
 
 
386
 
def unjellyFromDOM(document):
387
 
    """Convert an twisted.web.microdom.Document into a Python object.
388
 
    """
389
 
    du = DOMUnjellier()
390
 
    return du.unjelly(document)
391
 
 
392
 
 
393
 
def jellyToXML(object, file=None):
394
 
    """jellyToXML(object, [file]) -> None | string
395
 
 
396
 
    Converts a Python object to an XML stream.  If you pass a file, the XML
397
 
    will be written to that file; otherwise, a string of the XML will be
398
 
    returned.
399
 
    """
400
 
    document = jellyToDOM(object)
401
 
    if file:
402
 
        document.writexml(file, "", "  ", "\n")
403
 
    else:
404
 
        return document.toprettyxml("  ", "\n")
405
 
 
406
 
def unjellyFromXML(stringOrFile):
407
 
    """I convert a string or the contents of an XML file into a Python object.
408
 
    """
409
 
    if hasattr(stringOrFile, "read"):
410
 
        document = parse(stringOrFile)
411
 
    else:
412
 
        document = parseString(stringOrFile)
413
 
    return unjellyFromDOM(document)
414
 
 
415
 
 
416
 
from twisted.web.microdom import Element, Document, parse, parseString, NodeList