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 for details.
       5
       6  """
       7  AMF Utilities.
       8
       9  @since: 0.1.0
      10  """
      11
      12  import struct, calendar, datetime, types
      13
      14  import pyamf
      15
      16  try:
      17      from cStringIO import StringIO
      18  except ImportError:
      19      from StringIO import StringIO
      20
      21  xml_types = None
      22  ET = None
      23  negative_timestamp_broken = False
      24
      25 -def find_xml_lib():
      26      """
      27      Run through a predefined order looking through the various C{ElementTree}
      28      implementations so that any type can be encoded but PyAMF will return
      29      elements as the first implementation found.
      30
      31      We work through the C implementations first - then the pure Python
      32      versions. The downside to this is that a possible of three libraries will
      33      be loaded into memory that are not used but the libs are small
      34      (relatively) and the flexibility that this gives seems to outweigh the
      35      cost. Time will tell.
      36
      37      @since: 0.4
      38      """
      39      global xml_types, ET
      40
      41      xml_types = []
      42
      43      try:
      44          import xml.etree.cElementTree as cET
      45
      46          ET = cET
      47          xml_types.append(type(cET.Element('foo')))
      48      except ImportError:
      49          pass
      50
      51      try:
      52          import cElementTree as cET
      53
      54          if ET is None:
      55              ET = cET
      56
      57          xml_types.append(type(cET.Element('foo')))
      58      except ImportError:
      59          pass
      60
      61      try:
      62          import xml.etree.ElementTree as pET
      63
      64          if ET is None:
      65              ET = pET
      66
      67          xml_types.append(pET._ElementInterface)
      68      except ImportError:
      69          pass
      70
      71      try:
      72          import elementtree.ElementTree as pET
      73
      74          if ET is None:
      75              ET = pET
      76
      77          xml_types.append(pET._ElementInterface)
      78      except ImportError:
      79          pass
      80
      81      for x in xml_types[:]:
      82          # hack for jython
      83          if x.__name__ == 'instance':
      84              xml_types.remove(x)
      85
      86      xml_types = tuple(xml_types)
      87
      88      return xml_types
      89
      90 -class StringIOProxy(object):
      91      """
      92      I am a C{StringIO} type object containing byte data from the AMF stream.
      93
      94      @see: U{ByteArray on OSFlash (external)
      95      <http://osflash.org/documentation/amf3#x0c_-_bytearray>}
      96      @see: U{Parsing ByteArrays on OSFlash (external)
      97      <http://osflash.org/documentation/amf3/parsing_byte_arrays>}
      98      """
      99
     100      _wrapped_class = StringIO
     101
     102 -    def __init__(self, buf=None):
     103          """
     104          @raise TypeError: Unable to coerce C{buf} to C{StringIO}.
     105          """
     106          self._buffer = StringIOProxy._wrapped_class()
     107
     108          if isinstance(buf, (str, unicode)):
     109              self._buffer.write(buf)
     110          elif hasattr(buf, 'getvalue'):
     111              self._buffer.write(buf.getvalue())
     112          elif hasattr(buf, 'read') and hasattr(buf, 'seek') and hasattr(buf, 'tell'):
     113              old_pos = buf.tell()
     114              buf.seek(0)
     115              self._buffer.write(buf.read())
     116              buf.seek(old_pos)
     117          elif buf is None:
     118              pass
     119          else:
     120              raise TypeError("Unable to coerce buf->StringIO")
     121
     122          self._get_len()
     123          self._len_changed = False
     124          self._buffer.seek(0, 0)
     125
     126 -    def close(self):
     127          self._buffer.close()
     128          self._len = 0
     129          self._len_changed = False
     130
     131 -    def flush(self):
     132          self._buffer.flush()
     133
     134 -    def getvalue(self):
     135          return self._buffer.getvalue()
     136
     137 -    def next(self):
     138          return self._buffer.next()
     139
     140 -    def read(self, n=-1):
     141          bytes = self._buffer.read(n)
     142
     143          return bytes
     144
     145 -    def readline(self):
     146          line = self._buffer.readline()
     147
     148          return line
     149
     150 -    def readlines(self, sizehint=0):
     151          """
     152          @type sizehint: C{int}
     153          @param sizehint: Default is 0.
     154          @note: This function does not consume the buffer.
     155          """
     156          lines = self._buffer.readlines(sizehint)
     157
     158          return lines
     159
     160 -    def seek(self, pos, mode=0):
     161          return self._buffer.seek(pos, mode)
     162
     163 -    def tell(self):
     164          return self._buffer.tell()
     165
     166 -    def truncate(self, size=0):
     167          if size == 0:
     168              self._buffer = StringIOProxy._wrapped_class()
     169              self._len_changed = True
     170
     171              return
     172
     173          cur_pos = self.tell()
     174          self.seek(0)
     175          buf = self.read(size)
     176          self._buffer = StringIOProxy._wrapped_class()
     177
     178          self._buffer.write(buf)
     179          self.seek(cur_pos)
     180          self._len_changed = True
     181
     182 -    def write(self, s):
     183          self._buffer.write(s)
     184          self._len_changed = True
     185
     186 -    def writelines(self, iterable):
     187          self._buffer.writelines(iterable)
     188          self._len_changed = True
     189
     190 -    def _get_len(self):
     191          if hasattr(self._buffer, 'len'):
     192              self._len = self._buffer.len
     193
     194              return
     195
     196          old_pos = self._buffer.tell()
     197          self._buffer.seek(0, 2)
     198
     199          self._len = self._buffer.tell()
     200          self._buffer.seek(old_pos)
     201
     202 -    def __len__(self):
     203          if not self._len_changed:
     204              return self._len
     205
     206          self._get_len()
     207          self._len_changed = False
     208
     209          return self._len
     210
     211 -    def consume(self):
     212          """
     213          Chops the tail off the stream starting at 0 and ending at C{tell()}.
     214          The stream pointer is set to 0 at the end of this function.
     215
     216          @since: 0.4
     217          """
     218          bytes = self.read()
     219          self.truncate()
     220
     221          if len(bytes) > 0:
     222              self.write(bytes)
     223              self.seek(0)
     224
     225 -class DataTypeMixIn(object):
     226      """
     227      Provides methods for reading and writing basic data types for file-like
     228      objects.
     229      """
     230
     231      ENDIAN_NETWORK = "!"
     232      ENDIAN_NATIVE = "@"
     233      ENDIAN_LITTLE = "<"
     234      ENDIAN_BIG = ">"
     235
     236      endian = ENDIAN_NETWORK
     237
     238 -    def _read(self, length):
     239          """
     240          Reads C{length} bytes from the stream. If an attempt to read past the
     241          end of the buffer is made, L{EOFError} is raised.
     242          """
     243          bytes = self.read(length)
     244
     245          if len(bytes) != length:
     246              self.seek(0 - len(bytes), 1)
     247
     248              raise EOFError("Tried to read %d byte(s) from the stream" % length)
     249
     250          return bytes
     251
     252 -    def _is_big_endian(self):
     253          """
     254          Whether this system is big endian or not.
     255
     256          @rtype: C{bool}
     257          """
     258          if self.endian == DataTypeMixIn.ENDIAN_NATIVE:
     259              return DataTypeMixIn._system_endian == DataTypeMixIn.ENDIAN_BIG
     260
     261          return self.endian in (DataTypeMixIn.ENDIAN_BIG, DataTypeMixIn.ENDIAN_NETWORK)
     262
     263 -    def read_uchar(self):
     264          """
     265          Reads an C{unsigned char} from the stream.
     266          """
     267          return struct.unpack("B", self._read(1))[0]
     268
     269 -    def write_uchar(self, c):
     270          """
     271          Writes an C{unsigned char} to the stream.
     272
     273          @raise OverflowError: Not in range.
     274          """
     275          if not 0 <= c <= 255:
     276              raise OverflowError("Not in range, %d" % c)
     277
     278          self.write(struct.pack("B", c))
     279
     280 -    def read_char(self):
     281          """
     282          Reads a C{char} from the stream.
     283          """
     284          return struct.unpack("b", self._read(1))[0]
     285
     286 -    def write_char(self, c):
     287          """
     288          Write a C{char} to the stream.
     289
     290          @raise OverflowError: Not in range.
     291          """
     292          if not -128 <= c <= 127:
     293              raise OverflowError("Not in range, %d" % c)
     294
     295          self.write(struct.pack("b", c))
     296
     297 -    def read_ushort(self):
     298          """
     299          Reads a 2 byte unsigned integer from the stream.
     300          """
     301          return struct.unpack("%sH" % self.endian, self._read(2))[0]
     302
     303 -    def write_ushort(self, s):
     304          """
     305          Writes a 2 byte unsigned integer to the stream.
     306
     307          @raise OverflowError: Not in range.
     308          """
     309          if not 0 <= s <= 65535:
     310              raise OverflowError("Not in range, %d" % s)
     311
     312          self.write(struct.pack("%sH" % self.endian, s))
     313
     314 -    def read_short(self):
     315          """
     316          Reads a 2 byte integer from the stream.
     317          """
     318          return struct.unpack("%sh" % self.endian, self._read(2))[0]
     319
     320 -    def write_short(self, s):
     321          """
     322          Writes a 2 byte integer to the stream.
     323
     324          @raise OverflowError: Not in range.
     325          """
     326          if not -32768 <= s <= 32767:
     327              raise OverflowError("Not in range, %d" % s)
     328
     329          self.write(struct.pack("%sh" % self.endian, s))
     330
     331 -    def read_ulong(self):
     332          """
     333          Reads a 4 byte unsigned integer from the stream.
     334          """
     335          return struct.unpack("%sL" % self.endian, self._read(4))[0]
     336
     337 -    def write_ulong(self, l):
     338          """
     339          Writes a 4 byte unsigned integer to the stream.
     340
     341          @raise OverflowError: Not in range.
     342          """
     343          if not 0 <= l <= 4294967295:
     344              raise OverflowError("Not in range, %d" % l)
     345
     346          self.write(struct.pack("%sL" % self.endian, l))
     347
     348 -    def read_long(self):
     349          """
     350          Reads a 4 byte integer from the stream.
     351          """
     352          return struct.unpack("%sl" % self.endian, self._read(4))[0]
     353
     354 -    def write_long(self, l):
     355          """
     356          Writes a 4 byte integer to the stream.
     357
     358          @raise OverflowError: Not in range.
     359          """
     360          if not -2147483648 <= l <= 2147483647:
     361              raise OverflowError("Not in range, %d" % l)
     362
     363          self.write(struct.pack("%sl" % self.endian, l))
     364
     365 -    def read_24bit_uint(self):
     366          """
     367          Reads a 24 bit unsigned integer from the stream.
     368
     369          @since: 0.4
     370          """
     371          order = None
     372
     373          if not self._is_big_endian():
     374              order = [0, 8, 16]
     375          else:
     376              order = [16, 8, 0]
     377
     378          n = 0
     379
     380          for x in order:
     381              n += (self.read_uchar() << x)
     382
     383          return n
     384
     385 -    def write_24bit_uint(self, n):
     386          """
     387          Writes a 24 bit unsigned integer to the stream.
     388
     389          @since: 0.4
     390          """
     391          if not 0 <= n <= 0xffffff:
     392              raise OverflowError("n is out of range")
     393
     394          order = None
     395
     396          if not self._is_big_endian():
     397              order = [0, 8, 16]
     398          else:
     399              order = [16, 8, 0]
     400
     401          for x in order:
     402              self.write_uchar((n >> x) & 0xff)
     403
     404 -    def read_24bit_int(self):
     405          """
     406          Reads a 24 bit integer from the stream.
     407
     408          @since: 0.4
     409          """
     410          n = self.read_24bit_uint()
     411
     412          if n & 0x800000 != 0:
     413              # the int is signed
     414              n -= 0x1000000
     415
     416          return n
     417
     418 -    def write_24bit_int(self, n):
     419          """
     420          Writes a 24 bit integer to the stream.
     421
     422          @since: 0.4
     423          """
     424          if not -8388608 <= n <= 8388607:
     425              raise OverflowError("n is out of range")
     426
     427          order = None
     428
     429          if not self._is_big_endian():
     430              order = [0, 8, 16]
     431          else:
     432              order = [16, 8, 0]
     433
     434          if n < 0:
     435              n += 0x1000000
     436
     437          for x in order:
     438              self.write_uchar((n >> x) & 0xff)
     439
     440 -    def read_double(self):
     441          """
     442          Reads an 8 byte float from the stream.
     443          """
     444          return struct.unpack("%sd" % self.endian, self._read(8))[0]
     445
     446 -    def write_double(self, d):
     447          """
     448          Writes an 8 byte float to the stream.
     449          """
     450          self.write(struct.pack("%sd" % self.endian, d))
     451
     452 -    def read_float(self):
     453          """
     454          Reads a 4 byte float from the stream.
     455          """
     456          return struct.unpack("%sf" % self.endian, self._read(4))[0]
     457
     458 -    def write_float(self, f):
     459          """
     460          Writes a 4 byte float to the stream.
     461          """
     462          self.write(struct.pack("%sf" % self.endian, f))
     463
     464 -    def read_utf8_string(self, length):
     465          """
     466          Reads a UTF-8 string from the stream.
     467
     468          @rtype: C{unicode}
     469          """
     470          str = struct.unpack("%s%ds" % (self.endian, length), self.read(length))[0]
     471
     472          return unicode(str, "utf8")
     473
     474 -    def write_utf8_string(self, u):
     475          """
     476          Writes a unicode object to the stream in UTF-8
     477          """
     478          bytes = u.encode("utf8")
     479
     480          self.write(struct.pack("%s%ds" % (self.endian, len(bytes)), bytes))
     481
     482  if struct.pack('@H', 1)[0] == '\x01':
     483      DataTypeMixIn._system_endian = DataTypeMixIn.ENDIAN_LITTLE
     484  else:
     485      DataTypeMixIn._system_endian = DataTypeMixIn.ENDIAN_BIG
     486
     487 -class BufferedByteStream(StringIOProxy, DataTypeMixIn):
     488      """
     489      An extension of C{StringIO}.
     490
     491      Features:
     492       - Raises L{EOFError} if reading past end.
     493       - Allows you to C{peek()} at the next byte.
     494      """
     495
     496 -    def __init__(self, buf=None):
     497          """
     498          @param buf: Initial byte stream.
     499          @type buf: C{str} or C{StringIO} instance
     500          """
     501          StringIOProxy.__init__(self, buf=buf)
     502
     503          self.seek(0)
     504
     505 -    def read(self, length=-1):
     506          """
     507          Read bytes from stream.
     508
     509          If we are at the end of the buffer, a C{EOFError} is raised.
     510          If there is not enough buffer to be read and length is
     511          specified C{IOError} is raised.
     512
     513          @param length: Number of bytes to read.
     514          @type length: C{int}
     515          @raise EOFError: Reading past end of stream.
     516          @raise IOError: Length specified but not enough buffer
     517          available.
     518
     519          @rtype: array of C{char}
     520          @return: The bytes read from the stream.
     521          """
     522          if length > 0 and self.at_eof():
     523              raise EOFError
     524          if length > 0 and self.tell() + length > len(self):
     525              raise IOError
     526
     527          return StringIOProxy.read(self, length)
     528
     529 -    def peek(self, size=1):
     530          """
     531          Looks size bytes ahead in the stream, returning what it finds,
     532          returning the stream pointer to its initial position.
     533
     534          @param size: Default is 1.
     535          @type size: C{int}
     536          @raise ValueError: Trying to peek backwards.
     537
     538          @rtype:
     539          @return: Bytes.
     540          """
     541          if size == -1:
     542              return self.peek(len(self) - self.tell())
     543
     544          if size < -1:
     545              raise ValueError("Cannot peek backwards")
     546
     547          bytes = ''
     548          pos = self.tell()
     549
     550          while not self.at_eof() and len(bytes) != size:
     551              bytes += self.read(1)
     552
     553          self.seek(pos)
     554
     555          return bytes
     556
     557 -    def at_eof(self):
     558          """
     559          Returns true if C{next.read(1)} will trigger an C{EOFError}.
     560
     561          @rtype: C{bool}
     562          @return:
     563          """
     564          return self.tell() >= len(self)
     565
     566 -    def remaining(self):
     567          """
     568          Returns number of remaining bytes.
     569
     570          @rtype: C{number}
     571          @return: Number of remaining bytes.
     572          """
     573          return len(self) - self.tell()
     574
     575 -    def __add__(self, other):
     576          old_pos = self.tell()
     577          old_other_pos = other.tell()
     578
     579          new = BufferedByteStream(self)
     580
     581          other.seek(0)
     582          new.seek(0, 2)
     583          new.write(other.read())
     584
     585          self.seek(old_pos)
     586          other.seek(old_other_pos)
     587          new.seek(0)
     588
     589          return new
     590
     591 -def hexdump(data):
     592      """
     593      Get hexadecimal representation of C{StringIO} data.
     594
     595      @type data:
     596      @param data:
     597      @rtype: C{str}
     598      @return: Hexadecimal string.
     599      """
     600      import string
     601
     602      hex = ascii = buf = ""
     603      index = 0
     604
     605      for c in data:
     606          hex += "%02x " % ord(c)
     607          if c in string.printable and c not in string.whitespace:
     608              ascii += c
     609          else:
     610              ascii += "."
     611
     612          if len(ascii) == 16:
     613              buf += "%04x:  %s %s %s\n" % (index, hex[:24], hex[24:], ascii)
     614              hex = ascii = ""
     615              index += 16
     616
     617      if len(ascii):
     618          buf += "%04x:  %-24s %-24s %s\n" % (index, hex[:24], hex[24:], ascii)
     619
     620      return buf
     621
     622 -def get_timestamp(d):
     623      """
     624      Returns a UTC timestamp for a C{datetime.datetime} object.
     625
     626      @type d: C{datetime.datetime}
     627      @param d: The date object.
     628      @return: UTC timestamp.
     629      @rtype: C{str}
     630
     631      @note: Inspiration taken from the U{Intertwingly blog
     632      <http://intertwingly.net/blog/2007/09/02/Dealing-With-Dates>}.
     633      """
     634      if isinstance(d, datetime.date) and not isinstance(d, datetime.datetime):
     635          d = datetime.datetime.combine(d, datetime.time(0, 0, 0, 0))
     636
     637      msec = str(d.microsecond).rjust(6).replace(' ', '0')
     638
     639      return float('%s.%s' % (calendar.timegm(d.utctimetuple()), msec))
     640
     641 -def get_datetime(secs):
     642      """
     643      Return a UTC date from a timestamp.
     644
     645      @type secs: C{long}
     646      @param secs: Seconds since 1970.
     647      @return: UTC timestamp.
     648      @rtype: C{datetime.datetime}
     649      """
     650      if secs < 0 and negative_timestamp_broken:
     651          return datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=secs)
     652
     653      return datetime.datetime.utcfromtimestamp(secs)
     654
     655 -def make_classic_instance(klass):
     656      """
     657      Create an instance of a classic class (not inherited from ``object``)
     658      without calling __init__().
     659
     660      @type klass: C{class}
     661      @param klass: The classic class to create an instance for.
     662      @rtype:
     663      @return: instance created
     664      """
     665      assert isinstance(klass, types.ClassType), "not an old style class"
     666
     667      class _TemporaryClass:
     668          pass
     669
     670      inst = _TemporaryClass()
     671      inst.__class__ = klass
     672
     673      return inst
     674
     675 -def get_mro(C):
     676      """
     677      Compute the class precedence list (mro).
     678
     679      @raise TypeError: class type expected.
     680      """
     681      def merge(seqs):
     682          """
     683          @raise NameError: Inconsistent hierarchy.
     684          @raise TypeError: Class type expected.
     685          """
     686          res = []
     687          i = 0
     688
     689          while 1:
     690              nonemptyseqs = [seq for seq in seqs if seq]
     691              if not nonemptyseqs:
     692                  return res
     693
     694              i += 1
     695              for seq in nonemptyseqs:
     696                  cand = seq[0]
     697                  nothead = [s for s in nonemptyseqs if cand in s[1:]]
     698
     699                  if nothead:
     700                      cand = None
     701                  else:
     702                      break
     703
     704              if not cand:
     705                  raise NameError("Inconsistent hierarchy")
     706
     707              res.append(cand)
     708
     709              for seq in nonemptyseqs:
     710                  if seq[0] == cand:
     711                      del seq[0]
     712
     713      if not isinstance(C, (types.ClassType, types.ObjectType)):
     714          raise TypeError('class type expected')
     715
     716      if hasattr(C, '__mro__'):
     717          return C.__mro__
     718
     719      return merge([[C]] + map(get_mro, C.__bases__) + [list(C.__bases__)])
     720
     721 -def get_attrs(obj):
     722      """
     723      Gets a C{dict} of the attrs of an object in a predefined resolution order.
     724
     725      @raise AttributeError: A duplicate attribute was already found in this
     726          collection, are you mixing different key types?
     727      """
     728      if hasattr(obj, 'iteritems'):
     729          attrs = {}
     730
     731          for k, v in obj.iteritems():
     732              sk = str(k)
     733
     734              if sk in attrs.keys():
     735                  raise AttributeError('A duplicate attribute (%s) was '
     736                      'already found in this collection, are you mixing '
     737                      'different key types?' % (sk,))
     738
     739              attrs[sk] = v
     740
     741          return attrs
     742      elif hasattr(obj, '__dict__'):
     743          return obj.__dict__.copy()
     744      elif hasattr(obj, '__slots__'):
     745          attrs = {}
     746
     747          for k in obj.__slots__:
     748              attrs[k] = getattr(obj, k)
     749
     750          return attrs
     751
     752      return None
     753
     754 -def set_attrs(obj, attrs):
     755      """
     756      A generic function which applies a collection of attributes C{attrs} to
     757      object C{obj}
     758
     759      @param obj: An instance implementing the __setattr__ function
     760      @param attrs: A collection implementing the iteritems function
     761      @type attrs: Usually a dict
     762      """
     763      f = lambda n, v: setattr(obj, n, v)
     764
     765      if isinstance(obj, (list, dict)):
     766          f = obj.__setitem__
     767
     768      for k, v in attrs.iteritems():
     769          f(k, v)
     770
     771 -def get_class_alias(klass):
     772      for k, v in pyamf.ALIAS_TYPES.iteritems():
     773          for kl in v:
     774              if isinstance(kl, types.FunctionType):
     775                  if kl(klass) is True:
     776                      return k
     777              elif isinstance(kl, (type, (types.ClassType, types.ObjectType))):
     778                  if issubclass(klass, kl):
     779                      return k
     780
     781      return None
     782
     783 -class IndexedCollection(object):
     784
     785 -    def __init__(self, use_hash=False):
     786          if use_hash is True:
     787              self.func = hash
     788          else:
     789              self.func = id
     790
     791          self.clear()
     792
     793 -    def clear(self):
     794          self.list = []
     795          self.dict = {}
     796
     797 -    def getByReference(self, ref):
     798          """
     799          @raise TypeError: Bad reference type.
     800          @raise pyamf.ReferenceError: Reference not found.
     801          """
     802          if not isinstance(ref, (int, long)):
     803              raise TypeError("Bad reference type")
     804
     805          try:
     806              return self.list[ref]
     807          except IndexError:
     808              raise pyamf.ReferenceError("Reference %r not found" % (ref,))
     809
     810 -    def getReferenceTo(self, obj):
     811          """
     812          @raise pyamf.ReferenceError: Value not found.
     813          """
     814          try:
     815              return self.dict[self.func(obj)]
     816          except KeyError:
     817              raise pyamf.ReferenceError("Value %r not found" % (obj,))
     818
     819 -    def append(self, obj):
     820          h = self.func(obj)
     821
     822          try:
     823              return self.dict[h]
     824          except KeyError:
     825              self.list.append(obj)
     826              idx = len(self.list) - 1
     827              self.dict[h] = idx
     828
     829              return idx
     830
     831 -    def remove(self, obj):
     832          """
     833          @raise pyamf.ReferenceError: Trying to remove an invalid reference.
     834          """
     835          h = self.func(obj)
     836
     837          try:
     838              idx = self.dict[h]
     839          except KeyError:
     840              raise pyamf.ReferenceError("%r is not a valid reference" % (obj,))
     841
     842          del self.list[idx]
     843          del self.dict[h]
     844
     845          return idx
     846
     847 -    def __eq__(self, other):
     848          if isinstance(other, list):
     849              return self.list == other
     850          elif isinstance(other, dict):
     851              return self.dict == other
     852
     853          return False
     854
     855 -    def __len__(self):
     856          return len(self.list)
     857
     858 -    def __getitem__(self, idx):
     859          return self.getByReference(idx)
     860
     861 -    def __contains__(self, obj):
     862          try:
     863              r = self.getReferenceTo(obj)
     864          except pyamf.ReferenceError:
     865              r = None
     866
     867          return r is not None
     868
     869 -    def __repr__(self):
     870          return '<%s list=%r dict=%r>' % (self.__class__.__name__, self.list, self.dict)
     871
     872 -    def __iter__(self):
     873          return iter(self.list)
     874
     875 -class IndexedMap(IndexedCollection):
     876      """
     877      Like L{IndexedCollection}, but also maps to another object.
     878
     879      @since: 0.4
     880      """
     881
     882 -    def __init__(self, use_hash=False):
     883          IndexedCollection.__init__(self, use_hash)
     884          self.mapped = []
     885
     886 -    def clear(self):
     887          IndexedCollection.clear(self)
     888          self.mapped = []
     889
     890 -    def getMappedByReference(self, ref):
     891          """
     892          @raise TypeError: Bad reference type.
     893          @raise pyamf.ReferenceError: Reference not found.
     894          """
     895          if not isinstance(ref, (int, long)):
     896              raise TypeError("Bad reference type.")
     897
     898          try:
     899              return self.mapped[ref]
     900          except IndexError:
     901              raise pyamf.ReferenceError("Reference %r not found" % ref)
     902
     903 -    def append(self, obj):
     904          idx = IndexedCollection.append(self, obj)
     905          diff = (idx + 1) - len(self.mapped)
     906          for i in range(0, diff):
     907              self.mapped.append(None)
     908          return idx
     909
     910 -    def map(self, obj, mapped_obj):
     911          idx = self.append(obj)
     912          self.mapped[idx] = mapped_obj
     913          return idx
     914
     915 -    def remove(self, obj):
     916          idx = IndexedCollection.remove(self, obj)
     917          del self.mapped[idx]
     918          return idx
     919
     920 -def is_ET_element(obj):
     921      """
     922      Determines if the supplied C{obj} param is a valid ElementTree element.
     923      """
     924      return isinstance(obj, xml_types)
     925
     926 -def is_float_broken():
     927      """
     928      Older versions of Python (<=2.5) and the Windows platform are renowned for
     929      mixing up 'special' floats. This function determines whether this is the
     930      case.
     931
     932      @since: 0.4
     933      @rtype: C{bool}
     934      """
     935      # we do this instead of float('nan') because windows throws a wobbler.
     936      nan = 1e300000/1e300000
     937
     938      return str(nan) != str(struct.unpack("!d", '\xff\xf8\x00\x00\x00\x00\x00\x00')[0])
     939
     940  # init the module from here ..
     941
     942  find_xml_lib()
     943
     944  try:
     945      datetime.datetime.utcfromtimestamp(-31536000.0)
     946  except ValueError:
     947      negative_timestamp_broken = True
     948
     949  if is_float_broken():
     950      import fpconst
     951
     952 -    def read_double_workaround(self):
     953          bytes = self.read(8)
     954
     955          if self._is_big_endian():
     956              if bytes == '\xff\xf8\x00\x00\x00\x00\x00\x00':
     957                  return fpconst.NaN
     958
     959              if bytes == '\xff\xf0\x00\x00\x00\x00\x00\x00':
     960                  return fpconst.NegInf
     961
     962              if bytes == '\x7f\xf0\x00\x00\x00\x00\x00\x00':
     963                  return fpconst.PosInf
     964          else:
     965              if bytes == '\x00\x00\x00\x00\x00\x00\xf8\xff':
     966                  return fpconst.NaN
     967
     968              if bytes == '\x00\x00\x00\x00\x00\x00\xf0\xff':
     969                  return fpconst.NegInf
     970
     971              if bytes == '\x00\x00\x00\x00\x00\x00\xf0\x7f':
     972                  return fpconst.PosInf
     973
     974          return struct.unpack("%sd" % self.endian, bytes)[0]
     975
     976      DataTypeMixIn.read_double = read_double_workaround
     977
     978 -    def write_double_workaround(self, d):
     979          if fpconst.isNaN(d):
     980              if self._is_big_endian():
     981                  self.write('\xff\xf8\x00\x00\x00\x00\x00\x00')
     982              else:
     983                  self.write('\x00\x00\x00\x00\x00\x00\xf8\xff')
     984          elif fpconst.isNegInf(d):
     985              if self._is_big_endian():
     986                  self.write('\xff\xf0\x00\x00\x00\x00\x00\x00')
     987              else:
     988                  self.write('\x00\x00\x00\x00\x00\x00\xf0\xff')
     989          elif fpconst.isPosInf(d):
     990              if self._is_big_endian():
     991                  self.write('\x7f\xf0\x00\x00\x00\x00\x00\x00')
     992              else:
     993                  self.write('\x00\x00\x00\x00\x00\x00\xf0\x7f')
     994          else:
     995              write_double_workaround.old_func(self, d)
     996
     997      x = DataTypeMixIn.write_double
     998      DataTypeMixIn.write_double = write_double_workaround
     999      write_double_workaround.old_func = x
    1000
    1001  try:
    1002      from cpyamf.util import BufferedByteStream
    1003
    1004 -    class StringIOProxy(BufferedByteStream):
    1005          _wrapped_class = None
    1006
    1007 -        def __init__(self, *args, **kwargs):
    1008              BufferedByteStream.__init__(self, *args, **kwargs)
    1009              self._buffer = self
    1010
    1011 -    class DataTypeMixIn(BufferedByteStream):
    1012          ENDIAN_NETWORK = "!"
    1013          ENDIAN_NATIVE = "@"
    1014          ENDIAN_LITTLE = "<"
    1015          ENDIAN_BIG = ">"
    1016  except ImportError:
    1017      pass
    1018