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