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
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
83 if x.__name__ == 'instance':
84 xml_types.remove(x)
85
86 xml_types = tuple(xml_types)
87
88 return xml_types
89
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
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
127 self._buffer.close()
128 self._len = 0
129 self._len_changed = False
130
133
136
138 return self._buffer.next()
139
140 - def read(self, n=-1):
141 bytes = self._buffer.read(n)
142
143 return bytes
144
146 line = self._buffer.readline()
147
148 return line
149
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
164 return self._buffer.tell()
165
181
183 self._buffer.write(s)
184 self._len_changed = True
185
187 self._buffer.writelines(iterable)
188 self._len_changed = True
189
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
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
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
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
262
264 """
265 Reads an C{unsigned char} from the stream.
266 """
267 return struct.unpack("B", self._read(1))[0]
268
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
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 @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
298 """
299 Reads a 2 byte unsigned integer from the stream.
300 """
301 return struct.unpack("%sH" % self.endian, self._read(2))[0]
302
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
315 """
316 Reads a 2 byte integer from the stream.
317 """
318 return struct.unpack("%sh" % self.endian, self._read(2))[0]
319
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
332 """
333 Reads a 4 byte unsigned integer from the stream.
334 """
335 return struct.unpack("%sL" % self.endian, self._read(4))[0]
336
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
349 """
350 Reads a 4 byte integer from the stream.
351 """
352 return struct.unpack("%sl" % self.endian, self._read(4))[0]
353
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
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
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
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
414 n -= 0x1000000
415
416 return n
417
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
441 """
442 Reads an 8 byte float from the stream.
443 """
444 return struct.unpack("%sd" % self.endian, self._read(8))[0]
445
447 """
448 Writes an 8 byte float to the stream.
449 """
450 self.write(struct.pack("%sd" % self.endian, d))
451
453 """
454 Reads a 4 byte float from the stream.
455 """
456 return struct.unpack("%sf" % self.endian, self._read(4))[0]
457
459 """
460 Writes a 4 byte float to the stream.
461 """
462 self.write(struct.pack("%sf" % self.endian, f))
463
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
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
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
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
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
590
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
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
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
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
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
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
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
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
784
786 if use_hash is True:
787 self.func = hash
788 else:
789 self.func = id
790
791 self.clear()
792
794 self.list = []
795 self.dict = {}
796
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
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
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
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
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
856 return len(self.list)
857
860
868
870 return '<%s list=%r dict=%r>' % (self.__class__.__name__, self.list, self.dict)
871
873 return iter(self.list)
874
876 """
877 Like L{IndexedCollection}, but also maps to another object.
878
879 @since: 0.4
880 """
881
885
889
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
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
919
921 """
922 Determines if the supplied C{obj} param is a valid ElementTree element.
923 """
924 return isinstance(obj, xml_types)
925
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
936 nan = 1e300000/1e300000
937
938 return str(nan) != str(struct.unpack("!d", '\xff\xf8\x00\x00\x00\x00\x00\x00')[0])
939
940
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
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
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
1010
1016 except ImportError:
1017 pass
1018