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      return calendar.timegm(d.utctimetuple())
     638
     639 -def get_datetime(secs):
     640      """
     641      Return a UTC date from a timestamp.
     642
     643      @type secs: C{long}
     644      @param secs: Seconds since 1970.
     645      @return: UTC timestamp.
     646      @rtype: C{datetime.datetime}
     647      """
     648      if secs < 0 and negative_timestamp_broken:
     649          return datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=secs)
     650
     651      return datetime.datetime.utcfromtimestamp(secs)
     652
     653 -def make_classic_instance(klass):
     654      """
     655      Create an instance of a classic class (not inherited from ``object``)
     656      without calling __init__().
     657
     658      @type klass: C{class}
     659      @param klass: The classic class to create an instance for.
     660      @rtype:
     661      @return: instance created
     662      """
     663      assert isinstance(klass, types.ClassType), "not an old style class"
     664
     665      class _TemporaryClass:
     666          pass
     667
     668      inst = _TemporaryClass()
     669      inst.__class__ = klass
     670
     671      return inst
     672
     673 -def get_mro(C):
     674      """
     675      Compute the class precedence list (mro).
     676
     677      @raise TypeError: class type expected.
     678      """
     679      def merge(seqs):
     680          """
     681          @raise NameError: Inconsistent hierarchy.
     682          @raise TypeError: Class type expected.
     683          """
     684          res = []
     685          i = 0
     686
     687          while 1:
     688              nonemptyseqs = [seq for seq in seqs if seq]
     689              if not nonemptyseqs:
     690                  return res
     691
     692              i += 1
     693              for seq in nonemptyseqs:
     694                  cand = seq[0]
     695                  nothead = [s for s in nonemptyseqs if cand in s[1:]]
     696
     697                  if nothead:
     698                      cand = None
     699                  else:
     700                      break
     701
     702              if not cand:
     703                  raise NameError("Inconsistent hierarchy")
     704
     705              res.append(cand)
     706
     707              for seq in nonemptyseqs:
     708                  if seq[0] == cand:
     709                      del seq[0]
     710
     711      if not isinstance(C, (types.ClassType, types.ObjectType)):
     712          raise TypeError('class type expected')
     713
     714      if hasattr(C, '__mro__'):
     715          return C.__mro__
     716
     717      return merge([[C]] + map(get_mro, C.__bases__) + [list(C.__bases__)])
     718
     719 -def get_attrs(obj):
     720      """
     721      Gets a C{dict} of the attrs of an object in a predefined resolution order.
     722
     723      @raise AttributeError: A duplicate attribute was already found in this
     724          collection, are you mixing different key types?
     725      """
     726      if hasattr(obj, 'iteritems'):
     727          attrs = {}
     728
     729          for k, v in obj.iteritems():
     730              sk = str(k)
     731
     732              if sk in attrs.keys():
     733                  raise AttributeError('A duplicate attribute (%s) was '
     734                      'already found in this collection, are you mixing '
     735                      'different key types?' % (sk,))
     736
     737              attrs[sk] = v
     738
     739          return attrs
     740      elif hasattr(obj, '__dict__'):
     741          return obj.__dict__.copy()
     742      elif hasattr(obj, '__slots__'):
     743          attrs = {}
     744
     745          for k in obj.__slots__:
     746              attrs[k] = getattr(obj, k)
     747
     748          return attrs
     749
     750      return None
     751
     752 -def set_attrs(obj, attrs):
     753      """
     754      A generic function which applies a collection of attributes C{attrs} to
     755      object C{obj}
     756
     757      @param obj: An instance implementing the __setattr__ function
     758      @param attrs: A collection implementing the iteritems function
     759      @type attrs: Usually a dict
     760      """
     761      f = lambda n, v: setattr(obj, n, v)
     762
     763      if isinstance(obj, (list, dict)):
     764          f = obj.__setitem__
     765
     766      for k, v in attrs.iteritems():
     767          f(k, v)
     768
     769 -def get_class_alias(klass):
     770      for k, v in pyamf.ALIAS_TYPES.iteritems():
     771          for kl in v:
     772              if isinstance(kl, types.FunctionType):
     773                  if kl(klass) is True:
     774                      return k
     775              elif isinstance(kl, (type, (types.ClassType, types.ObjectType))):
     776                  if issubclass(klass, kl):
     777                      return k
     778
     779      return None
     780
     781 -class IndexedCollection(object):
     782
     783 -    def __init__(self, use_hash=False):
     784          if use_hash is True:
     785              self.func = hash
     786          else:
     787              self.func = id
     788
     789          self.clear()
     790
     791 -    def clear(self):
     792          self.list = []
     793          self.dict = {}
     794
     795 -    def getByReference(self, ref):
     796          """
     797          @raise TypeError: Bad reference type.
     798          @raise pyamf.ReferenceError: Reference not found.
     799          """
     800          if not isinstance(ref, (int, long)):
     801              raise TypeError("Bad reference type")
     802
     803          try:
     804              return self.list[ref]
     805          except IndexError:
     806              raise pyamf.ReferenceError("Reference %r not found" % (ref,))
     807
     808 -    def getReferenceTo(self, obj):
     809          """
     810          @raise pyamf.ReferenceError: Value not found.
     811          """
     812          try:
     813              return self.dict[self.func(obj)]
     814          except KeyError:
     815              raise pyamf.ReferenceError("Value %r not found" % (obj,))
     816
     817 -    def append(self, obj):
     818          h = self.func(obj)
     819
     820          try:
     821              return self.dict[h]
     822          except KeyError:
     823              self.list.append(obj)
     824              idx = len(self.list) - 1
     825              self.dict[h] = idx
     826
     827              return idx
     828
     829 -    def remove(self, obj):
     830          """
     831          @raise pyamf.ReferenceError: Trying to remove an invalid reference.
     832          """
     833          h = self.func(obj)
     834
     835          try:
     836              idx = self.dict[h]
     837          except KeyError:
     838              raise pyamf.ReferenceError("%r is not a valid reference" % (obj,))
     839
     840          del self.list[idx]
     841          del self.dict[h]
     842
     843          return idx
     844
     845 -    def __eq__(self, other):
     846          if isinstance(other, list):
     847              return self.list == other
     848          elif isinstance(other, dict):
     849              return self.dict == other
     850
     851          return False
     852
     853 -    def __len__(self):
     854          return len(self.list)
     855
     856 -    def __getitem__(self, idx):
     857          return self.getByReference(idx)
     858
     859 -    def __contains__(self, obj):
     860          try:
     861              r = self.getReferenceTo(obj)
     862          except pyamf.ReferenceError:
     863              r = None
     864
     865          return r is not None
     866
     867 -    def __repr__(self):
     868          return '<%s list=%r dict=%r>' % (self.__class__.__name__, self.list, self.dict)
     869
     870 -    def __iter__(self):
     871          return iter(self.list)
     872
     873 -class IndexedMap(IndexedCollection):
     874      """
     875      Like L{IndexedCollection}, but also maps to another object.
     876
     877      @since: 0.4
     878      """
     879
     880 -    def __init__(self, use_hash=False):
     881          IndexedCollection.__init__(self, use_hash)
     882          self.mapped = []
     883
     884 -    def clear(self):
     885          IndexedCollection.clear(self)
     886          self.mapped = []
     887
     888 -    def getMappedByReference(self, ref):
     889          """
     890          @raise TypeError: Bad reference type.
     891          @raise pyamf.ReferenceError: Reference not found.
     892          """
     893          if not isinstance(ref, (int, long)):
     894              raise TypeError("Bad reference type.")
     895
     896          try:
     897              return self.mapped[ref]
     898          except IndexError:
     899              raise pyamf.ReferenceError("Reference %r not found" % ref)
     900
     901 -    def append(self, obj):
     902          idx = IndexedCollection.append(self, obj)
     903          diff = (idx + 1) - len(self.mapped)
     904          for i in range(0, diff):
     905              self.mapped.append(None)
     906          return idx
     907
     908 -    def map(self, obj, mapped_obj):
     909          idx = self.append(obj)
     910          self.mapped[idx] = mapped_obj
     911          return idx
     912
     913 -    def remove(self, obj):
     914          idx = IndexedCollection.remove(self, obj)
     915          del self.mapped[idx]
     916          return idx
     917
     918 -def is_ET_element(obj):
     919      """
     920      Determines if the supplied C{obj} param is a valid ElementTree element.
     921      """
     922      return isinstance(obj, xml_types)
     923
     924 -def is_float_broken():
     925      """
     926      Older versions of Python (<=2.5) and the Windows platform are renowned for
     927      mixing up 'special' floats. This function determines whether this is the
     928      case.
     929
     930      @since: 0.4
     931      @rtype: C{bool}
     932      """
     933      # we do this instead of float('nan') because windows throws a wobbler.
     934      nan = 1e300000/1e300000
     935
     936      return str(nan) != str(struct.unpack("!d", '\xff\xf8\x00\x00\x00\x00\x00\x00')[0])
     937
     938  # init the module from here ..
     939
     940  find_xml_lib()
     941
     942  try:
     943      datetime.datetime.utcfromtimestamp(-31536000.0)
     944  except ValueError:
     945      negative_timestamp_broken = True
     946
     947  if is_float_broken():
     948      import fpconst
     949
     950 -    def read_double_workaround(self):
     951          bytes = self.read(8)
     952
     953          if self._is_big_endian():
     954              if bytes == '\xff\xf8\x00\x00\x00\x00\x00\x00':
     955                  return fpconst.NaN
     956
     957              if bytes == '\xff\xf0\x00\x00\x00\x00\x00\x00':
     958                  return fpconst.NegInf
     959
     960              if bytes == '\x7f\xf0\x00\x00\x00\x00\x00\x00':
     961                  return fpconst.PosInf
     962          else:
     963              if bytes == '\x00\x00\x00\x00\x00\x00\xf8\xff':
     964                  return fpconst.NaN
     965
     966              if bytes == '\x00\x00\x00\x00\x00\x00\xf0\xff':
     967                  return fpconst.NegInf
     968
     969              if bytes == '\x00\x00\x00\x00\x00\x00\xf0\x7f':
     970                  return fpconst.PosInf
     971
     972          return struct.unpack("%sd" % self.endian, bytes)[0]
     973
     974      DataTypeMixIn.read_double = read_double_workaround
     975
     976 -    def write_double_workaround(self, d):
     977          if fpconst.isNaN(d):
     978              if self._is_big_endian():
     979                  self.write('\xff\xf8\x00\x00\x00\x00\x00\x00')
     980              else:
     981                  self.write('\x00\x00\x00\x00\x00\x00\xf8\xff')
     982          elif fpconst.isNegInf(d):
     983              if self._is_big_endian():
     984                  self.write('\xff\xf0\x00\x00\x00\x00\x00\x00')
     985              else:
     986                  self.write('\x00\x00\x00\x00\x00\x00\xf0\xff')
     987          elif fpconst.isPosInf(d):
     988              if self._is_big_endian():
     989                  self.write('\x7f\xf0\x00\x00\x00\x00\x00\x00')
     990              else:
     991                  self.write('\x00\x00\x00\x00\x00\x00\xf0\x7f')
     992          else:
     993              write_double_workaround.old_func(self, d)
     994
     995      x = DataTypeMixIn.write_double
     996      DataTypeMixIn.write_double = write_double_workaround
     997      write_double_workaround.old_func = x
     998
     999  try:
    1000      from cpyamf.util import BufferedByteStream
    1001
    1002 -    class StringIOProxy(BufferedByteStream):
    1003          _wrapped_class = None
    1004
    1005 -        def __init__(self, *args, **kwargs):
    1006              BufferedByteStream.__init__(self, *args, **kwargs)
    1007              self._buffer = self
    1008
    1009 -    class DataTypeMixIn(BufferedByteStream):
    1010          ENDIAN_NETWORK = "!"
    1011          ENDIAN_NATIVE = "@"
    1012          ENDIAN_LITTLE = "<"
    1013          ENDIAN_BIG = ">"
    1014  except ImportError:
    1015      pass
    1016