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 return calendar.timegm(d.utctimetuple())
638
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
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
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
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
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
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
782
784 if use_hash is True:
785 self.func = hash
786 else:
787 self.func = id
788
789 self.clear()
790
792 self.list = []
793 self.dict = {}
794
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
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
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
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
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
854 return len(self.list)
855
858
866
868 return '<%s list=%r dict=%r>' % (self.__class__.__name__, self.list, self.dict)
869
871 return iter(self.list)
872
874 """
875 Like L{IndexedCollection}, but also maps to another object.
876
877 @since: 0.4
878 """
879
883
887
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
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
917
919 """
920 Determines if the supplied C{obj} param is a valid ElementTree element.
921 """
922 return isinstance(obj, xml_types)
923
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
934 nan = 1e300000/1e300000
935
936 return str(nan) != str(struct.unpack("!d", '\xff\xf8\x00\x00\x00\x00\x00\x00')[0])
937
938
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
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
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
1008
1014 except ImportError:
1015 pass
1016