1
# -*- test-case-name: twisted.test.test_jelly -*-
2
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
S-expression-based persistence of python objects.
8
It does something very much like L{Pickle<pickle>}; however, pickle's main goal
9
seems to be efficiency (both in space and time); jelly's main goals are
10
security, human readability, and portability to other environments.
12
This is how Jelly converts various objects to s-expressions.
15
True --> ['boolean', 'true']
21
[1, 2] --> ['list', 1, 2]
24
\"hello\" --> \"hello\"
30
{'a': 1, 'b': 'c'} --> ['dictionary', ['b', 'c'], ['a', 1]]
33
UserString --> ['module', 'UserString']
36
UserString.UserString --> ['class', ['module', 'UserString'], 'UserString']
39
string.join --> ['function', 'join', ['module', 'string']]
41
Instance: s is an instance of UserString.UserString, with a __dict__
43
[\"UserString.UserString\", ['dictionary', ['data', 'hello']]]
45
Class Method: UserString.UserString.center::
46
['method', 'center', ['None'], ['class', ['module', 'UserString'],
49
Instance Method: s.center, where s is an instance of UserString.UserString::
50
['method', 'center', ['instance', ['reference', 1, ['class',
51
['module', 'UserString'], 'UserString']], ['dictionary', ['data', 'd']]],
54
The C{set} builtin and the C{sets.Set} class are serialized to the same
55
thing, and unserialized to C{set} if available, else to C{sets.Set}. It means
56
that there's a possibility of type switching in the serialization process. The
57
solution is to always use C{set} if possible, and only use C{sets.Set} under
58
Python 2.3; this can be accomplished by using L{twisted.python.compat.set}.
60
The same rule applies for C{frozenset} and C{sets.ImmutableSet}.
62
@author: Glyph Lefkowitz
69
from types import StringType
70
from types import UnicodeType
71
from types import IntType
72
from types import TupleType
73
from types import ListType
74
from types import LongType
75
from types import FloatType
76
from types import FunctionType
77
from types import MethodType
78
from types import ModuleType
79
from types import DictionaryType
80
from types import InstanceType
81
from types import NoneType
82
from types import ClassType
86
from types import BooleanType
99
# Filter out deprecation warning for Python >= 2.6
100
warnings.filterwarnings("ignore", category=DeprecationWarning,
101
message="the sets module is deprecated", append=True)
104
warnings.filters.pop()
107
from new import instance
108
from new import instancemethod
109
from zope.interface import implements
112
from twisted.python.reflect import namedObject, qual
113
from twisted.persisted.crefutil import NotKnown, _Tuple, _InstanceMethod
114
from twisted.persisted.crefutil import _DictKeyAndValue, _Dereference
115
from twisted.persisted.crefutil import _Container
116
from twisted.python import runtime
117
from twisted.python.compat import reduce
119
from twisted.spread.interfaces import IJellyable, IUnjellyable
122
if runtime.platform.getType() == "java":
123
from org.python.core import PyStringMap
124
DictTypes = (DictionaryType, PyStringMap)
126
DictTypes = (DictionaryType,)
129
None_atom = "None" # N
131
class_atom = "class" # c
132
module_atom = "module" # m
133
function_atom = "function" # f
136
dereference_atom = 'dereference' # D
137
persistent_atom = 'persistent' # p
138
reference_atom = 'reference' # r
140
# mutable collections
141
dictionary_atom = "dictionary" # d
142
list_atom = 'list' # l
145
# immutable collections
146
# (assignment to __dict__ and __class__ still might go away!)
147
tuple_atom = "tuple" # t
148
instance_atom = 'instance' # i
149
frozenset_atom = 'frozenset'
153
unpersistable_atom = "unpersistable"# u
154
unjellyableRegistry = {}
155
unjellyableFactoryRegistry = {}
159
def _newInstance(cls, state):
161
Make a new instance of a class without calling its __init__ method.
162
'state' will be used to update inst.__dict__ . Supports both new- and
165
if not isinstance(cls, types.ClassType):
167
inst = cls.__new__(cls)
168
inst.__dict__.update(state) # Copy 'instance' behaviour
170
inst = instance(cls, state)
175
def _maybeClass(classnamep):
181
isObject = isinstance(classnamep, type)
182
if isinstance(classnamep, ClassType) or isObject:
183
return qual(classnamep)
188
def setUnjellyableForClass(classname, unjellyable):
190
Set which local class will represent a remote type.
192
If you have written a Copyable class that you expect your client to be
193
receiving, write a local "copy" class to represent it, then call::
195
jellier.setUnjellyableForClass('module.package.Class', MyCopier).
197
Call this at the module level immediately after its class
198
definition. MyCopier should be a subclass of RemoteCopy.
200
The classname may be a special tag returned by
201
'Copyable.getTypeToCopyFor' rather than an actual classname.
203
This call is also for cached classes, since there will be no
204
overlap. The rules are the same.
207
global unjellyableRegistry
208
classname = _maybeClass(classname)
209
unjellyableRegistry[classname] = unjellyable
210
globalSecurity.allowTypes(classname)
214
def setUnjellyableFactoryForClass(classname, copyFactory):
216
Set the factory to construct a remote instance of a type::
218
jellier.setUnjellyableFactoryForClass('module.package.Class', MyFactory)
220
Call this at the module level immediately after its class definition.
221
C{copyFactory} should return an instance or subclass of
222
L{RemoteCopy<pb.RemoteCopy>}.
224
Similar to L{setUnjellyableForClass} except it uses a factory instead
225
of creating an instance.
228
global unjellyableFactoryRegistry
229
classname = _maybeClass(classname)
230
unjellyableFactoryRegistry[classname] = copyFactory
231
globalSecurity.allowTypes(classname)
235
def setUnjellyableForClassTree(module, baseClass, prefix=None):
237
Set all classes in a module derived from C{baseClass} as copiers for
238
a corresponding remote class.
240
When you have a heirarchy of Copyable (or Cacheable) classes on one
241
side, and a mirror structure of Copied (or RemoteCache) classes on the
242
other, use this to setUnjellyableForClass all your Copieds for the
245
Each copyTag (the \"classname\" argument to getTypeToCopyFor, and
246
what the Copyable's getTypeToCopyFor returns) is formed from
247
adding a prefix to the Copied's class name. The prefix defaults
248
to module.__name__. If you wish the copy tag to consist of solely
249
the classname, pass the empty string \'\'.
251
@param module: a module object from which to pull the Copied classes.
252
(passing sys.modules[__name__] might be useful)
254
@param baseClass: the base class from which all your Copied classes derive.
256
@param prefix: the string prefixed to classnames to form the
260
prefix = module.__name__
263
prefix = "%s." % prefix
265
for i in dir(module):
266
i_ = getattr(module, i)
267
if type(i_) == types.ClassType:
268
if issubclass(i_, baseClass):
269
setUnjellyableForClass('%s%s' % (prefix, i), i_)
273
def getInstanceState(inst, jellier):
275
Utility method to default to 'normal' state rules in serialization.
277
if hasattr(inst, "__getstate__"):
278
state = inst.__getstate__()
280
state = inst.__dict__
281
sxp = jellier.prepare(inst)
282
sxp.extend([qual(inst.__class__), jellier.jelly(state)])
283
return jellier.preserve(inst, sxp)
287
def setInstanceState(inst, unjellier, jellyList):
289
Utility method to default to 'normal' state rules in unserialization.
291
state = unjellier.unjelly(jellyList[1])
292
if hasattr(inst, "__setstate__"):
293
inst.__setstate__(state)
295
inst.__dict__ = state
302
This is an instance of a class that comes back when something couldn't be
306
def __init__(self, reason):
308
Initialize an unpersistable object with a descriptive C{reason} string.
314
return "Unpersistable(%s)" % repr(self.reason)
320
Inherit from me to Jelly yourself directly with the `getStateFor'
323
implements(IJellyable)
325
def getStateFor(self, jellier):
329
def jellyFor(self, jellier):
331
@see: L{twisted.spread.interfaces.IJellyable.jellyFor}
333
sxp = jellier.prepare(self)
335
qual(self.__class__),
336
jellier.jelly(self.getStateFor(jellier))])
337
return jellier.preserve(self, sxp)
343
Inherit from me to Unjelly yourself directly with the
344
C{setStateFor} convenience method.
346
implements(IUnjellyable)
348
def setStateFor(self, unjellier, state):
349
self.__dict__ = state
352
def unjellyFor(self, unjellier, jellyList):
354
Perform the inverse operation of L{Jellyable.jellyFor}.
356
@see: L{twisted.spread.interfaces.IUnjellyable.unjellyFor}
358
state = unjellier.unjelly(jellyList[1])
359
self.setStateFor(unjellier, state)
366
(Internal) This class manages state for a call to jelly()
369
def __init__(self, taster, persistentStore, invoker):
374
# `preserved' is a dict of previously seen instances.
376
# `cooked' is a dict of previously backreferenced instances to their
381
self.persistentStore = persistentStore
382
self.invoker = invoker
385
def _cook(self, object):
387
(internal) Backreference an object.
389
Notes on this method for the hapless future maintainer: If I've already
390
gone through the prepare/preserve cycle on the specified object (it is
391
being referenced after the serializer is \"done with\" it, e.g. this
392
reference is NOT circular), the copy-in-place of aList is relevant,
393
since the list being modified is the actual, pre-existing jelly
394
expression that was returned for that object. If not, it's technically
395
superfluous, since the value in self.preserved didn't need to be set,
396
but the invariant that self.preserved[id(object)] is a list is
397
convenient because that means we don't have to test and create it or
398
not create it here, creating fewer code-paths. that's why
399
self.preserved is always set to a list.
401
Sorry that this code is so hard to follow, but Python objects are
402
tricky to persist correctly. -glyph
404
aList = self.preserved[id(object)]
405
newList = copy.copy(aList)
406
# make a new reference ID
408
self._ref_id = self._ref_id + 1
409
# replace the old list in-place, so that we don't have to track the
410
# previous reference to it.
411
aList[:] = [reference_atom, refid, newList]
412
self.cooked[id(object)] = [dereference_atom, refid]
416
def prepare(self, object):
418
(internal) Create a list for persisting an object to. This will allow
419
backreferences to be made internal to the object. (circular
422
The reason this needs to happen is that we don't generate an ID for
423
every object, so we won't necessarily know which ID the object will
424
have in the future. When it is 'cooked' ( see _cook ), it will be
425
assigned an ID, and the temporary placeholder list created here will be
426
modified in-place to create an expression that gives this object an ID:
427
[reference id# [object-jelly]].
430
# create a placeholder list to be preserved
431
self.preserved[id(object)] = []
432
# keep a reference to this object around, so it doesn't disappear!
433
# (This isn't always necessary, but for cases where the objects are
434
# dynamically generated by __getstate__ or getStateToCopyFor calls, it
435
# is; id() will return the same value for a different object if it gets
436
# garbage collected. This may be optimized later.)
437
self.cooker[id(object)] = object
441
def preserve(self, object, sexp):
443
(internal) Mark an object's persistent list for later referral.
445
# if I've been cooked in the meanwhile,
446
if id(object) in self.cooked:
447
# replace the placeholder empty list with the real one
448
self.preserved[id(object)][2] = sexp
449
# but give this one back.
450
sexp = self.preserved[id(object)]
452
self.preserved[id(object)] = sexp
455
constantTypes = {types.StringType : 1, types.IntType : 1,
456
types.FloatType : 1, types.LongType : 1}
459
def _checkMutable(self,obj):
461
if objId in self.cooked:
462
return self.cooked[objId]
463
if objId in self.preserved:
465
return self.cooked[objId]
468
def jelly(self, obj):
469
if isinstance(obj, Jellyable):
470
preRef = self._checkMutable(obj)
473
return obj.jellyFor(self)
475
if self.taster.isTypeAllowed(qual(objType)):
477
if ((objType is StringType) or
478
(objType is IntType) or
479
(objType is LongType) or
480
(objType is FloatType)):
482
elif objType is MethodType:
484
obj.im_func.__name__,
485
self.jelly(obj.im_self),
486
self.jelly(obj.im_class)]
488
elif UnicodeType and objType is UnicodeType:
489
return ['unicode', obj.encode('UTF-8')]
490
elif objType is NoneType:
492
elif objType is FunctionType:
494
return ['function', str(pickle.whichmodule(obj, obj.__name__))
497
elif objType is ModuleType:
498
return ['module', obj.__name__]
499
elif objType is BooleanType:
500
return ['boolean', obj and 'true' or 'false']
501
elif objType is datetime.datetime:
503
raise NotImplementedError(
504
"Currently can't jelly datetime objects with tzinfo")
505
return ['datetime', '%s %s %s %s %s %s %s' % (
506
obj.year, obj.month, obj.day, obj.hour,
507
obj.minute, obj.second, obj.microsecond)]
508
elif objType is datetime.time:
510
raise NotImplementedError(
511
"Currently can't jelly datetime objects with tzinfo")
512
return ['time', '%s %s %s %s' % (obj.hour, obj.minute,
513
obj.second, obj.microsecond)]
514
elif objType is datetime.date:
515
return ['date', '%s %s %s' % (obj.year, obj.month, obj.day)]
516
elif objType is datetime.timedelta:
517
return ['timedelta', '%s %s %s' % (obj.days, obj.seconds,
519
elif objType is ClassType or issubclass(objType, type):
520
return ['class', qual(obj)]
521
elif decimal is not None and objType is decimal.Decimal:
522
return self.jelly_decimal(obj)
524
preRef = self._checkMutable(obj)
528
sxp = self.prepare(obj)
529
if objType is ListType:
530
sxp.extend(self._jellyIterable(list_atom, obj))
531
elif objType is TupleType:
532
sxp.extend(self._jellyIterable(tuple_atom, obj))
533
elif objType in DictTypes:
534
sxp.append(dictionary_atom)
535
for key, val in obj.items():
536
sxp.append([self.jelly(key), self.jelly(val)])
537
elif (_set is not None and objType is set or
538
objType is _sets.Set):
539
sxp.extend(self._jellyIterable(set_atom, obj))
540
elif (_set is not None and objType is frozenset or
541
objType is _sets.ImmutableSet):
542
sxp.extend(self._jellyIterable(frozenset_atom, obj))
544
className = qual(obj.__class__)
546
if self.persistentStore:
547
persistent = self.persistentStore(obj, self)
548
if persistent is not None:
549
sxp.append(persistent_atom)
550
sxp.append(persistent)
551
elif self.taster.isClassAllowed(obj.__class__):
552
sxp.append(className)
553
if hasattr(obj, "__getstate__"):
554
state = obj.__getstate__()
557
sxp.append(self.jelly(state))
560
"instance of class %s deemed insecure" %
561
qual(obj.__class__), sxp)
562
return self.preserve(obj, sxp)
564
if objType is InstanceType:
565
raise InsecureJelly("Class not allowed for instance: %s %s" %
566
(obj.__class__, obj))
567
raise InsecureJelly("Type not allowed for object: %s %s" %
571
def _jellyIterable(self, atom, obj):
573
Jelly an iterable object.
575
@param atom: the identifier atom of the object.
578
@param obj: any iterable object.
579
@type obj: C{iterable}
581
@return: a generator of jellied data.
586
yield self.jelly(item)
589
def jelly_decimal(self, d):
591
Jelly a decimal object.
593
@param d: a decimal object to serialize.
594
@type d: C{decimal.Decimal}
596
@return: jelly for the decimal object.
599
sign, guts, exponent = d.as_tuple()
600
value = reduce(lambda left, right: left * 10 + right, guts)
603
return ['decimal', value, exponent]
606
def unpersistable(self, reason, sxp=None):
608
(internal) Returns an sexp: (unpersistable "reason"). Utility method
609
for making note that a particular object could not be serialized.
613
sxp.append(unpersistable_atom)
621
def __init__(self, taster, persistentLoad, invoker):
623
self.persistentLoad = persistentLoad
625
self.postCallbacks = []
626
self.invoker = invoker
629
def unjellyFull(self, obj):
630
o = self.unjelly(obj)
631
for m in self.postCallbacks:
636
def unjelly(self, obj):
637
if type(obj) is not types.ListType:
640
if not self.taster.isTypeAllowed(jelType):
641
raise InsecureJelly(jelType)
642
regClass = unjellyableRegistry.get(jelType)
643
if regClass is not None:
644
if isinstance(regClass, ClassType):
645
inst = _Dummy() # XXX chomp, chomp
646
inst.__class__ = regClass
647
method = inst.unjellyFor
648
elif isinstance(regClass, type):
649
# regClass.__new__ does not call regClass.__init__
650
inst = regClass.__new__(regClass)
651
method = inst.unjellyFor
653
method = regClass # this is how it ought to be done
654
val = method(self, obj)
655
if hasattr(val, 'postUnjelly'):
656
self.postCallbacks.append(inst.postUnjelly)
658
regFactory = unjellyableFactoryRegistry.get(jelType)
659
if regFactory is not None:
660
state = self.unjelly(obj[1])
661
inst = regFactory(state)
662
if hasattr(inst, 'postUnjelly'):
663
self.postCallbacks.append(inst.postUnjelly)
665
thunk = getattr(self, '_unjelly_%s'%jelType, None)
666
if thunk is not None:
669
nameSplit = jelType.split('.')
670
modName = '.'.join(nameSplit[:-1])
671
if not self.taster.isModuleAllowed(modName):
673
"Module %s not allowed (in type %s)." % (modName, jelType))
674
clz = namedObject(jelType)
675
if not self.taster.isClassAllowed(clz):
676
raise InsecureJelly("Class %s not allowed." % jelType)
677
if hasattr(clz, "__setstate__"):
678
ret = _newInstance(clz, {})
679
state = self.unjelly(obj[1])
680
ret.__setstate__(state)
682
state = self.unjelly(obj[1])
683
ret = _newInstance(clz, state)
684
if hasattr(clz, 'postUnjelly'):
685
self.postCallbacks.append(ret.postUnjelly)
689
def _unjelly_None(self, exp):
693
def _unjelly_unicode(self, exp):
695
return unicode(exp[0], "UTF-8")
697
return Unpersistable("Could not unpersist unicode: %s" % (exp[0],))
700
def _unjelly_decimal(self, exp):
702
Unjelly decimal objects, if decimal is available. If not, return a
703
L{Unpersistable} object instead.
706
return Unpersistable(
707
"Could not unpersist decimal: %s" % (exp[0] * (10**exp[1]),))
714
guts = decimal.Decimal(value).as_tuple()[1]
715
return decimal.Decimal((sign, guts, exponent))
718
def _unjelly_boolean(self, exp):
720
assert exp[0] in ('true', 'false')
721
return exp[0] == 'true'
723
return Unpersistable("Could not unpersist boolean: %s" % (exp[0],))
726
def _unjelly_datetime(self, exp):
727
return datetime.datetime(*map(int, exp[0].split()))
730
def _unjelly_date(self, exp):
731
return datetime.date(*map(int, exp[0].split()))
734
def _unjelly_time(self, exp):
735
return datetime.time(*map(int, exp[0].split()))
738
def _unjelly_timedelta(self, exp):
739
days, seconds, microseconds = map(int, exp[0].split())
740
return datetime.timedelta(
741
days=days, seconds=seconds, microseconds=microseconds)
744
def unjellyInto(self, obj, loc, jel):
745
o = self.unjelly(jel)
746
if isinstance(o, NotKnown):
747
o.addDependant(obj, loc)
752
def _unjelly_dereference(self, lst):
754
x = self.references.get(refid)
757
der = _Dereference(refid)
758
self.references[refid] = der
762
def _unjelly_reference(self, lst):
765
o = self.unjelly(exp)
766
ref = self.references.get(refid)
768
self.references[refid] = o
769
elif isinstance(ref, NotKnown):
770
ref.resolveDependants(o)
771
self.references[refid] = o
773
assert 0, "Multiple references with same ID!"
777
def _unjelly_tuple(self, lst):
781
if isinstance(self.unjellyInto(l, elem, lst[elem]), NotKnown):
789
def _unjelly_list(self, lst):
792
self.unjellyInto(l, elem, lst[elem])
796
def _unjellySetOrFrozenset(self, lst, containerType):
798
Helper method to unjelly set or frozenset.
800
@param lst: the content of the set.
803
@param containerType: the type of C{set} to use.
808
data = self.unjellyInto(l, elem, lst[elem])
809
if isinstance(data, NotKnown):
812
return _Container(l, containerType)
814
return containerType(l)
817
def _unjelly_set(self, lst):
819
Unjelly set using either the C{set} builtin if available, or
820
C{sets.Set} as fallback.
825
containerType = _sets.Set
826
return self._unjellySetOrFrozenset(lst, containerType)
829
def _unjelly_frozenset(self, lst):
831
Unjelly frozenset using either the C{frozenset} builtin if available,
832
or C{sets.ImmutableSet} as fallback.
835
containerType = frozenset
837
containerType = _sets.ImmutableSet
838
return self._unjellySetOrFrozenset(lst, containerType)
841
def _unjelly_dictionary(self, lst):
844
kvd = _DictKeyAndValue(d)
845
self.unjellyInto(kvd, 0, k)
846
self.unjellyInto(kvd, 1, v)
850
def _unjelly_module(self, rest):
852
if type(moduleName) != types.StringType:
854
"Attempted to unjelly a module with a non-string name.")
855
if not self.taster.isModuleAllowed(moduleName):
857
"Attempted to unjelly module named %r" % (moduleName,))
858
mod = __import__(moduleName, {}, {},"x")
862
def _unjelly_class(self, rest):
863
clist = rest[0].split('.')
864
modName = '.'.join(clist[:-1])
865
if not self.taster.isModuleAllowed(modName):
866
raise InsecureJelly("module %s not allowed" % modName)
867
klaus = namedObject(rest[0])
868
objType = type(klaus)
869
if objType not in (types.ClassType, types.TypeType):
871
"class %r unjellied to something that isn't a class: %r" % (
873
if not self.taster.isClassAllowed(klaus):
874
raise InsecureJelly("class not allowed: %s" % qual(klaus))
878
def _unjelly_function(self, rest):
879
modSplit = rest[0].split('.')
880
modName = '.'.join(modSplit[:-1])
881
if not self.taster.isModuleAllowed(modName):
882
raise InsecureJelly("Module not allowed: %s"% modName)
883
# XXX do I need an isFunctionAllowed?
884
function = namedObject(rest[0])
888
def _unjelly_persistent(self, rest):
889
if self.persistentLoad:
890
pload = self.persistentLoad(rest[0], self)
893
return Unpersistable("Persistent callback not found")
896
def _unjelly_instance(self, rest):
897
clz = self.unjelly(rest[0])
898
if type(clz) is not types.ClassType:
899
raise InsecureJelly("Instance found with non-class class.")
900
if hasattr(clz, "__setstate__"):
901
inst = _newInstance(clz, {})
902
state = self.unjelly(rest[1])
903
inst.__setstate__(state)
905
state = self.unjelly(rest[1])
906
inst = _newInstance(clz, state)
907
if hasattr(clz, 'postUnjelly'):
908
self.postCallbacks.append(inst.postUnjelly)
912
def _unjelly_unpersistable(self, rest):
913
return Unpersistable("Unpersistable data: %s" % (rest[0],))
916
def _unjelly_method(self, rest):
918
(internal) Unjelly a method.
921
im_self = self.unjelly(rest[1])
922
im_class = self.unjelly(rest[2])
923
if type(im_class) is not types.ClassType:
924
raise InsecureJelly("Method found with non-class class.")
925
if im_name in im_class.__dict__:
927
im = getattr(im_class, im_name)
928
elif isinstance(im_self, NotKnown):
929
im = _InstanceMethod(im_name, im_self, im_class)
931
im = instancemethod(im_class.__dict__[im_name],
935
raise TypeError('instance method changed')
942
(Internal) Dummy class, used for unserializing instances.
947
class _DummyNewStyle(object):
949
(Internal) Dummy class, used for unserializing instances of new-style
955
#### Published Interface.
958
class InsecureJelly(Exception):
960
This exception will be raised when a jelly is deemed `insecure'; e.g. it
961
contains a type, class, or module disallowed by the specified `taster'
966
class DummySecurityOptions:
968
DummySecurityOptions() -> insecure security options
969
Dummy security options -- this class will allow anything.
972
def isModuleAllowed(self, moduleName):
974
DummySecurityOptions.isModuleAllowed(moduleName) -> boolean
975
returns 1 if a module by that name is allowed, 0 otherwise
980
def isClassAllowed(self, klass):
982
DummySecurityOptions.isClassAllowed(class) -> boolean
983
Assumes the module has already been allowed. Returns 1 if the given
984
class is allowed, 0 otherwise.
989
def isTypeAllowed(self, typeName):
991
DummySecurityOptions.isTypeAllowed(typeName) -> boolean
992
Returns 1 if the given type is allowed, 0 otherwise.
998
class SecurityOptions:
1000
This will by default disallow everything, except for 'none'.
1003
basicTypes = ["dictionary", "list", "tuple",
1004
"reference", "dereference", "unpersistable",
1005
"persistent", "long_int", "long", "dict"]
1009
SecurityOptions() initialize.
1011
# I don't believe any of these types can ever pose a security hazard,
1012
# except perhaps "reference"...
1013
self.allowedTypes = {"None": 1,
1025
if hasattr(types, 'UnicodeType'):
1026
self.allowedTypes['unicode'] = 1
1027
if decimal is not None:
1028
self.allowedTypes['decimal'] = 1
1029
self.allowedTypes['set'] = 1
1030
self.allowedTypes['frozenset'] = 1
1031
self.allowedModules = {}
1032
self.allowedClasses = {}
1035
def allowBasicTypes(self):
1037
Allow all `basic' types. (Dictionary and list. Int, string, and float
1038
are implicitly allowed.)
1040
self.allowTypes(*self.basicTypes)
1043
def allowTypes(self, *types):
1045
SecurityOptions.allowTypes(typeString): Allow a particular type, by its
1049
if not isinstance(typ, str):
1051
self.allowedTypes[typ] = 1
1054
def allowInstancesOf(self, *classes):
1056
SecurityOptions.allowInstances(klass, klass, ...): allow instances
1057
of the specified classes
1059
This will also allow the 'instance', 'class' (renamed 'classobj' in
1060
Python 2.3), and 'module' types, as well as basic types.
1062
self.allowBasicTypes()
1063
self.allowTypes("instance", "class", "classobj", "module")
1064
for klass in classes:
1065
self.allowTypes(qual(klass))
1066
self.allowModules(klass.__module__)
1067
self.allowedClasses[klass] = 1
1070
def allowModules(self, *modules):
1072
SecurityOptions.allowModules(module, module, ...): allow modules by
1073
name. This will also allow the 'module' type.
1075
for module in modules:
1076
if type(module) == types.ModuleType:
1077
module = module.__name__
1078
self.allowedModules[module] = 1
1081
def isModuleAllowed(self, moduleName):
1083
SecurityOptions.isModuleAllowed(moduleName) -> boolean
1084
returns 1 if a module by that name is allowed, 0 otherwise
1086
return moduleName in self.allowedModules
1089
def isClassAllowed(self, klass):
1091
SecurityOptions.isClassAllowed(class) -> boolean
1092
Assumes the module has already been allowed. Returns 1 if the given
1093
class is allowed, 0 otherwise.
1095
return klass in self.allowedClasses
1098
def isTypeAllowed(self, typeName):
1100
SecurityOptions.isTypeAllowed(typeName) -> boolean
1101
Returns 1 if the given type is allowed, 0 otherwise.
1103
return (typeName in self.allowedTypes or '.' in typeName)
1106
globalSecurity = SecurityOptions()
1107
globalSecurity.allowBasicTypes()
1111
def jelly(object, taster=DummySecurityOptions(), persistentStore=None,
1114
Serialize to s-expression.
1116
Returns a list which is the serialized representation of an object. An
1117
optional 'taster' argument takes a SecurityOptions and will mark any
1118
insecure objects as unpersistable rather than serializing them.
1120
return _Jellier(taster, persistentStore, invoker).jelly(object)
1124
def unjelly(sexp, taster=DummySecurityOptions(), persistentLoad=None,
1127
Unserialize from s-expression.
1129
Takes an list that was the result from a call to jelly() and unserializes
1130
an arbitrary object from it. The optional 'taster' argument, an instance
1131
of SecurityOptions, will cause an InsecureJelly exception to be raised if a
1132
disallowed type, module, or class attempted to unserialize.
1134
return _Unjellier(taster, persistentLoad, invoker).unjellyFull(sexp)