1
2
3
4
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
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
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
69 self._buffer.close()
70 self._len = 0
71 self._len_changed = False
72
75
78
80 return self._buffer.next()
81
82 - def read(self, n=-1):
83 bytes = self._buffer.read(n)
84
85 return bytes
86
88 line = self._buffer.readline()
89
90 return line
91
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
106 return self._buffer.tell()
107
111
113 self._buffer.write(s)
114 self._len_changed = True
115
117 self._buffer.writelines(iterable)
118 self._len_changed = True
119
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
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
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
169 """
170 Reads an C{unsigned char} from the stream.
171 """
172 return struct.unpack("B", self._read(1))[0]
173
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
184 """
185 Reads a C{char} from the stream.
186 """
187 return struct.unpack("b", self._read(1))[0]
188
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
199 """
200 Reads a 2 byte unsigned integer from the stream.
201 """
202 return struct.unpack("%sH" % self.endian, self._read(2))[0]
203
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
214 """
215 Reads a 2 byte integer from the stream.
216 """
217 return struct.unpack("%sh" % self.endian, self._read(2))[0]
218
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
229 """
230 Reads a 4 byte unsigned integer from the stream.
231 """
232 return struct.unpack("%sL" % self.endian, self._read(4))[0]
233
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
244 """
245 Reads a 4 byte integer from the stream.
246 """
247 return struct.unpack("%sl" % self.endian, self._read(4))[0]
248
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
259 """
260 Reads an 8 byte float from the stream.
261 """
262 return struct.unpack("%sd" % self.endian, self._read(8))[0]
263
265 """
266 Writes an 8 byte float to the stream.
267 """
268 self.write(struct.pack("%sd" % self.endian, d))
269
271 """
272 Reads a 4 byte float from the stream.
273 """
274 return struct.unpack("%sf" % self.endian, self._read(4))[0]
275
277 """
278 Writes a 4 byte float to the stream.
279 """
280 self.write(struct.pack("%sf" % self.endian, f))
281
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
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
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
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
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
403
406 import warnings
407
408 warnings.warn('%s is deprecated and will be removed in 0.4' % \
409 type(self).__class__.__name__ , DeprecationWarning)
410
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
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
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
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
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
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
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
567
568 import fpconst
569
570 if not fpconst.isNaN(struct.unpack("!d", '\xff\xf8\x00\x00\x00\x00\x00\x00')[0]):
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
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