Author: rhs
Date: Mon Nov 10 07:19:29 2008
New Revision: 712673

URL: http://svn.apache.org/viewvc?rev=712673&view=rev
Log:
added a timestamp class to qpid.datatypes and modified codec to use it for 
AMQP's datetime type; this fixes support of datetime within maps where formerly 
decoding and reencoding the same value would switch an entry in a map from an 
AMQP datetime to an AMQP uint64

Modified:
    incubator/qpid/trunk/qpid/python/qpid/codec010.py
    incubator/qpid/trunk/qpid/python/qpid/datatypes.py
    incubator/qpid/trunk/qpid/python/qpid/spec010.py
    incubator/qpid/trunk/qpid/python/tests/codec010.py
    incubator/qpid/trunk/qpid/python/tests/datatypes.py

Modified: incubator/qpid/trunk/qpid/python/qpid/codec010.py
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/codec010.py?rev=712673&r1=712672&r2=712673&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/codec010.py (original)
+++ incubator/qpid/trunk/qpid/python/qpid/codec010.py Mon Nov 10 07:19:29 2008
@@ -17,8 +17,9 @@
 # under the License.
 #
 
+import datetime
 from packer import Packer
-from datatypes import serial, RangedSet, Struct
+from datatypes import serial, timestamp, RangedSet, Struct
 
 class CodecException(Exception): pass
 
@@ -103,9 +104,11 @@
     self.pack("!q", n)
 
   def read_datetime(self):
-    return self.read_uint64()
-  def write_datetime(self, n):
-    self.write_uint64(n)
+    return timestamp(self.read_uint64())
+  def write_datetime(self, t):
+    if isinstance(t, datetime.datetime):
+      t = timestamp(t)
+    self.write_uint64(t)
 
   def read_double(self):
     return self.unpack("!d")

Modified: incubator/qpid/trunk/qpid/python/qpid/datatypes.py
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/datatypes.py?rev=712673&r1=712672&r2=712673&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/datatypes.py (original)
+++ incubator/qpid/trunk/qpid/python/qpid/datatypes.py Mon Nov 10 07:19:29 2008
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-import threading, struct
+import threading, struct, datetime, time
 
 class Struct:
 
@@ -296,3 +296,51 @@
 
   def __repr__(self):
     return "UUID(%r)" % str(self)
+
+class timestamp(float):
+
+  def __new__(cls, obj=None):
+    if obj is None:
+      obj = time.time()
+    elif isinstance(obj, datetime.datetime):
+      obj = time.mktime(obj.timetuple()) + 1e-6 * obj.microsecond
+    return super(timestamp, cls).__new__(cls, obj)
+
+  def datetime(self):
+    return datetime.datetime.fromtimestamp(self)
+
+  def __add__(self, other):
+    if isinstance(other, datetime.timedelta):
+      return timestamp(self.datetime() + other)
+    else:
+      return timestamp(float(self) + other)
+
+  def __sub__(self, other):
+    if isinstance(other, datetime.timedelta):
+      return timestamp(self.datetime() - other)
+    else:
+      return timestamp(float(self) - other)
+
+  def __radd__(self, other):
+    if isinstance(other, datetime.timedelta):
+      return timestamp(self.datetime() + other)
+    else:
+      return timestamp(other + float(self))
+
+  def __rsub__(self, other):
+    if isinstance(other, datetime.timedelta):
+      return timestamp(self.datetime() - other)
+    else:
+      return timestamp(other - float(self))
+
+  def __neg__(self):
+    return timestamp(-float(self))
+
+  def __pos__(self):
+    return self
+
+  def __abs__(self):
+    return timestamp(abs(float(self)))
+
+  def __repr__(self):
+    return "timestamp(%r)" % float(self)

Modified: incubator/qpid/trunk/qpid/python/qpid/spec010.py
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/spec010.py?rev=712673&r1=712672&r2=712673&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/spec010.py (original)
+++ incubator/qpid/trunk/qpid/python/qpid/spec010.py Mon Nov 10 07:19:29 2008
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-import os, cPickle, datatypes
+import os, cPickle, datatypes, datetime
 from codec010 import StringCodec
 from util import mtime, fill
 
@@ -477,7 +477,9 @@
     None.__class__: "void",
     list: "list",
     tuple: "list",
-    dict: "map"
+    dict: "map",
+    datatypes.timestamp: "datetime",
+    datetime.datetime: "datetime"
     }
 
   def __init__(self, major, minor, port, children):

Modified: incubator/qpid/trunk/qpid/python/tests/codec010.py
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/tests/codec010.py?rev=712673&r1=712672&r2=712673&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/python/tests/codec010.py (original)
+++ incubator/qpid/trunk/qpid/python/tests/codec010.py Mon Nov 10 07:19:29 2008
@@ -17,22 +17,27 @@
 # under the License.
 #
 
+import time
+
 from unittest import TestCase
 from qpid.spec010 import load
 from qpid.codec010 import StringCodec
 from qpid.testlib import testrunner
+from qpid.datatypes import timestamp
 
 class CodecTest(TestCase):
 
   def setUp(self):
     self.spec = load(testrunner.get_spec_file("amqp.0-10.xml"))
 
-  def check(self, type, value):
+  def check(self, type, value, compare=True):
     t = self.spec[type]
     sc = StringCodec(self.spec)
     t.encode(sc, value)
     decoded = t.decode(sc)
-    assert decoded == value, "%s, %s" % (decoded, value)
+    if compare:
+      assert decoded == value, "%s, %s" % (decoded, value)
+    return decoded
 
   def testMapString(self):
     self.check("map", {"string": "this is a test"})
@@ -43,6 +48,15 @@
   def testMapLong(self):
     self.check("map", {"long": 2**32})
 
+  def testMapTimestamp(self):
+    decoded = self.check("map", {"timestamp": timestamp(0)})
+    assert isinstance(decoded["timestamp"], timestamp)
+
+  def testMapDatetime(self):
+    decoded = self.check("map", {"datetime": timestamp(0).datetime()}, 
compare=False)
+    assert isinstance(decoded["datetime"], timestamp)
+    assert decoded["datetime"] == 0.0
+
   def testMapNone(self):
     self.check("map", {"none": None})
 
@@ -53,12 +67,14 @@
     self.check("map", {"list": [1, "two", 3.0, -4]})
 
   def testMapAll(self):
-    self.check("map", {"string": "this is a test",
-                       "int": 3,
-                       "long": 2**32,
-                       "none": None,
-                       "map": {"string": "nested map"},
-                       "list": [1, "two", 3.0, -4]})
+    decoded = self.check("map", {"string": "this is a test",
+                                 "int": 3,
+                                 "long": 2**32,
+                                 "timestamp": timestamp(0),
+                                 "none": None,
+                                 "map": {"string": "nested map"},
+                                 "list": [1, "two", 3.0, -4]})
+    assert isinstance(decoded["timestamp"], timestamp)
 
   def testMapEmpty(self):
     self.check("map", {})
@@ -90,3 +106,7 @@
   def testInt16(self):
     self.check("int16", 3)
     self.check("int16", -3)
+
+  def testDatetime(self):
+    self.check("datetime", timestamp(0))
+    self.check("datetime", timestamp(long(time.time())))

Modified: incubator/qpid/trunk/qpid/python/tests/datatypes.py
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/tests/datatypes.py?rev=712673&r1=712672&r2=712673&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/python/tests/datatypes.py (original)
+++ incubator/qpid/trunk/qpid/python/tests/datatypes.py Mon Nov 10 07:19:29 2008
@@ -223,3 +223,35 @@
     assert m.get("fragment_properties") is None
     assert m.get("message_properties") == self.mp
     assert m.get("delivery_properties") == self.dp
+
+class TimestampTest(TestCase):
+
+  def check(self, expected, *values):
+    for v in values:
+      assert isinstance(v, timestamp)
+      assert v == expected
+      assert v == timestamp(expected)
+
+  def testAdd(self):
+    self.check(4.0,
+               timestamp(2.0) + 2.0,
+               2.0 + timestamp(2.0))
+
+  def testSub(self):
+    self.check(2.0,
+               timestamp(4.0) - 2.0,
+               4.0 - timestamp(2.0))
+
+  def testNeg(self):
+    self.check(-4.0, -timestamp(4.0))
+
+  def testPos(self):
+    self.check(+4.0, +timestamp(4.0))
+
+  def testAbs(self):
+    self.check(4.0, abs(timestamp(-4.0)))
+
+  def testConversion(self):
+    dt = timestamp(0).datetime()
+    t = timestamp(dt)
+    assert t == 0


Reply via email to