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

Source Code for Package pyamf.util

   1  # -*- coding: utf-8 -*- 
   2  # 
   3  # Copyright (c) 2007-2009 The PyAMF Project. 
   4  # See LICENSE.txt for details. 
   5   
   6  """ 
   7  AMF Utilities. 
   8   
   9  @since: 0.1.0 
  10  """ 
  11   
  12  import struct 
  13  import calendar 
  14  import datetime 
  15  import types 
  16  import inspect 
  17   
  18  import pyamf 
  19   
  20  try: 
  21      from cStringIO import StringIO 
  22  except ImportError: 
  23      from StringIO import StringIO 
  24   
  25  try: 
  26      set 
  27  except NameError: 
  28      from sets import Set as set 
  29   
  30   
  31  #: XML types. 
  32  xml_types = None 
  33  ET = None 
  34  #: On some Python versions retrieving a negative timestamp, like 
  35  #: C{datetime.datetime.utcfromtimestamp(-31536000.0)} is broken. 
  36  negative_timestamp_broken = False 
  37   
  38  int_types = [int] 
  39  str_types = [str] 
  40   
  41  # py3k support 
  42  try: 
  43      int_types.append(long) 
  44  except NameError: 
  45      pass 
  46   
  47  try: 
  48      str_types.append(unicode) 
  49  except NameError: 
  50      pass 
  51   
  52  #: Numeric types. 
  53  int_types = tuple(int_types) 
  54  #: String types. 
  55  str_types = tuple(str_types) 
  56   
  57  PosInf = 1e300000 
  58  NegInf = -1e300000 
  59  # we do this instead of float('nan') because windows throws a wobbler. 
  60  NaN = PosInf / PosInf 
  61   
  62   
63 -def find_xml_lib():
64 """ 65 Run through a predefined order looking through the various C{ElementTree} 66 implementations so that any type can be encoded but PyAMF will return 67 elements as the first implementation found. 68 69 We work through the C implementations first - then the pure Python 70 versions. The downside to this is that a possible of three libraries will 71 be loaded into memory that are not used but the libs are small 72 (relatively) and the flexibility that this gives seems to outweigh the 73 cost. Time will tell. 74 75 @since: 0.4 76 """ 77 global xml_types, ET 78 79 xml_types = [] 80 81 try: 82 import xml.etree.cElementTree as cET 83 84 ET = cET 85 xml_types.append(type(cET.Element('foo'))) 86 except ImportError: 87 pass 88 89 try: 90 import cElementTree as cET 91 92 if ET is None: 93 ET = cET 94 95 xml_types.append(type(cET.Element('foo'))) 96 except ImportError: 97 pass 98 99 try: 100 import xml.etree.ElementTree as pET 101 102 if ET is None: 103 ET = pET 104 105 xml_types.append(pET._ElementInterface) 106 except ImportError: 107 pass 108 109 try: 110 import elementtree.ElementTree as pET 111 112 if ET is None: 113 ET = pET 114 115 xml_types.append(pET._ElementInterface) 116 except ImportError: 117 pass 118 119 for x in xml_types[:]: 120 # hack for jython 121 if x.__name__ == 'instance': 122 xml_types.remove(x) 123 124 xml_types = tuple(xml_types) 125 126 return xml_types
127 128
129 -class StringIOProxy(object):
130 """ 131 I am a C{StringIO} type object containing byte data from the AMF stream. 132 133 @see: U{ByteArray on OSFlash (external) 134 <http://osflash.org/documentation/amf3#x0c_-_bytearray>} 135 @see: U{Parsing ByteArrays on OSFlash (external) 136 <http://osflash.org/documentation/amf3/parsing_byte_arrays>} 137 """ 138 139 _wrapped_class = StringIO 140
141 - def __init__(self, buf=None):
142 """ 143 @raise TypeError: Unable to coerce C{buf} to C{StringIO}. 144 """ 145 self._buffer = StringIOProxy._wrapped_class() 146 147 if isinstance(buf, (str, unicode)): 148 self._buffer.write(buf) 149 elif hasattr(buf, 'getvalue'): 150 self._buffer.write(buf.getvalue()) 151 elif hasattr(buf, 'read') and hasattr(buf, 'seek') and hasattr(buf, 'tell'): 152 old_pos = buf.tell() 153 buf.seek(0) 154 self._buffer.write(buf.read()) 155 buf.seek(old_pos) 156 elif buf is None: 157 pass 158 else: 159 raise TypeError("Unable to coerce buf->StringIO") 160 161 self._get_len() 162 self._len_changed = False 163 self._buffer.seek(0, 0)
164
165 - def getvalue(self):
166 """ 167 Get raw data from buffer. 168 """ 169 return self._buffer.getvalue()
170
171 - def read(self, n=-1):
172 """ 173 Reads C{n} bytes from the stream. 174 """ 175 bytes = self._buffer.read(n) 176 177 return bytes
178
179 - def seek(self, pos, mode=0):
180 """ 181 Sets the file-pointer offset, measured from the beginning of this stream, 182 at which the next write operation will occur. 183 184 @param pos: 185 @type pos: C{int} 186 @param mode: 187 @type mode: C{int} 188 """ 189 return self._buffer.seek(pos, mode)
190
191 - def tell(self):
192 """ 193 Returns the position of the stream pointer. 194 """ 195 return self._buffer.tell()
196
197 - def truncate(self, size=0):
198 """ 199 Truncates the stream to the specified length. 200 201 @param size: The length of the stream, in bytes. 202 @type size: C{int} 203 """ 204 if size == 0: 205 self._buffer = StringIOProxy._wrapped_class() 206 self._len_changed = True 207 208 return 209 210 cur_pos = self.tell() 211 self.seek(0) 212 buf = self.read(size) 213 self._buffer = StringIOProxy._wrapped_class() 214 215 self._buffer.write(buf) 216 self.seek(cur_pos) 217 self._len_changed = True
218
219 - def write(self, s):
220 """ 221 Writes the content of the specified C{s} into this buffer. 222 223 @param s: 224 @type s: 225 """ 226 self._buffer.write(s) 227 self._len_changed = True
228
229 - def _get_len(self):
230 """ 231 Return total number of bytes in buffer. 232 """ 233 if hasattr(self._buffer, 'len'): 234 self._len = self._buffer.len 235 236 return 237 238 old_pos = self._buffer.tell() 239 self._buffer.seek(0, 2) 240 241 self._len = self._buffer.tell() 242 self._buffer.seek(old_pos)
243
244 - def __len__(self):
245 if not self._len_changed: 246 return self._len 247 248 self._get_len() 249 self._len_changed = False 250 251 return self._len
252
253 - def consume(self):
254 """ 255 Chops the tail off the stream starting at 0 and ending at C{tell()}. 256 The stream pointer is set to 0 at the end of this function. 257 258 @since: 0.4 259 """ 260 try: 261 bytes = self.read() 262 except IOError: 263 bytes = '' 264 265 self.truncate() 266 267 if len(bytes) > 0: 268 self.write(bytes) 269 self.seek(0)
270 271
272 -class DataTypeMixIn(object):
273 """ 274 Provides methods for reading and writing basic data types for file-like 275 objects. 276 277 @ivar endian: Byte ordering used to represent the data. Default byte order 278 is L{ENDIAN_NETWORK}. 279 @type endian: C{str} 280 """ 281 282 #: Network byte order 283 ENDIAN_NETWORK = "!" 284 #: Native byte order 285 ENDIAN_NATIVE = "@" 286 #: Little endian 287 ENDIAN_LITTLE = "<" 288 #: Big endian 289 ENDIAN_BIG = ">" 290 291 endian = ENDIAN_NETWORK 292
293 - def _read(self, length):
294 """ 295 Reads C{length} bytes from the stream. If an attempt to read past the 296 end of the buffer is made, L{IOError} is raised. 297 """ 298 bytes = self.read(length) 299 300 if len(bytes) != length: 301 self.seek(0 - len(bytes), 1) 302 303 raise IOError("Tried to read %d byte(s) from the stream" % length) 304 305 return bytes
306
307 - def _is_big_endian(self):
308 """ 309 Whether this system is big endian or not. 310 311 @rtype: C{bool} 312 """ 313 if self.endian == DataTypeMixIn.ENDIAN_NATIVE: 314 return DataTypeMixIn._system_endian == DataTypeMixIn.ENDIAN_BIG 315 316 return self.endian in (DataTypeMixIn.ENDIAN_BIG, DataTypeMixIn.ENDIAN_NETWORK)
317
318 - def read_uchar(self):
319 """ 320 Reads an C{unsigned char} from the stream. 321 """ 322 return struct.unpack("B", self._read(1))[0]
323
324 - def write_uchar(self, c):
325 """ 326 Writes an C{unsigned char} to the stream. 327 328 @param c: Unsigned char 329 @type c: C{int} 330 @raise TypeError: Unexpected type for int C{c}. 331 @raise OverflowError: Not in range. 332 """ 333 if type(c) not in int_types: 334 raise TypeError('expected an int (got:%r)' % (type(c),)) 335 336 if not 0 <= c <= 255: 337 raise OverflowError("Not in range, %d" % c) 338 339 self.write(struct.pack("B", c))
340
341 - def read_char(self):
342 """ 343 Reads a C{char} from the stream. 344 """ 345 return struct.unpack("b", self._read(1))[0]
346
347 - def write_char(self, c):
348 """ 349 Write a C{char} to the stream. 350 351 @param c: char 352 @type c: C{int} 353 @raise TypeError: Unexpected type for int C{c}. 354 @raise OverflowError: Not in range. 355 """ 356 if type(c) not in int_types: 357 raise TypeError('expected an int (got:%r)' % (type(c),)) 358 359 if not -128 <= c <= 127: 360 raise OverflowError("Not in range, %d" % c) 361 362 self.write(struct.pack("b", c))
363
364 - def read_ushort(self):
365 """ 366 Reads a 2 byte unsigned integer from the stream. 367 """ 368 return struct.unpack("%sH" % self.endian, self._read(2))[0]
369
370 - def write_ushort(self, s):
371 """ 372 Writes a 2 byte unsigned integer to the stream. 373 374 @param s: 2 byte unsigned integer 375 @type s: C{int} 376 @raise TypeError: Unexpected type for int C{s}. 377 @raise OverflowError: Not in range. 378 """ 379 if type(s) not in int_types: 380 raise TypeError('expected an int (got:%r)' % (type(s),)) 381 382 if not 0 <= s <= 65535: 383 raise OverflowError("Not in range, %d" % s) 384 385 self.write(struct.pack("%sH" % self.endian, s))
386
387 - def read_short(self):
388 """ 389 Reads a 2 byte integer from the stream. 390 """ 391 return struct.unpack("%sh" % self.endian, self._read(2))[0]
392
393 - def write_short(self, s):
394 """ 395 Writes a 2 byte integer to the stream. 396 397 @param s: 2 byte integer 398 @type s: C{int} 399 @raise TypeError: Unexpected type for int C{s}. 400 @raise OverflowError: Not in range. 401 """ 402 if type(s) not in int_types: 403 raise TypeError('expected an int (got:%r)' % (type(s),)) 404 405 if not -32768 <= s <= 32767: 406 raise OverflowError("Not in range, %d" % s) 407 408 self.write(struct.pack("%sh" % self.endian, s))
409
410 - def read_ulong(self):
411 """ 412 Reads a 4 byte unsigned integer from the stream. 413 """ 414 return struct.unpack("%sL" % self.endian, self._read(4))[0]
415
416 - def write_ulong(self, l):
417 """ 418 Writes a 4 byte unsigned integer to the stream. 419 420 @param l: 4 byte unsigned integer 421 @type l: C{int} 422 @raise TypeError: Unexpected type for int C{l}. 423 @raise OverflowError: Not in range. 424 """ 425 if type(l) not in int_types: 426 raise TypeError('expected an int (got:%r)' % (type(l),)) 427 428 if not 0 <= l <= 4294967295: 429 raise OverflowError("Not in range, %d" % l) 430 431 self.write(struct.pack("%sL" % self.endian, l))
432
433 - def read_long(self):
434 """ 435 Reads a 4 byte integer from the stream. 436 """ 437 return struct.unpack("%sl" % self.endian, self._read(4))[0]
438
439 - def write_long(self, l):
440 """ 441 Writes a 4 byte integer to the stream. 442 443 @param l: 4 byte integer 444 @type l: C{int} 445 @raise TypeError: Unexpected type for int C{l}. 446 @raise OverflowError: Not in range. 447 """ 448 if type(l) not in int_types: 449 raise TypeError('expected an int (got:%r)' % (type(l),)) 450 451 if not -2147483648 <= l <= 2147483647: 452 raise OverflowError("Not in range, %d" % l) 453 454 self.write(struct.pack("%sl" % self.endian, l))
455
456 - def read_24bit_uint(self):
457 """ 458 Reads a 24 bit unsigned integer from the stream. 459 460 @since: 0.4 461 """ 462 order = None 463 464 if not self._is_big_endian(): 465 order = [0, 8, 16] 466 else: 467 order = [16, 8, 0] 468 469 n = 0 470 471 for x in order: 472 n += (self.read_uchar() << x) 473 474 return n
475
476 - def write_24bit_uint(self, n):
477 """ 478 Writes a 24 bit unsigned integer to the stream. 479 480 @since: 0.4 481 @param n: 24 bit unsigned integer 482 @type n: C{int} 483 @raise TypeError: Unexpected type for int C{n}. 484 @raise OverflowError: Not in range. 485 """ 486 if type(n) not in int_types: 487 raise TypeError('expected an int (got:%r)' % (type(n),)) 488 489 if not 0 <= n <= 0xffffff: 490 raise OverflowError("n is out of range") 491 492 order = None 493 494 if not self._is_big_endian(): 495 order = [0, 8, 16] 496 else: 497 order = [16, 8, 0] 498 499 for x in order: 500 self.write_uchar((n >> x) & 0xff)
501
502 - def read_24bit_int(self):
503 """ 504 Reads a 24 bit integer from the stream. 505 506 @since: 0.4 507 """ 508 n = self.read_24bit_uint() 509 510 if n & 0x800000 != 0: 511 # the int is signed 512 n -= 0x1000000 513 514 return n
515
516 - def write_24bit_int(self, n):
517 """ 518 Writes a 24 bit integer to the stream. 519 520 @since: 0.4 521 @param n: 24 bit integer 522 @type n: C{int} 523 @raise TypeError: Unexpected type for int C{n}. 524 @raise OverflowError: Not in range. 525 """ 526 if type(n) not in int_types: 527 raise TypeError('expected an int (got:%r)' % (type(n),)) 528 529 if not -8388608 <= n <= 8388607: 530 raise OverflowError("n is out of range") 531 532 order = None 533 534 if not self._is_big_endian(): 535 order = [0, 8, 16] 536 else: 537 order = [16, 8, 0] 538 539 if n < 0: 540 n += 0x1000000 541 542 for x in order: 543 self.write_uchar((n >> x) & 0xff)
544
545 - def read_double(self):
546 """ 547 Reads an 8 byte float from the stream. 548 """ 549 return struct.unpack("%sd" % self.endian, self._read(8))[0]
550
551 - def write_double(self, d):
552 """ 553 Writes an 8 byte float to the stream. 554 555 @param d: 8 byte float 556 @type d: C{float} 557 @raise TypeError: Unexpected type for float C{d}. 558 """ 559 if not type(d) is float: 560 raise TypeError('expected a float (got:%r)' % (type(d),)) 561 562 self.write(struct.pack("%sd" % self.endian, d))
563
564 - def read_float(self):
565 """ 566 Reads a 4 byte float from the stream. 567 """ 568 return struct.unpack("%sf" % self.endian, self._read(4))[0]
569
570 - def write_float(self, f):
571 """ 572 Writes a 4 byte float to the stream. 573 574 @param f: 4 byte float 575 @type f: C{float} 576 @raise TypeError: Unexpected type for float C{f}. 577 """ 578 if type(f) is not float: 579 raise TypeError('expected a float (got:%r)' % (type(f),)) 580 581 self.write(struct.pack("%sf" % self.endian, f))
582
583 - def read_utf8_string(self, length):
584 """ 585 Reads a UTF-8 string from the stream. 586 587 @rtype: C{unicode} 588 """ 589 str = struct.unpack("%s%ds" % (self.endian, length), self.read(length))[0] 590 591 return unicode(str, "utf8")
592
593 - def write_utf8_string(self, u):
594 """ 595 Writes a unicode object to the stream in UTF-8. 596 597 @param u: unicode object 598 @raise TypeError: Unexpected type for str C{u}. 599 """ 600 if type(u) not in str_types: 601 raise TypeError('expected a str (got:%r)' % (type(u),)) 602 603 bytes = u.encode("utf8") 604 605 self.write(struct.pack("%s%ds" % (self.endian, len(bytes)), bytes))
606 607 608 if struct.pack('@H', 1)[0] == '\x01': 609 DataTypeMixIn._system_endian = DataTypeMixIn.ENDIAN_LITTLE 610 else: 611 DataTypeMixIn._system_endian = DataTypeMixIn.ENDIAN_BIG 612 613
614 -class BufferedByteStream(StringIOProxy, DataTypeMixIn):
615 """ 616 An extension of C{StringIO}. 617 618 Features: 619 - Raises L{IOError} if reading past end. 620 - Allows you to C{peek()} at the next byte. 621 622 @see: L{cBufferedByteStream<cpyamf.util.cBufferedByteStream>} 623 """ 624
625 - def __init__(self, buf=None):
626 """ 627 @param buf: Initial byte stream. 628 @type buf: C{str} or C{StringIO} instance 629 """ 630 StringIOProxy.__init__(self, buf=buf) 631 632 self.seek(0)
633
634 - def read(self, length=-1):
635 """ 636 Reads up to the specified number of bytes from the stream into 637 the specified byte array of specified length. 638 639 @raise IOError: Attempted to read past the end of the buffer. 640 """ 641 if length == -1 and self.at_eof(): 642 raise IOError('Attempted to read from the buffer but already at ' 643 'the end') 644 elif length > 0 and self.tell() + length > len(self): 645 raise IOError('Attempted to read %d bytes from the buffer but ' 646 'only %d remain' % (length, len(self) - self.tell())) 647 648 return StringIOProxy.read(self, length)
649
650 - def peek(self, size=1):
651 """ 652 Looks C{size} bytes ahead in the stream, returning what it finds, 653 returning the stream pointer to its initial position. 654 655 @param size: Default is 1. 656 @type size: C{int} 657 @raise ValueError: Trying to peek backwards. 658 659 @rtype: 660 @return: Bytes. 661 """ 662 if size == -1: 663 return self.peek(len(self) - self.tell()) 664 665 if size < -1: 666 raise ValueError("Cannot peek backwards") 667 668 bytes = '' 669 pos = self.tell() 670 671 while not self.at_eof() and len(bytes) != size: 672 bytes += self.read(1) 673 674 self.seek(pos) 675 676 return bytes
677
678 - def remaining(self):
679 """ 680 Returns number of remaining bytes. 681 682 @rtype: C{number} 683 @return: Number of remaining bytes. 684 """ 685 return len(self) - self.tell()
686
687 - def at_eof(self):
688 """ 689 Returns C{True} if the internal pointer is at the end of the stream. 690 691 @rtype: C{bool} 692 """ 693 return self.tell() == len(self)
694
695 - def append(self, data):
696 """ 697 Append data to the end of the stream. The pointer will not move if 698 this operation is successful. 699 700 @param data: The data to append to the stream. 701 @type data: C{str} or C{unicode} 702 @raise TypeError: data is not C{str} or C{unicode} 703 """ 704 t = self.tell() 705 706 # seek to the end of the stream 707 self.seek(0, 2) 708 709 if hasattr(data, 'getvalue'): 710 self.write_utf8_string(data.getvalue()) 711 else: 712 self.write_utf8_string(data) 713 714 self.seek(t)
715
716 - def __add__(self, other):
717 old_pos = self.tell() 718 old_other_pos = other.tell() 719 720 new = BufferedByteStream(self) 721 722 other.seek(0) 723 new.seek(0, 2) 724 new.write(other.read()) 725 726 self.seek(old_pos) 727 other.seek(old_other_pos) 728 new.seek(0) 729 730 return new
731 732
733 -def hexdump(data):
734 """ 735 Get hexadecimal representation of C{StringIO} data. 736 737 @type data: 738 @param data: 739 @rtype: C{str} 740 @return: Hexadecimal string. 741 """ 742 import string 743 744 hex = ascii = buf = "" 745 index = 0 746 747 for c in data: 748 hex += "%02x " % ord(c) 749 if c in string.printable and c not in string.whitespace: 750 ascii += c 751 else: 752 ascii += "." 753 754 if len(ascii) == 16: 755 buf += "%04x: %s %s %s\n" % (index, hex[:24], hex[24:], ascii) 756 hex = ascii = "" 757 index += 16 758 759 if len(ascii): 760 buf += "%04x: %-24s %-24s %s\n" % (index, hex[:24], hex[24:], ascii) 761 762 return buf
763 764
765 -def get_timestamp(d):
766 """ 767 Returns a UTC timestamp for a C{datetime.datetime} object. 768 769 @type d: C{datetime.datetime} 770 @param d: The date object. 771 @return: UTC timestamp. 772 @rtype: C{str} 773 774 @note: Inspiration taken from the U{Intertwingly blog 775 <http://intertwingly.net/blog/2007/09/02/Dealing-With-Dates>}. 776 """ 777 if isinstance(d, datetime.date) and not isinstance(d, datetime.datetime): 778 d = datetime.datetime.combine(d, datetime.time(0, 0, 0, 0)) 779 780 msec = str(d.microsecond).rjust(6).replace(' ', '0') 781 782 return float('%s.%s' % (calendar.timegm(d.utctimetuple()), msec))
783 784
785 -def get_datetime(secs):
786 """ 787 Return a UTC date from a timestamp. 788 789 @type secs: C{long} 790 @param secs: Seconds since 1970. 791 @return: UTC timestamp. 792 @rtype: C{datetime.datetime} 793 """ 794 if secs < 0 and negative_timestamp_broken: 795 return datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=secs) 796 797 return datetime.datetime.utcfromtimestamp(secs)
798 799
800 -def get_properties(obj):
801 """ 802 @since: 0.5 803 """ 804 if hasattr(obj, 'keys'): 805 return set(obj.keys()) 806 elif hasattr(obj, '__dict__'): 807 return obj.__dict__.keys() 808 809 return []
810 811
812 -def get_attrs(obj):
813 """ 814 Gets a C{dict} of the attrs of an object in a predefined resolution order. 815 816 @raise AttributeError: A duplicate attribute was already found in this 817 collection, are you mixing different key types? 818 """ 819 if hasattr(obj, 'iteritems'): 820 attrs = {} 821 822 for k, v in obj.iteritems(): 823 sk = str(k) 824 825 if sk in attrs.keys(): 826 raise AttributeError('A duplicate attribute (%s) was ' 827 'already found in this collection, are you mixing ' 828 'different key types?' % (sk,)) 829 830 attrs[sk] = v 831 832 return attrs 833 elif hasattr(obj, '__dict__'): 834 return obj.__dict__.copy() 835 elif hasattr(obj, '__slots__'): 836 attrs = {} 837 838 for k in obj.__slots__: 839 attrs[k] = getattr(obj, k) 840 841 return attrs 842 843 return None
844 845
846 -def set_attrs(obj, attrs):
847 """ 848 A generic function which applies a collection of attributes C{attrs} to 849 object C{obj}. 850 851 @param obj: An instance implementing the C{__setattr__} function 852 @param attrs: A collection implementing the C{iteritems} function 853 @type attrs: Usually a dict 854 """ 855 if isinstance(obj, (list, dict)): 856 for k, v in attrs.iteritems(): 857 obj[k] = v 858 859 return 860 861 for k, v in attrs.iteritems(): 862 setattr(obj, k, v)
863 864
865 -def get_class_alias(klass):
866 """ 867 Returns a alias class suitable for klass. Defaults to L{pyamf.ClassAlias} 868 """ 869 for k, v in pyamf.ALIAS_TYPES.iteritems(): 870 for kl in v: 871 if isinstance(kl, types.FunctionType): 872 if kl(klass) is True: 873 return k 874 elif isinstance(kl, (type, (types.ClassType, types.ObjectType))): 875 if issubclass(klass, kl): 876 return k 877 878 return pyamf.ClassAlias
879 880
881 -def is_class_sealed(klass):
882 """ 883 Returns a C{boolean} whether or not the supplied class can accept dynamic 884 properties. 885 886 @rtype: C{bool} 887 @since: 0.5 888 """ 889 mro = inspect.getmro(klass) 890 new = False 891 892 if mro[-1] is object: 893 mro = mro[:-1] 894 new = True 895 896 for kls in mro: 897 if new and '__dict__' in kls.__dict__: 898 return False 899 900 if not hasattr(kls, '__slots__'): 901 return False 902 903 return True
904 905
906 -def get_class_meta(klass):
907 """ 908 Returns a C{dict} containing meta data based on the supplied class, useful 909 for class aliasing. 910 911 @rtype: C{dict} 912 @since: 0.5 913 """ 914 if not isinstance(klass, (type, types.ClassType)) or klass is object: 915 raise TypeError('klass must be a class object, got %r' % type(klass)) 916 917 meta = { 918 'static_attrs': None, 919 'exclude_attrs': None, 920 'readonly_attrs': None, 921 'amf3': None, 922 'dynamic': None, 923 'alias': None, 924 'external': None 925 } 926 927 if not hasattr(klass, '__amf__'): 928 return meta 929 930 a = klass.__amf__ 931 932 if type(a) is dict: 933 in_func = lambda x: x in a 934 get_func = a.__getitem__ 935 else: 936 in_func = lambda x: hasattr(a, x) 937 get_func = lambda x: getattr(a, x) 938 939 for prop in ['alias', 'amf3', 'dynamic', 'external']: 940 if in_func(prop): 941 meta[prop] = get_func(prop) 942 943 for prop in ['static', 'exclude', 'readonly']: 944 if in_func(prop): 945 meta[prop + '_attrs'] = list(get_func(prop)) 946 947 return meta
948 949
950 -class IndexedCollection(object):
951 """ 952 A class that provides a quick and clean way to store references and 953 referenced objects. 954 955 @note: All attributes on the instance are private. 956 @ivar exceptions: If C{True} then L{ReferenceError<pyamf.ReferenceError>} 957 will be raised, otherwise C{None} will be returned. 958 """ 959
960 - def __init__(self, use_hash=False, exceptions=True):
961 if use_hash is True: 962 self.func = hash 963 else: 964 self.func = id 965 966 self.exceptions = exceptions 967 968 self.clear()
969
970 - def clear(self):
971 """ 972 Clears the index. 973 """ 974 self.list = [] 975 self.dict = {}
976
977 - def getByReference(self, ref):
978 """ 979 Returns an object based on the reference. 980 981 @raise TypeError: Bad reference type. 982 @raise pyamf.ReferenceError: Reference not found. 983 """ 984 if not isinstance(ref, (int, long)): 985 raise TypeError("Bad reference type") 986 987 try: 988 return self.list[ref] 989 except IndexError: 990 if self.exceptions is False: 991 return None 992 993 raise pyamf.ReferenceError("Reference %r not found" % (ref,))
994
995 - def getReferenceTo(self, obj):
996 """ 997 Returns a reference to C{obj} if it is contained within this index. 998 999 @raise pyamf.ReferenceError: Value not found. 1000 """ 1001 try: 1002 return self.dict[self.func(obj)] 1003 except KeyError: 1004 if self.exceptions is False: 1005 return None 1006 1007 raise pyamf.ReferenceError("Value %r not found" % (obj,))
1008
1009 - def append(self, obj):
1010 """ 1011 Appends C{obj} to this index. 1012 1013 @note: Uniqueness is not checked 1014 @return: The reference to C{obj} in this index. 1015 """ 1016 h = self.func(obj) 1017 1018 self.list.append(obj) 1019 idx = len(self.list) - 1 1020 self.dict[h] = idx 1021 1022 return idx
1023
1024 - def __eq__(self, other):
1025 if isinstance(other, list): 1026 return self.list == other 1027 elif isinstance(other, dict): 1028 return self.dict == other 1029 1030 return False
1031
1032 - def __len__(self):
1033 return len(self.list)
1034
1035 - def __getitem__(self, idx):
1036 return self.getByReference(idx)
1037
1038 - def __contains__(self, obj):
1039 try: 1040 r = self.getReferenceTo(obj) 1041 except pyamf.ReferenceError: 1042 r = None 1043 1044 return r is not None
1045
1046 - def __repr__(self):
1047 return '<%s list=%r dict=%r>' % (self.__class__.__name__, self.list, self.dict)
1048
1049 - def __iter__(self):
1050 return iter(self.list)
1051 1052
1053 -class IndexedMap(IndexedCollection):
1054 """ 1055 Like L{IndexedCollection}, but also maps to another object. 1056 1057 @since: 0.4 1058 """ 1059
1060 - def __init__(self, use_hash=False, exceptions=True):
1062
1063 - def clear(self):
1064 """ 1065 Clears the index and mapping. 1066 """ 1067 IndexedCollection.clear(self) 1068 1069 self.mapped = []
1070
1071 - def getMappedByReference(self, ref):
1072 """ 1073 Returns the mapped object by reference. 1074 1075 @raise TypeError: Bad reference type. 1076 @raise pyamf.ReferenceError: Reference not found. 1077 """ 1078 if not isinstance(ref, (int, long)): 1079 raise TypeError("Bad reference type.") 1080 1081 try: 1082 return self.mapped[ref] 1083 except IndexError: 1084 if self.exceptions is False: 1085 return None 1086 1087 raise pyamf.ReferenceError("Reference %r not found" % ref)
1088
1089 - def append(self, obj):
1090 """ 1091 Appends C{obj} to this index. 1092 1093 @return: The reference to C{obj} in this index. 1094 """ 1095 idx = IndexedCollection.append(self, obj) 1096 diff = (idx + 1) - len(self.mapped) 1097 1098 for i in range(0, diff): 1099 self.mapped.append(None) 1100 1101 return idx
1102
1103 - def map(self, obj, mapped_obj):
1104 """ 1105 Maps an object. 1106 """ 1107 idx = self.append(obj) 1108 self.mapped[idx] = mapped_obj 1109 1110 return idx
1111 1112
1113 -def is_ET_element(obj):
1114 """ 1115 Determines if the supplied C{obj} param is a valid ElementTree element. 1116 """ 1117 return isinstance(obj, xml_types)
1118 1119
1120 -def is_float_broken():
1121 """ 1122 Older versions of Python (<=2.5) and the Windows platform are renowned for 1123 mixing up 'special' floats. This function determines whether this is the 1124 case. 1125 1126 @since: 0.4 1127 @rtype: C{bool} 1128 """ 1129 global NaN 1130 1131 return str(NaN) != str(struct.unpack("!d", '\xff\xf8\x00\x00\x00\x00\x00\x00')[0])
1132 1133
1134 -def isNaN(val):
1135 """ 1136 @since: 0.5 1137 """ 1138 return str(float(val)) == str(NaN)
1139 1140
1141 -def isPosInf(val):
1142 """ 1143 @since: 0.5 1144 """ 1145 return str(float(val)) == str(PosInf)
1146 1147
1148 -def isNegInf(val):
1149 """ 1150 @since: 0.5 1151 """ 1152 return str(float(val)) == str(NegInf)
1153 1154 1155 # init the module from here .. 1156 1157 find_xml_lib() 1158 1159 try: 1160 datetime.datetime.utcfromtimestamp(-31536000.0) 1161 except ValueError: 1162 negative_timestamp_broken = True 1163 1164 if is_float_broken():
1165 - def read_double_workaround(self):
1166 global PosInf, NegInf, NaN 1167 1168 """ 1169 Override the L{DataTypeMixIn.read_double} method to fix problems 1170 with doubles by using the third-party C{fpconst} library. 1171 """ 1172 bytes = self.read(8) 1173 1174 if self._is_big_endian(): 1175 if bytes == '\xff\xf8\x00\x00\x00\x00\x00\x00': 1176 return NaN 1177 1178 if bytes == '\xff\xf0\x00\x00\x00\x00\x00\x00': 1179 return NegInf 1180 1181 if bytes == '\x7f\xf0\x00\x00\x00\x00\x00\x00': 1182 return PosInf 1183 else: 1184 if bytes == '\x00\x00\x00\x00\x00\x00\xf8\xff': 1185 return NaN 1186 1187 if bytes == '\x00\x00\x00\x00\x00\x00\xf0\xff': 1188 return NegInf 1189 1190 if bytes == '\x00\x00\x00\x00\x00\x00\xf0\x7f': 1191 return PosInf 1192 1193 return struct.unpack("%sd" % self.endian, bytes)[0]
1194 1195 DataTypeMixIn.read_double = read_double_workaround 1196
1197 - def write_double_workaround(self, d):
1198 """ 1199 Override the L{DataTypeMixIn.write_double} method to fix problems 1200 with doubles by using the third-party C{fpconst} library. 1201 """ 1202 if type(d) is not float: 1203 raise TypeError('expected a float (got:%r)' % (type(d),)) 1204 1205 if isNaN(d): 1206 if self._is_big_endian(): 1207 self.write('\xff\xf8\x00\x00\x00\x00\x00\x00') 1208 else: 1209 self.write('\x00\x00\x00\x00\x00\x00\xf8\xff') 1210 elif isNegInf(d): 1211 if self._is_big_endian(): 1212 self.write('\xff\xf0\x00\x00\x00\x00\x00\x00') 1213 else: 1214 self.write('\x00\x00\x00\x00\x00\x00\xf0\xff') 1215 elif isPosInf(d): 1216 if self._is_big_endian(): 1217 self.write('\x7f\xf0\x00\x00\x00\x00\x00\x00') 1218 else: 1219 self.write('\x00\x00\x00\x00\x00\x00\xf0\x7f') 1220 else: 1221 write_double_workaround.old_func(self, d)
1222 1223 x = DataTypeMixIn.write_double 1224 DataTypeMixIn.write_double = write_double_workaround 1225 write_double_workaround.old_func = x 1226 1227 1228 try: 1229 from cpyamf.util import BufferedByteStream, IndexedCollection, IndexedMap 1230
1231 - class StringIOProxy(BufferedByteStream):
1232 _wrapped_class = None 1233
1234 - def __init__(self, *args, **kwargs):
1235 BufferedByteStream.__init__(self, *args, **kwargs) 1236 self._buffer = self
1237
1238 - class DataTypeMixIn(BufferedByteStream):
1239 #: Network byte order 1240 ENDIAN_NETWORK = "!" 1241 #: Native byte order 1242 ENDIAN_NATIVE = "@" 1243 #: Little endian 1244 ENDIAN_LITTLE = "<" 1245 #: Big endian 1246 ENDIAN_BIG = ">"
1247 except ImportError: 1248 pass 1249