| Index: third_party/twisted_8_1/twisted/spread/jelly.py
|
| diff --git a/third_party/twisted_8_1/twisted/spread/jelly.py b/third_party/twisted_8_1/twisted/spread/jelly.py
|
| deleted file mode 100644
|
| index 25602b83f474e3cc4dd05a693b671aa448c124d1..0000000000000000000000000000000000000000
|
| --- a/third_party/twisted_8_1/twisted/spread/jelly.py
|
| +++ /dev/null
|
| @@ -1,1136 +0,0 @@
|
| -# -*- test-case-name: twisted.test.test_jelly -*-
|
| -
|
| -# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
|
| -# See LICENSE for details.
|
| -
|
| -
|
| -"""
|
| -S-expression-based persistence of python objects.
|
| -
|
| -It does something very much like L{Pickle<pickle>}; however, pickle's main goal
|
| -seems to be efficiency (both in space and time); jelly's main goals are
|
| -security, human readability, and portability to other environments.
|
| -
|
| -This is how Jelly converts various objects to s-expressions.
|
| -
|
| -Boolean::
|
| - True --> ['boolean', 'true']
|
| -
|
| -Integer::
|
| - 1 --> 1
|
| -
|
| -List::
|
| - [1, 2] --> ['list', 1, 2]
|
| -
|
| -String::
|
| - \"hello\" --> \"hello\"
|
| -
|
| -Float::
|
| - 2.3 --> 2.3
|
| -
|
| -Dictionary::
|
| - {'a': 1, 'b': 'c'} --> ['dictionary', ['b', 'c'], ['a', 1]]
|
| -
|
| -Module::
|
| - UserString --> ['module', 'UserString']
|
| -
|
| -Class::
|
| - UserString.UserString --> ['class', ['module', 'UserString'], 'UserString']
|
| -
|
| -Function::
|
| - string.join --> ['function', 'join', ['module', 'string']]
|
| -
|
| -Instance: s is an instance of UserString.UserString, with a __dict__
|
| -{'data': 'hello'}::
|
| - [\"UserString.UserString\", ['dictionary', ['data', 'hello']]]
|
| -
|
| -Class Method: UserString.UserString.center::
|
| - ['method', 'center', ['None'], ['class', ['module', 'UserString'],
|
| - 'UserString']]
|
| -
|
| -Instance Method: s.center, where s is an instance of UserString.UserString::
|
| - ['method', 'center', ['instance', ['reference', 1, ['class',
|
| - ['module', 'UserString'], 'UserString']], ['dictionary', ['data', 'd']]],
|
| - ['dereference', 1]]
|
| -
|
| -The C{set} builtin and the C{sets.Set} class are serialized to the same
|
| -thing, and unserialized to C{set} if available, else to C{sets.Set}. It means
|
| -that there's a possibility of type switching in the serialization process. The
|
| -solution is to always use C{set} if possible, and only use C{sets.Set} under
|
| -Python 2.3; this can be accomplished by using L{twisted.python.compat.set}.
|
| -
|
| -The same rule applies for C{frozenset} and C{sets.ImmutableSet}.
|
| -
|
| -@author: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>}
|
| -"""
|
| -
|
| -__version__ = "$Revision: 1.48 $"[11:-2]
|
| -
|
| -# System Imports
|
| -import pickle
|
| -import types
|
| -import warnings
|
| -from types import StringType
|
| -from types import UnicodeType
|
| -from types import IntType
|
| -from types import TupleType
|
| -from types import ListType
|
| -from types import LongType
|
| -from types import FloatType
|
| -from types import FunctionType
|
| -from types import MethodType
|
| -from types import ModuleType
|
| -from types import DictionaryType
|
| -from types import InstanceType
|
| -from types import NoneType
|
| -from types import ClassType
|
| -import copy
|
| -
|
| -import datetime
|
| -from types import BooleanType
|
| -
|
| -try:
|
| - import decimal
|
| -except ImportError:
|
| - decimal = None
|
| -
|
| -try:
|
| - _set = set
|
| -except NameError:
|
| - _set = None
|
| -
|
| -try:
|
| - # Filter out deprecation warning for Python >= 2.6
|
| - warnings.filterwarnings("ignore", category=DeprecationWarning,
|
| - message="the sets module is deprecated", append=True)
|
| - import sets as _sets
|
| -finally:
|
| - warnings.filters.pop()
|
| -
|
| -
|
| -from new import instance
|
| -from new import instancemethod
|
| -from zope.interface import implements
|
| -
|
| -# Twisted Imports
|
| -from twisted.python.reflect import namedObject, qual
|
| -from twisted.persisted.crefutil import NotKnown, _Tuple, _InstanceMethod
|
| -from twisted.persisted.crefutil import _DictKeyAndValue, _Dereference
|
| -from twisted.persisted.crefutil import _Container
|
| -from twisted.python import runtime
|
| -
|
| -from twisted.spread.interfaces import IJellyable, IUnjellyable
|
| -
|
| -
|
| -if runtime.platform.getType() == "java":
|
| - from org.python.core import PyStringMap
|
| - DictTypes = (DictionaryType, PyStringMap)
|
| -else:
|
| - DictTypes = (DictionaryType,)
|
| -
|
| -
|
| -None_atom = "None" # N
|
| -# code
|
| -class_atom = "class" # c
|
| -module_atom = "module" # m
|
| -function_atom = "function" # f
|
| -
|
| -# references
|
| -dereference_atom = 'dereference' # D
|
| -persistent_atom = 'persistent' # p
|
| -reference_atom = 'reference' # r
|
| -
|
| -# mutable collections
|
| -dictionary_atom = "dictionary" # d
|
| -list_atom = 'list' # l
|
| -set_atom = 'set'
|
| -
|
| -# immutable collections
|
| -# (assignment to __dict__ and __class__ still might go away!)
|
| -tuple_atom = "tuple" # t
|
| -instance_atom = 'instance' # i
|
| -frozenset_atom = 'frozenset'
|
| -
|
| -
|
| -# errors
|
| -unpersistable_atom = "unpersistable"# u
|
| -unjellyableRegistry = {}
|
| -unjellyableFactoryRegistry = {}
|
| -
|
| -
|
| -
|
| -def _newInstance(cls, state):
|
| - """
|
| - Make a new instance of a class without calling its __init__ method.
|
| - 'state' will be used to update inst.__dict__ . Supports both new- and
|
| - old-style classes.
|
| - """
|
| - if not isinstance(cls, types.ClassType):
|
| - # new-style
|
| - inst = cls.__new__(cls)
|
| - inst.__dict__.update(state) # Copy 'instance' behaviour
|
| - else:
|
| - inst = instance(cls, state)
|
| - return inst
|
| -
|
| -
|
| -
|
| -def _maybeClass(classnamep):
|
| - try:
|
| - object
|
| - except NameError:
|
| - isObject = 0
|
| - else:
|
| - isObject = isinstance(classnamep, type)
|
| - if isinstance(classnamep, ClassType) or isObject:
|
| - return qual(classnamep)
|
| - return classnamep
|
| -
|
| -
|
| -
|
| -def setUnjellyableForClass(classname, unjellyable):
|
| - """
|
| - Set which local class will represent a remote type.
|
| -
|
| - If you have written a Copyable class that you expect your client to be
|
| - receiving, write a local "copy" class to represent it, then call::
|
| -
|
| - jellier.setUnjellyableForClass('module.package.Class', MyJellier).
|
| -
|
| - Call this at the module level immediately after its class
|
| - definition. MyCopier should be a subclass of RemoteCopy.
|
| -
|
| - The classname may be a special tag returned by
|
| - 'Copyable.getTypeToCopyFor' rather than an actual classname.
|
| -
|
| - This call is also for cached classes, since there will be no
|
| - overlap. The rules are the same.
|
| - """
|
| -
|
| - global unjellyableRegistry
|
| - classname = _maybeClass(classname)
|
| - unjellyableRegistry[classname] = unjellyable
|
| - globalSecurity.allowTypes(classname)
|
| -
|
| -
|
| -
|
| -def setUnjellyableFactoryForClass(classname, copyFactory):
|
| - """
|
| - Set the factory to construct a remote instance of a type::
|
| -
|
| - jellier.setFactoryForClass('module.package.Class', MyFactory)
|
| -
|
| - Call this at the module level immediately after its class definition.
|
| - C{copyFactory} should return an instance or subclass of
|
| - L{RemoteCopy<pb.RemoteCopy>}.
|
| -
|
| - Similar to L{setUnjellyableForClass} except it uses a factory instead
|
| - of creating an instance.
|
| - """
|
| -
|
| - global unjellyableFactoryRegistry
|
| - classname = _maybeClass(classname)
|
| - unjellyableFactoryRegistry[classname] = copyFactory
|
| - globalSecurity.allowTypes(classname)
|
| -
|
| -
|
| -
|
| -def setUnjellyableForClassTree(module, baseClass, prefix=None):
|
| - """
|
| - Set all classes in a module derived from C{baseClass} as copiers for
|
| - a corresponding remote class.
|
| -
|
| - When you have a heirarchy of Copyable (or Cacheable) classes on
|
| - one side, and a mirror structure of Copied (or RemoteCache)
|
| - classes on the other, use this to setCopierForClass all your
|
| - Copieds for the Copyables.
|
| -
|
| - Each copyTag (the \"classname\" argument to getTypeToCopyFor, and
|
| - what the Copyable's getTypeToCopyFor returns) is formed from
|
| - adding a prefix to the Copied's class name. The prefix defaults
|
| - to module.__name__. If you wish the copy tag to consist of solely
|
| - the classname, pass the empty string \'\'.
|
| -
|
| - @param module: a module object from which to pull the Copied classes.
|
| - (passing sys.modules[__name__] might be useful)
|
| -
|
| - @param baseClass: the base class from which all your Copied classes derive.
|
| -
|
| - @param prefix: the string prefixed to classnames to form the
|
| - unjellyableRegistry.
|
| - """
|
| - if prefix is None:
|
| - prefix = module.__name__
|
| -
|
| - if prefix:
|
| - prefix = "%s." % prefix
|
| -
|
| - for i in dir(module):
|
| - i_ = getattr(module, i)
|
| - if type(i_) == types.ClassType:
|
| - if issubclass(i_, baseClass):
|
| - setUnjellyableForClass('%s%s' % (prefix, i), i_)
|
| -
|
| -
|
| -
|
| -def getInstanceState(inst, jellier):
|
| - """
|
| - Utility method to default to 'normal' state rules in serialization.
|
| - """
|
| - if hasattr(inst, "__getstate__"):
|
| - state = inst.__getstate__()
|
| - else:
|
| - state = inst.__dict__
|
| - sxp = jellier.prepare(inst)
|
| - sxp.extend([qual(inst.__class__), jellier.jelly(state)])
|
| - return jellier.preserve(inst, sxp)
|
| -
|
| -
|
| -
|
| -def setInstanceState(inst, unjellier, jellyList):
|
| - """
|
| - Utility method to default to 'normal' state rules in unserialization.
|
| - """
|
| - state = unjellier.unjelly(jellyList[1])
|
| - if hasattr(inst, "__setstate__"):
|
| - inst.__setstate__(state)
|
| - else:
|
| - inst.__dict__ = state
|
| - return inst
|
| -
|
| -
|
| -
|
| -class Unpersistable:
|
| - """
|
| - This is an instance of a class that comes back when something couldn't be
|
| - unpersisted.
|
| - """
|
| -
|
| - def __init__(self, reason):
|
| - """
|
| - Initialize an unpersistable object with a descriptive C{reason} string.
|
| - """
|
| - self.reason = reason
|
| -
|
| -
|
| - def __repr__(self):
|
| - return "Unpersistable(%s)" % repr(self.reason)
|
| -
|
| -
|
| -
|
| -class Jellyable:
|
| - """
|
| - Inherit from me to Jelly yourself directly with the `getStateFor'
|
| - convenience method.
|
| - """
|
| - implements(IJellyable)
|
| -
|
| - def getStateFor(self, jellier):
|
| - return self.__dict__
|
| -
|
| -
|
| - def jellyFor(self, jellier):
|
| - """
|
| - @see: L{twisted.spread.interfaces.IJellyable.jellyFor}
|
| - """
|
| - sxp = jellier.prepare(self)
|
| - sxp.extend([
|
| - qual(self.__class__),
|
| - jellier.jelly(self.getStateFor(jellier))])
|
| - return jellier.preserve(self, sxp)
|
| -
|
| -
|
| -
|
| -class Unjellyable:
|
| - """
|
| - Inherit from me to Unjelly yourself directly with the
|
| - C{setStateFor} convenience method.
|
| - """
|
| - implements(IUnjellyable)
|
| -
|
| - def setStateFor(self, unjellier, state):
|
| - self.__dict__ = state
|
| -
|
| -
|
| - def unjellyFor(self, unjellier, jellyList):
|
| - """
|
| - Perform the inverse operation of L{Jellyable.jellyFor}.
|
| -
|
| - @see: L{twisted.spread.interfaces.IUnjellyable.unjellyFor}
|
| - """
|
| - state = unjellier.unjelly(jellyList[1])
|
| - self.setStateFor(unjellier, state)
|
| - return self
|
| -
|
| -
|
| -
|
| -class _Jellier:
|
| - """
|
| - (Internal) This class manages state for a call to jelly()
|
| - """
|
| -
|
| - def __init__(self, taster, persistentStore, invoker):
|
| - """
|
| - Initialize.
|
| - """
|
| - self.taster = taster
|
| - # `preserved' is a dict of previously seen instances.
|
| - self.preserved = {}
|
| - # `cooked' is a dict of previously backreferenced instances to their
|
| - # `ref' lists.
|
| - self.cooked = {}
|
| - self.cooker = {}
|
| - self._ref_id = 1
|
| - self.persistentStore = persistentStore
|
| - self.invoker = invoker
|
| -
|
| -
|
| - def _cook(self, object):
|
| - """
|
| - (internal) Backreference an object.
|
| -
|
| - Notes on this method for the hapless future maintainer: If I've already
|
| - gone through the prepare/preserve cycle on the specified object (it is
|
| - being referenced after the serializer is \"done with\" it, e.g. this
|
| - reference is NOT circular), the copy-in-place of aList is relevant,
|
| - since the list being modified is the actual, pre-existing jelly
|
| - expression that was returned for that object. If not, it's technically
|
| - superfluous, since the value in self.preserved didn't need to be set,
|
| - but the invariant that self.preserved[id(object)] is a list is
|
| - convenient because that means we don't have to test and create it or
|
| - not create it here, creating fewer code-paths. that's why
|
| - self.preserved is always set to a list.
|
| -
|
| - Sorry that this code is so hard to follow, but Python objects are
|
| - tricky to persist correctly. -glyph
|
| - """
|
| - aList = self.preserved[id(object)]
|
| - newList = copy.copy(aList)
|
| - # make a new reference ID
|
| - refid = self._ref_id
|
| - self._ref_id = self._ref_id + 1
|
| - # replace the old list in-place, so that we don't have to track the
|
| - # previous reference to it.
|
| - aList[:] = [reference_atom, refid, newList]
|
| - self.cooked[id(object)] = [dereference_atom, refid]
|
| - return aList
|
| -
|
| -
|
| - def prepare(self, object):
|
| - """
|
| - (internal) Create a list for persisting an object to. This will allow
|
| - backreferences to be made internal to the object. (circular
|
| - references).
|
| -
|
| - The reason this needs to happen is that we don't generate an ID for
|
| - every object, so we won't necessarily know which ID the object will
|
| - have in the future. When it is 'cooked' ( see _cook ), it will be
|
| - assigned an ID, and the temporary placeholder list created here will be
|
| - modified in-place to create an expression that gives this object an ID:
|
| - [reference id# [object-jelly]].
|
| - """
|
| -
|
| - # create a placeholder list to be preserved
|
| - self.preserved[id(object)] = []
|
| - # keep a reference to this object around, so it doesn't disappear!
|
| - # (This isn't always necessary, but for cases where the objects are
|
| - # dynamically generated by __getstate__ or getStateToCopyFor calls, it
|
| - # is; id() will return the same value for a different object if it gets
|
| - # garbage collected. This may be optimized later.)
|
| - self.cooker[id(object)] = object
|
| - return []
|
| -
|
| -
|
| - def preserve(self, object, sexp):
|
| - """
|
| - (internal) Mark an object's persistent list for later referral.
|
| - """
|
| - # if I've been cooked in the meanwhile,
|
| - if id(object) in self.cooked:
|
| - # replace the placeholder empty list with the real one
|
| - self.preserved[id(object)][2] = sexp
|
| - # but give this one back.
|
| - sexp = self.preserved[id(object)]
|
| - else:
|
| - self.preserved[id(object)] = sexp
|
| - return sexp
|
| -
|
| - constantTypes = {types.StringType : 1, types.IntType : 1,
|
| - types.FloatType : 1, types.LongType : 1}
|
| -
|
| -
|
| - def _checkMutable(self,obj):
|
| - objId = id(obj)
|
| - if objId in self.cooked:
|
| - return self.cooked[objId]
|
| - if objId in self.preserved:
|
| - self._cook(obj)
|
| - return self.cooked[objId]
|
| -
|
| -
|
| - def jelly(self, obj):
|
| - if isinstance(obj, Jellyable):
|
| - preRef = self._checkMutable(obj)
|
| - if preRef:
|
| - return preRef
|
| - return obj.jellyFor(self)
|
| - objType = type(obj)
|
| - if self.taster.isTypeAllowed(qual(objType)):
|
| - # "Immutable" Types
|
| - if ((objType is StringType) or
|
| - (objType is IntType) or
|
| - (objType is LongType) or
|
| - (objType is FloatType)):
|
| - return obj
|
| - elif objType is MethodType:
|
| - return ["method",
|
| - obj.im_func.__name__,
|
| - self.jelly(obj.im_self),
|
| - self.jelly(obj.im_class)]
|
| -
|
| - elif UnicodeType and objType is UnicodeType:
|
| - return ['unicode', obj.encode('UTF-8')]
|
| - elif objType is NoneType:
|
| - return ['None']
|
| - elif objType is FunctionType:
|
| - name = obj.__name__
|
| - return ['function', str(pickle.whichmodule(obj, obj.__name__))
|
| - + '.' +
|
| - name]
|
| - elif objType is ModuleType:
|
| - return ['module', obj.__name__]
|
| - elif objType is BooleanType:
|
| - return ['boolean', obj and 'true' or 'false']
|
| - elif objType is datetime.datetime:
|
| - if obj.tzinfo:
|
| - raise NotImplementedError(
|
| - "Currently can't jelly datetime objects with tzinfo")
|
| - return ['datetime', '%s %s %s %s %s %s %s' % (
|
| - obj.year, obj.month, obj.day, obj.hour,
|
| - obj.minute, obj.second, obj.microsecond)]
|
| - elif objType is datetime.time:
|
| - if obj.tzinfo:
|
| - raise NotImplementedError(
|
| - "Currently can't jelly datetime objects with tzinfo")
|
| - return ['time', '%s %s %s %s' % (obj.hour, obj.minute,
|
| - obj.second, obj.microsecond)]
|
| - elif objType is datetime.date:
|
| - return ['date', '%s %s %s' % (obj.year, obj.month, obj.day)]
|
| - elif objType is datetime.timedelta:
|
| - return ['timedelta', '%s %s %s' % (obj.days, obj.seconds,
|
| - obj.microseconds)]
|
| - elif objType is ClassType or issubclass(objType, type):
|
| - return ['class', qual(obj)]
|
| - elif decimal is not None and objType is decimal.Decimal:
|
| - return self.jelly_decimal(obj)
|
| - else:
|
| - preRef = self._checkMutable(obj)
|
| - if preRef:
|
| - return preRef
|
| - # "Mutable" Types
|
| - sxp = self.prepare(obj)
|
| - if objType is ListType:
|
| - sxp.extend(self._jellyIterable(list_atom, obj))
|
| - elif objType is TupleType:
|
| - sxp.extend(self._jellyIterable(tuple_atom, obj))
|
| - elif objType in DictTypes:
|
| - sxp.append(dictionary_atom)
|
| - for key, val in obj.items():
|
| - sxp.append([self.jelly(key), self.jelly(val)])
|
| - elif (_set is not None and objType is set or
|
| - objType is _sets.Set):
|
| - sxp.extend(self._jellyIterable(set_atom, obj))
|
| - elif (_set is not None and objType is frozenset or
|
| - objType is _sets.ImmutableSet):
|
| - sxp.extend(self._jellyIterable(frozenset_atom, obj))
|
| - else:
|
| - className = qual(obj.__class__)
|
| - persistent = None
|
| - if self.persistentStore:
|
| - persistent = self.persistentStore(obj, self)
|
| - if persistent is not None:
|
| - sxp.append(persistent_atom)
|
| - sxp.append(persistent)
|
| - elif self.taster.isClassAllowed(obj.__class__):
|
| - sxp.append(className)
|
| - if hasattr(obj, "__getstate__"):
|
| - state = obj.__getstate__()
|
| - else:
|
| - state = obj.__dict__
|
| - sxp.append(self.jelly(state))
|
| - else:
|
| - self.unpersistable(
|
| - "instance of class %s deemed insecure" %
|
| - qual(obj.__class__), sxp)
|
| - return self.preserve(obj, sxp)
|
| - else:
|
| - if objType is InstanceType:
|
| - raise InsecureJelly("Class not allowed for instance: %s %s" %
|
| - (obj.__class__, obj))
|
| - raise InsecureJelly("Type not allowed for object: %s %s" %
|
| - (objType, obj))
|
| -
|
| -
|
| - def _jellyIterable(self, atom, obj):
|
| - """
|
| - Jelly an iterable object.
|
| -
|
| - @param atom: the identifier atom of the object.
|
| - @type atom: C{str}
|
| -
|
| - @param obj: any iterable object.
|
| - @type obj: C{iterable}
|
| -
|
| - @return: a generator of jellied data.
|
| - @rtype: C{generator}
|
| - """
|
| - yield atom
|
| - for item in obj:
|
| - yield self.jelly(item)
|
| -
|
| -
|
| - def jelly_decimal(self, d):
|
| - """
|
| - Jelly a decimal object.
|
| -
|
| - @param d: a decimal object to serialize.
|
| - @type d: C{decimal.Decimal}
|
| -
|
| - @return: jelly for the decimal object.
|
| - @rtype: C{list}
|
| - """
|
| - sign, guts, exponent = d.as_tuple()
|
| - value = reduce(lambda left, right: left * 10 + right, guts)
|
| - if sign:
|
| - value = -value
|
| - return ['decimal', value, exponent]
|
| -
|
| -
|
| - def unpersistable(self, reason, sxp=None):
|
| - """
|
| - (internal) Returns an sexp: (unpersistable "reason"). Utility method
|
| - for making note that a particular object could not be serialized.
|
| - """
|
| - if sxp is None:
|
| - sxp = []
|
| - sxp.append(unpersistable_atom)
|
| - sxp.append(reason)
|
| - return sxp
|
| -
|
| -
|
| -
|
| -class _Unjellier:
|
| -
|
| - def __init__(self, taster, persistentLoad, invoker):
|
| - self.taster = taster
|
| - self.persistentLoad = persistentLoad
|
| - self.references = {}
|
| - self.postCallbacks = []
|
| - self.invoker = invoker
|
| -
|
| -
|
| - def unjellyFull(self, obj):
|
| - o = self.unjelly(obj)
|
| - for m in self.postCallbacks:
|
| - m()
|
| - return o
|
| -
|
| -
|
| - def unjelly(self, obj):
|
| - if type(obj) is not types.ListType:
|
| - return obj
|
| - jelType = obj[0]
|
| - if not self.taster.isTypeAllowed(jelType):
|
| - raise InsecureJelly(jelType)
|
| - regClass = unjellyableRegistry.get(jelType)
|
| - if regClass is not None:
|
| - if isinstance(regClass, ClassType):
|
| - inst = _Dummy() # XXX chomp, chomp
|
| - inst.__class__ = regClass
|
| - method = inst.unjellyFor
|
| - elif isinstance(regClass, type):
|
| - # regClass.__new__ does not call regClass.__init__
|
| - inst = regClass.__new__(regClass)
|
| - method = inst.unjellyFor
|
| - else:
|
| - method = regClass # this is how it ought to be done
|
| - val = method(self, obj)
|
| - if hasattr(val, 'postUnjelly'):
|
| - self.postCallbacks.append(inst.postUnjelly)
|
| - return val
|
| - regFactory = unjellyableFactoryRegistry.get(jelType)
|
| - if regFactory is not None:
|
| - state = self.unjelly(obj[1])
|
| - inst = regFactory(state)
|
| - if hasattr(inst, 'postUnjelly'):
|
| - self.postCallbacks.append(inst.postUnjelly)
|
| - return inst
|
| - thunk = getattr(self, '_unjelly_%s'%jelType, None)
|
| - if thunk is not None:
|
| - ret = thunk(obj[1:])
|
| - else:
|
| - nameSplit = jelType.split('.')
|
| - modName = '.'.join(nameSplit[:-1])
|
| - if not self.taster.isModuleAllowed(modName):
|
| - raise InsecureJelly(
|
| - "Module %s not allowed (in type %s)." % (modName, jelType))
|
| - clz = namedObject(jelType)
|
| - if not self.taster.isClassAllowed(clz):
|
| - raise InsecureJelly("Class %s not allowed." % jelType)
|
| - if hasattr(clz, "__setstate__"):
|
| - ret = _newInstance(clz, {})
|
| - state = self.unjelly(obj[1])
|
| - ret.__setstate__(state)
|
| - else:
|
| - state = self.unjelly(obj[1])
|
| - ret = _newInstance(clz, state)
|
| - if hasattr(clz, 'postUnjelly'):
|
| - self.postCallbacks.append(ret.postUnjelly)
|
| - return ret
|
| -
|
| -
|
| - def _unjelly_None(self, exp):
|
| - return None
|
| -
|
| -
|
| - def _unjelly_unicode(self, exp):
|
| - if UnicodeType:
|
| - return unicode(exp[0], "UTF-8")
|
| - else:
|
| - return Unpersistable("Could not unpersist unicode: %s" % (exp[0],))
|
| -
|
| -
|
| - def _unjelly_decimal(self, exp):
|
| - """
|
| - Unjelly decimal objects, if decimal is available. If not, return a
|
| - L{Unpersistable} object instead.
|
| - """
|
| - if decimal is None:
|
| - return Unpersistable(
|
| - "Could not unpersist decimal: %s" % (exp[0] * (10**exp[1]),))
|
| - value = exp[0]
|
| - exponent = exp[1]
|
| - if value < 0:
|
| - sign = 1
|
| - else:
|
| - sign = 0
|
| - guts = decimal.Decimal(value).as_tuple()[1]
|
| - return decimal.Decimal((sign, guts, exponent))
|
| -
|
| -
|
| - def _unjelly_boolean(self, exp):
|
| - if BooleanType:
|
| - assert exp[0] in ('true', 'false')
|
| - return exp[0] == 'true'
|
| - else:
|
| - return Unpersistable("Could not unpersist boolean: %s" % (exp[0],))
|
| -
|
| -
|
| - def _unjelly_datetime(self, exp):
|
| - return datetime.datetime(*map(int, exp[0].split()))
|
| -
|
| -
|
| - def _unjelly_date(self, exp):
|
| - return datetime.date(*map(int, exp[0].split()))
|
| -
|
| -
|
| - def _unjelly_time(self, exp):
|
| - return datetime.time(*map(int, exp[0].split()))
|
| -
|
| -
|
| - def _unjelly_timedelta(self, exp):
|
| - days, seconds, microseconds = map(int, exp[0].split())
|
| - return datetime.timedelta(
|
| - days=days, seconds=seconds, microseconds=microseconds)
|
| -
|
| -
|
| - def unjellyInto(self, obj, loc, jel):
|
| - o = self.unjelly(jel)
|
| - if isinstance(o, NotKnown):
|
| - o.addDependant(obj, loc)
|
| - obj[loc] = o
|
| - return o
|
| -
|
| -
|
| - def _unjelly_dereference(self, lst):
|
| - refid = lst[0]
|
| - x = self.references.get(refid)
|
| - if x is not None:
|
| - return x
|
| - der = _Dereference(refid)
|
| - self.references[refid] = der
|
| - return der
|
| -
|
| -
|
| - def _unjelly_reference(self, lst):
|
| - refid = lst[0]
|
| - exp = lst[1]
|
| - o = self.unjelly(exp)
|
| - ref = self.references.get(refid)
|
| - if (ref is None):
|
| - self.references[refid] = o
|
| - elif isinstance(ref, NotKnown):
|
| - ref.resolveDependants(o)
|
| - self.references[refid] = o
|
| - else:
|
| - assert 0, "Multiple references with same ID!"
|
| - return o
|
| -
|
| -
|
| - def _unjelly_tuple(self, lst):
|
| - l = range(len(lst))
|
| - finished = 1
|
| - for elem in l:
|
| - if isinstance(self.unjellyInto(l, elem, lst[elem]), NotKnown):
|
| - finished = 0
|
| - if finished:
|
| - return tuple(l)
|
| - else:
|
| - return _Tuple(l)
|
| -
|
| -
|
| - def _unjelly_list(self, lst):
|
| - l = range(len(lst))
|
| - for elem in l:
|
| - self.unjellyInto(l, elem, lst[elem])
|
| - return l
|
| -
|
| -
|
| - def _unjellySetOrFrozenset(self, lst, containerType):
|
| - """
|
| - Helper method to unjelly set or frozenset.
|
| -
|
| - @param lst: the content of the set.
|
| - @type lst: C{list}
|
| -
|
| - @param containerType: the type of C{set} to use.
|
| - """
|
| - l = range(len(lst))
|
| - finished = True
|
| - for elem in l:
|
| - data = self.unjellyInto(l, elem, lst[elem])
|
| - if isinstance(data, NotKnown):
|
| - finished = False
|
| - if not finished:
|
| - return _Container(l, containerType)
|
| - else:
|
| - return containerType(l)
|
| -
|
| -
|
| - def _unjelly_set(self, lst):
|
| - """
|
| - Unjelly set using either the C{set} builtin if available, or
|
| - C{sets.Set} as fallback.
|
| - """
|
| - if _set is not None:
|
| - containerType = set
|
| - else:
|
| - containerType = _sets.Set
|
| - return self._unjellySetOrFrozenset(lst, containerType)
|
| -
|
| -
|
| - def _unjelly_frozenset(self, lst):
|
| - """
|
| - Unjelly frozenset using either the C{frozenset} builtin if available,
|
| - or C{sets.ImmutableSet} as fallback.
|
| - """
|
| - if _set is not None:
|
| - containerType = frozenset
|
| - else:
|
| - containerType = _sets.ImmutableSet
|
| - return self._unjellySetOrFrozenset(lst, containerType)
|
| -
|
| -
|
| - def _unjelly_dictionary(self, lst):
|
| - d = {}
|
| - for k, v in lst:
|
| - kvd = _DictKeyAndValue(d)
|
| - self.unjellyInto(kvd, 0, k)
|
| - self.unjellyInto(kvd, 1, v)
|
| - return d
|
| -
|
| -
|
| - def _unjelly_module(self, rest):
|
| - moduleName = rest[0]
|
| - if type(moduleName) != types.StringType:
|
| - raise InsecureJelly(
|
| - "Attempted to unjelly a module with a non-string name.")
|
| - if not self.taster.isModuleAllowed(moduleName):
|
| - raise InsecureJelly(
|
| - "Attempted to unjelly module named %r" % (moduleName,))
|
| - mod = __import__(moduleName, {}, {},"x")
|
| - return mod
|
| -
|
| -
|
| - def _unjelly_class(self, rest):
|
| - clist = rest[0].split('.')
|
| - modName = '.'.join(clist[:-1])
|
| - if not self.taster.isModuleAllowed(modName):
|
| - raise InsecureJelly("module %s not allowed" % modName)
|
| - klaus = namedObject(rest[0])
|
| - if type(klaus) is not types.ClassType:
|
| - raise InsecureJelly(
|
| - "class %r unjellied to something that isn't a class: %r" % (
|
| - rest[0], klaus))
|
| - if not self.taster.isClassAllowed(klaus):
|
| - raise InsecureJelly("class not allowed: %s" % qual(klaus))
|
| - return klaus
|
| -
|
| -
|
| - def _unjelly_function(self, rest):
|
| - modSplit = rest[0].split('.')
|
| - modName = '.'.join(modSplit[:-1])
|
| - if not self.taster.isModuleAllowed(modName):
|
| - raise InsecureJelly("Module not allowed: %s"% modName)
|
| - # XXX do I need an isFunctionAllowed?
|
| - function = namedObject(rest[0])
|
| - return function
|
| -
|
| -
|
| - def _unjelly_persistent(self, rest):
|
| - if self.persistentLoad:
|
| - pload = self.persistentLoad(rest[0], self)
|
| - return pload
|
| - else:
|
| - return Unpersistable("Persistent callback not found")
|
| -
|
| -
|
| - def _unjelly_instance(self, rest):
|
| - clz = self.unjelly(rest[0])
|
| - if type(clz) is not types.ClassType:
|
| - raise InsecureJelly("Instance found with non-class class.")
|
| - if hasattr(clz, "__setstate__"):
|
| - inst = _newInstance(clz, {})
|
| - state = self.unjelly(rest[1])
|
| - inst.__setstate__(state)
|
| - else:
|
| - state = self.unjelly(rest[1])
|
| - inst = _newInstance(clz, state)
|
| - if hasattr(clz, 'postUnjelly'):
|
| - self.postCallbacks.append(inst.postUnjelly)
|
| - return inst
|
| -
|
| -
|
| - def _unjelly_unpersistable(self, rest):
|
| - return Unpersistable("Unpersistable data: %s" % (rest[0],))
|
| -
|
| -
|
| - def _unjelly_method(self, rest):
|
| - """
|
| - (internal) Unjelly a method.
|
| - """
|
| - im_name = rest[0]
|
| - im_self = self.unjelly(rest[1])
|
| - im_class = self.unjelly(rest[2])
|
| - if type(im_class) is not types.ClassType:
|
| - raise InsecureJelly("Method found with non-class class.")
|
| - if im_name in im_class.__dict__:
|
| - if im_self is None:
|
| - im = getattr(im_class, im_name)
|
| - elif isinstance(im_self, NotKnown):
|
| - im = _InstanceMethod(im_name, im_self, im_class)
|
| - else:
|
| - im = instancemethod(im_class.__dict__[im_name],
|
| - im_self,
|
| - im_class)
|
| - else:
|
| - raise TypeError('instance method changed')
|
| - return im
|
| -
|
| -
|
| -
|
| -class _Dummy:
|
| - """
|
| - (Internal) Dummy class, used for unserializing instances.
|
| - """
|
| -
|
| -
|
| -
|
| -class _DummyNewStyle(object):
|
| - """
|
| - (Internal) Dummy class, used for unserializing instances of new-style
|
| - classes.
|
| - """
|
| -
|
| -
|
| -
|
| -#### Published Interface.
|
| -
|
| -
|
| -class InsecureJelly(Exception):
|
| - """
|
| - This exception will be raised when a jelly is deemed `insecure'; e.g. it
|
| - contains a type, class, or module disallowed by the specified `taster'
|
| - """
|
| -
|
| -
|
| -
|
| -class DummySecurityOptions:
|
| - """
|
| - DummySecurityOptions() -> insecure security options
|
| - Dummy security options -- this class will allow anything.
|
| - """
|
| -
|
| - def isModuleAllowed(self, moduleName):
|
| - """
|
| - DummySecurityOptions.isModuleAllowed(moduleName) -> boolean
|
| - returns 1 if a module by that name is allowed, 0 otherwise
|
| - """
|
| - return 1
|
| -
|
| -
|
| - def isClassAllowed(self, klass):
|
| - """
|
| - DummySecurityOptions.isClassAllowed(class) -> boolean
|
| - Assumes the module has already been allowed. Returns 1 if the given
|
| - class is allowed, 0 otherwise.
|
| - """
|
| - return 1
|
| -
|
| -
|
| - def isTypeAllowed(self, typeName):
|
| - """
|
| - DummySecurityOptions.isTypeAllowed(typeName) -> boolean
|
| - Returns 1 if the given type is allowed, 0 otherwise.
|
| - """
|
| - return 1
|
| -
|
| -
|
| -
|
| -class SecurityOptions:
|
| - """
|
| - This will by default disallow everything, except for 'none'.
|
| - """
|
| -
|
| - basicTypes = ["dictionary", "list", "tuple",
|
| - "reference", "dereference", "unpersistable",
|
| - "persistent", "long_int", "long", "dict"]
|
| -
|
| - def __init__(self):
|
| - """
|
| - SecurityOptions() initialize.
|
| - """
|
| - # I don't believe any of these types can ever pose a security hazard,
|
| - # except perhaps "reference"...
|
| - self.allowedTypes = {"None": 1,
|
| - "bool": 1,
|
| - "boolean": 1,
|
| - "string": 1,
|
| - "str": 1,
|
| - "int": 1,
|
| - "float": 1,
|
| - "datetime": 1,
|
| - "time": 1,
|
| - "date": 1,
|
| - "timedelta": 1,
|
| - "NoneType": 1}
|
| - if hasattr(types, 'UnicodeType'):
|
| - self.allowedTypes['unicode'] = 1
|
| - if decimal is not None:
|
| - self.allowedTypes['decimal'] = 1
|
| - self.allowedTypes['set'] = 1
|
| - self.allowedTypes['frozenset'] = 1
|
| - self.allowedModules = {}
|
| - self.allowedClasses = {}
|
| -
|
| -
|
| - def allowBasicTypes(self):
|
| - """
|
| - Allow all `basic' types. (Dictionary and list. Int, string, and float
|
| - are implicitly allowed.)
|
| - """
|
| - self.allowTypes(*self.basicTypes)
|
| -
|
| -
|
| - def allowTypes(self, *types):
|
| - """
|
| - SecurityOptions.allowTypes(typeString): Allow a particular type, by its
|
| - name.
|
| - """
|
| - for typ in types:
|
| - if not isinstance(typ, str):
|
| - typ = qual(typ)
|
| - self.allowedTypes[typ] = 1
|
| -
|
| -
|
| - def allowInstancesOf(self, *classes):
|
| - """
|
| - SecurityOptions.allowInstances(klass, klass, ...): allow instances
|
| - of the specified classes
|
| -
|
| - This will also allow the 'instance', 'class' (renamed 'classobj' in
|
| - Python 2.3), and 'module' types, as well as basic types.
|
| - """
|
| - self.allowBasicTypes()
|
| - self.allowTypes("instance", "class", "classobj", "module")
|
| - for klass in classes:
|
| - self.allowTypes(qual(klass))
|
| - self.allowModules(klass.__module__)
|
| - self.allowedClasses[klass] = 1
|
| -
|
| -
|
| - def allowModules(self, *modules):
|
| - """
|
| - SecurityOptions.allowModules(module, module, ...): allow modules by
|
| - name. This will also allow the 'module' type.
|
| - """
|
| - for module in modules:
|
| - if type(module) == types.ModuleType:
|
| - module = module.__name__
|
| - self.allowedModules[module] = 1
|
| -
|
| -
|
| - def isModuleAllowed(self, moduleName):
|
| - """
|
| - SecurityOptions.isModuleAllowed(moduleName) -> boolean
|
| - returns 1 if a module by that name is allowed, 0 otherwise
|
| - """
|
| - return moduleName in self.allowedModules
|
| -
|
| -
|
| - def isClassAllowed(self, klass):
|
| - """
|
| - SecurityOptions.isClassAllowed(class) -> boolean
|
| - Assumes the module has already been allowed. Returns 1 if the given
|
| - class is allowed, 0 otherwise.
|
| - """
|
| - return klass in self.allowedClasses
|
| -
|
| -
|
| - def isTypeAllowed(self, typeName):
|
| - """
|
| - SecurityOptions.isTypeAllowed(typeName) -> boolean
|
| - Returns 1 if the given type is allowed, 0 otherwise.
|
| - """
|
| - return (typeName in self.allowedTypes or '.' in typeName)
|
| -
|
| -
|
| -globalSecurity = SecurityOptions()
|
| -globalSecurity.allowBasicTypes()
|
| -
|
| -
|
| -
|
| -def jelly(object, taster=DummySecurityOptions(), persistentStore=None,
|
| - invoker=None):
|
| - """
|
| - Serialize to s-expression.
|
| -
|
| - Returns a list which is the serialized representation of an object. An
|
| - optional 'taster' argument takes a SecurityOptions and will mark any
|
| - insecure objects as unpersistable rather than serializing them.
|
| - """
|
| - return _Jellier(taster, persistentStore, invoker).jelly(object)
|
| -
|
| -
|
| -
|
| -def unjelly(sexp, taster=DummySecurityOptions(), persistentLoad=None,
|
| - invoker=None):
|
| - """
|
| - Unserialize from s-expression.
|
| -
|
| - Takes an list that was the result from a call to jelly() and unserializes
|
| - an arbitrary object from it. The optional 'taster' argument, an instance
|
| - of SecurityOptions, will cause an InsecureJelly exception to be raised if a
|
| - disallowed type, module, or class attempted to unserialize.
|
| - """
|
| - return _Unjellier(taster, persistentLoad, invoker).unjellyFull(sexp)
|
|
|