You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kw...@apache.org on 2014/09/07 00:06:41 UTC
svn commit: r1622952 - in /qpid/trunk/qpid: python/qpid/codec.py
python/qpid/tests/codec.py
tests/src/py/qpid_tests/broker_0_9/messageheader.py
Author: kwall
Date: Sat Sep 6 22:06:41 2014
New Revision: 1622952
URL: http://svn.apache.org/r1622952
Log:
QPID-6085: [Python client] 08..091 implement sending/receiving of additional property types
Modified:
qpid/trunk/qpid/python/qpid/codec.py
qpid/trunk/qpid/python/qpid/tests/codec.py
qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/messageheader.py
Modified: qpid/trunk/qpid/python/qpid/codec.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/codec.py?rev=1622952&r1=1622951&r2=1622952&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/codec.py (original)
+++ qpid/trunk/qpid/python/qpid/codec.py Sat Sep 6 22:06:41 2014
@@ -26,14 +26,18 @@ fields.
The unit test for this module is located in tests/codec.py
"""
-import re, qpid, spec08
+import re, qpid, spec08, os
from cStringIO import StringIO
from struct import *
from reference import ReferenceId
+from logging import getLogger
+
+log = getLogger("qpid.codec")
class EOF(Exception):
pass
+# This code appears to be dead
TYPE_ALIASES = {
"long_string": "longstr",
"unsigned_int": "long"
@@ -56,36 +60,81 @@ class Codec:
self.incoming_bits = []
self.outgoing_bits = []
+ # Before 0-91, the AMQP's set of types did not include the boolean type. However,
+ # the 0-8 and 0-9 Java client uses this type so we encode/decode it too. However, this
+ # can be turned off by setting the followng environment value.
+ if "QPID_CODEC_DISABLE_0_91_BOOLEAN" in os.environ:
+ self.understand_boolean = False
+ else:
+ self.understand_boolean = True
+
+ log.debug("AMQP 0-91 boolean supported : %r", self.understand_boolean)
+
self.types = {}
self.codes = {}
+ self.integertypes = [int, long]
self.encodings = {
+ float: "double", # python uses 64bit floats, send them as doubles
basestring: "longstr",
- int: "long",
- long: "long",
None.__class__:"void",
list: "sequence",
tuple: "sequence",
dict: "table"
}
+ if self.understand_boolean:
+ self.encodings[bool] = "boolean"
+
for constant in self.spec.constants:
+ # This code appears to be dead
if constant.klass == "field-table-type":
type = constant.name.replace("field_table_", "")
self.typecode(constant.id, TYPE_ALIASES.get(type, type))
if not self.types:
+ # long-string 'S'
self.typecode(ord('S'), "longstr")
- self.typecode(ord('I'), "long")
+ # void 'V'
+ self.typecode(ord('V'), "void")
+ # long-int 'I' (32bit signed)
+ self.typecode(ord('I'), "signed_int")
+ # long-long-int 'l' (64bit signed)
+ # This is a long standing pre-0-91-spec type used by the Java
+ # client, 0-9-1 says it should be unsigned or use 'L')
+ self.typecode(ord('l'), "signed_long")
+ # double 'd'
+ self.typecode(ord('d'), "double")
+ # float 'f'
+ self.typecode(ord('f'), "float")
+
+ if self.understand_boolean:
+ self.typecode(ord('t'), "boolean")
+
+ ## The following are supported for decoding only ##
+
+ # short-short-uint 'b' (8bit signed)
+ self.types[ord('b')] = "signed_octet"
+ # short-int 's' (16bit signed)
+ # This is a long standing pre-0-91-spec type code used by the Java
+ # client to send shorts, it should really be a short-string, or for 0-9-1 use 'U'
+ self.types[ord('s')] = "signed_short"
def typecode(self, code, type):
self.types[code] = type
self.codes[type] = code
- def resolve(self, klass):
+ def resolve(self, klass, value):
+ if(klass in self.integertypes):
+ if (value >= -2147483648 and value <= 2147483647):
+ return "signed_int"
+ elif (value >= -9223372036854775808 and value <= 9223372036854775807):
+ return "signed_long"
+ else:
+ raise ValueError('Integer value is outwith the supported 64bit signed range')
if self.encodings.has_key(klass):
return self.encodings[klass]
for base in klass.__bases__:
- result = self.resolve(base)
+ result = self.resolve(base, value)
if result != None:
return result
@@ -168,6 +217,7 @@ class Codec:
if isinstance(type, spec08.Struct):
return self.decode_struct(type)
else:
+ log.debug("Decoding using method: decode_" + type)
return getattr(self, "decode_" + type)()
def encode_bit(self, o):
@@ -191,7 +241,7 @@ class Codec:
def encode_octet(self, o):
"""
- encodes octet (8 bits) data 'o' in network byte order
+ encodes an UNSIGNED octet (8 bits) data 'o' in network byte order
"""
# octet's valid range is [0,255]
@@ -202,13 +252,20 @@ class Codec:
def decode_octet(self):
"""
- decodes a octet (8 bits) encoded in network byte order
+ decodes an UNSIGNED octet (8 bits) encoded in network byte order
"""
return self.unpack("!B")
+ def decode_signed_octet(self):
+ """
+ decodes a signed octet (8 bits) encoded in network byte order
+ """
+ return self.unpack("!b")
+
def encode_short(self, o):
"""
- encodes short (16 bits) data 'o' in network byte order
+ encodes an UNSIGNED short (16 bits) data 'o' in network byte order
+ AMQP 0-9-1 type: short-uint
"""
# short int's valid range is [0,65535]
@@ -219,13 +276,22 @@ class Codec:
def decode_short(self):
"""
- decodes a short (16 bits) in network byte order
+ decodes an UNSIGNED short (16 bits) in network byte order
+ AMQP 0-9-1 type: short-uint
"""
return self.unpack("!H")
+ def decode_signed_short(self):
+ """
+ decodes a signed short (16 bits) in network byte order
+ AMQP 0-9-1 type: short-int
+ """
+ return self.unpack("!h")
+
def encode_long(self, o):
"""
- encodes long (32 bits) data 'o' in network byte order
+ encodes an UNSIGNED long (32 bits) data 'o' in network byte order
+ AMQP 0-9-1 type: long-uint
"""
# we need to check both bounds because on 64 bit platforms
@@ -237,31 +303,50 @@ class Codec:
def decode_long(self):
"""
- decodes a long (32 bits) in network byte order
+ decodes an UNSIGNED long (32 bits) in network byte order
+ AMQP 0-9-1 type: long-uint
"""
return self.unpack("!L")
def encode_signed_long(self, o):
+ """
+ encodes a signed long (64 bits) in network byte order
+ AMQP 0-9-1 type: long-long-int
+ """
self.pack("!q", o)
def decode_signed_long(self):
+ """
+ decodes a signed long (64 bits) in network byte order
+ AMQP 0-9-1 type: long-long-int
+ """
return self.unpack("!q")
def encode_signed_int(self, o):
+ """
+ encodes a signed int (32 bits) in network byte order
+ AMQP 0-9-1 type: long-int
+ """
self.pack("!l", o)
def decode_signed_int(self):
+ """
+ decodes a signed int (32 bits) in network byte order
+ AMQP 0-9-1 type: long-int
+ """
return self.unpack("!l")
def encode_longlong(self, o):
"""
- encodes long long (64 bits) data 'o' in network byte order
+ encodes an UNSIGNED long long (64 bits) data 'o' in network byte order
+ AMQP 0-9-1 type: long-long-uint
"""
self.pack("!Q", o)
def decode_longlong(self):
"""
- decodes a long long (64 bits) in network byte order
+ decodes an UNSIGNED long long (64 bits) in network byte order
+ AMQP 0-9-1 type: long-long-uint
"""
return self.unpack("!Q")
@@ -354,9 +439,9 @@ class Codec:
for key, value in tbl.items():
if self.spec.major == 8 and self.spec.minor == 0 and len(key) > 128:
raise ValueError("field table key too long: '%s'" % key)
- type = self.resolve(value.__class__)
+ type = self.resolve(value.__class__, value)
if type == None:
- raise ValueError("no encoding for: " + value.__class__)
+ raise ValueError("no encoding for: " + str(value.__class__))
codec.encode_shortstr(key)
codec.encode_octet(self.codes[type])
codec.encode(type, value)
@@ -373,7 +458,9 @@ class Codec:
result = {}
while self.nread - start < size:
key = self.decode_shortstr()
+ log.debug("Field table entry key: %r", key)
code = self.decode_octet()
+ log.debug("Field table entry type code: %r", code)
if self.types.has_key(code):
value = self.decode(self.types[code])
else:
@@ -383,6 +470,7 @@ class Codec:
else:
value = self.read(self.dec_num(w))
result[key] = value
+ log.debug("Field table entry value: %r", value)
return result
def encode_timestamp(self, t):
@@ -451,6 +539,13 @@ class Codec:
def decode_uuid(self):
return self.unpack("16s")
+ def encode_void(self,o):
+ #NO-OP, value is implicit in the type.
+ return
+
+ def decode_void(self):
+ return None
+
def enc_num(self, width, n):
if width == 1:
self.encode_octet(n)
@@ -565,6 +660,22 @@ class Codec:
result.append(value)
return result
+ def encode_boolean(self, s):
+ if (s):
+ self.pack("!c", "\x01")
+ else:
+ self.pack("!c", "\x00")
+
+ def decode_boolean(self):
+ b = self.unpack("!c")
+ if b == "\x00":
+ return False
+ else:
+ # AMQP spec says anything else is True
+ return True
+
+
+
def fixed(code):
return (code >> 6) != 2
Modified: qpid/trunk/qpid/python/qpid/tests/codec.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/tests/codec.py?rev=1622952&r1=1622951&r2=1622952&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/codec.py (original)
+++ qpid/trunk/qpid/python/qpid/tests/codec.py Sat Sep 6 22:06:41 2014
@@ -487,6 +487,134 @@ class ContentTestCase(BaseDataTypes):
"""
self.failUnlessEqual(self.readFunc('decode_content', '\x01\x00\x00\x00\x07dummyId').id, 'dummyId', 'reference content decode FAILED...')
+# -----------------------------------
+# -----------------------------------
+class BooleanTestCase(BaseDataTypes):
+
+ # -------------------
+ def test_true_encode(self):
+ self.failUnlessEqual(self.callFunc('encode_boolean', True), '\x01', 'True encoding FAILED...')
+
+ # -------------------
+ def test_true_decode(self):
+ self.failUnlessEqual(self.readFunc('decode_boolean', '\x01'), True, 'True decoding FAILED...')
+ self.failUnlessEqual(self.readFunc('decode_boolean', '\x02'), True, 'True decoding FAILED...')
+ self.failUnlessEqual(self.readFunc('decode_boolean', '\xFF'), True, 'True decoding FAILED...')
+
+ # -------------------
+ def test_false_encode(self):
+ self.failUnlessEqual(self.callFunc('encode_boolean', False), '\x00', 'False encoding FAILED...')
+
+ # -------------------
+ def test_false_decode(self):
+ self.failUnlessEqual(self.readFunc('decode_boolean', '\x00'), False, 'False decoding FAILED...')
+
+# -----------------------------------
+# -----------------------------------
+class ResolveTestCase(BaseDataTypes):
+
+ # -------------------
+ # Test resolving the value 1, which should implicitly be a python int
+ def test_resolve_int_1(self):
+ value = 1
+ expected = "signed_int"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving the value -1, which should implicitly be a python int
+ def test_resolve_int_negative_1(self):
+ value = -1
+ expected = "signed_int"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving the min signed 32bit integer value, -2^31
+ def test_resolve_int_min(self):
+ value = -2147483648 #-2^31
+ expected = "signed_int"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving the max signed 32bit integer value, 2^31 -1
+ def test_resolve_int_max(self):
+ value = 2147483647 #2^31 -1
+ expected = "signed_int"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving above the max signed 32bit integer value of 2^31 -1
+ # Should be a python long, but should be classed as a signed 64bit long on the wire either way
+ def test_resolve_int_above_signed_32bit_max(self):
+ value = 2147483648 #2^31, i.e 1 above the 32bit signed max
+ expected = "signed_long"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving above the max signed 32bit integer value of 2^31 -1
+ # As above except use an explicitly cast python long
+ def test_resolve_long_above_signed_32bit_max(self):
+ value = 2147483648L #2^31, i.e 1 above the 32bit signed max
+ expected = "signed_long"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving an explicitly cast python long of value 1, i.e less than the max signed 32bit integer value
+ # Should be encoded as a 32bit signed int on the wire
+ def test_resolve_long_1(self):
+ value = 1L
+ expected = "signed_int"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving the max signed 64bit integer value of 2^63 -1
+ # Should be a python long, but should be classed as a signed 64bit long on the wire either way
+ def test_resolve_64bit_signed_max(self):
+ value = 9223372036854775807 #2^63 -1
+ expected = "signed_long"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving the min signed 64bit integer value of -2^63
+ # Should be a python long, but should be classed as a signed 64bit long on the wire either way
+ def test_resolve_64bit_signed_min(self):
+ value = -9223372036854775808 # -2^63
+ expected = "signed_long"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving a value of 2^63, i.e more than the max a signed 64bit integer value can hold.
+ # Should throw an exception indicating the value can't be encoded.
+ def test_resolve_above_64bit_signed_max(self):
+ value = 9223372036854775808L #2^63
+ self.failUnlessRaises(Exception, self.codec.resolve, value.__class__, value)
+ # -------------------
+ # Test resolving a value of -2^63 -1, i.e less than the min a signed 64bit integer value can hold.
+ # Should throw an exception indicating the value can't be encoded.
+ def test_resolve_below_64bit_signed_min(self):
+ value = 9223372036854775808L # -2^63 -1
+ self.failUnlessRaises(Exception, self.codec.resolve, value.__class__, value)
+ # -------------------
+ # Test resolving a float. Should indicate use of double as python uses 64bit floats
+ def test_resolve_float(self):
+ value = 1.1
+ expected = "double"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving a string. Should indicate use of long string encoding
+ def test_resolve_string(self):
+ value = "myString"
+ expected = "longstr"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+ # -------------------
+ # Test resolving None. Should indicate use of a void encoding.
+ def test_resolve_None(self):
+ value = None
+ expected = "void"
+ resolved = self.codec.resolve(value.__class__, value)
+ self.failUnlessEqual(resolved, expected, "resolve FAILED...expected %s got %s" % (expected, resolved))
+
# ------------------------ #
# Pre - existing test code #
# ------------------------ #
Modified: qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/messageheader.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/messageheader.py?rev=1622952&r1=1622951&r2=1622952&view=diff
==============================================================================
--- qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/messageheader.py (original)
+++ qpid/trunk/qpid/tests/src/py/qpid_tests/broker_0_9/messageheader.py Sat Sep 6 22:06:41 2014
@@ -33,3 +33,29 @@ class MessageHeaderTests(TestBase):
self.queue_declare(queue="q")
q = self.consume("q")
self.assertPublishGet(q, routing_key="q", properties=props)
+
+ def test_message_with_boolean_header(self):
+ """The AMQP boolean type is not officially supported until 0-91 but the 0-8/9 Java client use its field value typecode.
+ Note: If you run this test with QPID_CODEC_DISABLE_0_91_BOOLEAN set, this test will still pass as the booleans are
+ coerced into integer."""
+
+ props={"headers":{"trueHeader":True, "falseHeader":False}}
+ self.queue_declare(queue="q")
+ q = self.consume("q")
+ self.assertPublishGet(q, routing_key="q", properties=props)
+
+ def test_message_with_negatives_longints_floats_and_None(self):
+ """ Tests sending and then receiving negative integers, longs, the None (void) value, and doubles."""
+ props={"headers":{"myIntMin": -2147483648,
+ "myIntMax": 2147483647,
+ "myLongMax": 9223372036854775807,
+ "myLongMin": -9223372036854775808,
+ "myNullString": None,
+ "myDouble1.1": 1.1,
+ "myDoubleMin": 4.9E-324,
+ "myDoubleMax": 1.7976931348623157E308}}
+
+ self.queue_declare(queue="q")
+ q = self.consume("q")
+ self.assertPublishGet(q, routing_key="q", properties=props)
+
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org