1
2
3
4
5
6 """
7 AMF Utilities.
8
9 @since: 0.1.0
10 """
11
12 import struct
13 import calendar
14 import datetime
15 import types
16 import inspect
17
18 import pyamf
19
20 try:
21 from cStringIO import StringIO
22 except ImportError:
23 from StringIO import StringIO
24
25 try:
26 set
27 except NameError:
28 from sets import Set as set
29
30
31
32 xml_types = None
33 ET = None
34
35
36 negative_timestamp_broken = False
37
38 int_types = [int]
39 str_types = [str]
40
41
42 try:
43 int_types.append(long)
44 except NameError:
45 pass
46
47 try:
48 str_types.append(unicode)
49 except NameError:
50 pass
51
52
53 int_types = tuple(int_types)
54
55 str_types = tuple(str_types)
56
57 PosInf = 1e300000
58 NegInf = -1e300000
59
60 NaN = PosInf / PosInf
61
62
64 """
65 Run through a predefined order looking through the various C{ElementTree}
66 implementations so that any type can be encoded but PyAMF will return
67 elements as the first implementation found.
68
69 We work through the C implementations first - then the pure Python
70 versions. The downside to this is that a possible of three libraries will
71 be loaded into memory that are not used but the libs are small
72 (relatively) and the flexibility that this gives seems to outweigh the
73 cost. Time will tell.
74
75 @since: 0.4
76 """
77 global xml_types, ET
78
79 xml_types = []
80
81 try:
82 import xml.etree.cElementTree as cET
83
84 ET = cET
85 xml_types.append(type(cET.Element('foo')))
86 except ImportError:
87 pass
88
89 try:
90 import cElementTree as cET
91
92 if ET is None:
93 ET = cET
94
95 xml_types.append(type(cET.Element('foo')))
96 except ImportError:
97 pass
98
99 try:
100 import xml.etree.ElementTree as pET
101
102 if ET is None:
103 ET = pET
104
105 xml_types.append(pET._ElementInterface)
106 except ImportError:
107 pass
108
109 try:
110 import elementtree.ElementTree as pET
111
112 if ET is None:
113 ET = pET
114
115 xml_types.append(pET._ElementInterface)
116 except ImportError:
117 pass
118
119 for x in xml_types[:]:
120
121 if x.__name__ == 'instance':
122 xml_types.remove(x)
123
124 xml_types = tuple(xml_types)
125
126 return xml_types
127
128
130 """
131 I am a C{StringIO} type object containing byte data from the AMF stream.
132
133 @see: U{ByteArray on OSFlash (external)
134 <http://osflash.org/documentation/amf3#x0c_-_bytearray>}
135 @see: U{Parsing ByteArrays on OSFlash (external)
136 <http://osflash.org/documentation/amf3/parsing_byte_arrays>}
137 """
138
139 _wrapped_class = StringIO
140
142 """
143 @raise TypeError: Unable to coerce C{buf} to C{StringIO}.
144 """
145 self._buffer = StringIOProxy._wrapped_class()
146
147 if isinstance(buf, (str, unicode)):
148 self._buffer.write(buf)
149 elif hasattr(buf, 'getvalue'):
150 self._buffer.write(buf.getvalue())
151 elif hasattr(buf, 'read') and hasattr(buf, 'seek') and hasattr(buf, 'tell'):
152 old_pos = buf.tell()
153 buf.seek(0)
154 self._buffer.write(buf.read())
155 buf.seek(old_pos)
156 elif buf is None:
157 pass
158 else:
159 raise TypeError("Unable to coerce buf->StringIO")
160
161 self._get_len()
162 self._len_changed = False
163 self._buffer.seek(0, 0)
164
166 """
167 Get raw data from buffer.
168 """
169 return self._buffer.getvalue()
170
171 - def read(self, n=-1):
172 """
173 Reads C{n} bytes from the stream.
174 """
175 bytes = self._buffer.read(n)
176
177 return bytes
178
179 - def seek(self, pos, mode=0):
180 """
181 Sets the file-pointer offset, measured from the beginning of this stream,
182 at which the next write operation will occur.
183
184 @param pos:
185 @type pos: C{int}
186 @param mode:
187 @type mode: C{int}
188 """
189 return self._buffer.seek(pos, mode)
190
192 """
193 Returns the position of the stream pointer.
194 """
195 return self._buffer.tell()
196
198 """
199 Truncates the stream to the specified length.
200
201 @param size: The length of the stream, in bytes.
202 @type size: C{int}
203 """
204 if size == 0:
205 self._buffer = StringIOProxy._wrapped_class()
206 self._len_changed = True
207
208 return
209
210 cur_pos = self.tell()
211 self.seek(0)
212 buf = self.read(size)
213 self._buffer = StringIOProxy._wrapped_class()
214
215 self._buffer.write(buf)
216 self.seek(cur_pos)
217 self._len_changed = True
218
220 """
221 Writes the content of the specified C{s} into this buffer.
222
223 @param s:
224 @type s:
225 """
226 self._buffer.write(s)
227 self._len_changed = True
228
230 """
231 Return total number of bytes in buffer.
232 """
233 if hasattr(self._buffer, 'len'):
234 self._len = self._buffer.len
235
236 return
237
238 old_pos = self._buffer.tell()
239 self._buffer.seek(0, 2)
240
241 self._len = self._buffer.tell()
242 self._buffer.seek(old_pos)
243
245 if not self._len_changed:
246 return self._len
247
248 self._get_len()
249 self._len_changed = False
250
251 return self._len
252
254 """
255 Chops the tail off the stream starting at 0 and ending at C{tell()}.
256 The stream pointer is set to 0 at the end of this function.
257
258 @since: 0.4
259 """
260 try:
261 bytes = self.read()
262 except IOError:
263 bytes = ''
264
265 self.truncate()
266
267 if len(bytes) > 0:
268 self.write(bytes)
269 self.seek(0)
270
271
273 """
274 Provides methods for reading and writing basic data types for file-like
275 objects.
276
277 @ivar endian: Byte ordering used to represent the data. Default byte order
278 is L{ENDIAN_NETWORK}.
279 @type endian: C{str}
280 """
281
282
283 ENDIAN_NETWORK = "!"
284
285 ENDIAN_NATIVE = "@"
286
287 ENDIAN_LITTLE = "<"
288
289 ENDIAN_BIG = ">"
290
291 endian = ENDIAN_NETWORK
292
293 - def _read(self, length):
294 """
295 Reads C{length} bytes from the stream. If an attempt to read past the
296 end of the buffer is made, L{IOError} is raised.
297 """
298 bytes = self.read(length)
299
300 if len(bytes) != length:
301 self.seek(0 - len(bytes), 1)
302
303 raise IOError("Tried to read %d byte(s) from the stream" % length)
304
305 return bytes
306
317
319 """
320 Reads an C{unsigned char} from the stream.
321 """
322 return struct.unpack("B", self._read(1))[0]
323
325 """
326 Writes an C{unsigned char} to the stream.
327
328 @param c: Unsigned char
329 @type c: C{int}
330 @raise TypeError: Unexpected type for int C{c}.
331 @raise OverflowError: Not in range.
332 """
333 if type(c) not in int_types:
334 raise TypeError('expected an int (got:%r)' % (type(c),))
335
336 if not 0 <= c <= 255:
337 raise OverflowError("Not in range, %d" % c)
338
339 self.write(struct.pack("B", c))
340
342 """
343 Reads a C{char} from the stream.
344 """
345 return struct.unpack("b", self._read(1))[0]
346
348 """
349 Write a C{char} to the stream.
350
351 @param c: char
352 @type c: C{int}
353 @raise TypeError: Unexpected type for int C{c}.
354 @raise OverflowError: Not in range.
355 """
356 if type(c) not in int_types:
357 raise TypeError('expected an int (got:%r)' % (type(c),))
358
359 if not -128 <= c <= 127:
360 raise OverflowError("Not in range, %d" % c)
361
362 self.write(struct.pack("b", c))
363
365 """
366 Reads a 2 byte unsigned integer from the stream.
367 """
368 return struct.unpack("%sH" % self.endian, self._read(2))[0]
369
371 """
372 Writes a 2 byte unsigned integer to the stream.
373
374 @param s: 2 byte unsigned integer
375 @type s: C{int}
376 @raise TypeError: Unexpected type for int C{s}.
377 @raise OverflowError: Not in range.
378 """
379 if type(s) not in int_types:
380 raise TypeError('expected an int (got:%r)' % (type(s),))
381
382 if not 0 <= s <= 65535:
383 raise OverflowError("Not in range, %d" % s)
384
385 self.write(struct.pack("%sH" % self.endian, s))
386
388 """
389 Reads a 2 byte integer from the stream.
390 """
391 return struct.unpack("%sh" % self.endian, self._read(2))[0]
392
394 """
395 Writes a 2 byte integer to the stream.
396
397 @param s: 2 byte integer
398 @type s: C{int}
399 @raise TypeError: Unexpected type for int C{s}.
400 @raise OverflowError: Not in range.
401 """
402 if type(s) not in int_types:
403 raise TypeError('expected an int (got:%r)' % (type(s),))
404
405 if not -32768 <= s <= 32767:
406 raise OverflowError("Not in range, %d" % s)
407
408 self.write(struct.pack("%sh" % self.endian, s))
409
411 """
412 Reads a 4 byte unsigned integer from the stream.
413 """
414 return struct.unpack("%sL" % self.endian, self._read(4))[0]
415
417 """
418 Writes a 4 byte unsigned integer to the stream.
419
420 @param l: 4 byte unsigned integer
421 @type l: C{int}
422 @raise TypeError: Unexpected type for int C{l}.
423 @raise OverflowError: Not in range.
424 """
425 if type(l) not in int_types:
426 raise TypeError('expected an int (got:%r)' % (type(l),))
427
428 if not 0 <= l <= 4294967295:
429 raise OverflowError("Not in range, %d" % l)
430
431 self.write(struct.pack("%sL" % self.endian, l))
432
434 """
435 Reads a 4 byte integer from the stream.
436 """
437 return struct.unpack("%sl" % self.endian, self._read(4))[0]
438
440 """
441 Writes a 4 byte integer to the stream.
442
443 @param l: 4 byte integer
444 @type l: C{int}
445 @raise TypeError: Unexpected type for int C{l}.
446 @raise OverflowError: Not in range.
447 """
448 if type(l) not in int_types:
449 raise TypeError('expected an int (got:%r)' % (type(l),))
450
451 if not -2147483648 <= l <= 2147483647:
452 raise OverflowError("Not in range, %d" % l)
453
454 self.write(struct.pack("%sl" % self.endian, l))
455
457 """
458 Reads a 24 bit unsigned integer from the stream.
459
460 @since: 0.4
461 """
462 order = None
463
464 if not self._is_big_endian():
465 order = [0, 8, 16]
466 else:
467 order = [16, 8, 0]
468
469 n = 0
470
471 for x in order:
472 n += (self.read_uchar() << x)
473
474 return n
475
477 """
478 Writes a 24 bit unsigned integer to the stream.
479
480 @since: 0.4
481 @param n: 24 bit unsigned integer
482 @type n: C{int}
483 @raise TypeError: Unexpected type for int C{n}.
484 @raise OverflowError: Not in range.
485 """
486 if type(n) not in int_types:
487 raise TypeError('expected an int (got:%r)' % (type(n),))
488
489 if not 0 <= n <= 0xffffff:
490 raise OverflowError("n is out of range")
491
492 order = None
493
494 if not self._is_big_endian():
495 order = [0, 8, 16]
496 else:
497 order = [16, 8, 0]
498
499 for x in order:
500 self.write_uchar((n >> x) & 0xff)
501
503 """
504 Reads a 24 bit integer from the stream.
505
506 @since: 0.4
507 """
508 n = self.read_24bit_uint()
509
510 if n & 0x800000 != 0:
511
512 n -= 0x1000000
513
514 return n
515
517 """
518 Writes a 24 bit integer to the stream.
519
520 @since: 0.4
521 @param n: 24 bit integer
522 @type n: C{int}
523 @raise TypeError: Unexpected type for int C{n}.
524 @raise OverflowError: Not in range.
525 """
526 if type(n) not in int_types:
527 raise TypeError('expected an int (got:%r)' % (type(n),))
528
529 if not -8388608 <= n <= 8388607:
530 raise OverflowError("n is out of range")
531
532 order = None
533
534 if not self._is_big_endian():
535 order = [0, 8, 16]
536 else:
537 order = [16, 8, 0]
538
539 if n < 0:
540 n += 0x1000000
541
542 for x in order:
543 self.write_uchar((n >> x) & 0xff)
544
546 """
547 Reads an 8 byte float from the stream.
548 """
549 return struct.unpack("%sd" % self.endian, self._read(8))[0]
550
552 """
553 Writes an 8 byte float to the stream.
554
555 @param d: 8 byte float
556 @type d: C{float}
557 @raise TypeError: Unexpected type for float C{d}.
558 """
559 if not type(d) is float:
560 raise TypeError('expected a float (got:%r)' % (type(d),))
561
562 self.write(struct.pack("%sd" % self.endian, d))
563
565 """
566 Reads a 4 byte float from the stream.
567 """
568 return struct.unpack("%sf" % self.endian, self._read(4))[0]
569
571 """
572 Writes a 4 byte float to the stream.
573
574 @param f: 4 byte float
575 @type f: C{float}
576 @raise TypeError: Unexpected type for float C{f}.
577 """
578 if type(f) is not float:
579 raise TypeError('expected a float (got:%r)' % (type(f),))
580
581 self.write(struct.pack("%sf" % self.endian, f))
582
584 """
585 Reads a UTF-8 string from the stream.
586
587 @rtype: C{unicode}
588 """
589 str = struct.unpack("%s%ds" % (self.endian, length), self.read(length))[0]
590
591 return unicode(str, "utf8")
592
594 """
595 Writes a unicode object to the stream in UTF-8.
596
597 @param u: unicode object
598 @raise TypeError: Unexpected type for str C{u}.
599 """
600 if type(u) not in str_types:
601 raise TypeError('expected a str (got:%r)' % (type(u),))
602
603 bytes = u.encode("utf8")
604
605 self.write(struct.pack("%s%ds" % (self.endian, len(bytes)), bytes))
606
607
608 if struct.pack('@H', 1)[0] == '\x01':
609 DataTypeMixIn._system_endian = DataTypeMixIn.ENDIAN_LITTLE
610 else:
611 DataTypeMixIn._system_endian = DataTypeMixIn.ENDIAN_BIG
612
613
615 """
616 An extension of C{StringIO}.
617
618 Features:
619 - Raises L{IOError} if reading past end.
620 - Allows you to C{peek()} at the next byte.
621
622 @see: L{cBufferedByteStream<cpyamf.util.cBufferedByteStream>}
623 """
624
626 """
627 @param buf: Initial byte stream.
628 @type buf: C{str} or C{StringIO} instance
629 """
630 StringIOProxy.__init__(self, buf=buf)
631
632 self.seek(0)
633
634 - def read(self, length=-1):
635 """
636 Reads up to the specified number of bytes from the stream into
637 the specified byte array of specified length.
638
639 @raise IOError: Attempted to read past the end of the buffer.
640 """
641 if length == -1 and self.at_eof():
642 raise IOError('Attempted to read from the buffer but already at '
643 'the end')
644 elif length > 0 and self.tell() + length > len(self):
645 raise IOError('Attempted to read %d bytes from the buffer but '
646 'only %d remain' % (length, len(self) - self.tell()))
647
648 return StringIOProxy.read(self, length)
649
650 - def peek(self, size=1):
651 """
652 Looks C{size} bytes ahead in the stream, returning what it finds,
653 returning the stream pointer to its initial position.
654
655 @param size: Default is 1.
656 @type size: C{int}
657 @raise ValueError: Trying to peek backwards.
658
659 @rtype:
660 @return: Bytes.
661 """
662 if size == -1:
663 return self.peek(len(self) - self.tell())
664
665 if size < -1:
666 raise ValueError("Cannot peek backwards")
667
668 bytes = ''
669 pos = self.tell()
670
671 while not self.at_eof() and len(bytes) != size:
672 bytes += self.read(1)
673
674 self.seek(pos)
675
676 return bytes
677
678 - def remaining(self):
679 """
680 Returns number of remaining bytes.
681
682 @rtype: C{number}
683 @return: Number of remaining bytes.
684 """
685 return len(self) - self.tell()
686
688 """
689 Returns C{True} if the internal pointer is at the end of the stream.
690
691 @rtype: C{bool}
692 """
693 return self.tell() == len(self)
694
696 """
697 Append data to the end of the stream. The pointer will not move if
698 this operation is successful.
699
700 @param data: The data to append to the stream.
701 @type data: C{str} or C{unicode}
702 @raise TypeError: data is not C{str} or C{unicode}
703 """
704 t = self.tell()
705
706
707 self.seek(0, 2)
708
709 if hasattr(data, 'getvalue'):
710 self.write_utf8_string(data.getvalue())
711 else:
712 self.write_utf8_string(data)
713
714 self.seek(t)
715
731
732
734 """
735 Get hexadecimal representation of C{StringIO} data.
736
737 @type data:
738 @param data:
739 @rtype: C{str}
740 @return: Hexadecimal string.
741 """
742 import string
743
744 hex = ascii = buf = ""
745 index = 0
746
747 for c in data:
748 hex += "%02x " % ord(c)
749 if c in string.printable and c not in string.whitespace:
750 ascii += c
751 else:
752 ascii += "."
753
754 if len(ascii) == 16:
755 buf += "%04x: %s %s %s\n" % (index, hex[:24], hex[24:], ascii)
756 hex = ascii = ""
757 index += 16
758
759 if len(ascii):
760 buf += "%04x: %-24s %-24s %s\n" % (index, hex[:24], hex[24:], ascii)
761
762 return buf
763
764
766 """
767 Returns a UTC timestamp for a C{datetime.datetime} object.
768
769 @type d: C{datetime.datetime}
770 @param d: The date object.
771 @return: UTC timestamp.
772 @rtype: C{str}
773
774 @note: Inspiration taken from the U{Intertwingly blog
775 <http://intertwingly.net/blog/2007/09/02/Dealing-With-Dates>}.
776 """
777 if isinstance(d, datetime.date) and not isinstance(d, datetime.datetime):
778 d = datetime.datetime.combine(d, datetime.time(0, 0, 0, 0))
779
780 msec = str(d.microsecond).rjust(6).replace(' ', '0')
781
782 return float('%s.%s' % (calendar.timegm(d.utctimetuple()), msec))
783
784
786 """
787 Return a UTC date from a timestamp.
788
789 @type secs: C{long}
790 @param secs: Seconds since 1970.
791 @return: UTC timestamp.
792 @rtype: C{datetime.datetime}
793 """
794 if secs < 0 and negative_timestamp_broken:
795 return datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=secs)
796
797 return datetime.datetime.utcfromtimestamp(secs)
798
799
801 """
802 @since: 0.5
803 """
804 if hasattr(obj, 'keys'):
805 return set(obj.keys())
806 elif hasattr(obj, '__dict__'):
807 return obj.__dict__.keys()
808
809 return []
810
811
813 """
814 Gets a C{dict} of the attrs of an object in a predefined resolution order.
815
816 @raise AttributeError: A duplicate attribute was already found in this
817 collection, are you mixing different key types?
818 """
819 if hasattr(obj, 'iteritems'):
820 attrs = {}
821
822 for k, v in obj.iteritems():
823 sk = str(k)
824
825 if sk in attrs.keys():
826 raise AttributeError('A duplicate attribute (%s) was '
827 'already found in this collection, are you mixing '
828 'different key types?' % (sk,))
829
830 attrs[sk] = v
831
832 return attrs
833 elif hasattr(obj, '__dict__'):
834 return obj.__dict__.copy()
835 elif hasattr(obj, '__slots__'):
836 attrs = {}
837
838 for k in obj.__slots__:
839 attrs[k] = getattr(obj, k)
840
841 return attrs
842
843 return None
844
845
847 """
848 A generic function which applies a collection of attributes C{attrs} to
849 object C{obj}.
850
851 @param obj: An instance implementing the C{__setattr__} function
852 @param attrs: A collection implementing the C{iteritems} function
853 @type attrs: Usually a dict
854 """
855 if isinstance(obj, (list, dict)):
856 for k, v in attrs.iteritems():
857 obj[k] = v
858
859 return
860
861 for k, v in attrs.iteritems():
862 setattr(obj, k, v)
863
864
866 """
867 Returns a alias class suitable for klass. Defaults to L{pyamf.ClassAlias}
868 """
869 for k, v in pyamf.ALIAS_TYPES.iteritems():
870 for kl in v:
871 if isinstance(kl, types.FunctionType):
872 if kl(klass) is True:
873 return k
874 elif isinstance(kl, (type, (types.ClassType, types.ObjectType))):
875 if issubclass(klass, kl):
876 return k
877
878 return pyamf.ClassAlias
879
880
882 """
883 Returns a C{boolean} whether or not the supplied class can accept dynamic
884 properties.
885
886 @rtype: C{bool}
887 @since: 0.5
888 """
889 mro = inspect.getmro(klass)
890 new = False
891
892 if mro[-1] is object:
893 mro = mro[:-1]
894 new = True
895
896 for kls in mro:
897 if new and '__dict__' in kls.__dict__:
898 return False
899
900 if not hasattr(kls, '__slots__'):
901 return False
902
903 return True
904
905
948
949
951 """
952 A class that provides a quick and clean way to store references and
953 referenced objects.
954
955 @note: All attributes on the instance are private.
956 @ivar exceptions: If C{True} then L{ReferenceError<pyamf.ReferenceError>}
957 will be raised, otherwise C{None} will be returned.
958 """
959
960 - def __init__(self, use_hash=False, exceptions=True):
969
971 """
972 Clears the index.
973 """
974 self.list = []
975 self.dict = {}
976
978 """
979 Returns an object based on the reference.
980
981 @raise TypeError: Bad reference type.
982 @raise pyamf.ReferenceError: Reference not found.
983 """
984 if not isinstance(ref, (int, long)):
985 raise TypeError("Bad reference type")
986
987 try:
988 return self.list[ref]
989 except IndexError:
990 if self.exceptions is False:
991 return None
992
993 raise pyamf.ReferenceError("Reference %r not found" % (ref,))
994
996 """
997 Returns a reference to C{obj} if it is contained within this index.
998
999 @raise pyamf.ReferenceError: Value not found.
1000 """
1001 try:
1002 return self.dict[self.func(obj)]
1003 except KeyError:
1004 if self.exceptions is False:
1005 return None
1006
1007 raise pyamf.ReferenceError("Value %r not found" % (obj,))
1008
1010 """
1011 Appends C{obj} to this index.
1012
1013 @note: Uniqueness is not checked
1014 @return: The reference to C{obj} in this index.
1015 """
1016 h = self.func(obj)
1017
1018 self.list.append(obj)
1019 idx = len(self.list) - 1
1020 self.dict[h] = idx
1021
1022 return idx
1023
1025 if isinstance(other, list):
1026 return self.list == other
1027 elif isinstance(other, dict):
1028 return self.dict == other
1029
1030 return False
1031
1033 return len(self.list)
1034
1037
1045
1047 return '<%s list=%r dict=%r>' % (self.__class__.__name__, self.list, self.dict)
1048
1050 return iter(self.list)
1051
1052
1054 """
1055 Like L{IndexedCollection}, but also maps to another object.
1056
1057 @since: 0.4
1058 """
1059
1060 - def __init__(self, use_hash=False, exceptions=True):
1062
1064 """
1065 Clears the index and mapping.
1066 """
1067 IndexedCollection.clear(self)
1068
1069 self.mapped = []
1070
1072 """
1073 Returns the mapped object by reference.
1074
1075 @raise TypeError: Bad reference type.
1076 @raise pyamf.ReferenceError: Reference not found.
1077 """
1078 if not isinstance(ref, (int, long)):
1079 raise TypeError("Bad reference type.")
1080
1081 try:
1082 return self.mapped[ref]
1083 except IndexError:
1084 if self.exceptions is False:
1085 return None
1086
1087 raise pyamf.ReferenceError("Reference %r not found" % ref)
1088
1090 """
1091 Appends C{obj} to this index.
1092
1093 @return: The reference to C{obj} in this index.
1094 """
1095 idx = IndexedCollection.append(self, obj)
1096 diff = (idx + 1) - len(self.mapped)
1097
1098 for i in range(0, diff):
1099 self.mapped.append(None)
1100
1101 return idx
1102
1103 - def map(self, obj, mapped_obj):
1104 """
1105 Maps an object.
1106 """
1107 idx = self.append(obj)
1108 self.mapped[idx] = mapped_obj
1109
1110 return idx
1111
1112
1114 """
1115 Determines if the supplied C{obj} param is a valid ElementTree element.
1116 """
1117 return isinstance(obj, xml_types)
1118
1119
1121 """
1122 Older versions of Python (<=2.5) and the Windows platform are renowned for
1123 mixing up 'special' floats. This function determines whether this is the
1124 case.
1125
1126 @since: 0.4
1127 @rtype: C{bool}
1128 """
1129 global NaN
1130
1131 return str(NaN) != str(struct.unpack("!d", '\xff\xf8\x00\x00\x00\x00\x00\x00')[0])
1132
1133
1135 """
1136 @since: 0.5
1137 """
1138 return str(float(val)) == str(NaN)
1139
1140
1142 """
1143 @since: 0.5
1144 """
1145 return str(float(val)) == str(PosInf)
1146
1147
1149 """
1150 @since: 0.5
1151 """
1152 return str(float(val)) == str(NegInf)
1153
1154
1155
1156
1157 find_xml_lib()
1158
1159 try:
1160 datetime.datetime.utcfromtimestamp(-31536000.0)
1161 except ValueError:
1162 negative_timestamp_broken = True
1163
1164 if is_float_broken():
1166 global PosInf, NegInf, NaN
1167
1168 """
1169 Override the L{DataTypeMixIn.read_double} method to fix problems
1170 with doubles by using the third-party C{fpconst} library.
1171 """
1172 bytes = self.read(8)
1173
1174 if self._is_big_endian():
1175 if bytes == '\xff\xf8\x00\x00\x00\x00\x00\x00':
1176 return NaN
1177
1178 if bytes == '\xff\xf0\x00\x00\x00\x00\x00\x00':
1179 return NegInf
1180
1181 if bytes == '\x7f\xf0\x00\x00\x00\x00\x00\x00':
1182 return PosInf
1183 else:
1184 if bytes == '\x00\x00\x00\x00\x00\x00\xf8\xff':
1185 return NaN
1186
1187 if bytes == '\x00\x00\x00\x00\x00\x00\xf0\xff':
1188 return NegInf
1189
1190 if bytes == '\x00\x00\x00\x00\x00\x00\xf0\x7f':
1191 return PosInf
1192
1193 return struct.unpack("%sd" % self.endian, bytes)[0]
1194
1195 DataTypeMixIn.read_double = read_double_workaround
1196
1198 """
1199 Override the L{DataTypeMixIn.write_double} method to fix problems
1200 with doubles by using the third-party C{fpconst} library.
1201 """
1202 if type(d) is not float:
1203 raise TypeError('expected a float (got:%r)' % (type(d),))
1204
1205 if isNaN(d):
1206 if self._is_big_endian():
1207 self.write('\xff\xf8\x00\x00\x00\x00\x00\x00')
1208 else:
1209 self.write('\x00\x00\x00\x00\x00\x00\xf8\xff')
1210 elif isNegInf(d):
1211 if self._is_big_endian():
1212 self.write('\xff\xf0\x00\x00\x00\x00\x00\x00')
1213 else:
1214 self.write('\x00\x00\x00\x00\x00\x00\xf0\xff')
1215 elif isPosInf(d):
1216 if self._is_big_endian():
1217 self.write('\x7f\xf0\x00\x00\x00\x00\x00\x00')
1218 else:
1219 self.write('\x00\x00\x00\x00\x00\x00\xf0\x7f')
1220 else:
1221 write_double_workaround.old_func(self, d)
1222
1223 x = DataTypeMixIn.write_double
1224 DataTypeMixIn.write_double = write_double_workaround
1225 write_double_workaround.old_func = x
1226
1227
1228 try:
1229 from cpyamf.util import BufferedByteStream, IndexedCollection, IndexedMap
1230
1237
1247 except ImportError:
1248 pass
1249