Package pyamf
[hide private]
[frames] | no frames]

Source Code for Package pyamf

  1  # Copyright (c) The PyAMF Project. 
  2  # See LICENSE.txt for details. 
  3   
  4  """ 
  5  U{PyAMF<http://pyamf.org>} provides Action Message Format (U{AMF 
  6  <http://en.wikipedia.org/wiki/Action_Message_Format>}) support for Python that 
  7  is compatible with the Adobe U{Flash Player 
  8  <http://en.wikipedia.org/wiki/Flash_Player>}. 
  9   
 10  @since: October 2007 
 11  @status: Production/Stable 
 12  """ 
 13   
 14  import types 
 15  import inspect 
 16   
 17  from pyamf import util, _version 
 18  from pyamf.adapters import register_adapters 
 19  from pyamf import python 
 20  from pyamf.alias import ClassAlias, UnknownClassAlias 
 21   
 22   
 23  __all__ = [ 
 24      'register_class', 
 25      'register_class_loader', 
 26      'encode', 
 27      'decode', 
 28      '__version__', 
 29      'version' 
 30  ] 
 31   
 32  #: PyAMF version number. 
 33  __version__ = version = _version.version 
 34   
 35  #: Class alias mapping support. Contains two types of keys: The string alias 
 36  #: related to the class and the class object itself. Both point to the linked 
 37  #: L{ClassAlias} object. 
 38  #: @see: L{register_class}, L{unregister_class}, and L{register_package} 
 39  CLASS_CACHE = {} 
 40  #: Class loaders. An iterable of callables that are handed a string alias and 
 41  #: return a class object or C{None} it not handled. 
 42  #: @see: L{register_class_loader} and L{unregister_class_loader} 
 43  CLASS_LOADERS = set() 
 44  #: Custom type map. 
 45  #: @see: L{get_type}, L{add_type}, and L{remove_type} 
 46  TYPE_MAP = {} 
 47  #: Maps error classes to string codes. 
 48  #: @see: L{add_error_class} and L{remove_error_class} 
 49  ERROR_CLASS_MAP = { 
 50      TypeError.__name__: TypeError, 
 51      KeyError.__name__: KeyError, 
 52      LookupError.__name__: LookupError, 
 53      IndexError.__name__: IndexError, 
 54      NameError.__name__: NameError, 
 55      ValueError.__name__: ValueError 
 56  } 
 57  #: Alias mapping support. 
 58  #: @see: L{get_class_alias}, L{register_alias_type}, and L{unregister_alias_type} 
 59  ALIAS_TYPES = {} 
 60   
 61  #: Specifies that objects are serialized using AMF for ActionScript 1.0 
 62  #: and 2.0 that were introduced in the Adobe Flash Player 6. 
 63  AMF0 = 0 
 64  #: Specifies that objects are serialized using AMF for ActionScript 3.0 
 65  #: that was introduced in the Adobe Flash Player 9. 
 66  AMF3 = 3 
 67  #: Supported AMF encoding types. 
 68  #: @see: L{AMF0}, L{AMF3}, and L{DEFAULT_ENCODING} 
 69  ENCODING_TYPES = (AMF0, AMF3) 
 70   
 71  #: Default encoding 
 72  DEFAULT_ENCODING = AMF3 
 73   
 74   
75 -class UndefinedType(object):
76 """ 77 Represents the C{undefined} value in the Adobe Flash Player client. 78 """
79 - def __repr__(self):
80 return 'pyamf.Undefined'
81 82 #: Represents the C{undefined} value in the Adobe Flash Player client. 83 Undefined = UndefinedType() 84 85
86 -class BaseError(Exception):
87 """ 88 Base AMF Error. 89 90 All AMF related errors should be subclassed from this class. 91 """
92 93
94 -class DecodeError(BaseError):
95 """ 96 Raised if there is an error in decoding an AMF data stream. 97 """
98 99
100 -class EOStream(BaseError):
101 """ 102 Raised if the data stream has come to a natural end. 103 """
104 105
106 -class ReferenceError(BaseError):
107 """ 108 Raised if an AMF data stream refers to a non-existent object or string 109 reference (in the case of AMF3). 110 """
111 112
113 -class EncodeError(BaseError):
114 """ 115 Raised if the element could not be encoded to AMF. 116 """
117 118
119 -class ASObject(dict):
120 """ 121 Represents a Flash Actionscript Object (typed or untyped). 122 123 I supply a C{dict} interface to support C{getattr}/C{setattr} calls. 124 """ 125
126 - class __amf__:
127 dynamic = True
128
129 - def __getattr__(self, k):
130 try: 131 return self[k] 132 except KeyError: 133 raise AttributeError('Unknown attribute \'%s\'' % (k,))
134
135 - def __setattr__(self, k, v):
136 self[k] = v
137
138 - def __repr__(self):
139 return dict.__repr__(self)
140
141 - def __hash__(self):
142 return id(self)
143 144
145 -class MixedArray(dict):
146 """ 147 Used to be able to specify the C{mixedarray} type. 148 """
149 150
151 -class TypedObject(dict):
152 """ 153 This class is used when a strongly typed object is decoded but there is no 154 registered class to apply it to. 155 156 This object can only be used for standard streams - i.e. not externalized 157 data. If encountered, a L{DecodeError} will be raised. 158 159 @ivar alias: The alias of the typed object. 160 @type alias: C{string} 161 @since: 0.4 162 """ 163
164 - def __init__(self, alias):
165 dict.__init__(self) 166 167 self.alias = alias
168
169 - def __readamf__(self, o):
170 raise DecodeError('Unable to decode an externalised stream with ' 171 'class alias \'%s\'.\n\nA class alias was found and because ' 172 'strict mode is False an attempt was made to decode the object ' 173 'automatically. To decode this stream, a registered class with ' 174 'the alias and a corresponding __readamf__ method will be ' 175 'required.' % (self.alias,))
176
177 - def __writeamf__(self, o):
178 raise EncodeError('Unable to encode an externalised stream with ' 179 'class alias \'%s\'.\n\nA class alias was found and because ' 180 'strict mode is False an attempt was made to encode the object ' 181 'automatically. To encode this stream, a registered class with ' 182 'the alias and a corresponding __writeamf__ method will be ' 183 'required.' % (self.alias,))
184 185
186 -class TypedObjectClassAlias(ClassAlias):
187 """ 188 The meta class for L{TypedObject} used to adapt PyAMF. 189 190 @since: 0.4 191 """ 192 193 klass = TypedObject 194
195 - def __init__(self, *args, **kwargs):
196 ClassAlias.__init__(self, self.klass, kwargs.pop('alias', args[0]))
197
198 - def createInstance(self, codec=None):
199 return self.klass(self.alias)
200
201 - def checkClass(kls, klass):
202 pass
203 204
205 -class ErrorAlias(ClassAlias):
206 """ 207 Adapts Python exception objects to Adobe Flash Player error objects. 208 209 @since: 0.5 210 """ 211
212 - def getCustomProperties(self):
213 self.exclude_attrs.update(['args'])
214
215 - def getEncodableAttributes(self, obj, **kwargs):
216 attrs = ClassAlias.getEncodableAttributes(self, obj, **kwargs) 217 218 attrs['message'] = str(obj) 219 attrs['name'] = obj.__class__.__name__ 220 221 return attrs
222 223
224 -def register_class(klass, alias=None):
225 """ 226 Registers a class to be used in the data streaming. This is the equivalent 227 to the C{[RemoteClass(alias="foobar")]} AS3 metatag. 228 229 @return: The registered L{ClassAlias} instance. 230 @see: L{unregister_class} 231 """ 232 meta = util.get_class_meta(klass) 233 234 if alias is not None: 235 meta['alias'] = alias 236 237 alias_klass = util.get_class_alias(klass) or ClassAlias 238 239 x = alias_klass(klass, defer=True, **meta) 240 241 if not x.anonymous: 242 CLASS_CACHE[x.alias] = x 243 244 CLASS_CACHE[klass] = x 245 246 return x
247 248
249 -def unregister_class(alias):
250 """ 251 Opposite of L{register_class}. 252 253 @raise UnknownClassAlias: Unknown alias. 254 """ 255 try: 256 x = CLASS_CACHE[alias] 257 except KeyError: 258 raise UnknownClassAlias('Unknown alias %r' % (alias,)) 259 260 if not x.anonymous: 261 del CLASS_CACHE[x.alias] 262 263 del CLASS_CACHE[x.klass] 264 265 return x
266 267
268 -def get_class_alias(klass_or_alias):
269 """ 270 Finds the L{ClassAlias} that is registered to C{klass_or_alias}. 271 272 If a string is supplied and no related L{ClassAlias} is found, the alias is 273 loaded via L{load_class}. 274 275 @raise UnknownClassAlias: Unknown alias 276 """ 277 if isinstance(klass_or_alias, python.str_types): 278 try: 279 return CLASS_CACHE[klass_or_alias] 280 except KeyError: 281 return load_class(klass_or_alias) 282 283 try: 284 return CLASS_CACHE[klass_or_alias] 285 except KeyError: 286 raise UnknownClassAlias('Unknown alias for %r' % (klass_or_alias,))
287 288
289 -def register_class_loader(loader):
290 """ 291 Registers a loader that is called to provide the C{class} for a specific 292 alias. 293 294 The C{loader} is provided with one argument, the class alias (as a string). 295 If the loader succeeds in finding a suitable class then it should return 296 that class, otherwise it should return C{None}. 297 298 An example:: 299 300 def lazy_load_from_my_module(alias): 301 if not alias.startswith('foo.bar.'): 302 return None 303 304 from foo import bar 305 306 if alias == 'foo.bar.Spam': 307 return bar.Spam 308 elif alias == 'foo.bar.Eggs': 309 return bar.Eggs 310 311 pyamf.register_class_loader(lazy_load_from_my_module) 312 313 @raise TypeError: C{loader} must be callable 314 @see: L{unregister_class_loader} 315 """ 316 if not hasattr(loader, '__call__'): 317 raise TypeError("loader must be callable") 318 319 CLASS_LOADERS.update([loader])
320 321
322 -def unregister_class_loader(loader):
323 """ 324 Unregisters a class loader. 325 326 @param loader: The class loader to be unregistered. 327 @raise LookupError: The C{loader} was not registered. 328 @see: L{register_class_loader} 329 """ 330 try: 331 CLASS_LOADERS.remove(loader) 332 except KeyError: 333 raise LookupError("loader not found")
334 335
336 -def load_class(alias):
337 """ 338 Finds the class registered to the alias. 339 340 The search is done in order: 341 1. Checks if the class name has been registered via L{register_class} 342 or L{register_package}. 343 2. Checks all functions registered via L{register_class_loader}. 344 3. Attempts to load the class via standard module loading techniques. 345 346 @param alias: The class name. 347 @type alias: C{string} 348 @raise UnknownClassAlias: The C{alias} was not found. 349 @raise TypeError: Expecting class type or L{ClassAlias} from loader. 350 @return: Class registered to the alias. 351 @rtype: C{classobj} 352 """ 353 # Try the CLASS_CACHE first 354 try: 355 return CLASS_CACHE[alias] 356 except KeyError: 357 pass 358 359 for loader in CLASS_LOADERS: 360 klass = loader(alias) 361 362 if klass is None: 363 continue 364 365 if isinstance(klass, python.class_types): 366 return register_class(klass, alias) 367 elif isinstance(klass, ClassAlias): 368 CLASS_CACHE[klass.alias] = klass 369 CLASS_CACHE[klass.klass] = klass 370 371 return klass 372 373 raise TypeError("Expecting class object or ClassAlias from loader") 374 375 mod_class = alias.split('.') 376 377 if mod_class: 378 module = '.'.join(mod_class[:-1]) 379 klass = mod_class[-1] 380 381 try: 382 module = util.get_module(module) 383 except (ImportError, AttributeError): 384 pass 385 else: 386 klass = getattr(module, klass) 387 388 if isinstance(klass, python.class_types): 389 return register_class(klass, alias) 390 elif isinstance(klass, ClassAlias): 391 CLASS_CACHE[klass.alias] = klass 392 CLASS_CACHE[klass.klass] = klass 393 394 return klass.klass 395 else: 396 raise TypeError("Expecting class type or ClassAlias from loader") 397 398 # All available methods for finding the class have been exhausted 399 raise UnknownClassAlias("Unknown alias for %r" % (alias,))
400 401
402 -def decode(stream, *args, **kwargs):
403 """ 404 A generator function to decode a datastream. 405 406 @param stream: AMF data to be decoded. 407 @type stream: byte data. 408 @kwarg encoding: AMF encoding type. One of L{ENCODING_TYPES}. 409 @return: A generator that will decode each element in the stream. 410 """ 411 encoding = kwargs.pop('encoding', DEFAULT_ENCODING) 412 decoder = get_decoder(encoding, stream, *args, **kwargs) 413 414 return decoder
415 416
417 -def encode(*args, **kwargs):
418 """ 419 A helper function to encode an element. 420 421 @param args: The python data to be encoded. 422 @kwarg encoding: AMF encoding type. One of L{ENCODING_TYPES}. 423 @return: A L{util.BufferedByteStream} object that contains the data. 424 """ 425 encoding = kwargs.pop('encoding', DEFAULT_ENCODING) 426 encoder = get_encoder(encoding, **kwargs) 427 428 [encoder.writeElement(el) for el in args] 429 430 stream = encoder.stream 431 stream.seek(0) 432 433 return stream
434 435
436 -def get_decoder(encoding, *args, **kwargs):
437 """ 438 Returns a L{codec.Decoder} capable of decoding AMF[C{encoding}] streams. 439 440 @raise ValueError: Unknown C{encoding}. 441 """ 442 def _get_decoder_class(): 443 if encoding == AMF0: 444 try: 445 from cpyamf import amf0 446 except ImportError: 447 from pyamf import amf0 448 449 return amf0.Decoder 450 elif encoding == AMF3: 451 try: 452 from cpyamf import amf3 453 except ImportError: 454 from pyamf import amf3 455 456 return amf3.Decoder 457 458 raise ValueError("Unknown encoding %r" % (encoding,))
459 460 return _get_decoder_class()(*args, **kwargs) 461 462
463 -def get_encoder(encoding, *args, **kwargs):
464 """ 465 Returns a L{codec.Encoder} capable of encoding AMF[C{encoding}] streams. 466 467 @raise ValueError: Unknown C{encoding}. 468 """ 469 def _get_encoder_class(): 470 if encoding == AMF0: 471 try: 472 from cpyamf import amf0 473 except ImportError: 474 from pyamf import amf0 475 476 return amf0.Encoder 477 elif encoding == AMF3: 478 try: 479 from cpyamf import amf3 480 except ImportError: 481 from pyamf import amf3 482 483 return amf3.Encoder 484 485 raise ValueError("Unknown encoding %r" % (encoding,))
486 487 return _get_encoder_class()(*args, **kwargs) 488 489
490 -def blaze_loader(alias):
491 """ 492 Loader for BlazeDS framework compatibility classes, specifically 493 implementing C{ISmallMessage}. 494 495 @see: U{BlazeDS<http://opensource.adobe.com/wiki/display/blazeds/BlazeDS>} 496 @since: 0.5 497 """ 498 if alias not in ['DSC', 'DSK']: 499 return 500 501 import pyamf.flex.messaging 502 503 return CLASS_CACHE[alias]
504 505
506 -def flex_loader(alias):
507 """ 508 Loader for L{Flex<pyamf.flex>} framework compatibility classes. 509 510 @raise UnknownClassAlias: Trying to load an unknown Flex compatibility class. 511 """ 512 if not alias.startswith('flex.'): 513 return 514 515 try: 516 if alias.startswith('flex.messaging.messages'): 517 import pyamf.flex.messaging 518 elif alias.startswith('flex.messaging.io'): 519 import pyamf.flex 520 elif alias.startswith('flex.data.messages'): 521 import pyamf.flex.data 522 523 return CLASS_CACHE[alias] 524 except KeyError: 525 raise UnknownClassAlias(alias)
526 527
528 -def add_type(type_, func=None):
529 """ 530 Adds a custom type to L{TYPE_MAP}. A custom type allows fine grain control 531 of what to encode to an AMF data stream. 532 533 @raise TypeError: Unable to add as a custom type (expected a class or callable). 534 @raise KeyError: Type already exists. 535 @see: L{get_type} and L{remove_type} 536 """ 537 def _check_type(type_): 538 if not (isinstance(type_, python.class_types) or 539 hasattr(type_, '__call__')): 540 raise TypeError(r'Unable to add '%r' as a custom type (expected a ' 541 'class or callable)' % (type_,))
542 543 if isinstance(type_, list): 544 type_ = tuple(type_) 545 546 if type_ in TYPE_MAP: 547 raise KeyError('Type %r already exists' % (type_,)) 548 549 if isinstance(type_, types.TupleType): 550 for x in type_: 551 _check_type(x) 552 else: 553 _check_type(type_) 554 555 TYPE_MAP[type_] = func 556 557
558 -def get_type(type_):
559 """ 560 Gets the declaration for the corresponding custom type. 561 562 @raise KeyError: Unknown type. 563 @see: L{add_type} and L{remove_type} 564 """ 565 if isinstance(type_, list): 566 type_ = tuple(type_) 567 568 for k, v in TYPE_MAP.iteritems(): 569 if k == type_: 570 return v 571 572 raise KeyError("Unknown type %r" % (type_,))
573 574
575 -def remove_type(type_):
576 """ 577 Removes the custom type declaration. 578 579 @return: Custom type declaration. 580 @see: L{add_type} and L{get_type} 581 """ 582 declaration = get_type(type_) 583 584 del TYPE_MAP[type_] 585 586 return declaration
587 588
589 -def add_error_class(klass, code):
590 """ 591 Maps an exception class to a string code. Used to map remoting C{onStatus} 592 objects to an exception class so that an exception can be built to 593 represent that error. 594 595 An example:: 596 597 >>> class AuthenticationError(Exception): 598 ... pass 599 ... 600 >>> pyamf.add_error_class(AuthenticationError, 'Auth.Failed') 601 >>> print pyamf.ERROR_CLASS_MAP 602 {'TypeError': <type 'exceptions.TypeError'>, 'IndexError': <type 'exceptions.IndexError'>, 603 'Auth.Failed': <class '__main__.AuthenticationError'>, 'KeyError': <type 'exceptions.KeyError'>, 604 'NameError': <type 'exceptions.NameError'>, 'LookupError': <type 'exceptions.LookupError'>} 605 606 @param klass: Exception class 607 @param code: Exception code 608 @type code: C{str} 609 @see: L{remove_error_class} 610 """ 611 if not isinstance(code, python.str_types): 612 code = code.decode('utf-8') 613 614 if not isinstance(klass, python.class_types): 615 raise TypeError("klass must be a class type") 616 617 mro = inspect.getmro(klass) 618 619 if not Exception in mro: 620 raise TypeError( 621 'Error classes must subclass the __builtin__.Exception class') 622 623 if code in ERROR_CLASS_MAP: 624 raise ValueError('Code %s is already registered' % (code,)) 625 626 ERROR_CLASS_MAP[code] = klass
627 628
629 -def remove_error_class(klass):
630 """ 631 Removes a class from the L{ERROR_CLASS_MAP}. 632 633 An example:: 634 635 >>> class AuthenticationError(Exception): 636 ... pass 637 ... 638 >>> pyamf.add_error_class(AuthenticationError, 'Auth.Failed') 639 >>> pyamf.remove_error_class(AuthenticationError) 640 641 @see: L{add_error_class} 642 """ 643 if isinstance(klass, python.str_types): 644 if klass not in ERROR_CLASS_MAP: 645 raise ValueError('Code %s is not registered' % (klass,)) 646 elif isinstance(klass, python.class_types): 647 classes = ERROR_CLASS_MAP.values() 648 if klass not in classes: 649 raise ValueError('Class %s is not registered' % (klass,)) 650 651 klass = ERROR_CLASS_MAP.keys()[classes.index(klass)] 652 else: 653 raise TypeError("Invalid type, expected class or string") 654 655 del ERROR_CLASS_MAP[klass]
656 657
658 -def register_alias_type(klass, *args):
659 """ 660 This function allows you to map subclasses of L{ClassAlias} to classes 661 listed in C{args}. 662 663 When an object is read/written from/to the AMF stream, a paired L{ClassAlias} 664 instance is created (or reused), based on the Python class of that object. 665 L{ClassAlias} provides important metadata for the class and can also control 666 how the equivalent Python object is created, how the attributes are applied 667 etc. 668 669 Use this function if you need to do something non-standard. 670 671 @since: 0.4 672 @see: 673 - L{pyamf.adapters._google_appengine_ext_db.DataStoreClassAlias} for a 674 good example. 675 - L{unregister_alias_type} 676 @raise RuntimeError: alias is already registered 677 @raise TypeError: Value supplied to C{klass} is not a class 678 @raise ValueError: 679 - New aliases must subclass L{pyamf.ClassAlias} 680 - At least one type must be supplied 681 """ 682 def check_type_registered(arg): 683 for k, v in ALIAS_TYPES.iteritems(): 684 for kl in v: 685 if arg is kl: 686 raise RuntimeError('%r is already registered under %r' % ( 687 arg, k))
688 689 if not isinstance(klass, python.class_types): 690 raise TypeError('klass must be class') 691 692 if not issubclass(klass, ClassAlias): 693 raise ValueError('New aliases must subclass pyamf.ClassAlias') 694 695 if len(args) == 0: 696 raise ValueError('At least one type must be supplied') 697 698 if len(args) == 1 and hasattr(args[0], '__call__'): 699 c = args[0] 700 701 check_type_registered(c) 702 else: 703 for arg in args: 704 if not isinstance(arg, python.class_types): 705 raise TypeError('%r must be class' % (arg,)) 706 707 check_type_registered(arg) 708 709 ALIAS_TYPES[klass] = args 710 711 for k, v in CLASS_CACHE.copy().iteritems(): 712 new_alias = util.get_class_alias(v.klass) 713 714 if new_alias is klass: 715 meta = util.get_class_meta(v.klass) 716 meta['alias'] = v.alias 717 718 alias_klass = klass(v.klass, **meta) 719 720 CLASS_CACHE[k] = alias_klass 721 CLASS_CACHE[v.klass] = alias_klass 722 723
724 -def unregister_alias_type(klass):
725 """ 726 Removes the klass from the L{ALIAS_TYPES} register. 727 728 @see: L{register_alias_type} 729 """ 730 return ALIAS_TYPES.pop(klass, None)
731 732
733 -def register_package(module=None, package=None, separator='.', ignore=[], 734 strict=True):
735 """ 736 This is a helper function that takes the concept of Actionscript packages 737 and registers all the classes in the supplied Python module under that 738 package. It auto-aliased all classes in C{module} based on the parent 739 C{package}. 740 741 @param module: The Python module that will contain all the classes to 742 auto alias. 743 @type module: C{module} or C{dict} 744 @param package: The base package name. e.g. 'com.example.app'. If this 745 is C{None} then the value is inferred from C{module.__name__}. 746 @type package: C{string} or C{None} 747 @param separator: The separator used to append to C{package} to form the 748 complete alias. 749 @param ignore: To give fine grain control over what gets aliased and what 750 doesn't, supply a list of classes that you B{do not} want to be aliased. 751 @type ignore: C{iterable} 752 @param strict: Whether only classes that originate from C{module} will be 753 registered. 754 755 @return: A dict of all the classes that were registered and their respective 756 L{ClassAlias} counterparts. 757 @since: 0.5 758 @raise TypeError: Cannot get a list of classes from C{module} 759 """ 760 if isinstance(module, python.str_types): 761 if module == '': 762 raise TypeError('Cannot get list of classes from %r' % (module,)) 763 764 package = module 765 module = None 766 767 if module is None: 768 import inspect 769 770 prev_frame = inspect.stack()[1][0] 771 module = prev_frame.f_locals 772 773 if type(module) is dict: 774 has = lambda x: x in module 775 get = module.__getitem__ 776 elif type(module) is list: 777 has = lambda x: x in module 778 get = module.__getitem__ 779 strict = False 780 else: 781 has = lambda x: hasattr(module, x) 782 get = lambda x: getattr(module, x) 783 784 if package is None: 785 if has('__name__'): 786 package = get('__name__') 787 else: 788 raise TypeError('Cannot get list of classes from %r' % (module,)) 789 790 if has('__all__'): 791 keys = get('__all__') 792 elif hasattr(module, '__dict__'): 793 keys = module.__dict__.keys() 794 elif hasattr(module, 'keys'): 795 keys = module.keys() 796 elif isinstance(module, list): 797 keys = range(len(module)) 798 else: 799 raise TypeError('Cannot get list of classes from %r' % (module,)) 800 801 def check_attr(attr): 802 if not isinstance(attr, python.class_types): 803 return False 804 805 if attr.__name__ in ignore: 806 return False 807 808 try: 809 if strict and attr.__module__ != get('__name__'): 810 return False 811 except AttributeError: 812 return False 813 814 return True
815 816 # gotta love python 817 classes = filter(check_attr, [get(x) for x in keys]) 818 819 registered = {} 820 821 for klass in classes: 822 alias = '%s%s%s' % (package, separator, klass.__name__) 823 824 registered[klass] = register_class(klass, alias) 825 826 return registered 827 828
829 -def set_default_etree(etree):
830 """ 831 Sets the default interface that will called apon to both de/serialise XML 832 entities. This means providing both C{tostring} and C{fromstring} functions. 833 834 For testing purposes, will return the previous value for this (if any). 835 """ 836 from pyamf import xml 837 838 return xml.set_default_interface(etree)
839 840 841 #: setup some some standard class registrations and class loaders. 842 register_class(ASObject) 843 register_class_loader(flex_loader) 844 register_class_loader(blaze_loader) 845 register_alias_type(TypedObjectClassAlias, TypedObject) 846 register_alias_type(ErrorAlias, Exception) 847 848 register_adapters() 849