Package pyamf :: Module amf3
[hide private]
[frames] | no frames]

Source Code for Module pyamf.amf3

   1  # -*- coding: utf-8 -*- 
   2  # 
   3  # Copyright (c) 2007-2008 The PyAMF Project. 
   4  # See LICENSE for details. 
   5   
   6  """ 
   7  AMF3 implementation. 
   8   
   9  C{AMF3} is the default serialization for 
  10  U{ActionScript<http://en.wikipedia.org/wiki/ActionScript>} 3.0 and provides 
  11  various advantages over L{AMF0<pyamf.amf0>}, which is used for ActionScript 1.0 
  12  and 2.0. It adds support for sending C{int} and C{uint} objects as integers and 
  13  supports data types that are available only in ActionScript 3.0, such as 
  14  L{ByteArray} and L{ArrayCollection}. 
  15   
  16  @see: U{Official AMF3 Specification in English (external) 
  17  <http://opensource.adobe.com/wiki/download/attachments/1114283/amf3_spec_121207.pdf>} 
  18  @see: U{Official AMF3 Specification in Japanese (external) 
  19  <http://opensource.adobe.com/wiki/download/attachments/1114283/JP_amf3_spec_121207.pdf>} 
  20  @see: U{AMF3 documentation on OSFlash (external) 
  21  <http://osflash.org/documentation/amf3>} 
  22   
  23  @author: U{Arnar Birgisson<mailto:arnarbi@gmail.com>} 
  24  @author: U{Thijs Triemstra<mailto:info@collab.nl>} 
  25  @author: U{Nick Joyce<mailto:nick@boxdesign.co.uk>} 
  26   
  27  @since: 0.1.0 
  28  """ 
  29   
  30  import types, datetime, zlib 
  31   
  32  import pyamf 
  33  from pyamf import util 
  34   
  35  try: 
  36      set() 
  37  except NameError: 
  38      from sets import Set as set 
  39   
40 -class ASTypes:
41 """ 42 All AMF3 data types used in ActionScript 3.0. 43 44 AMF represents ActionScript objects by a single byte representing type, and 45 then by a type-specific byte array that may be of fixed length, may contain 46 length information, or may come with its own end code. 47 48 @see: U{AMF3 data types on OSFlash (external) 49 <http://osflash.org/documentation/amf3#data_types>} 50 """ 51 #: The undefined type is represented by the undefined type marker. 52 #: No further information is encoded for this value. 53 UNDEFINED = 0x00 54 #: The undefined type is represented by the undefined type marker. 55 #: No further information is encoded for this value. 56 NULL = 0x01 57 #: The false type is represented by the false type marker and is 58 #: used to encode a Boolean value of C{false}. No further information 59 #: is encoded for this value. 60 #: @note: In ActionScript 3.0 the concept of a primitive and Object 61 #: form of Boolean does not exist. 62 BOOL_FALSE = 0x02 63 #: The true type is represented by the true type marker and is 64 #: used to encode a Boolean value of C{true}. No further information 65 #: is encoded for this value. 66 #: @note: In ActionScript 3.0 the concept of a primitive and Object 67 #: form of Boolean does not exist. 68 BOOL_TRUE = 0x03 69 #: In AMF 3 integers are serialized using a variable length unsigned 70 #: 29-bit integer. 71 #: @see: U{Parsing Integers on OSFlash (external) 72 #: <http://osflash.org/documentation/amf3/parsing_integers>} 73 INTEGER = 0x04 74 #: This type is used to encode an ActionScript Number 75 #: or an ActionScript C{int} of value greater than or equal to 2^28 76 #: or an ActionScript uint of value greater than or equal to 2^29. 77 #: The encoded value is is always an 8 byte IEEE-754 double precision 78 #: floating point value in network byte order (sign bit in low memory). 79 #: The AMF 3 number type is encoded in the same manner as the 80 #: AMF 0 L{Number<pyamf.amf0.ASTypes.NUMBER>} type. 81 NUMBER = 0x05 82 #: ActionScript String values are represented using a single string 83 #: type in AMF 3 - the concept of string and long string types from 84 #: AMF 0 is not used. Strings can be sent as a reference to a previously 85 #: occurring String by using an index to the implicit string reference 86 #: table. 87 #: Strings are encoding using UTF-8 - however the header may either 88 #: describe a string literal or a string reference. 89 STRING = 0x06 90 #: ActionScript 3.0 introduced a new XML type however the legacy 91 #: C{XMLDocument} type from ActionScript 1.0 and 2.0.is retained 92 #: in the language as C{flash.xml.XMLDocument}. Similar to AMF 0, the 93 #: structure of an C{XMLDocument} needs to be flattened into a string 94 #: representation for serialization. As with other strings in AMF, 95 #: the content is encoded in UTF-8. XMLDocuments can be sent as a reference 96 #: to a previously occurring C{XMLDocument} instance by using an index to 97 #: the implicit object reference table. 98 #: @see: U{OSFlash documentation (external) 99 #: <http://osflash.org/documentation/amf3#x07_-_xml_legacy_flash.xml.xmldocument_class>} 100 XML = 0x07 101 #: In AMF 3 an ActionScript Date is serialized simply as the number of 102 #: milliseconds elapsed since the epoch of midnight, 1st Jan 1970 in the 103 #: UTC time zone. Local time zone information is not sent. 104 DATE = 0x08 105 #: ActionScript Arrays are described based on the nature of their indices, 106 #: i.e. their type and how they are positioned in the Array. 107 ARRAY = 0x09 108 #: A single AMF 3 type handles ActionScript Objects and custom user classes. 109 OBJECT = 0x0a 110 #: ActionScript 3.0 introduces a new top-level XML class that supports 111 #: U{E4X<http://en.wikipedia.org/wiki/E4X>} syntax. 112 #: For serialization purposes the XML type needs to be flattened into a 113 #: string representation. As with other strings in AMF, the content is 114 #: encoded using UTF-8. 115 XMLSTRING = 0x0b 116 #: ActionScript 3.0 introduces the L{ByteArray} type to hold an Array 117 #: of bytes. AMF 3 serializes this type using a variable length encoding 118 #: 29-bit integer for the byte-length prefix followed by the raw bytes 119 #: of the L{ByteArray}. 120 #: @see: U{Parsing ByteArrays on OSFlash (external) 121 #: <http://osflash.org/documentation/amf3/parsing_byte_arrays>} 122 BYTEARRAY = 0x0c
123 124 #: List of available ActionScript types in AMF3. 125 ACTIONSCRIPT_TYPES = [] 126 127 for x in ASTypes.__dict__: 128 if not x.startswith('_'): 129 ACTIONSCRIPT_TYPES.append(ASTypes.__dict__[x]) 130 del x 131 132 #: Reference bit. 133 REFERENCE_BIT = 0x01 134
135 -class ObjectEncoding:
136 """ 137 AMF object encodings. 138 """ 139 #: Property list encoding. 140 #: The remaining integer-data represents the number of class members that 141 #: exist. The property names are read as string-data. The values are then 142 #: read as AMF3-data. 143 STATIC = 0x00 144 145 #: Externalizable object. 146 #: What follows is the value of the "inner" object, including type code. 147 #: This value appears for objects that implement IExternalizable, such as 148 #: L{ArrayCollection} and L{ObjectProxy}. 149 EXTERNAL = 0x01 150 151 #: Name-value encoding. 152 #: The property names and values are encoded as string-data followed by 153 #: AMF3-data until there is an empty string property name. If there is a 154 #: class-def reference there are no property names and the number of values 155 #: is equal to the number of properties in the class-def. 156 DYNAMIC = 0x02 157 158 #: Proxy object. 159 PROXY = 0x03
160
161 -class DataOutput(object):
162 """ 163 I am a C{StringIO} type object containing byte data from the AMF stream. 164 ActionScript 3.0 introduced the C{flash.utils.ByteArray} class to support 165 the manipulation of raw data in the form of an Array of bytes. 166 I provide a set of methods for writing binary data with ActionScript 3.0. 167 168 This class is the I/O counterpart to the L{DataInput} class, which reads 169 binary data. 170 171 @see: U{IDataOutput on Livedocs (external) 172 <http://livedocs.adobe.com/flex/201/langref/flash/utils/IDataOutput.html>} 173 """
174 - def __init__(self, encoder):
175 """ 176 @param encoder: Encoder containing the stream. 177 @type encoder: L{amf3.Encoder<pyamf.amf3.Encoder>} 178 """ 179 assert isinstance(encoder, Encoder) 180 181 self.encoder = encoder 182 self.stream = encoder.stream
183
184 - def writeBoolean(self, value):
185 """ 186 Writes a Boolean value. 187 188 @type value: C{bool} 189 @param value: A Boolean value determining which byte is written. 190 If the parameter is C{True}, C{1} is written; if C{False}, C{0} is 191 written. 192 193 @raise ValueError: Non-boolean value is found. 194 """ 195 if isinstance(value, bool): 196 if value is True: 197 self.stream.write_uchar(1) 198 else: 199 self.stream.write_uchar(0) 200 else: 201 raise ValueError("Non-boolean value found")
202
203 - def writeByte(self, value):
204 """ 205 Writes a byte. 206 207 @type value: C{int} 208 """ 209 self.stream.write_char(value)
210
211 - def writeDouble(self, value):
212 """ 213 Writes an IEEE 754 double-precision (64-bit) floating 214 point number. 215 216 @type value: C{number} 217 """ 218 self.stream.write_double(value)
219
220 - def writeFloat(self, value):
221 """ 222 Writes an IEEE 754 single-precision (32-bit) floating 223 point number. 224 225 @type value: C{float} 226 """ 227 self.stream.write_float(value)
228
229 - def writeInt(self, value):
230 """ 231 Writes a 32-bit signed integer. 232 233 @type value: C{int} 234 """ 235 self.stream.write_long(value)
236
237 - def writeMultiByte(self, value, charset):
238 """ 239 Writes a multibyte string to the datastream using the 240 specified character set. 241 242 @type value: C{str} 243 @param value: The string value to be written. 244 @type charset: C{str} 245 @param charset: The string denoting the character 246 set to use. Possible character set strings include 247 C{shift-jis}, C{cn-gb}, C{iso-8859-1} and others. 248 @see: U{Supported character sets on Livedocs (external) 249 <http://livedocs.adobe.com/flex/201/langref/charset-codes.html>} 250 """ 251 self.stream.write(unicode(value).encode(charset))
252
253 - def writeObject(self, value, use_references=True):
254 """ 255 Writes an object to data stream in AMF serialized format. 256 257 @param value: The object to be serialized. 258 @type use_references: C{bool} 259 @param use_references: 260 """ 261 self.encoder.writeElement(value, use_references)
262
263 - def writeShort(self, value):
264 """ 265 Writes a 16-bit integer. 266 267 @type value: C{int} 268 @param value: A byte value as an integer. 269 """ 270 self.stream.write_short(value)
271
272 - def writeUnsignedInt(self, value):
273 """ 274 Writes a 32-bit unsigned integer. 275 276 @type value: C{int} 277 @param value: A byte value as an unsigned integer. 278 """ 279 self.stream.write_ulong(value)
280
281 - def writeUTF(self, value):
282 """ 283 Writes a UTF-8 string to the data stream. 284 285 The length of the UTF-8 string in bytes is written first, 286 as a 16-bit integer, followed by the bytes representing the 287 characters of the string. 288 289 @type value: C{str} 290 @param value: The string value to be written. 291 """ 292 if not isinstance(value, unicode): 293 value = unicode(value, 'utf8') 294 295 buf = util.BufferedByteStream() 296 buf.write_utf8_string(value) 297 bytes = buf.getvalue() 298 299 self.stream.write_ushort(len(bytes)) 300 self.stream.write(bytes)
301
302 - def writeUTFBytes(self, value):
303 """ 304 Writes a UTF-8 string. Similar to L{writeUTF}, but does 305 not prefix the string with a 16-bit length word. 306 307 @type value: C{str} 308 @param value: The string value to be written. 309 """ 310 val = None 311 312 if isinstance(value, unicode): 313 val = value 314 else: 315 val = unicode(value, 'utf8') 316 317 self.stream.write_utf8_string(val)
318
319 -class DataInput(object):
320 """ 321 I provide a set of methods for reading binary data with ActionScript 3.0. 322 323 This class is the I/O counterpart to the L{DataOutput} class, 324 which writes binary data. 325 326 @see: U{IDataInput on Livedocs (external) 327 <http://livedocs.adobe.com/flex/201/langref/flash/utils/IDataInput.html>} 328 """
329 - def __init__(self, decoder):
330 """ 331 @param decoder: AMF3 decoder containing the stream. 332 @type decoder: L{amf3.Decoder<pyamf.amf3.Decoder>} 333 """ 334 assert isinstance(decoder, Decoder) 335 336 self.decoder = decoder 337 self.stream = decoder.stream
338
339 - def readBoolean(self):
340 """ 341 Read Boolean. 342 343 @raise ValueError: Error reading Boolean. 344 @rtype: C{bool} 345 @return: A Boolean value, C{True} if the byte 346 is nonzero, C{False} otherwise. 347 """ 348 byte = self.stream.read(1) 349 350 if byte == '\x00': 351 return False 352 elif byte == '\x01': 353 return True 354 else: 355 raise ValueError("Error reading boolean")
356
357 - def readByte(self):
358 """ 359 Reads a signed byte. 360 361 @rtype: C{int} 362 @return: The returned value is in the range -128 to 127. 363 """ 364 return self.stream.read_char()
365
366 - def readDouble(self):
367 """ 368 Reads an IEEE 754 double-precision floating point number from the 369 data stream. 370 371 @rtype: C{number} 372 @return: An IEEE 754 double-precision floating point number. 373 """ 374 return self.stream.read_double()
375
376 - def readFloat(self):
377 """ 378 Reads an IEEE 754 single-precision floating point number from the 379 data stream. 380 381 @rtype: C{number} 382 @return: An IEEE 754 single-precision floating point number. 383 """ 384 return self.stream.read_float()
385
386 - def readInt(self):
387 """ 388 Reads a signed 32-bit integer from the data stream. 389 390 @rtype: C{int} 391 @return: The returned value is in the range -2147483648 to 2147483647. 392 """ 393 return self.stream.read_long()
394
395 - def readMultiByte(self, length, charset):
396 """ 397 Reads a multibyte string of specified length from the data stream 398 using the specified character set. 399 400 @type length: C{int} 401 @param length: The number of bytes from the data stream to read. 402 403 @type charset: C{str} 404 @param charset: The string denoting the character set to use. 405 406 @rtype: C{str} 407 @return: UTF-8 encoded string. 408 """ 409 #FIXME nick: how to work out the code point byte size (on the fly)? 410 bytes = self.stream.read(length) 411 412 return unicode(bytes, charset)
413
414 - def readObject(self):
415 """ 416 Reads an object from the data stream. 417 418 @return: The deserialized object. 419 """ 420 return self.decoder.readElement()
421
422 - def readShort(self):
423 """ 424 Reads a signed 16-bit integer from the data stream. 425 426 @rtype: C{uint} 427 @return: The returned value is in the range -32768 to 32767. 428 """ 429 return self.stream.read_short()
430
431 - def readUnsignedByte(self):
432 """ 433 Reads an unsigned byte from the data stream. 434 435 @rtype: C{uint} 436 @return: The returned value is in the range 0 to 255. 437 """ 438 return self.stream.read_uchar()
439
440 - def readUnsignedInt(self):
441 """ 442 Reads an unsigned 32-bit integer from the data stream. 443 444 @rtype: C{uint} 445 @return: The returned value is in the range 0 to 4294967295. 446 """ 447 return self.stream.read_ulong()
448
449 - def readUnsignedShort(self):
450 """ 451 Reads an unsigned 16-bit integer from the data stream. 452 453 @rtype: C{uint} 454 @return: The returned value is in the range 0 to 65535. 455 """ 456 return self.stream.read_ushort()
457
458 - def readUTF(self):
459 """ 460 Reads a UTF-8 string from the data stream. 461 462 The string is assumed to be prefixed with an unsigned 463 short indicating the length in bytes. 464 465 @rtype: C{str} 466 @return: A UTF-8 string produced by the byte 467 representation of characters. 468 """ 469 length = self.stream.read_ushort() 470 return self.stream.read_utf8_string(length)
471
472 - def readUTFBytes(self, length):
473 """ 474 Reads a sequence of C{length} UTF-8 bytes from the data 475 stream and returns a string. 476 477 @type length: C{int} 478 @param length: The number of bytes from the data stream to read. 479 @rtype: C{str} 480 @return: A UTF-8 string produced by the byte representation of 481 characters of specified C{length}. 482 """ 483 return self.readMultiByte(length, 'utf-8')
484
485 -class ByteArray(util.BufferedByteStream, DataInput, DataOutput):
486 """ 487 I am a C{StringIO} type object containing byte data from the AMF stream. 488 ActionScript 3.0 introduced the C{flash.utils.ByteArray} class to support 489 the manipulation of raw data in the form of an Array of bytes. 490 491 Supports C{zlib} compression. 492 493 Possible uses of the C{ByteArray} class: 494 - Creating a custom protocol to connect to a client. 495 - Writing your own AMF/Remoting packet. 496 - Optimizing the size of your data by using custom data types. 497 498 @see: U{ByteArray on Livedocs (external) 499 <http://livedocs.adobe.com/flex/201/langref/flash/utils/ByteArray.html>} 500 """
501 - def __init__(self, *args, **kwargs):
502 self.context = kwargs.pop('context', Context()) 503 504 util.BufferedByteStream.__init__(self, *args, **kwargs) 505 DataInput.__init__(self, Decoder(self, self.context)) 506 DataOutput.__init__(self, Encoder(self, self.context)) 507 508 self.compressed = False
509
510 - def __cmp__(self, other):
511 if isinstance(other, ByteArray): 512 return cmp(self.getvalue(), other.getvalue()) 513 514 return cmp(self._buffer, other)
515
516 - def __str__(self):
517 buf = self.getvalue() 518 519 if self.compressed: 520 buf = zlib.compress(buf) 521 #FIXME nick: hacked 522 buf = buf[0] + '\xda' + buf[2:] 523 524 return buf
525
526 - def compress(self):
527 """ 528 Forces compression of the underlying stream. 529 """ 530 self.compressed = True
531
532 -class ClassDefinition(object):
533 """ 534 I contain meta relating to the class definition. 535 536 @ivar alias: The alias to this class definition. If this value is C{None}, 537 or an empty string, the class is considered to be anonymous. 538 @type alias: L{ClassAlias<pyamf.ClassAlias>} 539 @ivar encoding: The type of encoding to use when serializing the object. 540 @type encoding: C{int} 541 """
542 - def __init__(self, alias, encoding=ObjectEncoding.DYNAMIC):
543 if alias in (None, ''): 544 self.alias = None 545 elif isinstance(alias, pyamf.ClassAlias): 546 self.alias = alias 547 else: 548 self.alias = pyamf.get_class_alias(alias) 549 550 self.encoding = encoding 551 552 if self.alias and self.alias.attrs is not None: 553 self.static_attrs = self.alias.attrs 554 else: 555 self.static_attrs = []
556
557 - def _get_name(self):
558 if self.alias is None: 559 # anonymous class 560 return '' 561 562 if 'anonymous' in self.alias.metadata: 563 return '' 564 565 return str(self.alias)
566 567 name = property(_get_name) 568
569 - def _getClass(self):
570 """ 571 If C{alias} is C{None}, an L{anonymous class<pyamf.ASObject>} is 572 returned, otherwise the class is loaded externally. 573 """ 574 if self.alias in (None, ''): 575 # anonymous class 576 return pyamf.ASObject 577 578 return self.getClassAlias().klass
579
580 - def getClassAlias(self):
581 """ 582 Gets the class alias that is held by this definition. 583 584 @see: L{load_class<pyamf.load_class>}. 585 @raise UnknownClassAlias: Anonymous class definitions do not have 586 class aliases. 587 588 @rtype: L{ClassAlias<pyamf.ClassAlias>} 589 @return: Class definition. 590 """ 591 if not hasattr(self, '_alias'): 592 if self.name == '': 593 raise pyamf.UnknownClassAlias, 'Anonymous class definitions do not have class aliases' 594 595 self._alias = pyamf.load_class(self.alias) 596 597 return self._alias
598
599 - def getClass(self):
600 """ 601 Gets the referenced class that is held by this definition. 602 """ 603 if hasattr(self, '_alias'): 604 return self._alias 605 606 self._klass = self._getClass() 607 608 return self._klass
609 610 klass = property(getClass) 611
612 - def getAttrs(self, obj):
613 """ 614 Returns a C{tuple} containing a dict of static and dynamic attributes 615 for C{obj}. 616 """ 617 attrs = util.get_instance_attrs(obj, self.alias) 618 static_attrs = dynamic_attrs = None 619 620 if self.alias: 621 if self.alias.attrs: 622 static_attrs = {} 623 624 for attr in self.alias.attrs: 625 static_attrs[attr] = getattr(obj, attr) 626 627 if self.alias.attr_func: 628 dynamic_attrs = {} 629 630 for attr in self.alias.attr_func(obj): 631 dynamic_attrs[attr] = getattr(obj, attr) 632 else: 633 dynamic_attrs = attrs 634 else: 635 dynamic_attrs = attrs 636 637 return (static_attrs, dynamic_attrs)
638
639 -class Context(pyamf.BaseContext):
640 """ 641 I hold the AMF3 context for en/decoding streams. 642 643 @ivar strings: A list of string references. 644 @type strings: C{list} 645 @ivar classes: A list of L{ClassDefinition}. 646 @type classes: C{list} 647 @ivar legacy_xml: A list of legacy encoded XML documents. 648 @type legacy_xml: C{list} 649 """
650 - def clear(self):
651 """ 652 Resets the context. 653 """ 654 pyamf.BaseContext.clear(self) 655 656 self.strings = [] 657 658 self.classes = [] 659 self.rev_classes = {} 660 self.class_defs = {} 661 self.rev_class_defs = {} 662 663 self.legacy_xml = [] 664 self.rev_legacy_xml = {}
665
666 - def getString(self, ref):
667 """ 668 Gets a string based on a reference C{ref}. 669 670 @param ref: The reference index. 671 @type ref: C{str} 672 @raise ReferenceError: The referenced string could not be found. 673 674 @rtype: C{str} 675 @return: The referenced string. 676 """ 677 try: 678 return self.strings[ref] 679 except IndexError: 680 raise pyamf.ReferenceError, "String reference %d not found" % ref
681
682 - def getStringReference(self, s):
683 """ 684 Return string reference. 685 686 @type s: C{str} 687 @param s: The referenced string. 688 @raise ReferenceError: The string reference could not be found. 689 @return: The reference index to the string. 690 @rtype: C{int} 691 """ 692 try: 693 return self.strings.index(s) 694 except ValueError: 695 raise pyamf.ReferenceError, "Reference for string %r not found" % s
696
697 - def addString(self, s):
698 """ 699 Creates a reference to C{s}. If the reference already exists, that 700 reference is returned. 701 702 @type s: C{str} 703 @param s: The string to be referenced. 704 @raise ValueError: Trying to store a reference to an empty string. 705 706 @rtype: C{int} 707 @return: The reference index. 708 """ 709 if not isinstance(s, basestring): 710 raise TypeError 711 712 if len(s) == 0: 713 # do not store empty string references 714 raise ValueError, "Cannot store a reference to an empty string" 715 716 try: 717 return self.strings.index(s) 718 except ValueError: 719 self.strings.append(s) 720 721 return len(self.strings) - 1
722
723 - def getClassDefinition(self, ref):
724 """ 725 Return class reference. 726 727 @raise ReferenceError: The class reference could not be found. 728 @return: Class reference. 729 """ 730 try: 731 return self.classes[ref] 732 except IndexError: 733 raise pyamf.ReferenceError, "Class reference %d not found" % ref
734
735 - def getClassDefinitionReference(self, class_def):
736 """ 737 Return class definition reference. 738 739 @type class_def: L{ClassDefinition} or C{instance} or C{class} 740 @param class_def: The class definition reference to be found. 741 @raise ReferenceError: The reference could not be found. 742 @return: The reference to C{class_def}. 743 @rtype: C{int} 744 """ 745 if not isinstance(class_def, ClassDefinition): 746 if isinstance(class_def, (type, types.ClassType)): 747 try: 748 return self.rev_class_defs[class_def] 749 except KeyError: 750 raise pyamf.ReferenceError("Reference for class definition for %s not found" % 751 class_def) 752 elif isinstance(class_def, (types.InstanceType, types.ObjectType)): 753 try: 754 return self.class_defs[class_def.__class__] 755 except KeyError: 756 raise pyamf.ReferenceError("Reference for class definition for %s not found" % 757 class_def.__class__) 758 759 raise TypeError, 'unable to determine class for %r' % class_def 760 else: 761 try: 762 return self.rev_class_defs[id(class_def)] 763 except ValueError: 764 raise pyamf.ReferenceError, "Reference for class %s not found" % class_def.klass
765
766 - def addClassDefinition(self, class_def):
767 """ 768 Creates a reference to C{class_def}. 769 """ 770 try: 771 return self.rev_class_defs[id(class_def)] 772 except KeyError: 773 self.classes.append(class_def) 774 idx = len(self.classes) - 1 775 776 self.rev_classes[id(class_def)] = idx 777 self.class_defs[class_def.__class__] = class_def 778 self.rev_class_defs[id(class_def.__class__)] = idx 779 780 return idx
781
782 - def removeClassDefinition(self, class_def):
783 del self.rev_classes[id(class_def)] 784 del self.rev_class_defs[id(class_def.__class__)] 785 del self.class_defs[class_def.__class__]
786
787 - def getLegacyXML(self, ref):
788 """ 789 Return the legacy XML reference. This is the C{flash.xml.XMLDocument} 790 class in ActionScript 3.0 and the top-level C{XML} class in 791 ActionScript 1.0 and 2.0. 792 793 @type ref: C{int} 794 @param ref: The reference index. 795 @raise ReferenceError: The reference could not be found. 796 @return: Instance of L{ET<util.ET>} 797 """ 798 try: 799 return self.legacy_xml[ref] 800 except IndexError: 801 raise pyamf.ReferenceError( 802 "Legacy XML reference %d not found" % ref)
803
804 - def getLegacyXMLReference(self, doc):
805 """ 806 Return legacy XML reference. 807 808 @type doc: L{ET<util.ET>} 809 @param doc: The XML document to reference. 810 @raise ReferenceError: The reference could not be found. 811 @return: The reference to C{doc}. 812 @rtype: C{int} 813 """ 814 try: 815 return self.rev_legacy_xml[id(doc)] 816 except KeyError: 817 raise pyamf.ReferenceError, "Reference for document %r not found" % doc
818
819 - def addLegacyXML(self, doc):
820 """ 821 Creates a reference to U{doc}. 822 823 If U{doc} is already referenced that index will be returned. Otherwise 824 a new index will be created. 825 826 @type doc: L{ET<util.ET>} 827 @param doc: The XML document to reference. 828 @rtype: C{int} 829 @return: The reference to C{doc}. 830 """ 831 try: 832 return self.rev_legacy_xml[id(doc)] 833 except KeyError: 834 self.legacy_xml.append(doc) 835 836 idx = len(self.legacy_xml) - 1 837 self.rev_legacy_xml[id(doc)] = idx 838 839 return idx
840
841 - def __copy__(self):
842 return self.__class__()
843
844 -class Decoder(pyamf.BaseDecoder):
845 """ 846 Decodes an AMF3 data stream. 847 """ 848 context_class = Context 849 850 type_map = { 851 ASTypes.UNDEFINED: 'readUndefined', 852 ASTypes.NULL: 'readNull', 853 ASTypes.BOOL_FALSE: 'readBoolFalse', 854 ASTypes.BOOL_TRUE: 'readBoolTrue', 855 ASTypes.INTEGER: 'readSignedInteger', 856 ASTypes.NUMBER: 'readNumber', 857 ASTypes.STRING: 'readString', 858 ASTypes.XML: 'readXML', 859 ASTypes.DATE: 'readDate', 860 ASTypes.ARRAY: 'readArray', 861 ASTypes.OBJECT: 'readObject', 862 ASTypes.XMLSTRING: 'readXMLString', 863 ASTypes.BYTEARRAY: 'readByteArray', 864 } 865
866 - def readType(self):
867 """ 868 Read and returns the next byte in the stream and determine its type. 869 870 @raise DecodeError: AMF3 type not recognized 871 @return: AMF3 type 872 @rtype: C{int} 873 """ 874 type = self.stream.read_uchar() 875 876 if type not in ACTIONSCRIPT_TYPES: 877 raise pyamf.DecodeError, "Unknown AMF3 type 0x%02x at %d" % (type, self.stream.tell() - 1) 878 879 return type
880
881 - def readUndefined(self):
882 """ 883 Read undefined. 884 """ 885 return pyamf.Undefined
886
887 - def readNull(self):
888 """ 889 Read null. 890 891 @return: C{None} 892 @rtype: C{None} 893 """ 894 return None
895
896 - def readBoolFalse(self):
897 """ 898 Returns C{False}. 899 900 @return: C{False} 901 @rtype: C{bool} 902 """ 903 return False
904
905 - def readBoolTrue(self):
906 """ 907 Returns C{True}. 908 909 @return: C{True} 910 @rtype: C{bool} 911 """ 912 return True
913
914 - def readNumber(self):
915 """ 916 Read number. 917 """ 918 return self.stream.read_double()
919
920 - def readUnsignedInteger(self):
921 """ 922 Reads and returns an unsigned integer from the stream. 923 """ 924 return self.readInteger(False)
925
926 - def readSignedInteger(self):
927 """ 928 Reads and returns a signed integer from the stream. 929 """ 930 return self.readInteger(True)
931
932 - def readInteger(self, signed=False):
933 """ 934 Reads and returns an integer from the stream. 935 936 @see: U{Parsing integers on OSFlash 937 <http://osflash.org/amf3/parsing_integers>} for the AMF3 integer data 938 format. 939 """ 940 n = result = 0 941 b = self.stream.read_uchar() 942 943 while b & 0x80 != 0 and n < 3: 944 result <<= 7 945 result |= b & 0x7f 946 b = self.stream.read_uchar() 947 n += 1 948 949 if n < 3: 950 result <<= 7 951 result |= b 952 else: 953 result <<= 8 954 result |= b 955 956 if result & 0x10000000 != 0: 957 if signed: 958 result -= 0x20000000 959 else: 960 result <<= 1 961 result += 1 962 963 return result
964
965 - def readString(self, use_references=True):
966 """ 967 Reads and returns a string from the stream. 968 969 @type use_references: C{bool} 970 """ 971 def readLength(): 972 x = self.readUnsignedInteger() 973 974 return (x >> 1, x & REFERENCE_BIT == 0)
975 976 length, is_reference = readLength() 977 978 if use_references and is_reference: 979 return self.context.getString(length) 980 981 buf = self.stream.read(length) 982 result = unicode(buf, "utf8") 983 984 if len(result) != 0 and use_references: 985 self.context.addString(result) 986 987 return result
988
989 - def readDate(self):
990 """ 991 Read date from the stream. 992 993 The timezone is ignored as the date is always in UTC. 994 """ 995 ref = self.readUnsignedInteger() 996 997 if ref & REFERENCE_BIT == 0: 998 return self.context.getObject(ref >> 1) 999 1000 ms = self.stream.read_double() 1001 result = util.get_datetime(ms / 1000.0) 1002 1003 self.context.addObject(result) 1004 1005 return result
1006
1007 - def readArray(self):
1008 """ 1009 Reads an array from the stream. 1010 1011 @warning: There is a very specific problem with AMF3 where the first 1012 three bytes of an encoded empty C{dict} will mirror that of an encoded 1013 C{{'': 1, '2': 2}} 1014 1015 @see: U{Docuverse blog (external) 1016 <http://www.docuverse.com/blog/donpark/2007/05/14/flash-9-amf3-bug>} 1017 """ 1018 size = self.readUnsignedInteger() 1019 1020 if size & REFERENCE_BIT == 0: 1021 return self.context.getObject(size >> 1) 1022 1023 size >>= 1 1024 1025 key = self.readString() 1026 1027 if key == "": 1028 # integer indexes only -> python list 1029 result = [] 1030 self.context.addObject(result) 1031 1032 for i in xrange(size): 1033 result.append(self.readElement()) 1034 1035 else: 1036 result = pyamf.MixedArray() 1037 self.context.addObject(result) 1038 1039 while key != "": 1040 el = self.readElement() 1041 1042 try: 1043 result[str(key)] = el 1044 except UnicodeError: 1045 result[key] = el 1046 1047 key = self.readString() 1048 1049 for i in xrange(size): 1050 el = self.readElement() 1051 result[i] = el 1052 1053 return result
1054
1055 - def _getClassDefinition(self, ref):
1056 """ 1057 Reads class definition from the stream. 1058 """ 1059 class_ref = ref & REFERENCE_BIT == 0 1060 1061 ref >>= 1 1062 1063 if class_ref: 1064 class_def = self.context.getClassDefinition(ref) 1065 else: 1066 class_def = ClassDefinition(self.readString(), encoding=ref & 0x03) 1067 self.context.addClassDefinition(class_def) 1068 1069 return class_ref, class_def, ref >> 2
1070
1071 - def readObject(self):
1072 """ 1073 Reads an object from the stream. 1074 """ 1075 def readStatic(is_ref, class_def, obj, num_attrs): 1076 if not is_ref: 1077 for i in range(num_attrs): 1078 key = self.readString() 1079 1080 class_def.static_attrs.append(key) 1081 1082 for attr in class_def.static_attrs: 1083 setattr(obj, attr, self.readElement())
1084 1085 def readDynamic(is_ref, class_def, obj): 1086 attr = self.readString() 1087 1088 while attr != "": 1089 setattr(obj, attr, self.readElement()) 1090 attr = self.readString() 1091 1092 ref = self.readUnsignedInteger() 1093 1094 if ref & REFERENCE_BIT == 0: 1095 return self.context.getObject(ref >> 1) 1096 1097 ref >>= 1 1098 1099 class_ref, class_def, num_attrs = self._getClassDefinition(ref) 1100 1101 if class_def.alias and 'amf0' in class_def.alias.metadata: 1102 raise pyamf.EncodeError, "Decoding an object in amf3 tagged as amf0 only is not allowed" 1103 1104 if class_def.alias: 1105 obj = class_def.alias() 1106 else: 1107 klass = class_def.getClass() 1108 obj = klass() 1109 1110 obj_attrs = pyamf.ASObject() 1111 self.context.addObject(obj) 1112 1113 if class_def.encoding in (ObjectEncoding.EXTERNAL, ObjectEncoding.PROXY): 1114 obj.__readamf__(DataInput(self)) 1115 elif class_def.encoding == ObjectEncoding.DYNAMIC: 1116 readStatic(class_ref, class_def, obj_attrs, num_attrs) 1117 readDynamic(class_ref, class_def, obj_attrs) 1118 elif class_def.encoding == ObjectEncoding.STATIC: 1119 readStatic(class_ref, class_def, obj_attrs, num_attrs) 1120 else: 1121 raise pyamf.DecodeError, "Unknown object encoding" 1122 1123 if hasattr(obj, '__setstate__'): 1124 obj.__setstate__(obj_attrs) 1125 else: 1126 for k, v in obj_attrs.iteritems(): 1127 setattr(obj, k, v) 1128 1129 return obj 1130
1131 - def _readXML(self, legacy=False):
1132 """ 1133 Reads an object from the stream. 1134 1135 @type legacy: C{bool} 1136 @param legacy: The read XML is in 'legacy' format. 1137 """ 1138 ref = self.readUnsignedInteger() 1139 1140 if ref & REFERENCE_BIT == 0: 1141 return self.context.getObject(ref >> 1) 1142 1143 xmlstring = self.stream.read(ref >> 1) 1144 1145 x = util.ET.XML(xmlstring) 1146 self.context.addObject(x) 1147 1148 if legacy is True: 1149 self.context.addLegacyXML(x) 1150 1151 return x
1152
1153 - def readXMLString(self):
1154 """ 1155 Reads a string from the data stream and converts it into an XML Tree. 1156 1157 @return: The XML Document. 1158 @rtype: L{ET<util.ET>} 1159 """ 1160 return self._readXML()
1161
1162 - def readXML(self):
1163 """ 1164 Read a legacy XML Document from the stream. 1165 1166 @return: The XML Document. 1167 @rtype: L{ET<util.ET>} 1168 """ 1169 return self._readXML(True)
1170
1171 - def readByteArray(self):
1172 """ 1173 Reads a string of data from the stream. 1174 1175 Detects if the L{ByteArray} was compressed using C{zlib}. 1176 1177 @see: L{ByteArray} 1178 @note: This is not supported in ActionScript 1.0 and 2.0. 1179 """ 1180 ref = self.readUnsignedInteger() 1181 1182 if ref & REFERENCE_BIT == 0: 1183 return self.context.getObject(ref >> 1) 1184 1185 buffer = self.stream.read(ref >> 1) 1186 1187 try: 1188 buffer = zlib.decompress(buffer) 1189 compressed = True 1190 except zlib.error: 1191 compressed = False 1192 1193 obj = ByteArray(buffer, context=self.context) 1194 obj.compressed = compressed 1195 1196 self.context.addObject(obj) 1197 1198 return obj
1199
1200 -class Encoder(pyamf.BaseEncoder):
1201 """ 1202 Encodes an AMF3 data stream. 1203 """ 1204 context_class = Context 1205 1206 type_map = [ 1207 # Unsupported types go first 1208 ((types.BuiltinFunctionType, types.BuiltinMethodType,), 1209 "writeUnsupported"), 1210 ((bool,), "writeBoolean"), 1211 ((types.NoneType,), "writeNull"), 1212 ((int,long), "writeInteger"), 1213 ((float,), "writeNumber"), 1214 ((types.StringTypes,), "writeString"), 1215 ((ByteArray,), "writeByteArray"), 1216 ((datetime.date, datetime.datetime), "writeDate"), 1217 ((util.ET.iselement,), "writeXML"), 1218 ((lambda x: x is pyamf.Undefined,), "writeUndefined"), 1219 ((types.InstanceType, types.ObjectType,), "writeInstance"), 1220 ] 1221
1222 - def writeElement(self, data, use_references=True):
1223 """ 1224 Writes the data. 1225 1226 @type data: C{mixed} 1227 @param data: The data to be encoded to the AMF3 data stream. 1228 @type use_references: C{bool} 1229 @param use_references: 1230 @raise EncodeError: Unable to encode data. 1231 """ 1232 func = self._writeElementFunc(data) 1233 1234 if func is None: 1235 # XXX nick: Should we be generating a warning here? 1236 self.writeUnsupported(data) 1237 else: 1238 try: 1239 if isinstance(func, pyamf.CustomTypeFunc): 1240 func(data) 1241 else: 1242 func(data, use_references=use_references) 1243 except (KeyboardInterrupt, SystemExit): 1244 raise 1245 except: 1246 raise 1247 raise pyamf.EncodeError, "Unable to encode '%r'" % data
1248
1249 - def writeType(self, type):
1250 """ 1251 Writes the data type to the stream. 1252 1253 @param type: ActionScript type. 1254 @raise EncodeError: AMF3 type is not recognized. 1255 """ 1256 if type not in ACTIONSCRIPT_TYPES: 1257 raise pyamf.EncodeError("Unknown AMF3 type 0x%02x at %d" % ( 1258 type, self.stream.tell() - 1)) 1259 1260 self.stream.write_uchar(type)
1261
1262 - def writeUndefined(self, d, use_references=True):
1263 """ 1264 Writes an C{pyamf.Undefined} value to the stream. 1265 1266 @type use_references: C{bool} 1267 """ 1268 self.writeType(ASTypes.UNDEFINED)
1269
1270 - def writeNull(self, n, use_references=True):
1271 """ 1272 Writes a C{null} value to the stream. 1273 1274 @type n: 1275 @param n: C{null} data. 1276 @type use_references: C{bool} 1277 @param use_references: 1278 """ 1279 self.writeType(ASTypes.NULL)
1280
1281 - def writeBoolean(self, n, use_references=True):
1282 """ 1283 Writes a Boolean to the stream. 1284 1285 @param n: 1286 @type n: boolean data 1287 @type use_references: C{bool} 1288 @param use_references: 1289 """ 1290 if n: 1291 self.writeType(ASTypes.BOOL_TRUE) 1292 else: 1293 self.writeType(ASTypes.BOOL_FALSE)
1294
1295 - def _writeInteger(self, n):
1296 """ 1297 AMF3 integers are encoded. 1298 1299 @see: U{Parsing Integers on OSFlash 1300 <http://osflash.org/documentation/amf3/parsing_integers>} 1301 for more info. 1302 """ 1303 self.stream.write(_encode_int(n))
1304
1305 - def writeInteger(self, n, use_references=True):
1306 """ 1307 Writes an integer to the stream. 1308 1309 @type n: 1310 @param n: integer data. 1311 @type use_references: C{bool} 1312 @param use_references: 1313 """ 1314 if n & 0xf0000000 not in [0, 0xf0000000]: 1315 self.writeNumber(n) 1316 1317 return 1318 1319 self.writeType(ASTypes.INTEGER) 1320 self.stream.write(_encode_int(n))
1321
1322 - def writeNumber(self, n, use_references=True):
1323 """ 1324 Writes a non integer to the stream. 1325 1326 @type n: 1327 @param n: number data. 1328 @type use_references: C{bool} 1329 @param use_references: 1330 """ 1331 self.writeType(ASTypes.NUMBER) 1332 self.stream.write_double(n)
1333
1334 - def _writeString(self, n, use_references=True):
1335 """ 1336 Writes a raw string to the stream. 1337 1338 @type n: C{str} or C{unicode} 1339 @param n: string data. 1340 @type use_references: C{bool} 1341 @param use_references: 1342 """ 1343 if not isinstance(n, basestring): 1344 bytes = unicode(n).encode('utf8') 1345 n = bytes 1346 elif isinstance(n, unicode): 1347 bytes = n.encode('utf8') 1348 else: 1349 bytes = n 1350 1351 if len(bytes) == 0: 1352 self._writeInteger(REFERENCE_BIT) 1353 1354 return 1355 1356 if use_references: 1357 try: 1358 ref = self.context.getStringReference(n) 1359 self._writeInteger(ref << 1) 1360 1361 return 1362 except pyamf.ReferenceError: 1363 self.context.addString(n) 1364 1365 self._writeInteger((len(bytes) << 1) | REFERENCE_BIT) 1366 self.stream.write(bytes)
1367
1368 - def writeString(self, n, use_references=True):
1369 """ 1370 Writes a string to the stream. If C{n} is not a unicode string, an 1371 attempt will be made to convert it. 1372 1373 @type n: C{basestring} 1374 @param n: string data. 1375 @type use_references: C{bool} 1376 @param use_references: 1377 """ 1378 self.writeType(ASTypes.STRING) 1379 self._writeString(n, use_references)
1380
1381 - def writeDate(self, n, use_references=True):
1382 """ 1383 Writes a C{datetime} instance to the stream. 1384 1385 @type n: L{datetime} 1386 @param n: C{Date} data. 1387 @type use_references: C{bool} 1388 @param use_references: 1389 """ 1390 self.writeType(ASTypes.DATE) 1391 1392 if use_references is True: 1393 try: 1394 ref = self.context.getObjectReference(n) 1395 self._writeInteger(ref << 1) 1396 1397 return 1398 except pyamf.ReferenceError: 1399 self.context.addObject(n) 1400 1401 self._writeInteger(REFERENCE_BIT) 1402 1403 ms = util.get_timestamp(n) 1404 self.stream.write_double(ms * 1000.0)
1405
1406 - def writeList(self, n, use_references=True):
1407 """ 1408 Writes a C{tuple}, C{set} or C{list} to the stream. 1409 1410 @type n: One of C{__builtin__.tuple}, C{__builtin__.set} 1411 or C{__builtin__.list} 1412 @param n: C{list} data. 1413 @type use_references: C{bool} 1414 @param use_references: 1415 """ 1416 self.writeType(ASTypes.ARRAY) 1417 1418 if use_references is True: 1419 try: 1420 ref = self.context.getObjectReference(n) 1421 self._writeInteger(ref << 1) 1422 1423 return 1424 except pyamf.ReferenceError: 1425 self.context.addObject(n) 1426 1427 self._writeInteger(len(n) << 1 | REFERENCE_BIT) 1428 1429 self.stream.write_uchar(0x01) 1430 1431 for x in n: 1432 self.writeElement(x)
1433
1434 - def writeDict(self, n, use_references=True):
1435 """ 1436 Writes a C{dict} to the stream. 1437 1438 @type n: C{__builtin__.dict} 1439 @param n: C{dict} data. 1440 @type use_references: C{bool} 1441 @param use_references: 1442 @raise ValueError: Non C{int}/C{str} key value found in the C{dict} 1443 @raise EncodeError: C{dict} contains empty string keys. 1444 """ 1445 self.writeType(ASTypes.ARRAY) 1446 1447 if use_references: 1448 try: 1449 ref = self.context.getObjectReference(n) 1450 self._writeInteger(ref << 1) 1451 1452 return 1453 except pyamf.ReferenceError: 1454 self.context.addObject(n) 1455 1456 # The AMF3 spec demands that all str based indicies be listed first 1457 keys = n.keys() 1458 int_keys = [] 1459 str_keys = [] 1460 1461 for x in keys: 1462 if isinstance(x, (int, long)): 1463 int_keys.append(x) 1464 elif isinstance(x, (str, unicode)): 1465 str_keys.append(x) 1466 else: 1467 raise ValueError, "Non int/str key value found in dict" 1468 1469 # Make sure the integer keys are within range 1470 l = len(int_keys) 1471 1472 for x in int_keys: 1473 if l < x <= 0: 1474 # treat as a string key 1475 str_keys.append(x) 1476 del int_keys[int_keys.index(x)] 1477 1478 int_keys.sort() 1479 1480 # If integer keys don't start at 0, they will be treated as strings 1481 if len(int_keys) > 0 and int_keys[0] != 0: 1482 for x in int_keys: 1483 str_keys.append(str(x)) 1484 del int_keys[int_keys.index(x)] 1485 1486 self._writeInteger(len(int_keys) << 1 | REFERENCE_BIT) 1487 1488 for x in str_keys: 1489 # Design bug in AMF3 that cannot read/write empty key strings 1490 # http://www.docuverse.com/blog/donpark/2007/05/14/flash-9-amf3-bug 1491 # for more info 1492 if x == '': 1493 raise pyamf.EncodeError, "dicts cannot contain empty string keys" 1494 1495 self._writeString(x) 1496 self.writeElement(n[x]) 1497 1498 self.stream.write_uchar(0x01) 1499 1500 for k in int_keys: 1501 self.writeElement(n[k])
1502
1503 - def _getClassDefinition(self, obj):
1504 """ 1505 Builds a class definition based on the C{obj}. 1506 """ 1507 encoding = ObjectEncoding.DYNAMIC 1508 1509 alias = self.context.getClassAlias(obj.__class__) 1510 1511 if alias: 1512 if 'dynamic' in alias.metadata: 1513 encoding = ObjectEncoding.DYNAMIC 1514 elif 'static' in alias.metadata: 1515 encoding = ObjectEncoding.STATIC 1516 elif 'external' in alias.metadata: 1517 encoding = ObjectEncoding.EXTERNAL 1518 1519 class_def = ClassDefinition(alias, encoding) 1520 1521 if alias and encoding == ObjectEncoding.STATIC: 1522 if alias.attrs is not None: 1523 import copy 1524 1525 class_def.static_attrs = copy.copy(alias.attrs) 1526 else: 1527 if hasattr(obj, 'keys'): 1528 for k in obj.keys(): 1529 class_def.static_attrs.append(unicode(k)) 1530 elif hasattr(obj, 'iteritems'): 1531 for k, v in obj.iteritems(): 1532 class_def.static_attrs.append(unicode(k)) 1533 elif hasattr(obj, '__dict__'): 1534 for k in obj.__dict__.keys(): 1535 class_def.static_attrs.append(unicode(k)) 1536 else: 1537 raise pyamf.EncodingError, 'Unable to determine object attributes' 1538 1539 return class_def
1540
1541 - def writeInstance(self, obj, use_references=True):
1542 """ 1543 Read class definition. 1544 1545 @type use_references: C{bool} 1546 """ 1547 if obj.__class__ == pyamf.MixedArray: 1548 self.writeDict(obj, use_references) 1549 elif obj.__class__ in (list, set, tuple): 1550 self.writeList(obj, use_references) 1551 else: 1552 self.writeObject(obj, use_references)
1553
1554 - def writeObject(self, obj, use_references=True):
1555 """ 1556 Writes an object to the stream. 1557 1558 @type use_references: C{bool} 1559 @raise EncodeError: Unknown object encoding. 1560 """ 1561 self.writeType(ASTypes.OBJECT) 1562 1563 if use_references is True: 1564 try: 1565 ref = self.context.getObjectReference(obj) 1566 self._writeInteger(ref << 1) 1567 1568 return 1569 except pyamf.ReferenceError: 1570 self.context.addObject(obj) 1571 1572 try: 1573 ref = self.context.getClassDefinitionReference(obj) 1574 class_ref = True 1575 1576 self._writeInteger(ref << 2 | REFERENCE_BIT) 1577 except pyamf.ReferenceError: 1578 class_def = self._getClassDefinition(obj) 1579 self.context.addClassDefinition(class_def) 1580 class_ref = False 1581 ref = 0 1582 1583 if class_def.alias and 'amf0' in class_def.alias.metadata: 1584 raise pyamf.EncodeError, "Encoding an object in amf3 tagged as amf0 only" 1585 1586 if class_def.encoding != ObjectEncoding.EXTERNAL: 1587 if class_def.alias and class_def.alias.attrs is not None: 1588 ref += len(class_def.alias.attrs) << 4 1589 1590 self._writeInteger(ref | class_def.encoding << 2 | REFERENCE_BIT << 1 | REFERENCE_BIT) 1591 self._writeString(class_def.name) 1592 else: 1593 class_def = self._getClassDefinition(obj) 1594 1595 if class_def.encoding in (ObjectEncoding.EXTERNAL, ObjectEncoding.PROXY): 1596 obj.__writeamf__(DataOutput(self)) 1597 else: 1598 static_attrs, dynamic_attrs = class_def.getAttrs(obj) 1599 1600 if static_attrs is not None: 1601 if not class_ref: 1602 [self._writeString(attr) for attr in static_attrs.keys()] 1603 [self.writeElement(attr) for attr in static_attrs.values()] 1604 1605 if class_def.encoding == ObjectEncoding.DYNAMIC and dynamic_attrs is not None: 1606 for attr, value in dynamic_attrs.iteritems(): 1607 self._writeString(attr) 1608 self.writeElement(value) 1609 1610 self._writeString("")
1611
1612 - def writeByteArray(self, n, use_references=True):
1613 """ 1614 Writes a L{ByteArray} to the data stream. 1615 1616 @type n: L{ByteArray} 1617 @param n: Data. 1618 @type use_references: C{bool} 1619 @param use_references: 1620 """ 1621 self.writeType(ASTypes.BYTEARRAY) 1622 1623 if use_references: 1624 try: 1625 ref = self.context.getObjectReference(n) 1626 self._writeInteger(ref << 1) 1627 1628 return 1629 except pyamf.ReferenceError: 1630 self.context.addObject(n) 1631 1632 buf = str(n) 1633 l = len(buf) 1634 self._writeInteger(l << 1 | REFERENCE_BIT) 1635 self.stream.write(buf)
1636
1637 - def writeXML(self, n, use_references=True):
1638 """ 1639 Writes a XML string to the data stream. 1640 1641 @type n: L{ET<util.ET>} 1642 @param n: XML Document to encode. 1643 @type use_references: C{bool} 1644 @param use_references: 1645 """ 1646 try: 1647 self.context.getLegacyXMLReference(n) 1648 is_legacy = True 1649 except pyamf.ReferenceError: 1650 is_legacy = False 1651 1652 if is_legacy is True: 1653 self.writeType(ASTypes.XML) 1654 else: 1655 self.writeType(ASTypes.XMLSTRING) 1656 1657 if use_references: 1658 try: 1659 ref = self.context.getObjectReference(n) 1660 self._writeInteger(ref << 1) 1661 1662 return 1663 except pyamf.ReferenceError: 1664 self.context.addObject(n) 1665 1666 self._writeString(util.ET.tostring(n, 'utf-8'), False)
1667
1668 -def decode(stream, context=None):
1669 """ 1670 A helper function to decode an AMF3 datastream. 1671 1672 @type stream: L{BufferedByteStream<util.BufferedByteStream>} 1673 @param stream: AMF3 data. 1674 @type context: L{Context} 1675 @param context: Context. 1676 """ 1677 decoder = Decoder(stream, context) 1678 1679 while 1: 1680 try: 1681 yield decoder.readElement() 1682 except pyamf.EOStream: 1683 break
1684
1685 -def encode(*args, **kwargs):
1686 """ 1687 A helper function to encode an element into AMF3 format. 1688 1689 @type args: List of args to encode. 1690 @param context: Any initial context to use. 1691 @type context: L{Context} 1692 @return: C{StringIO} type object containing the encoded AMF3 data. 1693 @rtype: L{util.BufferedByteStream} 1694 """ 1695 context = kwargs.get('context', None) 1696 buf = util.BufferedByteStream() 1697 encoder = Encoder(buf, context) 1698 1699 for element in args: 1700 encoder.writeElement(element) 1701 1702 return buf
1703
1704 -def _encode_int(n):
1705 if n & 0xf0000000 not in [0, 0xf0000000]: 1706 raise ValueError, "Out of range" 1707 1708 bytes = '' 1709 real_value = None 1710 1711 if n < 0: 1712 n += 0x20000000 1713 1714 if n > 0x1fffff: 1715 real_value = n 1716 n >>= 1 1717 bytes += chr(0x80 | ((n >> 21) & 0xff)) 1718 1719 if n > 0x3fff: 1720 bytes += chr(0x80 | ((n >> 14) & 0xff)) 1721 1722 if n > 0x7f: 1723 bytes += chr(0x80 | ((n >> 7) & 0xff)) 1724 1725 if real_value is not None: 1726 n = real_value 1727 1728 if n > 0x1fffff: 1729 bytes += chr(n & 0xff) 1730 else: 1731 bytes += chr(n & 0x7f) 1732 1733 return bytes
1734