1
# -*- test-case-name: twisted.test.test_jelly -*-
3
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4
# See LICENSE for details.
7
"""S-expression-based persistence of python objects.
11
Future Plans: Optimization. Lots of optimization. No semantic breakages
12
should be necessary, but if small tweaks are required to gain acceptable
13
large-scale performance then they will be made. Although Glyph is the
14
maintainer, Bruce Mitchener will be supervising most of the optimization work
17
I do something very much like L{Pickle<pickle>}; however, pickle's main goal
18
seems to be efficiency (both in space and time); jelly's main goals are
19
security, human readability, and portability to other environments.
22
This is how Jelly converts various objects to s-expressions:
24
Boolean: True --> ['boolean', 'true']
28
List: [1, 2] --> ['list', 1, 2]
30
String: \"hello\" --> \"hello\"
34
Dictionary: {'a' : 1, 'b' : 'c'} --> ['dictionary', ['b', 'c'], ['a', 1]]
36
Module: UserString --> ['module', 'UserString']
38
Class: UserString.UserString --> ['class', ['module', 'UserString'], 'UserString']
40
Function: string.join --> ['function', 'join', ['module', 'string']]
42
Instance: s is an instance of UserString.UserString, with a __dict__ {'data': 'hello'}:
43
[\"UserString.UserString\", ['dictionary', ['data', 'hello']]]
45
# ['instance', ['class', ['module', 'UserString'], 'UserString'], ['dictionary', ['data', 'hello']]]
47
Class Method: UserString.UserString.center:
48
['method', 'center', ['None'], ['class', ['module', 'UserString'], 'UserString']]
50
Instance Method: s.center, where s is an instance of UserString.UserString:
51
['method', 'center', ['instance', ['reference', 1, ['class', ['module', 'UserString'], 'UserString']], ['dictionary', ['data', 'd']]], ['dereference', 1]]
53
@author: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>}
56
__version__ = "$Revision: 1.48 $"[11:-2]
62
from types import StringType
64
from types import UnicodeType
67
from types import IntType
68
from types import TupleType
69
from types import ListType
70
from types import LongType
71
from types import FloatType
72
from types import FunctionType
73
from types import MethodType
74
from types import ModuleType
75
from types import DictionaryType
76
from types import InstanceType
77
from types import NoneType
78
from types import ClassType
82
from types import BooleanType
84
from new import instance
85
from new import instancemethod
86
from zope.interface import implements
89
from twisted.python.reflect import namedObject, qual
90
from twisted.persisted.crefutil import NotKnown, _Tuple, _InstanceMethod, _DictKeyAndValue, _Dereference
91
from twisted.python import runtime
93
from twisted.spread.interfaces import IJellyable, IUnjellyable
96
if runtime.platform.getType() == "java":
97
from org.python.core import PyStringMap
98
DictTypes = (DictionaryType, PyStringMap)
100
DictTypes = (DictionaryType,)
103
None_atom = "None" # N
105
class_atom = "class" # c
106
module_atom = "module" # m
107
function_atom = "function" # f
110
dereference_atom = 'dereference' # D
111
persistent_atom = 'persistent' # p
112
reference_atom = 'reference' # r
114
# mutable collections
115
dictionary_atom = "dictionary" # d
116
list_atom = 'list' # l
118
# immutable collections
119
# (assignment to __dict__ and __class__ still might go away!)
120
tuple_atom = "tuple" # t
121
instance_atom = 'instance' # i
125
unpersistable_atom = "unpersistable"# u
126
unjellyableRegistry = {}
127
unjellyableFactoryRegistry = {}
129
def _newInstance(cls, state):
130
"""Make a new instance of a class without calling its __init__ method.
131
'state' will be used to update inst.__dict__ . Supports both new- and
134
if not isinstance(cls, types.ClassType):
136
inst = cls.__new__(cls)
137
inst.__dict__.update(state) # Copy 'instance' behaviour
139
inst = instance(cls, state)
143
def _maybeClass(classnamep):
149
isObject = isinstance(classnamep, type)
150
if isinstance(classnamep, ClassType) or isObject:
151
return qual(classnamep)
154
def setUnjellyableForClass(classname, unjellyable):
155
"""Set which local class will represent a remote type.
157
If you have written a Copyable class that you expect your client to be
158
receiving, write a local "copy" class to represent it, then call::
160
jellier.setUnjellyableForClass('module.package.Class', MyJellier).
162
Call this at the module level immediately after its class
163
definition. MyCopier should be a subclass of RemoteCopy.
165
The classname may be a special tag returned by
166
'Copyable.getTypeToCopyFor' rather than an actual classname.
168
This call is also for cached classes, since there will be no
169
overlap. The rules are the same.
172
global unjellyableRegistry
173
classname = _maybeClass(classname)
174
unjellyableRegistry[classname] = unjellyable
175
globalSecurity.allowTypes(classname)
177
def setUnjellyableFactoryForClass(classname, copyFactory):
179
Set the factory to construct a remote instance of a type::
181
jellier.setFactoryForClass('module.package.Class', MyFactory)
183
Call this at the module level immediately after its class definition.
184
C{copyFactory} should return an instance or subclass of
185
L{RemoteCopy<pb.RemoteCopy>}.
187
Similar to L{setUnjellyableForClass} except it uses a factory instead
188
of creating an instance.
191
global unjellyableFactoryRegistry
192
classname = _maybeClass(classname)
193
unjellyableFactoryRegistry[classname] = copyFactory
194
globalSecurity.allowTypes(classname)
197
def setUnjellyableForClassTree(module, baseClass, prefix=None):
199
Set all classes in a module derived from C{baseClass} as copiers for
200
a corresponding remote class.
202
When you have a heirarchy of Copyable (or Cacheable) classes on
203
one side, and a mirror structure of Copied (or RemoteCache)
204
classes on the other, use this to setCopierForClass all your
205
Copieds for the Copyables.
207
Each copyTag (the \"classname\" argument to getTypeToCopyFor, and
208
what the Copyable's getTypeToCopyFor returns) is formed from
209
adding a prefix to the Copied's class name. The prefix defaults
210
to module.__name__. If you wish the copy tag to consist of solely
211
the classname, pass the empty string \'\'.
213
@param module: a module object from which to pull the Copied classes.
214
(passing sys.modules[__name__] might be useful)
216
@param baseClass: the base class from which all your Copied classes derive.
218
@param prefix: the string prefixed to classnames to form the
222
prefix = module.__name__
225
prefix = "%s." % prefix
227
for i in dir(module):
228
i_ = getattr(module, i)
229
if type(i_) == types.ClassType:
230
if issubclass(i_, baseClass):
231
setUnjellyableForClass('%s%s' % (prefix, i), i_)
233
def getInstanceState(inst, jellier):
234
"""Utility method to default to 'normal' state rules in serialization.
236
if hasattr(inst, "__getstate__"):
237
state = inst.__getstate__()
239
state = inst.__dict__
240
sxp = jellier.prepare(inst)
241
sxp.extend([qual(inst.__class__), jellier.jelly(state)])
242
return jellier.preserve(inst, sxp)
244
def setInstanceState(inst, unjellier, jellyList):
245
"""Utility method to default to 'normal' state rules in unserialization.
247
state = unjellier.unjelly(jellyList[1])
248
if hasattr(inst, "__setstate__"):
249
inst.__setstate__(state)
251
inst.__dict__ = state
256
This is an instance of a class that comes back when something couldn't be
259
def __init__(self, reason):
261
Initialize an unpersistable object with a descriptive `reason' string.
266
return "Unpersistable(%s)" % repr(self.reason)
271
Inherit from me to Jelly yourself directly with the `getStateFor'
274
implements(IJellyable)
276
def getStateFor(self, jellier):
279
def jellyFor(self, jellier):
281
@see L{twisted.spread.interfaces.IJellyable.jellyFor}
283
sxp = jellier.prepare(self)
285
qual(self.__class__),
286
jellier.jelly(self.getStateFor(jellier))])
287
return jellier.preserve(self, sxp)
293
Inherit from me to Unjelly yourself directly with the
294
`setStateFor' convenience method.
296
implements(IUnjellyable)
298
def setStateFor(self, unjellier, state):
299
self.__dict__ = state
301
def unjellyFor(self, unjellier, jellyList):
303
Perform the inverse operation of L{Jellyable.jellyFor}.
305
@see L{twisted.spread.interfaces.IUnjellyable.unjellyFor}
307
state = unjellier.unjelly(jellyList[1])
308
self.setStateFor(unjellier, state)
314
"""(Internal) This class manages state for a call to jelly()
316
def __init__(self, taster, persistentStore, invoker):
320
# `preserved' is a dict of previously seen instances.
322
# `cooked' is a dict of previously backreferenced instances to their `ref' lists.
326
self.persistentStore = persistentStore
327
self.invoker = invoker
329
def _cook(self, object):
332
backreference an object.
334
Notes on this method for the hapless future maintainer: If I've already
335
gone through the prepare/preserve cycle on the specified object (it is
336
being referenced after the serializer is \"done with\" it, e.g. this
337
reference is NOT circular), the copy-in-place of aList is relevant,
338
since the list being modified is the actual, pre-existing jelly
339
expression that was returned for that object. If not, it's technically
340
superfluous, since the value in self.preserved didn't need to be set,
341
but the invariant that self.preserved[id(object)] is a list is
342
convenient because that means we don't have to test and create it or
343
not create it here, creating fewer code-paths. that's why
344
self.preserved is always set to a list.
346
Sorry that this code is so hard to follow, but Python objects are
347
tricky to persist correctly. -glyph
350
aList = self.preserved[id(object)]
351
newList = copy.copy(aList)
352
# make a new reference ID
354
self._ref_id = self._ref_id + 1
355
# replace the old list in-place, so that we don't have to track the
356
# previous reference to it.
357
aList[:] = [reference_atom, refid, newList]
358
self.cooked[id(object)] = [dereference_atom, refid]
361
def prepare(self, object):
363
create a list for persisting an object to. this will allow
364
backreferences to be made internal to the object. (circular
367
The reason this needs to happen is that we don't generate an ID for
368
every object, so we won't necessarily know which ID the object will
369
have in the future. When it is 'cooked' ( see _cook ), it will be
370
assigned an ID, and the temporary placeholder list created here will be
371
modified in-place to create an expression that gives this object an ID:
372
[reference id# [object-jelly]].
375
# create a placeholder list to be preserved
376
self.preserved[id(object)] = []
377
# keep a reference to this object around, so it doesn't disappear!
378
# (This isn't always necessary, but for cases where the objects are
379
# dynamically generated by __getstate__ or getStateToCopyFor calls, it
380
# is; id() will return the same value for a different object if it gets
381
# garbage collected. This may be optimized later.)
382
self.cooker[id(object)] = object
385
def preserve(self, object, sexp):
387
mark an object's persistent list for later referral
389
#if I've been cooked in the meanwhile,
390
if self.cooked.has_key(id(object)):
391
# replace the placeholder empty list with the real one
392
self.preserved[id(object)][2] = sexp
393
# but give this one back.
394
sexp = self.preserved[id(object)]
396
self.preserved[id(object)] = sexp
399
constantTypes = {types.StringType : 1, types.IntType : 1,
400
types.FloatType : 1, types.LongType : 1}
402
def _checkMutable(self,obj):
404
if self.cooked.has_key(objId):
405
return self.cooked[objId]
406
if self.preserved.has_key(objId):
408
return self.cooked[objId]
410
def jelly(self, obj):
411
if isinstance(obj, Jellyable):
412
preRef = self._checkMutable(obj)
415
return obj.jellyFor(self)
417
if self.taster.isTypeAllowed(qual(objType)):
419
if ((objType is StringType) or
420
(objType is IntType) or
421
(objType is LongType) or
422
(objType is FloatType)):
424
elif objType is MethodType:
426
obj.im_func.__name__,
427
self.jelly(obj.im_self),
428
self.jelly(obj.im_class)]
430
elif UnicodeType and objType is UnicodeType:
431
return ['unicode', obj.encode('UTF-8')]
432
elif objType is NoneType:
434
elif objType is FunctionType:
436
return ['function', str(pickle.whichmodule(obj, obj.__name__))
439
elif objType is ModuleType:
440
return ['module', obj.__name__]
441
elif objType is BooleanType:
442
return ['boolean', obj and 'true' or 'false']
443
elif objType is datetime.datetime:
445
raise NotImplementedError, "Currently can't jelly datetime objects with tzinfo"
446
return ['datetime', '%s %s %s %s %s %s %s' % (obj.year, obj.month, obj.day, obj.hour, obj.minute, obj.second, obj.microsecond)]
447
elif objType is datetime.time:
449
raise NotImplementedError, "Currently can't jelly datetime objects with tzinfo"
450
return ['time', '%s %s %s %s' % (obj.hour, obj.minute, obj.second, obj.microsecond)]
451
elif objType is datetime.date:
452
return ['date', '%s %s %s' % (obj.year, obj.month, obj.day)]
453
elif objType is datetime.timedelta:
454
return ['timedelta', '%s %s %s' % (obj.days, obj.seconds, obj.microseconds)]
455
elif objType is ClassType or issubclass(objType, type):
456
return ['class', qual(obj)]
458
preRef = self._checkMutable(obj)
462
sxp = self.prepare(obj)
463
if objType is ListType:
464
sxp.append(list_atom)
466
sxp.append(self.jelly(item))
467
elif objType is TupleType:
468
sxp.append(tuple_atom)
470
sxp.append(self.jelly(item))
471
elif objType in DictTypes:
472
sxp.append(dictionary_atom)
473
for key, val in obj.items():
474
sxp.append([self.jelly(key), self.jelly(val)])
476
className = qual(obj.__class__)
478
if self.persistentStore:
479
persistent = self.persistentStore(obj, self)
480
if persistent is not None:
481
sxp.append(persistent_atom)
482
sxp.append(persistent)
483
elif self.taster.isClassAllowed(obj.__class__):
484
sxp.append(className)
485
if hasattr(obj, "__getstate__"):
486
state = obj.__getstate__()
489
sxp.append(self.jelly(state))
492
"instance of class %s deemed insecure" %
493
qual(obj.__class__), sxp)
494
return self.preserve(obj, sxp)
496
if objType is InstanceType:
497
raise InsecureJelly("Class not allowed for instance: %s %s" %
498
(obj.__class__, obj))
499
raise InsecureJelly("Type not allowed for object: %s %s" %
502
def unpersistable(self, reason, sxp=None):
504
Returns an sexp: (unpersistable "reason"). Utility method for making
505
note that a particular object could not be serialized.
509
sxp.append(unpersistable_atom)
515
def __init__(self, taster, persistentLoad, invoker):
517
self.persistentLoad = persistentLoad
519
self.postCallbacks = []
520
self.invoker = invoker
522
def unjellyFull(self, obj):
523
o = self.unjelly(obj)
524
for m in self.postCallbacks:
528
def unjelly(self, obj):
529
if type(obj) is not types.ListType:
532
if not self.taster.isTypeAllowed(jelType):
533
raise InsecureJelly(jelType)
534
regClass = unjellyableRegistry.get(jelType)
535
if regClass is not None:
536
if isinstance(regClass, ClassType):
537
inst = _Dummy() # XXX chomp, chomp
538
inst.__class__ = regClass
539
method = inst.unjellyFor
540
elif isinstance(regClass, type):
541
# regClass.__new__ does not call regClass.__init__
542
inst = regClass.__new__(regClass)
543
method = inst.unjellyFor
545
method = regClass # this is how it ought to be done
546
val = method(self, obj)
547
if hasattr(val, 'postUnjelly'):
548
self.postCallbacks.append(inst.postUnjelly)
550
regFactory = unjellyableFactoryRegistry.get(jelType)
551
if regFactory is not None:
552
state = self.unjelly(obj[1])
553
inst = regFactory(state)
554
if hasattr(inst, 'postUnjelly'):
555
self.postCallbacks.append(inst.postUnjelly)
557
thunk = getattr(self, '_unjelly_%s'%jelType, None)
558
if thunk is not None:
561
nameSplit = string.split(jelType, '.')
562
modName = string.join(nameSplit[:-1], '.')
563
if not self.taster.isModuleAllowed(modName):
564
raise InsecureJelly("Module %s not allowed (in type %s)." % (modName, jelType))
565
clz = namedObject(jelType)
566
if not self.taster.isClassAllowed(clz):
567
raise InsecureJelly("Class %s not allowed." % jelType)
568
if hasattr(clz, "__setstate__"):
569
ret = _newInstance(clz, {})
570
state = self.unjelly(obj[1])
571
ret.__setstate__(state)
573
state = self.unjelly(obj[1])
574
ret = _newInstance(clz, state)
575
if hasattr(clz, 'postUnjelly'):
576
self.postCallbacks.append(ret.postUnjelly)
579
def _unjelly_None(self, exp):
582
def _unjelly_unicode(self, exp):
584
return unicode(exp[0], "UTF-8")
586
return Unpersistable(exp[0])
588
def _unjelly_boolean(self, exp):
590
assert exp[0] in ('true', 'false')
591
return exp[0] == 'true'
593
return Unpersistable(exp[0])
595
def _unjelly_datetime(self, exp):
596
return datetime.datetime(*map(int, exp[0].split()))
598
def _unjelly_date(self, exp):
599
return datetime.date(*map(int, exp[0].split()))
601
def _unjelly_time(self, exp):
602
return datetime.time(*map(int, exp[0].split()))
604
def _unjelly_timedelta(self, exp):
605
days, seconds, microseconds = map(int, exp[0].split())
606
return datetime.timedelta(days=days, seconds=seconds, microseconds=microseconds)
608
def unjellyInto(self, obj, loc, jel):
609
o = self.unjelly(jel)
610
if isinstance(o, NotKnown):
611
o.addDependant(obj, loc)
615
def _unjelly_dereference(self, lst):
617
x = self.references.get(refid)
620
der = _Dereference(refid)
621
self.references[refid] = der
624
def _unjelly_reference(self, lst):
627
o = self.unjelly(exp)
628
ref = self.references.get(refid)
630
self.references[refid] = o
631
elif isinstance(ref, NotKnown):
632
ref.resolveDependants(o)
633
self.references[refid] = o
635
assert 0, "Multiple references with same ID!"
638
def _unjelly_tuple(self, lst):
642
if isinstance(self.unjellyInto(l, elem, lst[elem]), NotKnown):
649
def _unjelly_list(self, lst):
652
self.unjellyInto(l, elem, lst[elem])
655
def _unjelly_dictionary(self, lst):
658
kvd = _DictKeyAndValue(d)
659
self.unjellyInto(kvd, 0, k)
660
self.unjellyInto(kvd, 1, v)
664
def _unjelly_module(self, rest):
666
if type(moduleName) != types.StringType:
667
raise InsecureJelly("Attempted to unjelly a module with a non-string name.")
668
if not self.taster.isModuleAllowed(moduleName):
669
raise InsecureJelly("Attempted to unjelly module named %s" % repr(moduleName))
670
mod = __import__(moduleName, {}, {},"x")
673
def _unjelly_class(self, rest):
674
clist = string.split(rest[0], '.')
675
modName = string.join(clist[:-1], '.')
676
if not self.taster.isModuleAllowed(modName):
677
raise InsecureJelly("module %s not allowed" % modName)
678
klaus = namedObject(rest[0])
679
if type(klaus) is not types.ClassType:
680
raise InsecureJelly("class %s unjellied to something that isn't a class: %s" % (repr(rest[0]), repr(klaus)))
681
if not self.taster.isClassAllowed(klaus):
682
raise InsecureJelly("class not allowed: %s" % qual(klaus))
685
def _unjelly_function(self, rest):
686
modSplit = string.split(rest[0], '.')
687
modName = string.join(modSplit[:-1], '.')
688
if not self.taster.isModuleAllowed(modName):
689
raise InsecureJelly("Module not allowed: %s"% modName)
690
# XXX do I need an isFunctionAllowed?
691
function = namedObject(rest[0])
694
def _unjelly_persistent(self, rest):
695
if self.persistentLoad:
696
pload = self.persistentLoad(rest[0], self)
699
return Unpersistable("persistent callback not found")
701
def _unjelly_instance(self, rest):
702
clz = self.unjelly(rest[0])
703
if type(clz) is not types.ClassType:
704
raise InsecureJelly("Instance found with non-class class.")
705
if hasattr(clz, "__setstate__"):
706
inst = _newInstance(clz, {})
707
state = self.unjelly(rest[1])
708
inst.__setstate__(state)
710
state = self.unjelly(rest[1])
711
inst = _newInstance(clz, state)
712
if hasattr(clz, 'postUnjelly'):
713
self.postCallbacks.append(inst.postUnjelly)
716
def _unjelly_unpersistable(self, rest):
717
return Unpersistable(rest[0])
719
def _unjelly_method(self, rest):
720
''' (internal) unjelly a method
723
im_self = self.unjelly(rest[1])
724
im_class = self.unjelly(rest[2])
725
if type(im_class) is not types.ClassType:
726
raise InsecureJelly("Method found with non-class class.")
727
if im_class.__dict__.has_key(im_name):
729
im = getattr(im_class, im_name)
730
elif isinstance(im_self, NotKnown):
731
im = _InstanceMethod(im_name, im_self, im_class)
733
im = instancemethod(im_class.__dict__[im_name],
737
raise 'instance method changed'
743
Dummy class, used for unserializing instances.
745
class _DummyNewStyle(object):
747
Dummy class, used for unserializing instances of new-style classes.
753
#### Published Interface.
756
class InsecureJelly(Exception):
758
This exception will be raised when a jelly is deemed `insecure'; e.g. it
759
contains a type, class, or module disallowed by the specified `taster'
764
class DummySecurityOptions:
765
"""DummySecurityOptions() -> insecure security options
766
Dummy security options -- this class will allow anything.
768
def isModuleAllowed(self, moduleName):
769
"""DummySecurityOptions.isModuleAllowed(moduleName) -> boolean
770
returns 1 if a module by that name is allowed, 0 otherwise
774
def isClassAllowed(self, klass):
775
"""DummySecurityOptions.isClassAllowed(class) -> boolean
776
Assumes the module has already been allowed. Returns 1 if the given
777
class is allowed, 0 otherwise.
781
def isTypeAllowed(self, typeName):
782
"""DummySecurityOptions.isTypeAllowed(typeName) -> boolean
783
Returns 1 if the given type is allowed, 0 otherwise.
789
class SecurityOptions:
791
This will by default disallow everything, except for 'none'.
794
basicTypes = ["dictionary", "list", "tuple",
795
"reference", "dereference", "unpersistable",
796
"persistent", "long_int", "long", "dict"]
802
# I don't believe any of these types can ever pose a security hazard,
803
# except perhaps "reference"...
804
self.allowedTypes = {"None": 1,
816
if hasattr(types, 'UnicodeType'):
817
self.allowedTypes['unicode'] = 1
818
self.allowedModules = {}
819
self.allowedClasses = {}
821
def allowBasicTypes(self):
822
"""SecurityOptions.allowBasicTypes()
823
Allow all `basic' types. (Dictionary and list. Int, string, and float are implicitly allowed.)
825
self.allowTypes(*self.basicTypes)
827
def allowTypes(self, *types):
828
"""SecurityOptions.allowTypes(typeString): Allow a particular type, by its name.
831
if not isinstance(typ, str):
833
self.allowedTypes[typ] = 1
835
def allowInstancesOf(self, *classes):
836
"""SecurityOptions.allowInstances(klass, klass, ...): allow instances
837
of the specified classes
839
This will also allow the 'instance', 'class' (renamed 'classobj' in
840
Python 2.3), and 'module' types, as well as basic types.
842
self.allowBasicTypes()
843
self.allowTypes("instance", "class", "classobj", "module")
844
for klass in classes:
845
self.allowTypes(qual(klass))
846
self.allowModules(klass.__module__)
847
self.allowedClasses[klass] = 1
849
def allowModules(self, *modules):
850
"""SecurityOptions.allowModules(module, module, ...): allow modules by name
851
This will also allow the 'module' type.
853
for module in modules:
854
if type(module) == types.ModuleType:
855
module = module.__name__
856
self.allowedModules[module] = 1
858
def isModuleAllowed(self, moduleName):
859
"""SecurityOptions.isModuleAllowed(moduleName) -> boolean
860
returns 1 if a module by that name is allowed, 0 otherwise
862
return self.allowedModules.has_key(moduleName)
864
def isClassAllowed(self, klass):
865
"""SecurityOptions.isClassAllowed(class) -> boolean
866
Assumes the module has already been allowed. Returns 1 if the given
867
class is allowed, 0 otherwise.
869
return self.allowedClasses.has_key(klass)
871
def isTypeAllowed(self, typeName):
872
"""SecurityOptions.isTypeAllowed(typeName) -> boolean
873
Returns 1 if the given type is allowed, 0 otherwise.
875
return (self.allowedTypes.has_key(typeName) or
879
globalSecurity = SecurityOptions()
880
globalSecurity.allowBasicTypes()
882
def jelly(object, taster = DummySecurityOptions(), persistentStore=None, invoker=None):
883
"""Serialize to s-expression.
885
Returns a list which is the serialized representation of an object. An
886
optional 'taster' argument takes a SecurityOptions and will mark any
887
insecure objects as unpersistable rather than serializing them.
889
return _Jellier(taster, persistentStore, invoker).jelly(object)
892
def unjelly(sexp, taster = DummySecurityOptions(), persistentLoad=None, invoker=None):
893
"""Unserialize from s-expression.
895
Takes an list that was the result from a call to jelly() and unserializes
896
an arbitrary object from it. The optional 'taster' argument, an instance
897
of SecurityOptions, will cause an InsecureJelly exception to be raised if a
898
disallowed type, module, or class attempted to unserialize.
900
return _Unjellier(taster, persistentLoad, invoker).unjellyFull(sexp)