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

Source Code for Module pyamf.amf0

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (c) 2007-2009 The PyAMF Project. 
  4  # See LICENSE.txt for details. 
  5   
  6  """ 
  7  AMF0 implementation. 
  8   
  9  C{AMF0} supports the basic data types used for the NetConnection, NetStream, 
 10  LocalConnection, SharedObjects and other classes in the Adobe Flash Player. 
 11   
 12  @see: U{Official AMF0 Specification in English (external) 
 13  <http://opensource.adobe.com/wiki/download/attachments/1114283/amf0_spec_121207.pdf>} 
 14  @see: U{Official AMF0 Specification in Japanese (external) 
 15  <http://opensource.adobe.com/wiki/download/attachments/1114283/JP_amf0_spec_121207.pdf>} 
 16  @see: U{AMF documentation on OSFlash (external) 
 17  <http://osflash.org/documentation/amf>} 
 18   
 19  @since: 0.1 
 20  """ 
 21   
 22  import datetime 
 23  import types 
 24  import copy 
 25   
 26  import pyamf 
 27  from pyamf import util 
 28   
 29   
 30  #: Represented as 9 bytes: 1 byte for C{0×00} and 8 bytes a double 
 31  #: representing the value of the number. 
 32  TYPE_NUMBER      = '\x00' 
 33  #: Represented as 2 bytes: 1 byte for C{0×01} and a second, C{0×00} 
 34  #: for C{False}, C{0×01} for C{True}. 
 35  TYPE_BOOL        = '\x01' 
 36  #: Represented as 3 bytes + len(String): 1 byte C{0×02}, then a UTF8 string, 
 37  #: including the top two bytes representing string length as a C{int}. 
 38  TYPE_STRING      = '\x02' 
 39  #: Represented as 1 byte, C{0×03}, then pairs of UTF8 string, the key, and 
 40  #: an AMF element, ended by three bytes, C{0×00} C{0×00} C{0×09}. 
 41  TYPE_OBJECT      = '\x03' 
 42  #: MovieClip does not seem to be supported by Remoting. 
 43  #: It may be used by other AMF clients such as SharedObjects. 
 44  TYPE_MOVIECLIP   = '\x04' 
 45  #: 1 single byte, C{0×05} indicates null. 
 46  TYPE_NULL        = '\x05' 
 47  #: 1 single byte, C{0×06} indicates null. 
 48  TYPE_UNDEFINED   = '\x06' 
 49  #: When an ActionScript object refers to itself, such C{this.self = this}, 
 50  #: or when objects are repeated within the same scope (for example, as the 
 51  #: two parameters of the same function called), a code of C{0×07} and an 
 52  #: C{int}, the reference number, are written. 
 53  TYPE_REFERENCE   = '\x07' 
 54  #: A MixedArray is indicated by code C{0×08}, then a Long representing the 
 55  #: highest numeric index in the array, or 0 if there are none or they are 
 56  #: all negative. After that follow the elements in key : value pairs. 
 57  TYPE_MIXEDARRAY  = '\x08' 
 58  #: @see: L{TYPE_OBJECT} 
 59  TYPE_OBJECTTERM  = '\x09' 
 60  #: An array is indicated by C{0x0A}, then a Long for array length, then the 
 61  #: array elements themselves. Arrays are always sparse; values for 
 62  #: inexistant keys are set to null (C{0×06}) to maintain sparsity. 
 63  TYPE_ARRAY       = '\x0A' 
 64  #: Date is represented as C{00x0B}, then a double, then an C{int}. The double 
 65  #: represents the number of milliseconds since 01/01/1970. The C{int} represents 
 66  #: the timezone offset in minutes between GMT. Note for the latter than values 
 67  #: greater than 720 (12 hours) are represented as M{2^16} - the value. Thus GMT+1 
 68  #: is 60 while GMT-5 is 65236. 
 69  TYPE_DATE        = '\x0B' 
 70  #: LongString is reserved for strings larger then M{2^16} characters long. It 
 71  #: is represented as C{00x0C} then a LongUTF. 
 72  TYPE_LONGSTRING  = '\x0C' 
 73  #: Trying to send values which don’t make sense, such as prototypes, functions, 
 74  #: built-in objects, etc. will be indicated by a single C{00x0D} byte. 
 75  TYPE_UNSUPPORTED = '\x0D' 
 76  #: Remoting Server -> Client only. 
 77  #: @see: L{RecordSet} 
 78  #: @see: U{RecordSet structure on OSFlash (external) 
 79  #: <http://osflash.org/documentation/amf/recordset>} 
 80  TYPE_RECORDSET   = '\x0E' 
 81  #: The XML element is indicated by C{00x0F} and followed by a LongUTF containing 
 82  #: the string representation of the XML object. The receiving gateway may which 
 83  #: to wrap this string inside a language-specific standard XML object, or simply 
 84  #: pass as a string. 
 85  TYPE_XML         = '\x0F' 
 86  #: A typed object is indicated by C{0×10}, then a UTF string indicating class 
 87  #: name, and then the same structure as a normal C{0×03} Object. The receiving 
 88  #: gateway may use a mapping scheme, or send back as a vanilla object or 
 89  #: associative array. 
 90  TYPE_TYPEDOBJECT = '\x10' 
 91  #: An AMF message sent from an AVM+ client such as the Flash Player 9 may break 
 92  #: out into L{AMF3<pyamf.amf3>} mode. In this case the next byte will be the 
 93  #: AMF3 type code and the data will be in AMF3 format until the decoded object 
 94  #: reaches it’s logical conclusion (for example, an object has no more keys). 
 95  TYPE_AMF3        = '\x11' 
 96   
 97   
98 -class Context(pyamf.BaseContext):
99 """ 100 I hold the AMF0 context for en/decoding streams. 101 102 AMF0 object references start at index 1. 103 104 @ivar amf3_objs: A list of objects that have been decoded in 105 L{AMF3<pyamf.amf3>}. 106 @type amf3_objs: L{util.IndexedCollection} 107 """ 108
109 - def __init__(self, **kwargs):
110 self.amf3_objs = [] 111 112 pyamf.BaseContext.__init__(self, **kwargs)
113
114 - def clear(self):
115 """ 116 Clears the context. 117 """ 118 pyamf.BaseContext.clear(self) 119 120 self.amf3_objs = [] 121 122 if hasattr(self, 'amf3_context'): 123 self.amf3_context.clear()
124
125 - def hasAMF3ObjectReference(self, obj):
126 """ 127 Gets a reference for an object. 128 129 @raise ReferenceError: Unknown AMF3 object reference. 130 """ 131 return obj in self.amf3_objs 132 o = self.amf3_objs.getReferenceTo(obj) 133 134 if o is None and self.exceptions: 135 raise pyamf.ReferenceError( 136 'Unknown AMF3 reference for (%r)' % (obj,)) 137 138 return o
139
140 - def addAMF3Object(self, obj):
141 """ 142 Adds an AMF3 reference to C{obj}. 143 144 @type obj: C{mixed} 145 @param obj: The object to add to the context. 146 @rtype: C{int} 147 @return: Reference to C{obj}. 148 """ 149 return self.amf3_objs.append(obj)
150
151 - def __copy__(self):
152 cpy = self.__class__(exceptions=self.exceptions) 153 cpy.amf3_objs = copy.copy(self.amf3_objs) 154 155 return cpy
156 157
158 -class Decoder(pyamf.BaseDecoder):
159 """ 160 Decodes an AMF0 stream. 161 """ 162 163 context_class = Context 164 165 # XXX nick: Do we need to support TYPE_MOVIECLIP here? 166 type_map = { 167 TYPE_NUMBER: 'readNumber', 168 TYPE_BOOL: 'readBoolean', 169 TYPE_STRING: 'readString', 170 TYPE_OBJECT: 'readObject', 171 TYPE_NULL: 'readNull', 172 TYPE_UNDEFINED: 'readUndefined', 173 TYPE_REFERENCE: 'readReference', 174 TYPE_MIXEDARRAY: 'readMixedArray', 175 TYPE_ARRAY: 'readList', 176 TYPE_DATE: 'readDate', 177 TYPE_LONGSTRING: 'readLongString', 178 # TODO: do we need a special value here? 179 TYPE_UNSUPPORTED:'readNull', 180 TYPE_XML: 'readXML', 181 TYPE_TYPEDOBJECT:'readTypedObject', 182 TYPE_AMF3: 'readAMF3' 183 } 184
185 - def readNumber(self):
186 """ 187 Reads a ActionScript C{Number} value. 188 189 In ActionScript 1 and 2 the C{NumberASTypes} type represents all numbers, 190 both floats and integers. 191 192 @rtype: C{int} or C{float} 193 """ 194 return _check_for_int(self.stream.read_double())
195
196 - def readBoolean(self):
197 """ 198 Reads a ActionScript C{Boolean} value. 199 200 @rtype: C{bool} 201 @return: Boolean. 202 """ 203 return bool(self.stream.read_uchar())
204
205 - def readNull(self):
206 """ 207 Reads a ActionScript C{null} value. 208 209 @return: C{None} 210 @rtype: C{None} 211 """ 212 return None
213
214 - def readUndefined(self):
215 """ 216 Reads an ActionScript C{undefined} value. 217 218 @return: L{Undefined<pyamf.Undefined>} 219 """ 220 return pyamf.Undefined
221
222 - def readMixedArray(self):
223 """ 224 Read mixed array. 225 226 @rtype: C{dict} 227 @return: C{dict} read from the stream 228 """ 229 len = self.stream.read_ulong() 230 obj = pyamf.MixedArray() 231 self.context.addObject(obj) 232 self._readObject(obj) 233 ikeys = [] 234 235 for key in obj.keys(): 236 try: 237 ikey = int(key) 238 ikeys.append((key, ikey)) 239 obj[ikey] = obj[key] 240 del obj[key] 241 except ValueError: 242 # XXX: do we want to ignore this? 243 pass 244 245 ikeys.sort() 246 247 return obj
248
249 - def readList(self):
250 """ 251 Read a C{list} from the data stream. 252 253 @rtype: C{list} 254 @return: C{list} 255 """ 256 obj = [] 257 self.context.addObject(obj) 258 len = self.stream.read_ulong() 259 260 for i in xrange(len): 261 obj.append(self.readElement()) 262 263 return obj
264
265 - def readTypedObject(self):
266 """ 267 Reads an ActionScript object from the stream and attempts to 268 'cast' it. 269 270 @see: L{load_class<pyamf.load_class>} 271 """ 272 classname = self.readString() 273 alias = None 274 275 try: 276 alias = pyamf.get_class_alias(classname) 277 278 ret = alias.createInstance(codec=self) 279 except pyamf.UnknownClassAlias: 280 if self.strict: 281 raise 282 283 ret = pyamf.TypedObject(classname) 284 285 self.context.addObject(ret) 286 self._readObject(ret, alias) 287 288 return ret
289
290 - def readAMF3(self):
291 """ 292 Read AMF3 elements from the data stream. 293 294 @rtype: C{mixed} 295 @return: The AMF3 element read from the stream 296 """ 297 if not hasattr(self.context, 'amf3_context'): 298 self.context.amf3_context = pyamf.get_context(pyamf.AMF3, exceptions=False) 299 300 if not hasattr(self.context, 'amf3_decoder'): 301 self.context.amf3_decoder = pyamf.get_decoder( 302 pyamf.AMF3, self.stream, self.context.amf3_context) 303 304 decoder = self.context.amf3_decoder 305 element = decoder.readElement() 306 self.context.addAMF3Object(element) 307 308 return element
309
310 - def readString(self):
311 """ 312 Reads a string from the data stream. 313 314 @rtype: C{str} 315 @return: string 316 """ 317 len = self.stream.read_ushort() 318 return self.stream.read_utf8_string(len)
319
320 - def _readObject(self, obj, alias=None):
321 obj_attrs = dict() 322 323 key = self.readString().encode('utf8') 324 325 while self.stream.peek() != TYPE_OBJECTTERM: 326 obj_attrs[key] = self.readElement() 327 key = self.readString().encode('utf8') 328 329 # discard the end marker (TYPE_OBJECTTERM) 330 self.stream.read(1) 331 332 if alias: 333 alias.applyAttributes(obj, obj_attrs, codec=self) 334 else: 335 util.set_attrs(obj, obj_attrs)
336
337 - def readObject(self):
338 """ 339 Reads an object from the data stream. 340 341 @rtype: L{ASObject<pyamf.ASObject>} 342 """ 343 obj = pyamf.ASObject() 344 self.context.addObject(obj) 345 346 self._readObject(obj) 347 348 return obj
349
350 - def readReference(self):
351 """ 352 Reads a reference from the data stream. 353 354 @raise pyamf.ReferenceError: Unknown reference. 355 """ 356 idx = self.stream.read_ushort() 357 358 o = self.context.getObject(idx) 359 360 if o is None: 361 raise pyamf.ReferenceError('Unknown reference %d' % (idx,)) 362 363 return o
364
365 - def readDate(self):
366 """ 367 Reads a UTC date from the data stream. Client and servers are 368 responsible for applying their own timezones. 369 370 Date: C{0x0B T7 T6} .. C{T0 Z1 Z2 T7} to C{T0} form a 64 bit 371 Big Endian number that specifies the number of nanoseconds 372 that have passed since 1/1/1970 0:00 to the specified time. 373 This format is UTC 1970. C{Z1} and C{Z0} for a 16 bit Big 374 Endian number indicating the indicated time's timezone in 375 minutes. 376 """ 377 ms = self.stream.read_double() / 1000.0 378 tz = self.stream.read_short() 379 380 # Timezones are ignored 381 d = util.get_datetime(ms) 382 383 if self.timezone_offset: 384 d = d + self.timezone_offset 385 386 self.context.addObject(d) 387 388 return d
389
390 - def readLongString(self):
391 """ 392 Read UTF8 string. 393 """ 394 len = self.stream.read_ulong() 395 396 return self.stream.read_utf8_string(len)
397
398 - def readXML(self):
399 """ 400 Read XML. 401 """ 402 data = self.readLongString() 403 xml = util.ET.fromstring(data) 404 self.context.addObject(xml) 405 406 return xml
407 408
409 -class Encoder(pyamf.BaseEncoder):
410 """ 411 Encodes an AMF0 stream. 412 413 @ivar use_amf3: A flag to determine whether this encoder knows about AMF3. 414 @type use_amf3: C{bool} 415 """ 416 417 context_class = Context 418 419 type_map = [ 420 ((types.BuiltinFunctionType, types.BuiltinMethodType, 421 types.FunctionType, types.GeneratorType, types.ModuleType, 422 types.LambdaType, types.MethodType), "writeFunc"), 423 ((types.NoneType,), "writeNull"), 424 ((bool,), "writeBoolean"), 425 ((int,long,float), "writeNumber"), 426 ((types.StringTypes,), "writeString"), 427 ((pyamf.ASObject,), "writeObject"), 428 ((pyamf.MixedArray,), "writeMixedArray"), 429 ((types.ListType, types.TupleType,), "writeArray"), 430 ((datetime.date, datetime.datetime, datetime.time), "writeDate"), 431 ((util.is_ET_element,), "writeXML"), 432 ((lambda x: x is pyamf.Undefined,), "writeUndefined"), 433 ((types.ClassType, types.TypeType), "writeClass"), 434 ((types.InstanceType,types.ObjectType,), "writeObject"), 435 ] 436
437 - def __init__(self, *args, **kwargs):
438 self.use_amf3 = kwargs.pop('use_amf3', False) 439 440 pyamf.BaseEncoder.__init__(self, *args, **kwargs)
441
442 - def writeType(self, t):
443 """ 444 Writes the type to the stream. 445 446 @type t: C{str} 447 @param t: ActionScript type. 448 449 @raise pyamf.EncodeError: AMF0 type is not recognized. 450 """ 451 self.stream.write(t)
452
453 - def writeUndefined(self, data):
454 """ 455 Writes the L{undefined<TYPE_UNDEFINED>} data type to the stream. 456 457 @param data: The C{undefined} data to be encoded to the AMF0 data 458 stream. 459 @type data: C{undefined} data 460 """ 461 self.writeType(TYPE_UNDEFINED)
462
463 - def writeClass(self, *args, **kwargs):
464 """ 465 Classes cannot be serialised. 466 """ 467 raise pyamf.EncodeError("Class objects cannot be serialised")
468
469 - def writeFunc(self, *args, **kwargs):
470 """ 471 Functions cannot be serialised. 472 """ 473 raise pyamf.EncodeError("Callables cannot be serialised")
474
475 - def writeUnsupported(self, data):
476 """ 477 Writes L{unsupported<TYPE_UNSUPPORTED>} data type to the 478 stream. 479 480 @param data: The C{unsupported} data to be encoded to the AMF0 481 data stream. 482 @type data: C{unsupported} data 483 """ 484 self.writeType(TYPE_UNSUPPORTED)
485
486 - def _writeElementFunc(self, data):
487 """ 488 Gets a function based on the type of data. 489 490 @see: L{pyamf.BaseEncoder._writeElementFunc} 491 """ 492 # There is a very specific use case that we must check for. 493 # In the context there is an array of amf3_objs that contain 494 # references to objects that are to be encoded in amf3. 495 496 if self.use_amf3 and self.context.hasAMF3ObjectReference(data): 497 return self.writeAMF3 498 499 return pyamf.BaseEncoder._writeElementFunc(self, data)
500
501 - def writeElement(self, data):
502 """ 503 Writes the data. 504 505 @type data: C{mixed} 506 @param data: The data to be encoded to the AMF0 data stream. 507 @raise EncodeError: Cannot find encoder func. 508 """ 509 func = self._writeElementFunc(data) 510 511 if func is None: 512 raise pyamf.EncodeError("Cannot find encoder func for %r" % (data,)) 513 514 func(data)
515
516 - def writeNull(self, n):
517 """ 518 Write null type to data stream. 519 520 @type n: C{None} 521 @param n: Is ignored. 522 """ 523 self.writeType(TYPE_NULL)
524
525 - def writeArray(self, a):
526 """ 527 Write array to the stream. 528 529 @type a: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 530 @param a: The array data to be encoded to the AMF0 data stream. 531 """ 532 alias = self.context.getClassAlias(a.__class__) 533 534 if alias.external: 535 # a is a subclassed list with a registered alias - push to the 536 # correct method 537 self.writeObject(a) 538 539 return 540 541 if self.writeReference(a) is not None: 542 return 543 544 self.context.addObject(a) 545 546 self.writeType(TYPE_ARRAY) 547 self.stream.write_ulong(len(a)) 548 549 for data in a: 550 self.writeElement(data)
551
552 - def writeNumber(self, n):
553 """ 554 Write number to the data stream. 555 556 @type n: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 557 @param n: The number data to be encoded to the AMF0 data stream. 558 """ 559 self.writeType(TYPE_NUMBER) 560 self.stream.write_double(float(n))
561
562 - def writeBoolean(self, b):
563 """ 564 Write boolean to the data stream. 565 566 @type b: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 567 @param b: The boolean data to be encoded to the AMF0 data stream. 568 """ 569 self.writeType(TYPE_BOOL) 570 571 if b: 572 self.stream.write_uchar(1) 573 else: 574 self.stream.write_uchar(0)
575
576 - def writeString(self, s, writeType=True):
577 """ 578 Write string to the data stream. 579 580 @type s: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 581 @param s: The string data to be encoded to the AMF0 data stream. 582 @type writeType: C{bool} 583 @param writeType: Write data type. 584 """ 585 t = type(s) 586 587 if t is str: 588 pass 589 elif isinstance(s, unicode): 590 s = s.encode('utf8') 591 elif not isinstance(s, basestring): 592 s = unicode(s).encode('utf8') 593 594 l = len(s) 595 596 if writeType: 597 if l > 0xffff: 598 self.writeType(TYPE_LONGSTRING) 599 else: 600 self.writeType(TYPE_STRING) 601 602 if l > 0xffff: 603 self.stream.write_ulong(l) 604 else: 605 self.stream.write_ushort(l) 606 607 self.stream.write(s)
608
609 - def writeReference(self, o):
610 """ 611 Write reference to the data stream. 612 613 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 614 @param o: The reference data to be encoded to the AMF0 data 615 stream. 616 """ 617 idx = self.context.getObjectReference(o) 618 619 if idx is None: 620 return None 621 622 self.writeType(TYPE_REFERENCE) 623 self.stream.write_ushort(idx) 624 625 return idx
626
627 - def _writeDict(self, o):
628 """ 629 Write C{dict} to the data stream. 630 631 @type o: C{iterable} 632 @param o: The C{dict} data to be encoded to the AMF0 data 633 stream. 634 """ 635 for key, val in o.iteritems(): 636 self.writeString(key, False) 637 self.writeElement(val)
638
639 - def writeMixedArray(self, o):
640 """ 641 Write mixed array to the data stream. 642 643 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 644 @param o: The mixed array data to be encoded to the AMF0 645 data stream. 646 """ 647 if self.writeReference(o) is not None: 648 return 649 650 self.context.addObject(o) 651 self.writeType(TYPE_MIXEDARRAY) 652 653 # TODO: optimise this 654 # work out the highest integer index 655 try: 656 # list comprehensions to save the day 657 max_index = max([y[0] for y in o.items() 658 if isinstance(y[0], (int, long))]) 659 660 if max_index < 0: 661 max_index = 0 662 except ValueError: 663 max_index = 0 664 665 self.stream.write_ulong(max_index) 666 667 self._writeDict(o) 668 self._writeEndObject()
669
670 - def _writeEndObject(self):
671 self.stream.write('\x00\x00') 672 self.writeType(TYPE_OBJECTTERM)
673
674 - def writeObject(self, o):
675 """ 676 Write object to the stream. 677 678 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 679 @param o: The object data to be encoded to the AMF0 data stream. 680 """ 681 if self.use_amf3: 682 self.writeAMF3(o) 683 684 return 685 686 if self.writeReference(o) is not None: 687 return 688 689 self.context.addObject(o) 690 alias = self.context.getClassAlias(o.__class__) 691 692 alias.compile() 693 694 if alias.amf3: 695 self.writeAMF3(o) 696 697 return 698 699 if alias.anonymous: 700 self.writeType(TYPE_OBJECT) 701 else: 702 self.writeType(TYPE_TYPEDOBJECT) 703 self.writeString(alias.alias, False) 704 705 sa, da = alias.getEncodableAttributes(o, codec=self) 706 707 if sa: 708 for key in alias.static_attrs: 709 self.writeString(key, False) 710 self.writeElement(sa[key]) 711 712 if da: 713 for key, value in da.iteritems(): 714 self.writeString(key, False) 715 self.writeElement(value) 716 717 self._writeEndObject()
718
719 - def writeDate(self, d):
720 """ 721 Writes a date to the data stream. 722 723 @type d: Instance of C{datetime.datetime} 724 @param d: The date to be encoded to the AMF0 data stream. 725 """ 726 if isinstance(d, datetime.time): 727 raise pyamf.EncodeError('A datetime.time instance was found but ' 728 'AMF0 has no way to encode time objects. Please use ' 729 'datetime.datetime instead (got:%r)' % (d,)) 730 731 # According to the Red5 implementation of AMF0, dates references are 732 # created, but not used. 733 if self.timezone_offset is not None: 734 d -= self.timezone_offset 735 736 secs = util.get_timestamp(d) 737 tz = 0 738 739 self.writeType(TYPE_DATE) 740 self.stream.write_double(secs * 1000.0) 741 self.stream.write_short(tz)
742
743 - def writeXML(self, e):
744 """ 745 Write XML to the data stream. 746 747 @type e: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 748 @param e: The XML data to be encoded to the AMF0 data stream. 749 """ 750 if self.use_amf3 is True: 751 self.writeAMF3(e) 752 753 return 754 755 self.writeType(TYPE_XML) 756 757 data = util.ET.tostring(e, 'utf-8') 758 self.stream.write_ulong(len(data)) 759 self.stream.write(data)
760
761 - def writeAMF3(self, data):
762 """ 763 Writes an element to the datastream in L{AMF3<pyamf.amf3>} format. 764 765 @type data: C{mixed} 766 @param data: The data to be encoded to the AMF0 data stream. 767 """ 768 if not hasattr(self.context, 'amf3_context'): 769 self.context.amf3_context = pyamf.get_context(pyamf.AMF3, exceptions=False) 770 771 if not hasattr(self.context, 'amf3_encoder'): 772 self.context.amf3_encoder = pyamf.get_encoder( 773 pyamf.AMF3, self.stream, self.context.amf3_context) 774 775 self.context.addAMF3Object(data) 776 encoder = self.context.amf3_encoder 777 778 self.writeType(TYPE_AMF3) 779 encoder.writeElement(data)
780 781
782 -def decode(*args, **kwargs):
783 """ 784 A helper function to decode an AMF0 datastream. 785 """ 786 decoder = Decoder(*args, **kwargs) 787 788 while 1: 789 try: 790 yield decoder.readElement() 791 except pyamf.EOStream: 792 break
793 794
795 -def encode(*args, **kwargs):
796 """ 797 A helper function to encode an element into the AMF0 format. 798 799 @type element: C{mixed} 800 @keyword element: The element to encode 801 @type context: L{Context<pyamf.amf0.Context>} 802 @keyword context: AMF0 C{Context} to use for the encoding. This holds 803 previously referenced objects etc. 804 @rtype: C{StringIO} 805 @return: The encoded stream. 806 """ 807 encoder = Encoder(**kwargs) 808 809 for element in args: 810 encoder.writeElement(element) 811 812 return encoder.stream
813 814
815 -class RecordSet(object):
816 """ 817 I represent the C{RecordSet} class used in Adobe Flash Remoting to hold 818 (amongst other things) SQL records. 819 820 @ivar columns: The columns to send. 821 @type columns: List of strings. 822 @ivar items: The C{RecordSet} data. 823 @type items: List of lists, the order of the data corresponds to the order 824 of the columns. 825 @ivar service: Service linked to the C{RecordSet}. 826 @type service: 827 @ivar id: The id of the C{RecordSet}. 828 @type id: C{str} 829 830 @see: U{RecordSet on OSFlash (external) 831 <http://osflash.org/documentation/amf/recordset>} 832 """ 833
834 - class __amf__:
835 alias = 'RecordSet' 836 static = ('serverInfo',) 837 dynamic = False
838
839 - def __init__(self, columns=[], items=[], service=None, id=None):
840 self.columns = columns 841 self.items = items 842 self.service = service 843 self.id = id
844
845 - def _get_server_info(self):
846 ret = pyamf.ASObject(totalCount=len(self.items), cursor=1, version=1, 847 initialData=self.items, columnNames=self.columns) 848 849 if self.service is not None: 850 ret.update({'serviceName': str(self.service['name'])}) 851 852 if self.id is not None: 853 ret.update({'id':str(self.id)}) 854 855 return ret
856
857 - def _set_server_info(self, val):
858 self.columns = val['columnNames'] 859 self.items = val['initialData'] 860 861 try: 862 # TODO nick: find relevant service and link in here. 863 self.service = dict(name=val['serviceName']) 864 except KeyError: 865 self.service = None 866 867 try: 868 self.id = val['id'] 869 except KeyError: 870 self.id = None
871 872 serverInfo = property(_get_server_info, _set_server_info) 873
874 - def __repr__(self):
875 ret = '<%s.%s object' % (self.__module__, self.__class__.__name__) 876 877 if self.id is not None: 878 ret += ' id=%s' % self.id 879 880 if self.service is not None: 881 ret += ' service=%s' % self.service 882 883 ret += ' at 0x%x>' % id(self) 884 885 return ret
886 887 pyamf.register_class(RecordSet) 888 889
890 -def _check_for_int(x):
891 """ 892 This is a compatibility function that takes a C{float} and converts it to an 893 C{int} if the values are equal. 894 """ 895 try: 896 y = int(x) 897 except (OverflowError, ValueError): 898 pass 899 else: 900 # There is no way in AMF0 to distinguish between integers and floats 901 if x == x and y == x: 902 return y 903 904 return x
905 906 # check for some Python 2.3 problems with floats 907 try: 908 float('nan') 909 except ValueError: 910 pass 911 else: 912 if float('nan') == 0:
913 - def check_nan(func):
914 def f2(x): 915 if str(x).lower().find('nan') >= 0: 916 return x 917 918 return f2.func(x)
919 f2.func = func 920 921 return f2 922 923 _check_for_int = check_nan(_check_for_int) 924