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

Source Code for Package pyamf.util

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (c) 2007-2008 The PyAMF Project. 
  4  # See LICENSE for details. 
  5   
  6  """ 
  7  AMF Utilities. 
  8   
  9  @author: U{Arnar Birgisson<mailto:arnarbi@gmail.com>} 
 10  @author: U{Thijs Triemstra<mailto:info@collab.nl>} 
 11  @author: U{Nick Joyce<mailto:nick@boxdesign.co.uk>} 
 12   
 13  @since: 0.1.0 
 14  """ 
 15   
 16  import struct, calendar, datetime, types, sys 
 17   
 18  try: 
 19      from cStringIO import StringIO 
 20  except ImportError: 
 21      from StringIO import StringIO 
 22   
 23  try: 
 24      import xml.etree.ElementTree as ET 
 25  except ImportError: 
 26      try: 
 27          import cElementTree as ET 
 28          ET._ElementInterface = ET.ElementTree 
 29      except ImportError: 
 30          import elementtree.ElementTree as ET 
 31   
32 -class StringIOProxy(object):
33 """ 34 I am a C{StringIO} type object containing byte data from the AMF stream. 35 36 @see: U{ByteArray on OSFlash (external) 37 <http://osflash.org/documentation/amf3#x0c_-_bytearray>} 38 @see: U{Parsing ByteArrays on OSFlash (external) 39 <http://osflash.org/documentation/amf3/parsing_byte_arrays>} 40 """ 41 42 _wrapped_class = StringIO 43
44 - def __init__(self, buf=None):
45 """ 46 @raise TypeError: Unable to coerce C{buf} to C{StringIO}. 47 """ 48 self._buffer = StringIOProxy._wrapped_class() 49 50 if isinstance(buf, (str, unicode)): 51 self._buffer.write(buf) 52 elif hasattr(buf, 'getvalue'): 53 self._buffer.write(buf.getvalue()) 54 elif hasattr(buf, 'read') and hasattr(buf, 'seek') and hasattr(buf, 'tell'): 55 old_pos = buf.tell() 56 buf.seek(0) 57 self._buffer.write(buf.read()) 58 buf.seek(old_pos) 59 elif buf is None: 60 pass 61 else: 62 raise TypeError, "Unable to coerce buf->StringIO" 63 64 self._get_len() 65 self._len_changed = False 66 self._buffer.seek(0, 0)
67
68 - def close(self):
69 self._buffer.close() 70 self._len = 0 71 self._len_changed = False
72
73 - def flush(self):
74 self._buffer.flush()
75
76 - def getvalue(self):
77 return self._buffer.getvalue()
78
79 - def next(self):
80 return self._buffer.next()
81
82 - def read(self, n=-1):
83 bytes = self._buffer.read(n) 84 85 return bytes
86
87 - def readline(self):
88 line = self._buffer.readline() 89 90 return line
91
92 - def readlines(self, sizehint=0):
93 """ 94 @type sizehint: C{int} 95 @param sizehint: 96 @note: This function does not consume the buffer. 97 """ 98 lines = self._buffer.readlines(sizehint) 99 100 return lines
101
102 - def seek(self, pos, mode=0):
103 return self._buffer.seek(pos, mode)
104
105 - def tell(self):
106 return self._buffer.tell()
107
108 - def truncate(self, size=0):
109 self._buffer = StringIOProxy._wrapped_class() 110 self._len_changed = True
111
112 - def write(self, s):
113 self._buffer.write(s) 114 self._len_changed = True
115
116 - def writelines(self, iterable):
117 self._buffer.writelines(iterable) 118 self._len_changed = True
119
120 - def _get_len(self):
121 if hasattr(self._buffer, 'len'): 122 self._len = self._buffer.len 123 124 return 125 126 old_pos = self._buffer.tell() 127 self._buffer.seek(0, 2) 128 129 self._len = self._buffer.tell() 130 self._buffer.seek(old_pos)
131
132 - def __len__(self):
133 if not self._len_changed: 134 return self._len 135 136 self._get_len() 137 self._len_changed = False 138 139 return self._len
140
141 -class DataTypeMixIn(object):
142 """ 143 Provides methods for reading and writing basic data types for file-like 144 objects. 145 """ 146 147 ENDIAN_NETWORK = "!" 148 ENDIAN_NATIVE = "@" 149 ENDIAN_LITTLE = "<" 150 ENDIAN_BIG = ">" 151 152 endian = ENDIAN_NETWORK 153
154 - def _read(self, length):
155 """ 156 Reads C{length} bytes from the stream. If an attempt to read past the 157 end of the buffer is made, L{EOFError} is raised. 158 """ 159 bytes = self.read(length) 160 161 if len(bytes) != length: 162 self.seek(0 - len(bytes), 1) 163 164 raise EOFError("Tried to read %d byte(s) from the stream" % length) 165 166 return bytes
167
168 - def read_uchar(self):
169 """ 170 Reads an C{unsigned char} from the stream. 171 """ 172 return struct.unpack("B", self._read(1))[0]
173
174 - def write_uchar(self, c):
175 """ 176 Writes an C{unsigned char} to the stream. 177 """ 178 if not 0 <= c <= 255: 179 raise ValueError("Not in range, %d" % c) 180 181 self.write(struct.pack("B", c))
182
183 - def read_char(self):
184 """ 185 Reads a C{char} from the stream. 186 """ 187 return struct.unpack("b", self._read(1))[0]
188
189 - def write_char(self, c):
190 """ 191 Write a C{char} to the stream. 192 """ 193 if not -128 <= c <= 127: 194 raise ValueError("Not in range, %d" % c) 195 196 self.write(struct.pack("b", c))
197
198 - def read_ushort(self):
199 """ 200 Reads a 2 byte unsigned integer from the stream. 201 """ 202 return struct.unpack("%sH" % self.endian, self._read(2))[0]
203
204 - def write_ushort(self, s):
205 """ 206 Writes a 2 byte unsigned integer to the stream. 207 """ 208 if not 0 <= s <= 65535: 209 raise ValueError("Not in range, %d" % s) 210 211 self.write(struct.pack("%sH" % self.endian, s))
212
213 - def read_short(self):
214 """ 215 Reads a 2 byte integer from the stream. 216 """ 217 return struct.unpack("%sh" % self.endian, self._read(2))[0]
218
219 - def write_short(self, s):
220 """ 221 Writes a 2 byte integer to the stream. 222 """ 223 if not -32768 <= s <= 32767: 224 raise ValueError("Not in range, %d" % s) 225 226 self.write(struct.pack("%sh" % self.endian, s))
227
228 - def read_ulong(self):
229 """ 230 Reads a 4 byte unsigned integer from the stream. 231 """ 232 return struct.unpack("%sL" % self.endian, self._read(4))[0]
233
234 - def write_ulong(self, l):
235 """ 236 Writes a 4 byte unsigned integer to the stream. 237 """ 238 if not 0 <= l <= 4294967295: 239 raise ValueError("Not in range, %d" % l) 240 241 self.write(struct.pack("%sL" % self.endian, l))
242
243 - def read_long(self):
244 """ 245 Reads a 4 byte integer from the stream. 246 """ 247 return struct.unpack("%sl" % self.endian, self._read(4))[0]
248
249 - def write_long(self, l):
250 """ 251 Writes a 4 byte integer to the stream. 252 """ 253 if not -2147483648 <= l <= 2147483647: 254 raise ValueError("Not in range, %d" % l) 255 256 self.write(struct.pack("%sl" % self.endian, l))
257
258 - def read_double(self):
259 """ 260 Reads an 8 byte float from the stream. 261 """ 262 return struct.unpack("%sd" % self.endian, self._read(8))[0]
263
264 - def write_double(self, d):
265 """ 266 Writes an 8 byte float to the stream. 267 """ 268 self.write(struct.pack("%sd" % self.endian, d))
269
270 - def read_float(self):
271 """ 272 Reads a 4 byte float from the stream. 273 """ 274 return struct.unpack("%sf" % self.endian, self._read(4))[0]
275
276 - def write_float(self, f):
277 """ 278 Writes a 4 byte float to the stream. 279 """ 280 self.write(struct.pack("%sf" % self.endian, f))
281
282 - def read_utf8_string(self, length):
283 """ 284 Reads a UTF-8 string from the stream. 285 286 @rtype: C{unicode} 287 """ 288 str = struct.unpack("%s%ds" % (self.endian, length), self.read(length))[0] 289 290 return unicode(str, "utf8")
291
292 - def write_utf8_string(self, u):
293 """ 294 Writes a unicode object to the stream in UTF-8 295 """ 296 bytes = u.encode("utf8") 297 298 self.write(struct.pack("%s%ds" % (self.endian, len(bytes)), bytes))
299
300 -class BufferedByteStream(StringIOProxy, DataTypeMixIn):
301 """ 302 An extension of C{StringIO}. 303 304 Features: 305 - Raises C{EOFError} if reading past end. 306 - Allows you to C{peek()} at the next byte. 307 """ 308
309 - def __init__(self, buf=None):
310 """ 311 @param buf: Initial byte stream. 312 @type buf: C{str} or C{StringIO} instance 313 """ 314 StringIOProxy.__init__(self, buf=buf) 315 316 self.seek(0)
317
318 - def read(self, length=-1):
319 """ 320 Read bytes from stream. 321 322 If we are at the end of the buffer, a C{EOFError} is raised. 323 If there is not enough buffer to be read and length is 324 specified C{IOError} is raised. 325 326 @param length: Number of bytes to read. 327 @type length: C{int} 328 @raise EOFError: Reading past end of stream. 329 @raise IOError: Length specified but not enough buffer 330 available. 331 332 @rtype: array of C{char} 333 @return: The bytes read from the stream. 334 """ 335 if length > 0 and self.at_eof(): 336 raise EOFError 337 if length > 0 and self.tell() + length > len(self): 338 raise IOError 339 340 return StringIOProxy.read(self, length)
341
342 - def peek(self, size=1):
343 """ 344 Looks size bytes ahead in the stream, returning what it finds, 345 returning the stream pointer to its initial position. 346 347 @param size: 348 @type size: C{int} 349 @raise ValueError: Trying to peek backwards. 350 351 @rtype: 352 @return: Bytes. 353 """ 354 if size == -1: 355 return self.peek(len(self) - self.tell()) 356 357 if size < -1: 358 raise ValueError("Cannot peek backwards") 359 360 bytes = '' 361 pos = self.tell() 362 363 while not self.at_eof() and len(bytes) != size: 364 bytes += self.read(1) 365 366 self.seek(pos) 367 368 return bytes
369
370 - def at_eof(self):
371 """ 372 Returns true if C{next.read(1)} will trigger an C{EOFError}. 373 374 @rtype: C{bool} 375 @return: 376 """ 377 return self.tell() >= len(self)
378
379 - def remaining(self):
380 """ 381 Returns number of remaining bytes. 382 383 @rtype: C{number} 384 @return: Number of remaining bytes. 385 """ 386 return len(self) - self.tell()
387
388 - def __add__(self, other):
389 old_pos = self.tell() 390 old_other_pos = other.tell() 391 392 new = BufferedByteStream(self) 393 394 other.seek(0) 395 new.seek(0, 2) 396 new.write(other.read()) 397 398 self.seek(old_pos) 399 other.seek(old_other_pos) 400 new.seek(0) 401 402 return new
403
404 -class NetworkIOMixIn(DataTypeMixIn):
405 - def __init__(self, *args, **kwargs):
406 import warnings 407 408 warnings.warn('%s is deprecated and will be removed in 0.4' % \ 409 type(self).__class__.__name__ , DeprecationWarning)
410
411 -def hexdump(data):
412 """ 413 Get hexadecimal representation of C{StringIO} data. 414 415 @type data: 416 @param data: 417 @rtype: C{str} 418 @return: Hexadecimal string. 419 """ 420 import string 421 422 hex = ascii = buf = "" 423 index = 0 424 425 for c in data: 426 hex += "%02x " % ord(c) 427 if c in string.printable and c not in string.whitespace: 428 ascii += c 429 else: 430 ascii += "." 431 432 if len(ascii) == 16: 433 buf += "%04x: %s %s %s\n" % (index, hex[:24], hex[24:], ascii) 434 hex = ascii = "" 435 index += 16 436 437 if len(ascii): 438 buf += "%04x: %-24s %-24s %s\n" % (index, hex[:24], hex[24:], ascii) 439 440 return buf
441
442 -def get_timestamp(d):
443 """ 444 Returns a UTC timestamp for a C{datetime.datetime} object. 445 446 @type d: C{datetime.datetime} 447 @param d: 448 @return: UTC timestamp. 449 @rtype: C{str} 450 451 @note: Inspiration taken from the U{Intertwingly blog 452 <http://intertwingly.net/blog/2007/09/02/Dealing-With-Dates>}. 453 """ 454 if isinstance(d, datetime.date) and not isinstance(d, datetime.datetime): 455 d = datetime.datetime.combine(d, datetime.time(0, 0, 0, 0)) 456 457 return calendar.timegm(d.utctimetuple())
458
459 -def get_datetime(secs):
460 """ 461 Return a UTC date from a timestamp. 462 463 @type secs: C{long} 464 @param secs: Seconds since 1970. 465 @return: UTC timestamp. 466 @rtype: C{datetime.datetime} 467 """ 468 return datetime.datetime.utcfromtimestamp(secs)
469
470 -def make_classic_instance(klass):
471 """ 472 Create an instance of a classic class (not inherited from ``object``) 473 without calling __init__(). 474 475 @type klass: C{class} 476 @param klass: The classic class to create an instance for. 477 @rtype: 478 @return: instance created 479 """ 480 assert isinstance(klass, types.ClassType), "not an old style class" 481 482 class _TemporaryClass: 483 pass
484 485 inst = _TemporaryClass() 486 inst.__class__ = klass 487 488 return inst 489
490 -def get_mro(C):
491 """ 492 Compute the class precedence list (mro) 493 """ 494 def merge(seqs): 495 res = [] 496 i = 0 497 498 while 1: 499 nonemptyseqs = [seq for seq in seqs if seq] 500 if not nonemptyseqs: 501 return res 502 503 i += 1 504 for seq in nonemptyseqs: 505 cand = seq[0] 506 nothead = [s for s in nonemptyseqs if cand in s[1:]] 507 508 if nothead: 509 cand = None 510 else: 511 break 512 513 if not cand: 514 raise NameError, "Inconsistent hierarchy" 515 516 res.append(cand) 517 518 for seq in nonemptyseqs: 519 if seq[0] == cand: 520 del seq[0]
521 522 if not isinstance(C, (types.ClassType, types.ObjectType)): 523 raise TypeError, 'class type expected' 524 525 if hasattr(C, '__mro__'): 526 return C.__mro__ 527 528 return merge([[C]] + map(get_mro, C.__bases__) + [list(C.__bases__)]) 529
530 -def get_attrs(obj):
531 """ 532 Gets a dict of the attrs of an object in a predefined resolution order 533 """ 534 if hasattr(obj, '__getstate__'): 535 return obj.__getstate__() 536 elif hasattr(obj, 'iteritems'): 537 attrs = {} 538 539 for k, v in obj.iteritems(): 540 attrs[k] = v 541 542 return attrs 543 elif hasattr(obj, '__dict__'): 544 return obj.__dict__ 545 546 return None
547
548 -def get_instance_attrs(obj, alias):
549 obj_attrs = None 550 551 if alias is not None: 552 attrs = alias.getAttrs(obj) 553 554 if attrs is not None: 555 obj_attrs = {} 556 557 for at in attrs: 558 obj_attrs[at] = getattr(obj, at) 559 560 if obj_attrs is None: 561 obj_attrs = get_attrs(obj) 562 563 return obj_attrs
564 565 if sys.version_info < (2, 5) or sys.platform.startswith('win'): 566 # workaround for python2.4's shortcomings with exceptional floats 567 # see: http://blog.pyamf.org/archives/when-is-nan-not-a-number-with-python-24 568 import fpconst 569 570 if not fpconst.isNaN(struct.unpack("!d", '\xff\xf8\x00\x00\x00\x00\x00\x00')[0]):
571 - def read_double_workaround(self):
572 bytes = self._read(8) 573 574 if bytes == '\xff\xf8\x00\x00\x00\x00\x00\x00': 575 return fpconst.NaN 576 577 if bytes == '\xff\xf0\x00\x00\x00\x00\x00\x00': 578 return fpconst.NegInf 579 580 if bytes == '\x7f\xf0\x00\x00\x00\x00\x00\x00': 581 return fpconst.PosInf 582 583 return struct.unpack("%sd" % self.endian, bytes)[0]
584 585 DataTypeMixIn.read_double = read_double_workaround 586
587 - def write_double_workaround(self, d):
588 if fpconst.isNaN(d): 589 self.write('\xff\xf8\x00\x00\x00\x00\x00\x00') 590 elif fpconst.isNegInf(d): 591 self.write('\xff\xf0\x00\x00\x00\x00\x00\x00') 592 elif fpconst.isPosInf(d): 593 self.write('\x7f\xf0\x00\x00\x00\x00\x00\x00') 594 else: 595 write_double_workaround.old_func(self, d)
596 597 x = DataTypeMixIn.write_double 598 DataTypeMixIn.write_double = write_double_workaround 599 write_double_workaround.old_func = x 600