Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: third_party/twisted_8_1/twisted/spread/jelly.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.test.test_jelly -*-
2
3 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6
7 """
8 S-expression-based persistence of python objects.
9
10 It does something very much like L{Pickle<pickle>}; however, pickle's main goal
11 seems to be efficiency (both in space and time); jelly's main goals are
12 security, human readability, and portability to other environments.
13
14 This is how Jelly converts various objects to s-expressions.
15
16 Boolean::
17 True --> ['boolean', 'true']
18
19 Integer::
20 1 --> 1
21
22 List::
23 [1, 2] --> ['list', 1, 2]
24
25 String::
26 \"hello\" --> \"hello\"
27
28 Float::
29 2.3 --> 2.3
30
31 Dictionary::
32 {'a': 1, 'b': 'c'} --> ['dictionary', ['b', 'c'], ['a', 1]]
33
34 Module::
35 UserString --> ['module', 'UserString']
36
37 Class::
38 UserString.UserString --> ['class', ['module', 'UserString'], 'UserString']
39
40 Function::
41 string.join --> ['function', 'join', ['module', 'string']]
42
43 Instance: s is an instance of UserString.UserString, with a __dict__
44 {'data': 'hello'}::
45 [\"UserString.UserString\", ['dictionary', ['data', 'hello']]]
46
47 Class Method: UserString.UserString.center::
48 ['method', 'center', ['None'], ['class', ['module', 'UserString'],
49 'UserString']]
50
51 Instance Method: s.center, where s is an instance of UserString.UserString::
52 ['method', 'center', ['instance', ['reference', 1, ['class',
53 ['module', 'UserString'], 'UserString']], ['dictionary', ['data', 'd']]],
54 ['dereference', 1]]
55
56 The C{set} builtin and the C{sets.Set} class are serialized to the same
57 thing, and unserialized to C{set} if available, else to C{sets.Set}. It means
58 that there's a possibility of type switching in the serialization process. The
59 solution is to always use C{set} if possible, and only use C{sets.Set} under
60 Python 2.3; this can be accomplished by using L{twisted.python.compat.set}.
61
62 The same rule applies for C{frozenset} and C{sets.ImmutableSet}.
63
64 @author: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>}
65 """
66
67 __version__ = "$Revision: 1.48 $"[11:-2]
68
69 # System Imports
70 import pickle
71 import types
72 import warnings
73 from types import StringType
74 from types import UnicodeType
75 from types import IntType
76 from types import TupleType
77 from types import ListType
78 from types import LongType
79 from types import FloatType
80 from types import FunctionType
81 from types import MethodType
82 from types import ModuleType
83 from types import DictionaryType
84 from types import InstanceType
85 from types import NoneType
86 from types import ClassType
87 import copy
88
89 import datetime
90 from types import BooleanType
91
92 try:
93 import decimal
94 except ImportError:
95 decimal = None
96
97 try:
98 _set = set
99 except NameError:
100 _set = None
101
102 try:
103 # Filter out deprecation warning for Python >= 2.6
104 warnings.filterwarnings("ignore", category=DeprecationWarning,
105 message="the sets module is deprecated", append=True)
106 import sets as _sets
107 finally:
108 warnings.filters.pop()
109
110
111 from new import instance
112 from new import instancemethod
113 from zope.interface import implements
114
115 # Twisted Imports
116 from twisted.python.reflect import namedObject, qual
117 from twisted.persisted.crefutil import NotKnown, _Tuple, _InstanceMethod
118 from twisted.persisted.crefutil import _DictKeyAndValue, _Dereference
119 from twisted.persisted.crefutil import _Container
120 from twisted.python import runtime
121
122 from twisted.spread.interfaces import IJellyable, IUnjellyable
123
124
125 if runtime.platform.getType() == "java":
126 from org.python.core import PyStringMap
127 DictTypes = (DictionaryType, PyStringMap)
128 else:
129 DictTypes = (DictionaryType,)
130
131
132 None_atom = "None" # N
133 # code
134 class_atom = "class" # c
135 module_atom = "module" # m
136 function_atom = "function" # f
137
138 # references
139 dereference_atom = 'dereference' # D
140 persistent_atom = 'persistent' # p
141 reference_atom = 'reference' # r
142
143 # mutable collections
144 dictionary_atom = "dictionary" # d
145 list_atom = 'list' # l
146 set_atom = 'set'
147
148 # immutable collections
149 # (assignment to __dict__ and __class__ still might go away!)
150 tuple_atom = "tuple" # t
151 instance_atom = 'instance' # i
152 frozenset_atom = 'frozenset'
153
154
155 # errors
156 unpersistable_atom = "unpersistable"# u
157 unjellyableRegistry = {}
158 unjellyableFactoryRegistry = {}
159
160
161
162 def _newInstance(cls, state):
163 """
164 Make a new instance of a class without calling its __init__ method.
165 'state' will be used to update inst.__dict__ . Supports both new- and
166 old-style classes.
167 """
168 if not isinstance(cls, types.ClassType):
169 # new-style
170 inst = cls.__new__(cls)
171 inst.__dict__.update(state) # Copy 'instance' behaviour
172 else:
173 inst = instance(cls, state)
174 return inst
175
176
177
178 def _maybeClass(classnamep):
179 try:
180 object
181 except NameError:
182 isObject = 0
183 else:
184 isObject = isinstance(classnamep, type)
185 if isinstance(classnamep, ClassType) or isObject:
186 return qual(classnamep)
187 return classnamep
188
189
190
191 def setUnjellyableForClass(classname, unjellyable):
192 """
193 Set which local class will represent a remote type.
194
195 If you have written a Copyable class that you expect your client to be
196 receiving, write a local "copy" class to represent it, then call::
197
198 jellier.setUnjellyableForClass('module.package.Class', MyJellier).
199
200 Call this at the module level immediately after its class
201 definition. MyCopier should be a subclass of RemoteCopy.
202
203 The classname may be a special tag returned by
204 'Copyable.getTypeToCopyFor' rather than an actual classname.
205
206 This call is also for cached classes, since there will be no
207 overlap. The rules are the same.
208 """
209
210 global unjellyableRegistry
211 classname = _maybeClass(classname)
212 unjellyableRegistry[classname] = unjellyable
213 globalSecurity.allowTypes(classname)
214
215
216
217 def setUnjellyableFactoryForClass(classname, copyFactory):
218 """
219 Set the factory to construct a remote instance of a type::
220
221 jellier.setFactoryForClass('module.package.Class', MyFactory)
222
223 Call this at the module level immediately after its class definition.
224 C{copyFactory} should return an instance or subclass of
225 L{RemoteCopy<pb.RemoteCopy>}.
226
227 Similar to L{setUnjellyableForClass} except it uses a factory instead
228 of creating an instance.
229 """
230
231 global unjellyableFactoryRegistry
232 classname = _maybeClass(classname)
233 unjellyableFactoryRegistry[classname] = copyFactory
234 globalSecurity.allowTypes(classname)
235
236
237
238 def setUnjellyableForClassTree(module, baseClass, prefix=None):
239 """
240 Set all classes in a module derived from C{baseClass} as copiers for
241 a corresponding remote class.
242
243 When you have a heirarchy of Copyable (or Cacheable) classes on
244 one side, and a mirror structure of Copied (or RemoteCache)
245 classes on the other, use this to setCopierForClass all your
246 Copieds for the Copyables.
247
248 Each copyTag (the \"classname\" argument to getTypeToCopyFor, and
249 what the Copyable's getTypeToCopyFor returns) is formed from
250 adding a prefix to the Copied's class name. The prefix defaults
251 to module.__name__. If you wish the copy tag to consist of solely
252 the classname, pass the empty string \'\'.
253
254 @param module: a module object from which to pull the Copied classes.
255 (passing sys.modules[__name__] might be useful)
256
257 @param baseClass: the base class from which all your Copied classes derive.
258
259 @param prefix: the string prefixed to classnames to form the
260 unjellyableRegistry.
261 """
262 if prefix is None:
263 prefix = module.__name__
264
265 if prefix:
266 prefix = "%s." % prefix
267
268 for i in dir(module):
269 i_ = getattr(module, i)
270 if type(i_) == types.ClassType:
271 if issubclass(i_, baseClass):
272 setUnjellyableForClass('%s%s' % (prefix, i), i_)
273
274
275
276 def getInstanceState(inst, jellier):
277 """
278 Utility method to default to 'normal' state rules in serialization.
279 """
280 if hasattr(inst, "__getstate__"):
281 state = inst.__getstate__()
282 else:
283 state = inst.__dict__
284 sxp = jellier.prepare(inst)
285 sxp.extend([qual(inst.__class__), jellier.jelly(state)])
286 return jellier.preserve(inst, sxp)
287
288
289
290 def setInstanceState(inst, unjellier, jellyList):
291 """
292 Utility method to default to 'normal' state rules in unserialization.
293 """
294 state = unjellier.unjelly(jellyList[1])
295 if hasattr(inst, "__setstate__"):
296 inst.__setstate__(state)
297 else:
298 inst.__dict__ = state
299 return inst
300
301
302
303 class Unpersistable:
304 """
305 This is an instance of a class that comes back when something couldn't be
306 unpersisted.
307 """
308
309 def __init__(self, reason):
310 """
311 Initialize an unpersistable object with a descriptive C{reason} string.
312 """
313 self.reason = reason
314
315
316 def __repr__(self):
317 return "Unpersistable(%s)" % repr(self.reason)
318
319
320
321 class Jellyable:
322 """
323 Inherit from me to Jelly yourself directly with the `getStateFor'
324 convenience method.
325 """
326 implements(IJellyable)
327
328 def getStateFor(self, jellier):
329 return self.__dict__
330
331
332 def jellyFor(self, jellier):
333 """
334 @see: L{twisted.spread.interfaces.IJellyable.jellyFor}
335 """
336 sxp = jellier.prepare(self)
337 sxp.extend([
338 qual(self.__class__),
339 jellier.jelly(self.getStateFor(jellier))])
340 return jellier.preserve(self, sxp)
341
342
343
344 class Unjellyable:
345 """
346 Inherit from me to Unjelly yourself directly with the
347 C{setStateFor} convenience method.
348 """
349 implements(IUnjellyable)
350
351 def setStateFor(self, unjellier, state):
352 self.__dict__ = state
353
354
355 def unjellyFor(self, unjellier, jellyList):
356 """
357 Perform the inverse operation of L{Jellyable.jellyFor}.
358
359 @see: L{twisted.spread.interfaces.IUnjellyable.unjellyFor}
360 """
361 state = unjellier.unjelly(jellyList[1])
362 self.setStateFor(unjellier, state)
363 return self
364
365
366
367 class _Jellier:
368 """
369 (Internal) This class manages state for a call to jelly()
370 """
371
372 def __init__(self, taster, persistentStore, invoker):
373 """
374 Initialize.
375 """
376 self.taster = taster
377 # `preserved' is a dict of previously seen instances.
378 self.preserved = {}
379 # `cooked' is a dict of previously backreferenced instances to their
380 # `ref' lists.
381 self.cooked = {}
382 self.cooker = {}
383 self._ref_id = 1
384 self.persistentStore = persistentStore
385 self.invoker = invoker
386
387
388 def _cook(self, object):
389 """
390 (internal) Backreference an object.
391
392 Notes on this method for the hapless future maintainer: If I've already
393 gone through the prepare/preserve cycle on the specified object (it is
394 being referenced after the serializer is \"done with\" it, e.g. this
395 reference is NOT circular), the copy-in-place of aList is relevant,
396 since the list being modified is the actual, pre-existing jelly
397 expression that was returned for that object. If not, it's technically
398 superfluous, since the value in self.preserved didn't need to be set,
399 but the invariant that self.preserved[id(object)] is a list is
400 convenient because that means we don't have to test and create it or
401 not create it here, creating fewer code-paths. that's why
402 self.preserved is always set to a list.
403
404 Sorry that this code is so hard to follow, but Python objects are
405 tricky to persist correctly. -glyph
406 """
407 aList = self.preserved[id(object)]
408 newList = copy.copy(aList)
409 # make a new reference ID
410 refid = self._ref_id
411 self._ref_id = self._ref_id + 1
412 # replace the old list in-place, so that we don't have to track the
413 # previous reference to it.
414 aList[:] = [reference_atom, refid, newList]
415 self.cooked[id(object)] = [dereference_atom, refid]
416 return aList
417
418
419 def prepare(self, object):
420 """
421 (internal) Create a list for persisting an object to. This will allow
422 backreferences to be made internal to the object. (circular
423 references).
424
425 The reason this needs to happen is that we don't generate an ID for
426 every object, so we won't necessarily know which ID the object will
427 have in the future. When it is 'cooked' ( see _cook ), it will be
428 assigned an ID, and the temporary placeholder list created here will be
429 modified in-place to create an expression that gives this object an ID:
430 [reference id# [object-jelly]].
431 """
432
433 # create a placeholder list to be preserved
434 self.preserved[id(object)] = []
435 # keep a reference to this object around, so it doesn't disappear!
436 # (This isn't always necessary, but for cases where the objects are
437 # dynamically generated by __getstate__ or getStateToCopyFor calls, it
438 # is; id() will return the same value for a different object if it gets
439 # garbage collected. This may be optimized later.)
440 self.cooker[id(object)] = object
441 return []
442
443
444 def preserve(self, object, sexp):
445 """
446 (internal) Mark an object's persistent list for later referral.
447 """
448 # if I've been cooked in the meanwhile,
449 if id(object) in self.cooked:
450 # replace the placeholder empty list with the real one
451 self.preserved[id(object)][2] = sexp
452 # but give this one back.
453 sexp = self.preserved[id(object)]
454 else:
455 self.preserved[id(object)] = sexp
456 return sexp
457
458 constantTypes = {types.StringType : 1, types.IntType : 1,
459 types.FloatType : 1, types.LongType : 1}
460
461
462 def _checkMutable(self,obj):
463 objId = id(obj)
464 if objId in self.cooked:
465 return self.cooked[objId]
466 if objId in self.preserved:
467 self._cook(obj)
468 return self.cooked[objId]
469
470
471 def jelly(self, obj):
472 if isinstance(obj, Jellyable):
473 preRef = self._checkMutable(obj)
474 if preRef:
475 return preRef
476 return obj.jellyFor(self)
477 objType = type(obj)
478 if self.taster.isTypeAllowed(qual(objType)):
479 # "Immutable" Types
480 if ((objType is StringType) or
481 (objType is IntType) or
482 (objType is LongType) or
483 (objType is FloatType)):
484 return obj
485 elif objType is MethodType:
486 return ["method",
487 obj.im_func.__name__,
488 self.jelly(obj.im_self),
489 self.jelly(obj.im_class)]
490
491 elif UnicodeType and objType is UnicodeType:
492 return ['unicode', obj.encode('UTF-8')]
493 elif objType is NoneType:
494 return ['None']
495 elif objType is FunctionType:
496 name = obj.__name__
497 return ['function', str(pickle.whichmodule(obj, obj.__name__))
498 + '.' +
499 name]
500 elif objType is ModuleType:
501 return ['module', obj.__name__]
502 elif objType is BooleanType:
503 return ['boolean', obj and 'true' or 'false']
504 elif objType is datetime.datetime:
505 if obj.tzinfo:
506 raise NotImplementedError(
507 "Currently can't jelly datetime objects with tzinfo")
508 return ['datetime', '%s %s %s %s %s %s %s' % (
509 obj.year, obj.month, obj.day, obj.hour,
510 obj.minute, obj.second, obj.microsecond)]
511 elif objType is datetime.time:
512 if obj.tzinfo:
513 raise NotImplementedError(
514 "Currently can't jelly datetime objects with tzinfo")
515 return ['time', '%s %s %s %s' % (obj.hour, obj.minute,
516 obj.second, obj.microsecond)]
517 elif objType is datetime.date:
518 return ['date', '%s %s %s' % (obj.year, obj.month, obj.day)]
519 elif objType is datetime.timedelta:
520 return ['timedelta', '%s %s %s' % (obj.days, obj.seconds,
521 obj.microseconds)]
522 elif objType is ClassType or issubclass(objType, type):
523 return ['class', qual(obj)]
524 elif decimal is not None and objType is decimal.Decimal:
525 return self.jelly_decimal(obj)
526 else:
527 preRef = self._checkMutable(obj)
528 if preRef:
529 return preRef
530 # "Mutable" Types
531 sxp = self.prepare(obj)
532 if objType is ListType:
533 sxp.extend(self._jellyIterable(list_atom, obj))
534 elif objType is TupleType:
535 sxp.extend(self._jellyIterable(tuple_atom, obj))
536 elif objType in DictTypes:
537 sxp.append(dictionary_atom)
538 for key, val in obj.items():
539 sxp.append([self.jelly(key), self.jelly(val)])
540 elif (_set is not None and objType is set or
541 objType is _sets.Set):
542 sxp.extend(self._jellyIterable(set_atom, obj))
543 elif (_set is not None and objType is frozenset or
544 objType is _sets.ImmutableSet):
545 sxp.extend(self._jellyIterable(frozenset_atom, obj))
546 else:
547 className = qual(obj.__class__)
548 persistent = None
549 if self.persistentStore:
550 persistent = self.persistentStore(obj, self)
551 if persistent is not None:
552 sxp.append(persistent_atom)
553 sxp.append(persistent)
554 elif self.taster.isClassAllowed(obj.__class__):
555 sxp.append(className)
556 if hasattr(obj, "__getstate__"):
557 state = obj.__getstate__()
558 else:
559 state = obj.__dict__
560 sxp.append(self.jelly(state))
561 else:
562 self.unpersistable(
563 "instance of class %s deemed insecure" %
564 qual(obj.__class__), sxp)
565 return self.preserve(obj, sxp)
566 else:
567 if objType is InstanceType:
568 raise InsecureJelly("Class not allowed for instance: %s %s" %
569 (obj.__class__, obj))
570 raise InsecureJelly("Type not allowed for object: %s %s" %
571 (objType, obj))
572
573
574 def _jellyIterable(self, atom, obj):
575 """
576 Jelly an iterable object.
577
578 @param atom: the identifier atom of the object.
579 @type atom: C{str}
580
581 @param obj: any iterable object.
582 @type obj: C{iterable}
583
584 @return: a generator of jellied data.
585 @rtype: C{generator}
586 """
587 yield atom
588 for item in obj:
589 yield self.jelly(item)
590
591
592 def jelly_decimal(self, d):
593 """
594 Jelly a decimal object.
595
596 @param d: a decimal object to serialize.
597 @type d: C{decimal.Decimal}
598
599 @return: jelly for the decimal object.
600 @rtype: C{list}
601 """
602 sign, guts, exponent = d.as_tuple()
603 value = reduce(lambda left, right: left * 10 + right, guts)
604 if sign:
605 value = -value
606 return ['decimal', value, exponent]
607
608
609 def unpersistable(self, reason, sxp=None):
610 """
611 (internal) Returns an sexp: (unpersistable "reason"). Utility method
612 for making note that a particular object could not be serialized.
613 """
614 if sxp is None:
615 sxp = []
616 sxp.append(unpersistable_atom)
617 sxp.append(reason)
618 return sxp
619
620
621
622 class _Unjellier:
623
624 def __init__(self, taster, persistentLoad, invoker):
625 self.taster = taster
626 self.persistentLoad = persistentLoad
627 self.references = {}
628 self.postCallbacks = []
629 self.invoker = invoker
630
631
632 def unjellyFull(self, obj):
633 o = self.unjelly(obj)
634 for m in self.postCallbacks:
635 m()
636 return o
637
638
639 def unjelly(self, obj):
640 if type(obj) is not types.ListType:
641 return obj
642 jelType = obj[0]
643 if not self.taster.isTypeAllowed(jelType):
644 raise InsecureJelly(jelType)
645 regClass = unjellyableRegistry.get(jelType)
646 if regClass is not None:
647 if isinstance(regClass, ClassType):
648 inst = _Dummy() # XXX chomp, chomp
649 inst.__class__ = regClass
650 method = inst.unjellyFor
651 elif isinstance(regClass, type):
652 # regClass.__new__ does not call regClass.__init__
653 inst = regClass.__new__(regClass)
654 method = inst.unjellyFor
655 else:
656 method = regClass # this is how it ought to be done
657 val = method(self, obj)
658 if hasattr(val, 'postUnjelly'):
659 self.postCallbacks.append(inst.postUnjelly)
660 return val
661 regFactory = unjellyableFactoryRegistry.get(jelType)
662 if regFactory is not None:
663 state = self.unjelly(obj[1])
664 inst = regFactory(state)
665 if hasattr(inst, 'postUnjelly'):
666 self.postCallbacks.append(inst.postUnjelly)
667 return inst
668 thunk = getattr(self, '_unjelly_%s'%jelType, None)
669 if thunk is not None:
670 ret = thunk(obj[1:])
671 else:
672 nameSplit = jelType.split('.')
673 modName = '.'.join(nameSplit[:-1])
674 if not self.taster.isModuleAllowed(modName):
675 raise InsecureJelly(
676 "Module %s not allowed (in type %s)." % (modName, jelType))
677 clz = namedObject(jelType)
678 if not self.taster.isClassAllowed(clz):
679 raise InsecureJelly("Class %s not allowed." % jelType)
680 if hasattr(clz, "__setstate__"):
681 ret = _newInstance(clz, {})
682 state = self.unjelly(obj[1])
683 ret.__setstate__(state)
684 else:
685 state = self.unjelly(obj[1])
686 ret = _newInstance(clz, state)
687 if hasattr(clz, 'postUnjelly'):
688 self.postCallbacks.append(ret.postUnjelly)
689 return ret
690
691
692 def _unjelly_None(self, exp):
693 return None
694
695
696 def _unjelly_unicode(self, exp):
697 if UnicodeType:
698 return unicode(exp[0], "UTF-8")
699 else:
700 return Unpersistable("Could not unpersist unicode: %s" % (exp[0],))
701
702
703 def _unjelly_decimal(self, exp):
704 """
705 Unjelly decimal objects, if decimal is available. If not, return a
706 L{Unpersistable} object instead.
707 """
708 if decimal is None:
709 return Unpersistable(
710 "Could not unpersist decimal: %s" % (exp[0] * (10**exp[1]),))
711 value = exp[0]
712 exponent = exp[1]
713 if value < 0:
714 sign = 1
715 else:
716 sign = 0
717 guts = decimal.Decimal(value).as_tuple()[1]
718 return decimal.Decimal((sign, guts, exponent))
719
720
721 def _unjelly_boolean(self, exp):
722 if BooleanType:
723 assert exp[0] in ('true', 'false')
724 return exp[0] == 'true'
725 else:
726 return Unpersistable("Could not unpersist boolean: %s" % (exp[0],))
727
728
729 def _unjelly_datetime(self, exp):
730 return datetime.datetime(*map(int, exp[0].split()))
731
732
733 def _unjelly_date(self, exp):
734 return datetime.date(*map(int, exp[0].split()))
735
736
737 def _unjelly_time(self, exp):
738 return datetime.time(*map(int, exp[0].split()))
739
740
741 def _unjelly_timedelta(self, exp):
742 days, seconds, microseconds = map(int, exp[0].split())
743 return datetime.timedelta(
744 days=days, seconds=seconds, microseconds=microseconds)
745
746
747 def unjellyInto(self, obj, loc, jel):
748 o = self.unjelly(jel)
749 if isinstance(o, NotKnown):
750 o.addDependant(obj, loc)
751 obj[loc] = o
752 return o
753
754
755 def _unjelly_dereference(self, lst):
756 refid = lst[0]
757 x = self.references.get(refid)
758 if x is not None:
759 return x
760 der = _Dereference(refid)
761 self.references[refid] = der
762 return der
763
764
765 def _unjelly_reference(self, lst):
766 refid = lst[0]
767 exp = lst[1]
768 o = self.unjelly(exp)
769 ref = self.references.get(refid)
770 if (ref is None):
771 self.references[refid] = o
772 elif isinstance(ref, NotKnown):
773 ref.resolveDependants(o)
774 self.references[refid] = o
775 else:
776 assert 0, "Multiple references with same ID!"
777 return o
778
779
780 def _unjelly_tuple(self, lst):
781 l = range(len(lst))
782 finished = 1
783 for elem in l:
784 if isinstance(self.unjellyInto(l, elem, lst[elem]), NotKnown):
785 finished = 0
786 if finished:
787 return tuple(l)
788 else:
789 return _Tuple(l)
790
791
792 def _unjelly_list(self, lst):
793 l = range(len(lst))
794 for elem in l:
795 self.unjellyInto(l, elem, lst[elem])
796 return l
797
798
799 def _unjellySetOrFrozenset(self, lst, containerType):
800 """
801 Helper method to unjelly set or frozenset.
802
803 @param lst: the content of the set.
804 @type lst: C{list}
805
806 @param containerType: the type of C{set} to use.
807 """
808 l = range(len(lst))
809 finished = True
810 for elem in l:
811 data = self.unjellyInto(l, elem, lst[elem])
812 if isinstance(data, NotKnown):
813 finished = False
814 if not finished:
815 return _Container(l, containerType)
816 else:
817 return containerType(l)
818
819
820 def _unjelly_set(self, lst):
821 """
822 Unjelly set using either the C{set} builtin if available, or
823 C{sets.Set} as fallback.
824 """
825 if _set is not None:
826 containerType = set
827 else:
828 containerType = _sets.Set
829 return self._unjellySetOrFrozenset(lst, containerType)
830
831
832 def _unjelly_frozenset(self, lst):
833 """
834 Unjelly frozenset using either the C{frozenset} builtin if available,
835 or C{sets.ImmutableSet} as fallback.
836 """
837 if _set is not None:
838 containerType = frozenset
839 else:
840 containerType = _sets.ImmutableSet
841 return self._unjellySetOrFrozenset(lst, containerType)
842
843
844 def _unjelly_dictionary(self, lst):
845 d = {}
846 for k, v in lst:
847 kvd = _DictKeyAndValue(d)
848 self.unjellyInto(kvd, 0, k)
849 self.unjellyInto(kvd, 1, v)
850 return d
851
852
853 def _unjelly_module(self, rest):
854 moduleName = rest[0]
855 if type(moduleName) != types.StringType:
856 raise InsecureJelly(
857 "Attempted to unjelly a module with a non-string name.")
858 if not self.taster.isModuleAllowed(moduleName):
859 raise InsecureJelly(
860 "Attempted to unjelly module named %r" % (moduleName,))
861 mod = __import__(moduleName, {}, {},"x")
862 return mod
863
864
865 def _unjelly_class(self, rest):
866 clist = rest[0].split('.')
867 modName = '.'.join(clist[:-1])
868 if not self.taster.isModuleAllowed(modName):
869 raise InsecureJelly("module %s not allowed" % modName)
870 klaus = namedObject(rest[0])
871 if type(klaus) is not types.ClassType:
872 raise InsecureJelly(
873 "class %r unjellied to something that isn't a class: %r" % (
874 rest[0], klaus))
875 if not self.taster.isClassAllowed(klaus):
876 raise InsecureJelly("class not allowed: %s" % qual(klaus))
877 return klaus
878
879
880 def _unjelly_function(self, rest):
881 modSplit = rest[0].split('.')
882 modName = '.'.join(modSplit[:-1])
883 if not self.taster.isModuleAllowed(modName):
884 raise InsecureJelly("Module not allowed: %s"% modName)
885 # XXX do I need an isFunctionAllowed?
886 function = namedObject(rest[0])
887 return function
888
889
890 def _unjelly_persistent(self, rest):
891 if self.persistentLoad:
892 pload = self.persistentLoad(rest[0], self)
893 return pload
894 else:
895 return Unpersistable("Persistent callback not found")
896
897
898 def _unjelly_instance(self, rest):
899 clz = self.unjelly(rest[0])
900 if type(clz) is not types.ClassType:
901 raise InsecureJelly("Instance found with non-class class.")
902 if hasattr(clz, "__setstate__"):
903 inst = _newInstance(clz, {})
904 state = self.unjelly(rest[1])
905 inst.__setstate__(state)
906 else:
907 state = self.unjelly(rest[1])
908 inst = _newInstance(clz, state)
909 if hasattr(clz, 'postUnjelly'):
910 self.postCallbacks.append(inst.postUnjelly)
911 return inst
912
913
914 def _unjelly_unpersistable(self, rest):
915 return Unpersistable("Unpersistable data: %s" % (rest[0],))
916
917
918 def _unjelly_method(self, rest):
919 """
920 (internal) Unjelly a method.
921 """
922 im_name = rest[0]
923 im_self = self.unjelly(rest[1])
924 im_class = self.unjelly(rest[2])
925 if type(im_class) is not types.ClassType:
926 raise InsecureJelly("Method found with non-class class.")
927 if im_name in im_class.__dict__:
928 if im_self is None:
929 im = getattr(im_class, im_name)
930 elif isinstance(im_self, NotKnown):
931 im = _InstanceMethod(im_name, im_self, im_class)
932 else:
933 im = instancemethod(im_class.__dict__[im_name],
934 im_self,
935 im_class)
936 else:
937 raise TypeError('instance method changed')
938 return im
939
940
941
942 class _Dummy:
943 """
944 (Internal) Dummy class, used for unserializing instances.
945 """
946
947
948
949 class _DummyNewStyle(object):
950 """
951 (Internal) Dummy class, used for unserializing instances of new-style
952 classes.
953 """
954
955
956
957 #### Published Interface.
958
959
960 class InsecureJelly(Exception):
961 """
962 This exception will be raised when a jelly is deemed `insecure'; e.g. it
963 contains a type, class, or module disallowed by the specified `taster'
964 """
965
966
967
968 class DummySecurityOptions:
969 """
970 DummySecurityOptions() -> insecure security options
971 Dummy security options -- this class will allow anything.
972 """
973
974 def isModuleAllowed(self, moduleName):
975 """
976 DummySecurityOptions.isModuleAllowed(moduleName) -> boolean
977 returns 1 if a module by that name is allowed, 0 otherwise
978 """
979 return 1
980
981
982 def isClassAllowed(self, klass):
983 """
984 DummySecurityOptions.isClassAllowed(class) -> boolean
985 Assumes the module has already been allowed. Returns 1 if the given
986 class is allowed, 0 otherwise.
987 """
988 return 1
989
990
991 def isTypeAllowed(self, typeName):
992 """
993 DummySecurityOptions.isTypeAllowed(typeName) -> boolean
994 Returns 1 if the given type is allowed, 0 otherwise.
995 """
996 return 1
997
998
999
1000 class SecurityOptions:
1001 """
1002 This will by default disallow everything, except for 'none'.
1003 """
1004
1005 basicTypes = ["dictionary", "list", "tuple",
1006 "reference", "dereference", "unpersistable",
1007 "persistent", "long_int", "long", "dict"]
1008
1009 def __init__(self):
1010 """
1011 SecurityOptions() initialize.
1012 """
1013 # I don't believe any of these types can ever pose a security hazard,
1014 # except perhaps "reference"...
1015 self.allowedTypes = {"None": 1,
1016 "bool": 1,
1017 "boolean": 1,
1018 "string": 1,
1019 "str": 1,
1020 "int": 1,
1021 "float": 1,
1022 "datetime": 1,
1023 "time": 1,
1024 "date": 1,
1025 "timedelta": 1,
1026 "NoneType": 1}
1027 if hasattr(types, 'UnicodeType'):
1028 self.allowedTypes['unicode'] = 1
1029 if decimal is not None:
1030 self.allowedTypes['decimal'] = 1
1031 self.allowedTypes['set'] = 1
1032 self.allowedTypes['frozenset'] = 1
1033 self.allowedModules = {}
1034 self.allowedClasses = {}
1035
1036
1037 def allowBasicTypes(self):
1038 """
1039 Allow all `basic' types. (Dictionary and list. Int, string, and float
1040 are implicitly allowed.)
1041 """
1042 self.allowTypes(*self.basicTypes)
1043
1044
1045 def allowTypes(self, *types):
1046 """
1047 SecurityOptions.allowTypes(typeString): Allow a particular type, by its
1048 name.
1049 """
1050 for typ in types:
1051 if not isinstance(typ, str):
1052 typ = qual(typ)
1053 self.allowedTypes[typ] = 1
1054
1055
1056 def allowInstancesOf(self, *classes):
1057 """
1058 SecurityOptions.allowInstances(klass, klass, ...): allow instances
1059 of the specified classes
1060
1061 This will also allow the 'instance', 'class' (renamed 'classobj' in
1062 Python 2.3), and 'module' types, as well as basic types.
1063 """
1064 self.allowBasicTypes()
1065 self.allowTypes("instance", "class", "classobj", "module")
1066 for klass in classes:
1067 self.allowTypes(qual(klass))
1068 self.allowModules(klass.__module__)
1069 self.allowedClasses[klass] = 1
1070
1071
1072 def allowModules(self, *modules):
1073 """
1074 SecurityOptions.allowModules(module, module, ...): allow modules by
1075 name. This will also allow the 'module' type.
1076 """
1077 for module in modules:
1078 if type(module) == types.ModuleType:
1079 module = module.__name__
1080 self.allowedModules[module] = 1
1081
1082
1083 def isModuleAllowed(self, moduleName):
1084 """
1085 SecurityOptions.isModuleAllowed(moduleName) -> boolean
1086 returns 1 if a module by that name is allowed, 0 otherwise
1087 """
1088 return moduleName in self.allowedModules
1089
1090
1091 def isClassAllowed(self, klass):
1092 """
1093 SecurityOptions.isClassAllowed(class) -> boolean
1094 Assumes the module has already been allowed. Returns 1 if the given
1095 class is allowed, 0 otherwise.
1096 """
1097 return klass in self.allowedClasses
1098
1099
1100 def isTypeAllowed(self, typeName):
1101 """
1102 SecurityOptions.isTypeAllowed(typeName) -> boolean
1103 Returns 1 if the given type is allowed, 0 otherwise.
1104 """
1105 return (typeName in self.allowedTypes or '.' in typeName)
1106
1107
1108 globalSecurity = SecurityOptions()
1109 globalSecurity.allowBasicTypes()
1110
1111
1112
1113 def jelly(object, taster=DummySecurityOptions(), persistentStore=None,
1114 invoker=None):
1115 """
1116 Serialize to s-expression.
1117
1118 Returns a list which is the serialized representation of an object. An
1119 optional 'taster' argument takes a SecurityOptions and will mark any
1120 insecure objects as unpersistable rather than serializing them.
1121 """
1122 return _Jellier(taster, persistentStore, invoker).jelly(object)
1123
1124
1125
1126 def unjelly(sexp, taster=DummySecurityOptions(), persistentLoad=None,
1127 invoker=None):
1128 """
1129 Unserialize from s-expression.
1130
1131 Takes an list that was the result from a call to jelly() and unserializes
1132 an arbitrary object from it. The optional 'taster' argument, an instance
1133 of SecurityOptions, will cause an InsecureJelly exception to be raised if a
1134 disallowed type, module, or class attempted to unserialize.
1135 """
1136 return _Unjellier(taster, persistentLoad, invoker).unjellyFull(sexp)
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/spread/interfaces.py ('k') | third_party/twisted_8_1/twisted/spread/pb.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698