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) |