You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by as...@apache.org on 2018/05/25 22:21:47 UTC

[1/6] qpid-proton git commit: PROTON-1848: [Python] Remove Python 2.5 and earlier compatibility - Remove need for most compatibility hacks - Fix some seemingly odd conversion functions - Hidden all compatibility code in _compat module - will probably g

Repository: qpid-proton
Updated Branches:
  refs/heads/master b6b29ec73 -> d28fecf5d


PROTON-1848: [Python] Remove Python 2.5 and earlier compatibility
- Remove need for most compatibility hacks
- Fix some seemingly odd conversion functions
- Hidden all compatibility code in _compat module
  - will probably get rid of raise_ when rewriting reactor
  - can get rid of string_type by doing unicode type hack in reactor.py
  - leaving iteritems & unichr


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/51934030
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/51934030
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/51934030

Branch: refs/heads/master
Commit: 51934030922396feeca796863c58f9e43691638f
Parents: b6b29ec
Author: Andrew Stitcher <as...@apache.org>
Authored: Tue Apr 24 10:38:57 2018 -0400
Committer: Andrew Stitcher <as...@apache.org>
Committed: Wed May 23 16:27:10 2018 -0400

----------------------------------------------------------------------
 python/proton/__init__.py              | 178 ++++++++++------------------
 python/proton/_compat.py               |  57 +++------
 python/proton/reactor.py               |  20 ++--
 tests/python/proton_tests/codec.py     |  34 +++---
 tests/python/proton_tests/engine.py    |  68 +++++------
 tests/python/proton_tests/interop.py   |   5 +-
 tests/python/proton_tests/message.py   |  21 ++--
 tests/python/proton_tests/sasl.py      |  51 ++++----
 tests/python/proton_tests/transport.py |  57 +++++----
 9 files changed, 203 insertions(+), 288 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/python/proton/__init__.py
----------------------------------------------------------------------
diff --git a/python/proton/__init__.py b/python/proton/__init__.py
index 60f7323..6ee0d68 100644
--- a/python/proton/__init__.py
+++ b/python/proton/__init__.py
@@ -32,94 +32,43 @@ from __future__ import absolute_import
 
 from cproton import *
 from .wrapper import Wrapper
-from proton import _compat
-
-import logging, weakref, socket, sys, threading
-
-try:
-  handler = logging.NullHandler()
-except AttributeError:
-  class NullHandler(logging.Handler):
-    def handle(self, record):
-        pass
+from . import _compat
+
+import logging
+import socket
+import sys
+import threading
+import uuid
+import weakref
+
+# This private NullHandler is required for Python 2.6,
+# when we no longer support 2.6 this replace NullHandler class definition and assignment with:
+#  handler = logging.NullHandler()
+class NullHandler(logging.Handler):
+  def handle(self, record):
+      pass
 
-    def emit(self, record):
-        pass
+  def emit(self, record):
+      pass
 
-    def createLock(self):
-        self.lock = None
+  def createLock(self):
+      self.lock = None
 
-  handler = NullHandler()
+handler = NullHandler()
 
 log = logging.getLogger("proton")
 log.addHandler(handler)
 
-try:
-  import uuid
-
-  def generate_uuid():
-    return uuid.uuid4()
-
-except ImportError:
-  """
-  No 'native' UUID support.  Provide a very basic UUID type that is a compatible subset of the uuid type provided by more modern python releases.
-  """
-  import struct
-  class uuid:
-    class UUID:
-      def __init__(self, hex=None, bytes=None):
-        if [hex, bytes].count(None) != 1:
-          raise TypeError("need one of hex or bytes")
-        if bytes is not None:
-          self.bytes = bytes
-        elif hex is not None:
-          fields=hex.split("-")
-          fields[4:5] = [fields[4][:4], fields[4][4:]]
-          self.bytes = struct.pack("!LHHHHL", *[int(x,16) for x in fields])
-
-      def __cmp__(self, other):
-        if isinstance(other, uuid.UUID):
-          return cmp(self.bytes, other.bytes)
-        else:
-          return -1
-
-      def __str__(self):
-        return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack("!LHHHHL", self.bytes)
-
-      def __repr__(self):
-        return "UUID(%r)" % str(self)
-
-      def __hash__(self):
-        return self.bytes.__hash__()
-
-  import os, random, time
-  rand = random.Random()
-  rand.seed((os.getpid(), time.time(), socket.gethostname()))
-  def random_uuid():
-    data = [rand.randint(0, 255) for i in xrange(16)]
-
-    # From RFC4122, the version bits are set to 0100
-    data[6] &= 0x0F
-    data[6] |= 0x40
-
-    # From RFC4122, the top two bits of byte 8 get set to 01
-    data[8] &= 0x3F
-    data[8] |= 0x80
-    return "".join(map(chr, data))
-
-  def uuid4():
-    return uuid.UUID(bytes=random_uuid())
-
-  def generate_uuid():
-    return uuid4()
+def generate_uuid():
+  return uuid.uuid4()
 
 #
 # Hacks to provide Python2 <---> Python3 compatibility
 #
-try:
-  bytes()
-except NameError:
-  bytes = str
+# The results are
+# |       |long|unicode|
+# |python2|long|unicode|
+# |python3| int|    str|
 try:
   long()
 except NameError:
@@ -241,10 +190,15 @@ class Message(object):
 
   def _check_property_keys(self):
       for k in self.properties.keys():
-        if not isinstance(k, (bytes, str, unicode)):
-          raise MessageException('Application property key is not unicode string: key=%s %s' % (str(k), type(k)))
-        if isinstance(k, bytes):
-          self.properties[_compat.bin2str(k)] = self.properties.pop(k)
+        if isinstance(k, unicode):
+          # py2 unicode, py3 str (via hack definition)
+          continue
+        # If key is binary then change to string
+        elif isinstance(k, str):
+          # py2 str
+          self.properties[k.encode('utf-8')] = self.properties.pop(k)
+        else:
+          raise MessageException('Application property key is not string type: key=%s %s' % (str(k), type(k)))
 
   def _pre_encode(self):
     inst = Data(pn_message_instructions(self._msg))
@@ -376,7 +330,7 @@ The number of delivery attempts made for this message.
   def _get_id(self):
     return self._id.get_object()
   def _set_id(self, value):
-    if type(value) in _compat.INT_TYPES:
+    if type(value) in (int, long):
       value = ulong(value)
     self._id.rewind()
     self._id.put_object(value)
@@ -432,7 +386,7 @@ The reply-to address for the message.
   def _get_correlation_id(self):
     return self._correlation_id.get_object()
   def _set_correlation_id(self, value):
-    if type(value) in _compat.INT_TYPES:
+    if type(value) in (int, long):
       value = ulong(value)
     self._correlation_id.rewind()
     self._correlation_id.put_object(value)
@@ -908,7 +862,7 @@ class Data:
   def type_name(type): return Data.type_names[type]
 
   def __init__(self, capacity=16):
-    if type(capacity) in _compat.INT_TYPES:
+    if type(capacity) in (int, long):
       self._data = pn_data(capacity)
       self._free = True
     else:
@@ -1419,7 +1373,7 @@ class Data:
     If the current node is a char, returns its value, returns 0
     otherwise.
     """
-    return char(_compat.unichar(pn_data_get_char(self._data)))
+    return char(_compat.unichr(pn_data_get_char(self._data)))
 
   def get_ulong(self):
     """
@@ -1827,37 +1781,33 @@ def millis2timeout(millis):
   return millis2secs(millis)
 
 def unicode2utf8(string):
-    """Some Proton APIs expect a null terminated string. Convert python text
-    types to UTF8 to avoid zero bytes introduced by other multi-byte encodings.
-    This method will throw if the string cannot be converted.
-    """
-    if string is None:
-        return None
-    if _compat.IS_PY2:
-        if isinstance(string, unicode):
-            return string.encode('utf-8')
-        elif isinstance(string, str):
-            return string
-    else:
-        # decoding a string results in bytes
-        if isinstance(string, str):
-            string = string.encode('utf-8')
-            # fall through
-        if isinstance(string, bytes):
-            return string.decode('utf-8')
-    raise TypeError("Unrecognized string type: %r (%s)" % (string, type(string)))
+  """Some Proton APIs expect a null terminated string. Convert python text
+  types to UTF8 to avoid zero bytes introduced by other multi-byte encodings.
+  This method will throw if the string cannot be converted.
+  """
+  if string is None:
+    return None
+  elif isinstance(string, str):
+    # Must be py2 or py3 str
+    # The swig binding converts py3 str -> utf8 char* and back sutomatically
+    return string
+  elif isinstance(string, unicode):
+    # This must be python2 unicode as we already detected py3 str above
+    return string.encode('utf-8')
+  # Anything else illegal - specifically python3 bytes
+  raise TypeError("Unrecognized string type: %r (%s)" % (string, type(string)))
 
 def utf82unicode(string):
-    """Covert C strings returned from proton-c into python unicode"""
-    if string is None:
-        return None
-    if isinstance(string, _compat.TEXT_TYPES):
-        # already unicode
-        return string
-    elif isinstance(string, _compat.BINARY_TYPES):
-        return string.decode('utf8')
-    else:
-        raise TypeError("Unrecognized string type")
+  """Convert C strings returned from proton-c into python unicode"""
+  if string is None:
+    return None
+  elif isinstance(string, unicode):
+    # py2 unicode, py3 str (via hack definition)
+    return string
+  elif isinstance(string, bytes):
+    # py2 str (via hack definition), py3 bytes
+    return string.decode('utf8')
+  raise TypeError("Unrecognized string type")
 
 class Connection(Wrapper, Endpoint):
   """

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/python/proton/_compat.py
----------------------------------------------------------------------
diff --git a/python/proton/_compat.py b/python/proton/_compat.py
index c8815f4..afd82e3 100644
--- a/python/proton/_compat.py
+++ b/python/proton/_compat.py
@@ -22,16 +22,17 @@ Utilities to help Proton support both python2 and python3.
 """
 
 import sys
-import types
-IS_PY2 = sys.version_info[0] == 2
-IS_PY3 = sys.version_info[0] == 3
 
-if IS_PY3:
-    INT_TYPES = (int,)
-    TEXT_TYPES = (str,)
-    STRING_TYPES = (str,)
-    BINARY_TYPES = (bytes,)
-    CLASS_TYPES = (type,)
+# bridge between py2 Queue renamed as py3 queue
+try:
+    import Queue as queue
+except ImportError:
+    import queue
+
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+    string_types = (str,)
 
     def raise_(t, v=None, tb=None):
         """Mimic the old 2.x raise behavior:
@@ -44,29 +45,13 @@ if IS_PY3:
         else:
             raise v.with_traceback(tb)
 
-    def bin2str(s, encoding='utf-8'):
-        return s
-
-    def iteritems(d):
-        return iter(d.items())
-
-    def unichar(i):
-        return chr(i)
-
-    def str2bin(s, encoding='latin-1'):
-        """Convert str to binary type"""
-        return s.encode(encoding)
-
-    def str2unicode(s):
-        return s
+    def iteritems(d, **kw):
+        return iter(d.items(**kw))
 
+    unichr = chr
 else:
-    INT_TYPES = (int, long)
-    TEXT_TYPES = (unicode,)
     # includes both unicode and non-unicode strings:
-    STRING_TYPES = (basestring,)
-    BINARY_TYPES = (str,)
-    CLASS_TYPES = (type, types.ClassType)
+    string_types = (basestring,)
 
     # the raise syntax will cause a parse error in Py3, so 'sneak' in a
     # definition that won't cause the parser to barf
@@ -74,17 +59,9 @@ else:
     raise t, v, tb
 """)
 
-    def bin2str(s, encoding='utf-8'):
-        return s.decode(encoding)
-
     def iteritems(d, **kw):
-        return d.iteritems()
-
-    def unichar(i):
-        return unichr(i)
+        return d.iteritems(**kw)
 
-    def str2bin(s, encoding='latin-1'):
-        return s
+    unichr = unichr
 
-    def str2unicode(s):
-        return unicode(s, "unicode_escape")
+__all__ = [ 'PY3', 'queue', 'string_types', 'raise_', 'iteritems', 'unichr']
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/python/proton/reactor.py
----------------------------------------------------------------------
diff --git a/python/proton/reactor.py b/python/proton/reactor.py
index 0fa1c4b..d5d5183 100644
--- a/python/proton/reactor.py
+++ b/python/proton/reactor.py
@@ -19,6 +19,9 @@ from __future__ import absolute_import
 #
 import logging, os, socket, time, types
 from heapq import heappush, heappop, nsmallest
+
+import traceback
+
 from proton import Collector, Connection, ConnectionException, Delivery, Described, dispatch
 from proton import Endpoint, Event, EventBase, EventType, generate_uuid, Handler, Link, Message
 from proton import ProtonException, PN_ACCEPTED, PN_PYREF, SASL, Session, SSL, SSLDomain, SSLUnavailable, symbol
@@ -27,16 +30,13 @@ from select import select
 from proton.handlers import OutgoingMessageHandler
 from proton import unicode2utf8, utf82unicode
 
-import traceback
 from proton import WrappedHandler, _chandler, secs2millis, millis2secs, timeout2millis, millis2timeout, Selectable
 from .wrapper import Wrapper, PYCTX
 from cproton import *
+
 from . import _compat
 
-try:
-    import Queue
-except ImportError:
-    import queue as Queue
+from ._compat import queue
 
 log = logging.getLogger("proton")
 
@@ -259,7 +259,7 @@ class EventInjector(object):
     needed, to allow the event loop to end if needed.
     """
     def __init__(self):
-        self.queue = Queue.Queue()
+        self.queue = queue.Queue()
         self.pipe = os.pipe()
         self._closed = False
 
@@ -269,7 +269,7 @@ class EventInjector(object):
         of the reactor to which this EventInjector was added.
         """
         self.queue.put(event)
-        os.write(self.pipe[1], _compat.str2bin("!"))
+        os.write(self.pipe[1], b"!")
 
     def close(self):
         """
@@ -278,7 +278,7 @@ class EventInjector(object):
         then this will be removed from the set of interest.
         """
         self._closed = True
-        os.write(self.pipe[1], _compat.str2bin("!"))
+        os.write(self.pipe[1], b"!")
 
     def fileno(self):
         return self.pipe[0]
@@ -806,7 +806,7 @@ class Container(Reactor):
         Various LinkOptions can be specified to further control the
         attachment.
         """
-        if isinstance(context, _compat.STRING_TYPES):
+        if isinstance(context, _compat.string_types):
             context = Url(context)
         if isinstance(context, Url) and not target:
             target = context.path
@@ -847,7 +847,7 @@ class Container(Reactor):
         Various LinkOptions can be specified to further control the
         attachment.
         """
-        if isinstance(context, _compat.STRING_TYPES):
+        if isinstance(context, _compat.string_types):
             context = Url(context)
         if isinstance(context, Url) and not source:
             source = context.path

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/tests/python/proton_tests/codec.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/codec.py b/tests/python/proton_tests/codec.py
index 27e70cf..e13dcfe 100644
--- a/tests/python/proton_tests/codec.py
+++ b/tests/python/proton_tests/codec.py
@@ -20,11 +20,8 @@
 import os, sys
 from . import common
 from proton import *
-from proton._compat import raise_, str2unicode, unichar, str2bin
-try:
-  from uuid import uuid4
-except ImportError:
-  from proton import uuid4
+from proton._compat import raise_
+from uuid import uuid4
 
 class Test(common.Test):
 
@@ -281,8 +278,7 @@ class DataTest(Test):
     self._test("double", 0, 1, 2, 3, 0.1, 0.2, 0.3, -1, -2, -3, -0.1, -0.2, -0.3)
 
   def testBinary(self):
-    self._test("binary", str2bin("this"), str2bin("is"), str2bin("a"), str2bin("test"),
-               str2bin("of" "b\x00inary"))
+    self._test("binary", b"this", b"is", b"a", b"test",b"of" b"b\x00inary")
 
   def testSymbol(self):
     self._test("symbol", symbol("this is a symbol test"), symbol("bleh"), symbol("blah"))
@@ -291,7 +287,7 @@ class DataTest(Test):
     self._test("timestamp", timestamp(0), timestamp(12345), timestamp(1000000))
 
   def testChar(self):
-    self._test("char", char('a'), char('b'), char('c'), char(unichar(0x20AC)))
+    self._test("char", char('a'), char('b'), char('c'), char(u'\u20AC'))
 
   def testUUID(self):
     self._test("uuid", uuid4(), uuid4(), uuid4())
@@ -303,7 +299,7 @@ class DataTest(Test):
     self._test("decimal64", decimal64(0), decimal64(1), decimal64(2), decimal64(3), decimal64(4), decimal64(2**60))
 
   def testDecimal128(self):
-    self._test("decimal128", decimal128(str2bin("fdsaasdf;lkjjkl;")), decimal128(str2bin("x"*16)))
+    self._test("decimal128", decimal128(b"fdsaasdf;lkjjkl;"), decimal128(b"x"*16))
 
   def testCopy(self):
     self.data.put_described()
@@ -344,10 +340,10 @@ class DataTest(Test):
     obj = {symbol("key"): timestamp(1234),
            ulong(123): "blah",
            char("c"): "bleh",
-           str2unicode("desc"): Described(symbol("url"), str2unicode("http://example.org")),
-           str2unicode("array"): Array(UNDESCRIBED, Data.INT, 1, 2, 3),
-           str2unicode("list"): [1, 2, 3, None, 4],
-           str2unicode("boolean"): True}
+           u"desc": Described(symbol("url"), u"http://example.org"),
+           u"array": Array(UNDESCRIBED, Data.INT, 1, 2, 3),
+           u"list": [1, 2, 3, None, 4],
+           u"boolean": True}
     self.data.put_object(obj)
     enc = self.data.encode()
     data = Data()
@@ -359,7 +355,7 @@ class DataTest(Test):
 
   def testBuffer(self):
     try:
-      self.data.put_object(buffer(str2bin("foo")))
+      self.data.put_object(buffer(b"foo"))
     except NameError:
       # python >= 3.0 does not have `buffer`
       return
@@ -368,11 +364,11 @@ class DataTest(Test):
     data.rewind()
     assert data.next()
     assert data.type() == Data.BINARY
-    assert data.get_object() == str2bin("foo")
+    assert data.get_object() == b"foo"
 
   def testMemoryView(self):
     try:
-      self.data.put_object(memoryview(str2bin("foo")))
+      self.data.put_object(memoryview(b"foo"))
     except NameError:
       # python <= 2.6 does not have `memoryview`
       return
@@ -381,10 +377,10 @@ class DataTest(Test):
     data.rewind()
     assert data.next()
     assert data.type() == Data.BINARY
-    assert data.get_object() == str2bin("foo")
+    assert data.get_object() == b"foo"
 
   def testLookup(self):
-    obj = {symbol("key"): str2unicode("value"),
+    obj = {symbol("key"): u"value",
            symbol("pi"): 3.14159,
            symbol("list"): [1, 2, 3, 4]}
     self.data.put_object(obj)
@@ -396,7 +392,7 @@ class DataTest(Test):
     assert self.data.get_object() == 3.14159
     self.data.rewind()
     assert self.data.lookup("key")
-    assert self.data.get_object() == str2unicode("value")
+    assert self.data.get_object() == u"value"
     self.data.rewind()
     assert self.data.lookup("list")
     assert self.data.get_object() == [1, 2, 3, 4]

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/tests/python/proton_tests/engine.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/engine.py b/tests/python/proton_tests/engine.py
index 38a3b4d..c0ea31d 100644
--- a/tests/python/proton_tests/engine.py
+++ b/tests/python/proton_tests/engine.py
@@ -25,7 +25,6 @@ from time import time, sleep
 from proton import *
 from .common import pump, Skipped
 from proton.reactor import Reactor
-from proton._compat import str2bin
 
 
 # older versions of gc do not provide the garbage list
@@ -50,7 +49,7 @@ try:
     bytearray()
 except:
     def bytearray(x):
-        return str2bin('\x00') * x
+        return b'\x00' * x
 
 OUTPUT_SIZE = 10*1024
 
@@ -836,7 +835,7 @@ class TransferTest(Test):
     assert tag == "tag", tag
     assert d.writable
 
-    n = self.snd.send(str2bin("this is a test"))
+    n = self.snd.send(b"this is a test")
     assert self.snd.advance()
     assert self.c1.work_head is None
 
@@ -849,7 +848,7 @@ class TransferTest(Test):
   def test_multiframe(self):
     self.rcv.flow(1)
     self.snd.delivery("tag")
-    msg = str2bin("this is a test")
+    msg = b"this is a test"
     n = self.snd.send(msg)
     assert n == len(msg)
 
@@ -864,9 +863,9 @@ class TransferTest(Test):
     assert binary == msg, (binary, msg)
 
     binary = self.rcv.recv(1024)
-    assert binary == str2bin("")
+    assert binary == b""
 
-    msg = str2bin("this is more")
+    msg = b"this is more"
     n = self.snd.send(msg)
     assert n == len(msg)
     assert self.snd.advance()
@@ -885,7 +884,7 @@ class TransferTest(Test):
     self.pump()
 
     sd = self.snd.delivery("tag")
-    msg = str2bin("this is a test")
+    msg = b"this is a test"
     n = self.snd.send(msg)
     assert n == len(msg)
     assert self.snd.advance()
@@ -984,7 +983,7 @@ class TransferTest(Test):
 
     for x in range(10):
         self.snd.delivery("tag%d" % x)
-        msg = str2bin("this is a test")
+        msg = b"this is a test"
         n = self.snd.send(msg)
         assert n == len(msg)
         assert self.snd.advance()
@@ -1567,7 +1566,7 @@ class CreditTest(Test):
 
     sd = self.snd.delivery("tagA")
     assert sd
-    n = self.snd.send(str2bin("A"))
+    n = self.snd.send(b"A")
     assert n == 1
     self.pump()
     self.snd.advance()
@@ -1584,7 +1583,7 @@ class CreditTest(Test):
     assert self.rcv.credit == 10, self.rcv.credit
 
     data = self.rcv.recv(10)
-    assert data == str2bin("A"), data
+    assert data == b"A", data
     self.rcv.advance()
     self.pump()
     assert self.snd.credit == 9, self.snd.credit
@@ -1605,7 +1604,7 @@ class CreditTest(Test):
 
     sd = self.snd.delivery("tagB")
     assert sd
-    n = self.snd.send(str2bin("B"))
+    n = self.snd.send(b"B")
     assert n == 1
     self.snd.advance()
     self.pump()
@@ -1619,7 +1618,7 @@ class CreditTest(Test):
 
     sd = self.snd.delivery("tagC")
     assert sd
-    n = self.snd.send(str2bin("C"))
+    n = self.snd.send(b"C")
     assert n == 1
     self.snd.advance()
     self.pump()
@@ -1634,10 +1633,10 @@ class CreditTest(Test):
     assert self.rcv.credit == 2, self.rcv.credit
 
     data = self.rcv.recv(10)
-    assert data == str2bin("B"), data
+    assert data == b"B", data
     self.rcv.advance()
     data = self.rcv.recv(10)
-    assert data == str2bin("C"), data
+    assert data == b"C", data
     self.rcv.advance()
     self.pump()
     assert self.snd.credit == 0, self.snd.credit
@@ -1971,8 +1970,9 @@ class PipelineTest(Test):
     snd.open()
 
     for i in range(10):
-      d = snd.delivery("delivery-%s" % i)
-      snd.send(str2bin("delivery-%s" % i))
+      t = "delivery-%s" % i
+      d = snd.delivery(t)
+      snd.send(t.encode('ascii'))
       d.settle()
 
     snd.close()
@@ -2402,7 +2402,7 @@ class EventTest(CollectorTest):
     self.expect(Event.CONNECTION_INIT, Event.SESSION_INIT,
                 Event.LINK_INIT, Event.LINK_LOCAL_OPEN, Event.TRANSPORT)
     snd.delivery("delivery")
-    snd.send(str2bin("Hello World!"))
+    snd.send(b"Hello World!")
     snd.advance()
     self.pump()
     self.expect()
@@ -2418,7 +2418,7 @@ class EventTest(CollectorTest):
     snd, rcv = self.testFlowEvents()
     snd.open()
     dlv = snd.delivery("delivery")
-    snd.send(str2bin("Hello World!"))
+    snd.send(b"Hello World!")
     assert snd.advance()
     self.expect(Event.LINK_LOCAL_OPEN, Event.TRANSPORT)
     self.pump()
@@ -2449,7 +2449,7 @@ class EventTest(CollectorTest):
     t.bind(c)
     self.expect(Event.CONNECTION_BOUND)
     assert t.condition is None
-    t.push(str2bin("asdf"))
+    t.push(b"asdf")
     self.expect(Event.TRANSPORT_ERROR, Event.TRANSPORT_TAIL_CLOSED)
     assert t.condition is not None
     assert t.condition.name == "amqp:connection:framing-error"
@@ -2646,9 +2646,9 @@ class SaslEventTest(CollectorTest):
     transport.bind(conn)
     self.expect(Event.CONNECTION_INIT, Event.CONNECTION_BOUND)
 
-    transport.push(str2bin('AMQP\x03\x01\x00\x00\x00\x00\x00 \x02\x01\x00\x00\x00SA'
-                           '\xd0\x00\x00\x00\x10\x00\x00\x00\x02\xa3\tANONYMOUS@'
-                           'AMQP\x00\x01\x00\x00'))
+    transport.push(b'AMQP\x03\x01\x00\x00\x00\x00\x00 \x02\x01\x00\x00\x00SA'
+                   b'\xd0\x00\x00\x00\x10\x00\x00\x00\x02\xa3\tANONYMOUS@'
+                   b'AMQP\x00\x01\x00\x00')
     self.expect(Event.TRANSPORT)
     for i in range(1024):
       p = transport.pending()
@@ -2664,16 +2664,16 @@ class SaslEventTest(CollectorTest):
     s.allowed_mechs("ANONYMOUS PLAIN")
     transport.bind(conn)
     self.expect(Event.CONNECTION_INIT, Event.CONNECTION_BOUND)
-    transport.push(str2bin(
+    transport.push(
         # SASL
-        'AMQP\x03\x01\x00\x00'
+        b'AMQP\x03\x01\x00\x00'
         # @sasl-mechanisms(64) [sasl-server-mechanisms=@PN_SYMBOL[:ANONYMOUS]]
-        '\x00\x00\x00\x1c\x02\x01\x00\x00\x00S@\xc0\x0f\x01\xe0\x0c\x01\xa3\tANONYMOUS'
+        b'\x00\x00\x00\x1c\x02\x01\x00\x00\x00S@\xc0\x0f\x01\xe0\x0c\x01\xa3\tANONYMOUS'
         # @sasl-outcome(68) [code=0]
-        '\x00\x00\x00\x10\x02\x01\x00\x00\x00SD\xc0\x03\x01P\x00'
+        b'\x00\x00\x00\x10\x02\x01\x00\x00\x00SD\xc0\x03\x01P\x00'
         # AMQP
-        'AMQP\x00\x01\x00\x00'
-         ))
+        b'AMQP\x00\x01\x00\x00'
+        )
     self.expect(Event.TRANSPORT)
     p = transport.pending()
     bytes = transport.peek(p)
@@ -2695,16 +2695,16 @@ class SaslEventTest(CollectorTest):
     bytes = transport.peek(p)
     transport.pop(p)
     self.expect(Event.CONNECTION_INIT, Event.CONNECTION_BOUND)
-    transport.push(str2bin(
+    transport.push(
         # SASL
-        'AMQP\x03\x01\x00\x00'
+        b'AMQP\x03\x01\x00\x00'
         # @sasl-mechanisms(64) [sasl-server-mechanisms=@PN_SYMBOL[:ANONYMOUS]]
-        '\x00\x00\x00\x1c\x02\x01\x00\x00\x00S@\xc0\x0f\x01\xe0\x0c\x01\xa3\tANONYMOUS'
+        b'\x00\x00\x00\x1c\x02\x01\x00\x00\x00S@\xc0\x0f\x01\xe0\x0c\x01\xa3\tANONYMOUS'
         # @sasl-outcome(68) [code=0]
-        '\x00\x00\x00\x10\x02\x01\x00\x00\x00SD\xc0\x03\x01P\x00'
+        b'\x00\x00\x00\x10\x02\x01\x00\x00\x00SD\xc0\x03\x01P\x00'
         # AMQP
-        'AMQP\x00\x01\x00\x00'
-        ))
+        b'AMQP\x00\x01\x00\x00'
+        )
     self.expect(Event.TRANSPORT)
     p = transport.pending()
     bytes = transport.peek(p)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/tests/python/proton_tests/interop.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/interop.py b/tests/python/proton_tests/interop.py
index b330f22..2825fe4 100644
--- a/tests/python/proton_tests/interop.py
+++ b/tests/python/proton_tests/interop.py
@@ -20,7 +20,6 @@
 from proton import *
 import os
 from . import common
-from proton._compat import str2bin
 
 
 def find_test_interop_dir():
@@ -102,10 +101,10 @@ class InteropTest(common.Test):
 
     def test_strings(self):
         self.decode_data_file("strings")
-        self.assert_next(Data.BINARY, str2bin("abc\0defg"))
+        self.assert_next(Data.BINARY, b"abc\0defg")
         self.assert_next(Data.STRING, "abcdefg")
         self.assert_next(Data.SYMBOL, "abcdefg")
-        self.assert_next(Data.BINARY, str2bin(""))
+        self.assert_next(Data.BINARY, b"")
         self.assert_next(Data.STRING, "")
         self.assert_next(Data.SYMBOL, "")
         assert self.data.next() is None

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/tests/python/proton_tests/message.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/message.py b/tests/python/proton_tests/message.py
index 199f932..05a067c 100644
--- a/tests/python/proton_tests/message.py
+++ b/tests/python/proton_tests/message.py
@@ -20,11 +20,7 @@
 import os
 from . import common
 from proton import *
-from proton._compat import str2bin
-try:
-  from uuid import uuid4
-except ImportError:
-  from proton import uuid4
+from uuid import uuid4
 
 class Test(common.Test):
 
@@ -76,8 +72,7 @@ class AccessorsTest(Test):
     self._test("delivery_count", 0, range(0, 1024))
 
   def testUserId(self):
-    self._test("user_id", str2bin(""), (str2bin("asdf"), str2bin("fdsa"),
-                                      str2bin("asd\x00fdsa"), str2bin("")))
+    self._test("user_id", b"", (b"asdf", b"fdsa", b"asd\x00fdsa", b""))
 
   def testAddress(self):
     self._test_str("address")
@@ -214,21 +209,21 @@ class CodecTest(Test):
 
   def testDefaultCreationExpiryDecode(self):
     # This is a message with everything filled explicitly as null or zero in LIST32 HEADER and PROPERTIES lists
-    data = str2bin('\x00\x53\x70\xd0\x00\x00\x00\x0a\x00\x00\x00\x05\x42\x40\x40\x42\x52\x00\x00\x53\x73\xd0\x00\x00\x00\x12\x00\x00\x00\x0d\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x52\x00\x40')
+    data = b'\x00\x53\x70\xd0\x00\x00\x00\x0a\x00\x00\x00\x05\x42\x40\x40\x42\x52\x00\x00\x53\x73\xd0\x00\x00\x00\x12\x00\x00\x00\x0d\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x52\x00\x40'
     msg2 = Message()
     msg2.decode(data)
     assert msg2.expiry_time == 0, (msg2.expiry_time)
     assert msg2.creation_time == 0, (msg2.creation_time)
 
     # The same message with LIST8s instead
-    data = str2bin('\x00\x53\x70\xc0\x07\x05\x42\x40\x40\x42\x52\x00\x00\x53\x73\xc0\x0f\x0d\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x52\x00\x40')
+    data = b'\x00\x53\x70\xc0\x07\x05\x42\x40\x40\x42\x52\x00\x00\x53\x73\xc0\x0f\x0d\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x52\x00\x40'
     msg3 = Message()
     msg3.decode(data)
     assert msg2.expiry_time == 0, (msg2.expiry_time)
     assert msg2.creation_time == 0, (msg2.creation_time)
 
     # Minified message with zero length HEADER and PROPERTIES lists
-    data = str2bin('\x00\x53\x70\x45' '\x00\x53\x73\x45')
+    data = b'\x00\x53\x70\x45' b'\x00\x53\x73\x45'
     msg4 = Message()
     msg4.decode(data)
     assert msg2.expiry_time == 0, (msg2.expiry_time)
@@ -252,19 +247,19 @@ class CodecTest(Test):
 
   def testDefaultPriorityDecode(self):
     # This is a message with everything filled explicitly as null or zero in LIST32 HEADER and PROPERTIES lists
-    data = str2bin('\x00\x53\x70\xd0\x00\x00\x00\x0a\x00\x00\x00\x05\x42\x40\x40\x42\x52\x00\x00\x53\x73\xd0\x00\x00\x00\x22\x00\x00\x00\x0d\x40\x40\x40\x40\x40\x40\x40\x40\x83\x00\x00\x00\x00\x00\x00\x00\x00\x83\x00\x00\x00\x00\x00\x00\x00\x00\x40\x52\x00\x40')
+    data = b'\x00\x53\x70\xd0\x00\x00\x00\x0a\x00\x00\x00\x05\x42\x40\x40\x42\x52\x00\x00\x53\x73\xd0\x00\x00\x00\x22\x00\x00\x00\x0d\x40\x40\x40\x40\x40\x40\x40\x40\x83\x00\x00\x00\x00\x00\x00\x00\x00\x83\x00\x00\x00\x00\x00\x00\x00\x00\x40\x52\x00\x40'
     msg2 = Message()
     msg2.decode(data)
     assert msg2.priority == 4, (msg2.priority)
 
     # The same message with LIST8s instead
-    data = str2bin('\x00\x53\x70\xc0\x07\x05\x42\x40\x40\x42\x52\x00\x00\x53\x73\xc0\x1f\x0d\x40\x40\x40\x40\x40\x40\x40\x40\x83\x00\x00\x00\x00\x00\x00\x00\x00\x83\x00\x00\x00\x00\x00\x00\x00\x00\x40\x52\x00\x40')
+    data = b'\x00\x53\x70\xc0\x07\x05\x42\x40\x40\x42\x52\x00\x00\x53\x73\xc0\x1f\x0d\x40\x40\x40\x40\x40\x40\x40\x40\x83\x00\x00\x00\x00\x00\x00\x00\x00\x83\x00\x00\x00\x00\x00\x00\x00\x00\x40\x52\x00\x40'
     msg3 = Message()
     msg3.decode(data)
     assert msg3.priority == 4, (msg3.priority)
 
     # Minified message with zero length HEADER and PROPERTIES lists
-    data = str2bin('\x00\x53\x70\x45' '\x00\x53\x73\x45')
+    data = b'\x00\x53\x70\x45' b'\x00\x53\x73\x45'
     msg4 = Message()
     msg4.decode(data)
     assert msg4.priority == 4, (msg4.priority)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/tests/python/proton_tests/sasl.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/sasl.py b/tests/python/proton_tests/sasl.py
index 804c828..68ae200 100644
--- a/tests/python/proton_tests/sasl.py
+++ b/tests/python/proton_tests/sasl.py
@@ -24,7 +24,6 @@ from . import engine
 
 from proton import *
 from .common import pump, Skipped
-from proton._compat import str2bin
 
 def _sslCertpath(file):
     """ Return the full path to the certificate, keyfile, etc.
@@ -113,20 +112,20 @@ class SaslTest(Test):
     assert self.s2.outcome is None
 
     # Push client bytes into server
-    self.t2.push(str2bin(
+    self.t2.push(
         # SASL
-        'AMQP\x03\x01\x00\x00'
+        b'AMQP\x03\x01\x00\x00'
         # @sasl-init(65) [mechanism=:ANONYMOUS, initial-response=b"anonymous@fuschia"]
-        '\x00\x00\x002\x02\x01\x00\x00\x00SA\xd0\x00\x00\x00"\x00\x00\x00\x02\xa3\x09ANONYMOUS\xa0\x11anonymous@fuschia'
+        b'\x00\x00\x002\x02\x01\x00\x00\x00SA\xd0\x00\x00\x00"\x00\x00\x00\x02\xa3\x09ANONYMOUS\xa0\x11anonymous@fuschia'
         # SASL (again illegally)
-        'AMQP\x03\x01\x00\x00'
+        b'AMQP\x03\x01\x00\x00'
         # @sasl-init(65) [mechanism=:ANONYMOUS, initial-response=b"anonymous@fuschia"]
-        '\x00\x00\x002\x02\x01\x00\x00\x00SA\xd0\x00\x00\x00"\x00\x00\x00\x02\xa3\x09ANONYMOUS\xa0\x11anonymous@fuschia'
+        b'\x00\x00\x002\x02\x01\x00\x00\x00SA\xd0\x00\x00\x00"\x00\x00\x00\x02\xa3\x09ANONYMOUS\xa0\x11anonymous@fuschia'
         # AMQP
-        'AMQP\x00\x01\x00\x00'
+        b'AMQP\x00\x01\x00\x00'
         # @open(16) [container-id="", channel-max=1234]
-        '\x00\x00\x00!\x02\x00\x00\x00\x00S\x10\xd0\x00\x00\x00\x11\x00\x00\x00\x0a\xa1\x00@@`\x04\xd2@@@@@@'
-        ))
+        b'\x00\x00\x00!\x02\x00\x00\x00\x00S\x10\xd0\x00\x00\x00\x11\x00\x00\x00\x0a\xa1\x00@@`\x04\xd2@@@@@@'
+        )
 
     consumeAllOuput(self.t2)
 
@@ -144,16 +143,16 @@ class SaslTest(Test):
     assert self.s2.outcome is None
 
     # Push client bytes into server
-    self.t2.push(str2bin(
+    self.t2.push(
         # SASL
-        'AMQP\x03\x01\x00\x00'
+        b'AMQP\x03\x01\x00\x00'
         # @sasl-init(65) [mechanism=:ANONYMOUS, initial-response=b"anonymous@fuschia"]
-        '\x00\x00\x002\x02\x01\x00\x00\x00SA\xd0\x00\x00\x00"\x00\x00\x00\x02\xa3\x09ANONYMOUS\xa0\x11anonymous@fuschia'
+        b'\x00\x00\x002\x02\x01\x00\x00\x00SA\xd0\x00\x00\x00"\x00\x00\x00\x02\xa3\x09ANONYMOUS\xa0\x11anonymous@fuschia'
         # AMQP
-        'AMQP\x00\x01\x00\x00'
+        b'AMQP\x00\x01\x00\x00'
         # @open(16) [container-id="", channel-max=1234]
-        '\x00\x00\x00!\x02\x00\x00\x00\x00S\x10\xd0\x00\x00\x00\x11\x00\x00\x00\x0a\xa1\x00@@`\x04\xd2@@@@@@'
-        ))
+        b'\x00\x00\x00!\x02\x00\x00\x00\x00S\x10\xd0\x00\x00\x00\x11\x00\x00\x00\x0a\xa1\x00@@`\x04\xd2@@@@@@'
+        )
 
     consumeAllOuput(self.t2)
 
@@ -173,18 +172,18 @@ class SaslTest(Test):
     # Push server bytes into client
     # Commented out lines in this test are where the client input processing doesn't
     # run after output processing even though there is input waiting
-    self.t1.push(str2bin(
+    self.t1.push(
         # SASL
-        'AMQP\x03\x01\x00\x00'
+        b'AMQP\x03\x01\x00\x00'
         # @sasl-mechanisms(64) [sasl-server-mechanisms=@PN_SYMBOL[:ANONYMOUS]]
-        '\x00\x00\x00\x1c\x02\x01\x00\x00\x00S@\xc0\x0f\x01\xe0\x0c\x01\xa3\tANONYMOUS'
+        b'\x00\x00\x00\x1c\x02\x01\x00\x00\x00S@\xc0\x0f\x01\xe0\x0c\x01\xa3\tANONYMOUS'
         # @sasl-outcome(68) [code=0]
-        '\x00\x00\x00\x10\x02\x01\x00\x00\x00SD\xc0\x03\x01P\x00'
+        b'\x00\x00\x00\x10\x02\x01\x00\x00\x00SD\xc0\x03\x01P\x00'
         # AMQP
-        'AMQP\x00\x01\x00\x00'
+        b'AMQP\x00\x01\x00\x00'
         # @open(16) [container-id="", channel-max=1234]
-        '\x00\x00\x00!\x02\x00\x00\x00\x00S\x10\xd0\x00\x00\x00\x11\x00\x00\x00\x0a\xa1\x00@@`\x04\xd2@@@@@@'
-        ))
+        b'\x00\x00\x00!\x02\x00\x00\x00\x00S\x10\xd0\x00\x00\x00\x11\x00\x00\x00\x0a\xa1\x00@@`\x04\xd2@@@@@@'
+        )
 
     consumeAllOuput(self.t1)
 
@@ -217,17 +216,17 @@ class SaslTest(Test):
 
     out = self.t1.peek(1024)
     self.t1.pop(len(out))
-    self.t1.push(str2bin("AMQP\x03\x01\x00\x00"))
+    self.t1.push(b"AMQP\x03\x01\x00\x00")
     out = self.t1.peek(1024)
     self.t1.pop(len(out))
-    self.t1.push(str2bin("\x00\x00\x00"))
+    self.t1.push(b"\x00\x00\x00")
     out = self.t1.peek(1024)
     self.t1.pop(len(out))
 
-    self.t1.push(str2bin("6\x02\x01\x00\x00\x00S@\xc0\x29\x01\xe0\x26\x04\xa3\x05PLAIN\x0aDIGEST-MD5\x09ANONYMOUS\x08CRAM-MD5"))
+    self.t1.push(b"6\x02\x01\x00\x00\x00S@\xc0\x29\x01\xe0\x26\x04\xa3\x05PLAIN\x0aDIGEST-MD5\x09ANONYMOUS\x08CRAM-MD5")
     out = self.t1.peek(1024)
     self.t1.pop(len(out))
-    self.t1.push(str2bin("\x00\x00\x00\x10\x02\x01\x00\x00\x00SD\xc0\x03\x01P\x00"))
+    self.t1.push(b"\x00\x00\x00\x10\x02\x01\x00\x00\x00SD\xc0\x03\x01P\x00")
     out = self.t1.peek(1024)
     self.t1.pop(len(out))
     while out:

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51934030/tests/python/proton_tests/transport.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/transport.py b/tests/python/proton_tests/transport.py
index 5a5cea2..5390d07 100644
--- a/tests/python/proton_tests/transport.py
+++ b/tests/python/proton_tests/transport.py
@@ -21,7 +21,6 @@ import os
 import sys
 from . import common
 from proton import *
-from proton._compat import str2bin
 
 
 class Test(common.Test):
@@ -63,16 +62,16 @@ class ClientTransportTest(Test):
     assert self.conn.remote_condition.name == name, self.conn.remote_condition
 
   def testEOS(self):
-    self.transport.push(str2bin("")) # should be a noop
+    self.transport.push(b"") # should be a noop
     self.transport.close_tail() # should result in framing error
     self.assert_error(u'amqp:connection:framing-error')
 
   def testPartial(self):
-    self.transport.push(str2bin("AMQ")) # partial header
+    self.transport.push(b"AMQ") # partial header
     self.transport.close_tail() # should result in framing error
     self.assert_error(u'amqp:connection:framing-error')
 
-  def testGarbage(self, garbage=str2bin("GARBAGE_")):
+  def testGarbage(self, garbage=b"GARBAGE_"):
     self.transport.push(garbage)
     self.assert_error(u'amqp:connection:framing-error')
     assert self.transport.pending() < 0
@@ -80,35 +79,35 @@ class ClientTransportTest(Test):
     assert self.transport.pending() < 0
 
   def testSmallGarbage(self):
-    self.testGarbage(str2bin("XXX"))
+    self.testGarbage(b"XXX")
 
   def testBigGarbage(self):
-    self.testGarbage(str2bin("GARBAGE_XXX"))
+    self.testGarbage(b"GARBAGE_XXX")
 
   def testHeader(self):
-    self.transport.push(str2bin("AMQP\x00\x01\x00\x00"))
+    self.transport.push(b"AMQP\x00\x01\x00\x00")
     self.transport.close_tail()
     self.assert_error(u'amqp:connection:framing-error')
 
   def testHeaderBadDOFF1(self):
     """Verify doff > size error"""
-    self.testGarbage(str2bin("AMQP\x00\x01\x00\x00\x00\x00\x00\x08\x08\x00\x00\x00"))
+    self.testGarbage(b"AMQP\x00\x01\x00\x00\x00\x00\x00\x08\x08\x00\x00\x00")
 
   def testHeaderBadDOFF2(self):
     """Verify doff < 2 error"""
-    self.testGarbage(str2bin("AMQP\x00\x01\x00\x00\x00\x00\x00\x08\x01\x00\x00\x00"))
+    self.testGarbage(b"AMQP\x00\x01\x00\x00\x00\x00\x00\x08\x01\x00\x00\x00")
 
   def testHeaderBadSize(self):
     """Verify size > max_frame_size error"""
     self.transport.max_frame_size = 512
-    self.testGarbage(str2bin("AMQP\x00\x01\x00\x00\x00\x00\x02\x01\x02\x00\x00\x00"))
+    self.testGarbage(b"AMQP\x00\x01\x00\x00\x00\x00\x02\x01\x02\x00\x00\x00")
 
   def testProtocolNotSupported(self):
-    self.transport.push(str2bin("AMQP\x01\x01\x0a\x00"))
+    self.transport.push(b"AMQP\x01\x01\x0a\x00")
     p = self.transport.pending()
     assert p >= 8, p
     bytes = self.transport.peek(p)
-    assert bytes[:8] == str2bin("AMQP\x00\x01\x00\x00")
+    assert bytes[:8] == b"AMQP\x00\x01\x00\x00"
     self.transport.pop(p)
     self.drain()
     assert self.transport.closed
@@ -127,8 +126,8 @@ class ClientTransportTest(Test):
     trn = Transport()
     trn.bind(conn)
     out = trn.peek(1024)
-    assert str2bin("test-container") in out, repr(out)
-    assert str2bin("test-hostname") in out, repr(out)
+    assert b"test-container" in out, repr(out)
+    assert b"test-hostname" in out, repr(out)
     self.transport.push(out)
 
     c = Connection()
@@ -185,7 +184,7 @@ class ClientTransportTest(Test):
     self.transport.pop(len(dat2) - len(dat1))
     dat3 = self.transport.peek(1024)
     self.transport.pop(len(dat3))
-    assert self.transport.peek(1024) == str2bin("")
+    assert self.transport.peek(1024) == b""
 
     self.peer.push(dat1)
     self.peer.push(dat2[len(dat1):])
@@ -228,50 +227,50 @@ class ServerTransportTest(Test):
 
   # TODO: This may no longer be testing anything
   def testEOS(self):
-    self.transport.push(str2bin("")) # should be a noop
+    self.transport.push(b"") # should be a noop
     self.transport.close_tail()
     p = self.transport.pending()
     self.drain()
     assert self.transport.closed
 
   def testPartial(self):
-    self.transport.push(str2bin("AMQ")) # partial header
+    self.transport.push(b"AMQ") # partial header
     self.transport.close_tail()
     p = self.transport.pending()
     assert p >= 8, p
     bytes = self.transport.peek(p)
-    assert bytes[:8] == str2bin("AMQP\x00\x01\x00\x00")
+    assert bytes[:8] == b"AMQP\x00\x01\x00\x00"
     self.transport.pop(p)
     self.drain()
     assert self.transport.closed
 
-  def testGarbage(self, garbage="GARBAGE_"):
-    self.transport.push(str2bin(garbage))
+  def testGarbage(self, garbage=b"GARBAGE_"):
+    self.transport.push(garbage)
     p = self.transport.pending()
     assert p >= 8, p
     bytes = self.transport.peek(p)
-    assert bytes[:8] == str2bin("AMQP\x00\x01\x00\x00")
+    assert bytes[:8] == b"AMQP\x00\x01\x00\x00"
     self.transport.pop(p)
     self.drain()
     assert self.transport.closed
 
   def testSmallGarbage(self):
-    self.testGarbage("XXX")
+    self.testGarbage(b"XXX")
 
   def testBigGarbage(self):
-    self.testGarbage("GARBAGE_XXX")
+    self.testGarbage(b"GARBAGE_XXX")
 
   def testHeader(self):
-    self.transport.push(str2bin("AMQP\x00\x01\x00\x00"))
+    self.transport.push(b"AMQP\x00\x01\x00\x00")
     self.transport.close_tail()
     self.assert_error(u'amqp:connection:framing-error')
 
   def testProtocolNotSupported(self):
-    self.transport.push(str2bin("AMQP\x01\x01\x0a\x00"))
+    self.transport.push(b"AMQP\x01\x01\x0a\x00")
     p = self.transport.pending()
     assert p >= 8, p
     bytes = self.transport.peek(p)
-    assert bytes[:8] == str2bin("AMQP\x00\x01\x00\x00")
+    assert bytes[:8] == b"AMQP\x00\x01\x00\x00"
     self.transport.pop(p)
     self.drain()
     assert self.transport.closed
@@ -290,8 +289,8 @@ class ServerTransportTest(Test):
     trn = Transport()
     trn.bind(conn)
     out = trn.peek(1024)
-    assert str2bin("test-container") in out, repr(out)
-    assert str2bin("test-hostname") in out, repr(out)
+    assert b"test-container" in out, repr(out)
+    assert b"test-hostname" in out, repr(out)
     self.transport.push(out)
 
     c = Connection()
@@ -348,7 +347,7 @@ class ServerTransportTest(Test):
     self.transport.pop(len(dat2) - len(dat1))
     dat3 = self.transport.peek(1024)
     self.transport.pop(len(dat3))
-    assert self.transport.peek(1024) == str2bin("")
+    assert self.transport.peek(1024) == b""
 
     self.peer.push(dat1)
     self.peer.push(dat2[len(dat1):])


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[4/6] qpid-proton git commit: PROTON-1850: Split up proton __init__.py into multiple files - Reformatted python source to (mostly) PEP-8 standards - Control what gets exported from __init__ by restricting what it imports - Move most of the reactor implem

Posted by as...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_common.py
----------------------------------------------------------------------
diff --git a/python/proton/_common.py b/python/proton/_common.py
new file mode 100644
index 0000000..3715c6a
--- /dev/null
+++ b/python/proton/_common.py
@@ -0,0 +1,91 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+#
+# Hacks to provide Python2 <---> Python3 compatibility
+#
+# The results are
+# |       |long|unicode|
+# |python2|long|unicode|
+# |python3| int|    str|
+try:
+    long()
+except NameError:
+    long = int
+try:
+    unicode()
+except NameError:
+    unicode = str
+
+
+def isinteger(value):
+    return isinstance(value, (int, long))
+
+
+def isstring(value):
+    return isinstance(value, (str, unicode))
+
+
+class Constant(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def __repr__(self):
+        return self.name
+
+
+def secs2millis(secs):
+    return long(secs * 1000)
+
+
+def millis2secs(millis):
+    return float(millis) / 1000.0
+
+
+def unicode2utf8(string):
+    """Some Proton APIs expect a null terminated string. Convert python text
+    types to UTF8 to avoid zero bytes introduced by other multi-byte encodings.
+    This method will throw if the string cannot be converted.
+    """
+    if string is None:
+        return None
+    elif isinstance(string, str):
+        # Must be py2 or py3 str
+        # The swig binding converts py3 str -> utf8 char* and back sutomatically
+        return string
+    elif isinstance(string, unicode):
+        # This must be python2 unicode as we already detected py3 str above
+        return string.encode('utf-8')
+    # Anything else illegal - specifically python3 bytes
+    raise TypeError("Unrecognized string type: %r (%s)" % (string, type(string)))
+
+
+def utf82unicode(string):
+    """Convert C strings returned from proton-c into python unicode"""
+    if string is None:
+        return None
+    elif isinstance(string, unicode):
+        # py2 unicode, py3 str (via hack definition)
+        return string
+    elif isinstance(string, bytes):
+        # py2 str (via hack definition), py3 bytes
+        return string.decode('utf8')
+    raise TypeError("Unrecognized string type")

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_compat.py
----------------------------------------------------------------------
diff --git a/python/proton/_compat.py b/python/proton/_compat.py
index afd82e3..eae4c84 100644
--- a/python/proton/_compat.py
+++ b/python/proton/_compat.py
@@ -32,8 +32,6 @@ except ImportError:
 PY3 = sys.version_info[0] == 3
 
 if PY3:
-    string_types = (str,)
-
     def raise_(t, v=None, tb=None):
         """Mimic the old 2.x raise behavior:
         Raise an exception of type t with value v using optional traceback tb
@@ -45,23 +43,22 @@ if PY3:
         else:
             raise v.with_traceback(tb)
 
+
     def iteritems(d, **kw):
         return iter(d.items(**kw))
 
+
     unichr = chr
 else:
-    # includes both unicode and non-unicode strings:
-    string_types = (basestring,)
-
     # the raise syntax will cause a parse error in Py3, so 'sneak' in a
     # definition that won't cause the parser to barf
-    exec("""def raise_(t, v=None, tb=None):
+    exec ("""def raise_(t, v=None, tb=None):
     raise t, v, tb
 """)
 
+
     def iteritems(d, **kw):
         return d.iteritems(**kw)
 
-    unichr = unichr
 
-__all__ = [ 'PY3', 'queue', 'string_types', 'raise_', 'iteritems', 'unichr']
\ No newline at end of file
+    unichr = unichr

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_condition.py
----------------------------------------------------------------------
diff --git a/python/proton/_condition.py b/python/proton/_condition.py
new file mode 100644
index 0000000..e5dbde9
--- /dev/null
+++ b/python/proton/_condition.py
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+from cproton import pn_condition_clear, pn_condition_set_name, pn_condition_set_description, pn_condition_info, \
+    pn_condition_is_set, pn_condition_get_name, pn_condition_get_description
+
+from ._data import Data, dat2obj
+
+
+class Condition:
+
+    def __init__(self, name, description=None, info=None):
+        self.name = name
+        self.description = description
+        self.info = info
+
+    def __repr__(self):
+        return "Condition(%s)" % ", ".join([repr(x) for x in
+                                            (self.name, self.description, self.info)
+                                            if x])
+
+    def __eq__(self, o):
+        if not isinstance(o, Condition): return False
+        return self.name == o.name and \
+               self.description == o.description and \
+               self.info == o.info
+
+
+def obj2cond(obj, cond):
+    pn_condition_clear(cond)
+    if obj:
+        pn_condition_set_name(cond, str(obj.name))
+        pn_condition_set_description(cond, obj.description)
+        info = Data(pn_condition_info(cond))
+        if obj.info:
+            info.put_object(obj.info)
+
+
+def cond2obj(cond):
+    if pn_condition_is_set(cond):
+        return Condition(pn_condition_get_name(cond),
+                         pn_condition_get_description(cond),
+                         dat2obj(pn_condition_info(cond)))
+    else:
+        return None

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_data.py
----------------------------------------------------------------------
diff --git a/python/proton/_data.py b/python/proton/_data.py
new file mode 100644
index 0000000..f4ad381
--- /dev/null
+++ b/python/proton/_data.py
@@ -0,0 +1,1129 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+import uuid
+
+from cproton import PN_TIMESTAMP, PN_FLOAT, PN_DESCRIBED, PN_DECIMAL64, PN_UBYTE, PN_UUID, PN_NULL, PN_BINARY, \
+    PN_LIST, PN_OVERFLOW, PN_MAP, PN_LONG, PN_SHORT, PN_CHAR, PN_UINT, PN_ULONG, PN_STRING, PN_USHORT, PN_DOUBLE, \
+    PN_BYTE, PN_DECIMAL32, PN_DECIMAL128, PN_ARRAY, PN_SYMBOL, PN_BOOL, PN_INT, \
+    pn_data_get_binary, pn_data_get_decimal64, pn_data_put_symbol, pn_data_put_float, \
+    pn_data_is_array_described, pn_data_exit, pn_data_put_uint, pn_data_put_decimal128, \
+    pn_data_lookup, pn_data_put_char, pn_data_encoded_size, pn_data_get_bool, \
+    pn_data_get_short, pn_data_prev, pn_data_type, pn_data_widen, pn_data_put_decimal64, \
+    pn_data_put_string, pn_data_get_array, pn_data_put_ulong, pn_data_get_byte, pn_data_get_symbol, pn_data_encode, \
+    pn_data_rewind, pn_data_put_bool, pn_data_is_null, pn_data_error, \
+    pn_data_put_double, pn_data_copy, pn_data_put_int, pn_data_get_ubyte, pn_data_free, pn_data_clear, \
+    pn_data_get_double, pn_data_put_byte, pn_data_put_uuid, pn_data_put_ushort, pn_data_is_described, \
+    pn_data_get_float, pn_data_get_uint, pn_data_put_described, pn_data_get_decimal128, pn_data, \
+    pn_data_get_array_type, pn_data_put_map, pn_data_put_list, pn_data_get_string, pn_data_get_char, \
+    pn_data_put_decimal32, pn_data_enter, pn_data_put_short, pn_data_put_timestamp, \
+    pn_data_get_long, pn_data_get_map, pn_data_narrow, pn_data_put_array, pn_data_get_ushort, \
+    pn_data_get_int, pn_data_get_list, pn_data_get_ulong, pn_data_put_ubyte, \
+    pn_data_format, pn_data_dump, pn_data_get_uuid, pn_data_get_decimal32, \
+    pn_data_put_binary, pn_data_get_timestamp, pn_data_decode, pn_data_next, pn_data_put_null, pn_data_put_long, \
+    pn_error_text
+
+from ._common import Constant
+from ._exceptions import EXCEPTIONS, DataException
+
+from . import _compat
+
+#
+# Hacks to provide Python2 <---> Python3 compatibility
+#
+# The results are
+# |       |long|unicode|
+# |python2|long|unicode|
+# |python3| int|    str|
+try:
+    long()
+except NameError:
+    long = int
+try:
+    unicode()
+except NameError:
+    unicode = str
+
+
+class UnmappedType:
+
+    def __init__(self, msg):
+        self.msg = msg
+
+    def __repr__(self):
+        return "UnmappedType(%s)" % self.msg
+
+
+class ulong(long):
+
+    def __repr__(self):
+        return "ulong(%s)" % long.__repr__(self)
+
+
+class timestamp(long):
+
+    def __repr__(self):
+        return "timestamp(%s)" % long.__repr__(self)
+
+
+class symbol(unicode):
+
+    def __repr__(self):
+        return "symbol(%s)" % unicode.__repr__(self)
+
+
+class char(unicode):
+
+    def __repr__(self):
+        return "char(%s)" % unicode.__repr__(self)
+
+
+class byte(int):
+
+    def __repr__(self):
+        return "byte(%s)" % int.__repr__(self)
+
+
+class short(int):
+
+    def __repr__(self):
+        return "short(%s)" % int.__repr__(self)
+
+
+class int32(int):
+
+    def __repr__(self):
+        return "int32(%s)" % int.__repr__(self)
+
+
+class ubyte(int):
+
+    def __repr__(self):
+        return "ubyte(%s)" % int.__repr__(self)
+
+
+class ushort(int):
+
+    def __repr__(self):
+        return "ushort(%s)" % int.__repr__(self)
+
+
+class uint(long):
+
+    def __repr__(self):
+        return "uint(%s)" % long.__repr__(self)
+
+
+class float32(float):
+
+    def __repr__(self):
+        return "float32(%s)" % float.__repr__(self)
+
+
+class decimal32(int):
+
+    def __repr__(self):
+        return "decimal32(%s)" % int.__repr__(self)
+
+
+class decimal64(long):
+
+    def __repr__(self):
+        return "decimal64(%s)" % long.__repr__(self)
+
+
+class decimal128(bytes):
+
+    def __repr__(self):
+        return "decimal128(%s)" % bytes.__repr__(self)
+
+
+class Described(object):
+
+    def __init__(self, descriptor, value):
+        self.descriptor = descriptor
+        self.value = value
+
+    def __repr__(self):
+        return "Described(%r, %r)" % (self.descriptor, self.value)
+
+    def __eq__(self, o):
+        if isinstance(o, Described):
+            return self.descriptor == o.descriptor and self.value == o.value
+        else:
+            return False
+
+
+UNDESCRIBED = Constant("UNDESCRIBED")
+
+
+class Array(object):
+
+    def __init__(self, descriptor, type, *elements):
+        self.descriptor = descriptor
+        self.type = type
+        self.elements = elements
+
+    def __iter__(self):
+        return iter(self.elements)
+
+    def __repr__(self):
+        if self.elements:
+            els = ", %s" % (", ".join(map(repr, self.elements)))
+        else:
+            els = ""
+        return "Array(%r, %r%s)" % (self.descriptor, self.type, els)
+
+    def __eq__(self, o):
+        if isinstance(o, Array):
+            return self.descriptor == o.descriptor and \
+                   self.type == o.type and self.elements == o.elements
+        else:
+            return False
+
+
+class Data:
+    """
+    The L{Data} class provides an interface for decoding, extracting,
+    creating, and encoding arbitrary AMQP data. A L{Data} object
+    contains a tree of AMQP values. Leaf nodes in this tree correspond
+    to scalars in the AMQP type system such as L{ints<INT>} or
+    L{strings<STRING>}. Non-leaf nodes in this tree correspond to
+    compound values in the AMQP type system such as L{lists<LIST>},
+    L{maps<MAP>}, L{arrays<ARRAY>}, or L{described values<DESCRIBED>}.
+    The root node of the tree is the L{Data} object itself and can have
+    an arbitrary number of children.
+
+    A L{Data} object maintains the notion of the current sibling node
+    and a current parent node. Siblings are ordered within their parent.
+    Values are accessed and/or added by using the L{next}, L{prev},
+    L{enter}, and L{exit} methods to navigate to the desired location in
+    the tree and using the supplied variety of put_*/get_* methods to
+    access or add a value of the desired type.
+
+    The put_* methods will always add a value I{after} the current node
+    in the tree. If the current node has a next sibling the put_* method
+    will overwrite the value on this node. If there is no current node
+    or the current node has no next sibling then one will be added. The
+    put_* methods always set the added/modified node to the current
+    node. The get_* methods read the value of the current node and do
+    not change which node is current.
+
+    The following types of scalar values are supported:
+
+     - L{NULL}
+     - L{BOOL}
+     - L{UBYTE}
+     - L{USHORT}
+     - L{SHORT}
+     - L{UINT}
+     - L{INT}
+     - L{ULONG}
+     - L{LONG}
+     - L{FLOAT}
+     - L{DOUBLE}
+     - L{BINARY}
+     - L{STRING}
+     - L{SYMBOL}
+
+    The following types of compound values are supported:
+
+     - L{DESCRIBED}
+     - L{ARRAY}
+     - L{LIST}
+     - L{MAP}
+    """
+
+    NULL = PN_NULL; "A null value."
+    BOOL = PN_BOOL; "A boolean value."
+    UBYTE = PN_UBYTE; "An unsigned byte value."
+    BYTE = PN_BYTE; "A signed byte value."
+    USHORT = PN_USHORT; "An unsigned short value."
+    SHORT = PN_SHORT; "A short value."
+    UINT = PN_UINT; "An unsigned int value."
+    INT = PN_INT; "A signed int value."
+    CHAR = PN_CHAR; "A character value."
+    ULONG = PN_ULONG; "An unsigned long value."
+    LONG = PN_LONG; "A signed long value."
+    TIMESTAMP = PN_TIMESTAMP; "A timestamp value."
+    FLOAT = PN_FLOAT; "A float value."
+    DOUBLE = PN_DOUBLE; "A double value."
+    DECIMAL32 = PN_DECIMAL32; "A DECIMAL32 value."
+    DECIMAL64 = PN_DECIMAL64; "A DECIMAL64 value."
+    DECIMAL128 = PN_DECIMAL128; "A DECIMAL128 value."
+    UUID = PN_UUID; "A UUID value."
+    BINARY = PN_BINARY; "A binary string."
+    STRING = PN_STRING; "A unicode string."
+    SYMBOL = PN_SYMBOL; "A symbolic string."
+    DESCRIBED = PN_DESCRIBED; "A described value."
+    ARRAY = PN_ARRAY; "An array value."
+    LIST = PN_LIST; "A list value."
+    MAP = PN_MAP; "A map value."
+
+    type_names = {
+        NULL: "null",
+        BOOL: "bool",
+        BYTE: "byte",
+        UBYTE: "ubyte",
+        SHORT: "short",
+        USHORT: "ushort",
+        INT: "int",
+        UINT: "uint",
+        CHAR: "char",
+        LONG: "long",
+        ULONG: "ulong",
+        TIMESTAMP: "timestamp",
+        FLOAT: "float",
+        DOUBLE: "double",
+        DECIMAL32: "decimal32",
+        DECIMAL64: "decimal64",
+        DECIMAL128: "decimal128",
+        UUID: "uuid",
+        BINARY: "binary",
+        STRING: "string",
+        SYMBOL: "symbol",
+        DESCRIBED: "described",
+        ARRAY: "array",
+        LIST: "list",
+        MAP: "map"
+    }
+
+    @classmethod
+    def type_name(type):
+        return Data.type_names[type]
+
+    def __init__(self, capacity=16):
+        if isinstance(capacity, (int, long)):
+            self._data = pn_data(capacity)
+            self._free = True
+        else:
+            self._data = capacity
+            self._free = False
+
+    def __del__(self):
+        if self._free and hasattr(self, "_data"):
+            pn_data_free(self._data)
+            del self._data
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, DataException)
+            raise exc("[%s]: %s" % (err, pn_error_text(pn_data_error(self._data))))
+        else:
+            return err
+
+    def clear(self):
+        """
+        Clears the data object.
+        """
+        pn_data_clear(self._data)
+
+    def rewind(self):
+        """
+        Clears current node and sets the parent to the root node.  Clearing the
+        current node sets it _before_ the first node, calling next() will advance to
+        the first node.
+        """
+        assert self._data is not None
+        pn_data_rewind(self._data)
+
+    def next(self):
+        """
+        Advances the current node to its next sibling and returns its
+        type. If there is no next sibling the current node remains
+        unchanged and None is returned.
+        """
+        found = pn_data_next(self._data)
+        if found:
+            return self.type()
+        else:
+            return None
+
+    def prev(self):
+        """
+        Advances the current node to its previous sibling and returns its
+        type. If there is no previous sibling the current node remains
+        unchanged and None is returned.
+        """
+        found = pn_data_prev(self._data)
+        if found:
+            return self.type()
+        else:
+            return None
+
+    def enter(self):
+        """
+        Sets the parent node to the current node and clears the current node.
+        Clearing the current node sets it _before_ the first child,
+        call next() advances to the first child.
+        """
+        return pn_data_enter(self._data)
+
+    def exit(self):
+        """
+        Sets the current node to the parent node and the parent node to
+        its own parent.
+        """
+        return pn_data_exit(self._data)
+
+    def lookup(self, name):
+        return pn_data_lookup(self._data, name)
+
+    def narrow(self):
+        pn_data_narrow(self._data)
+
+    def widen(self):
+        pn_data_widen(self._data)
+
+    def type(self):
+        """
+        Returns the type of the current node.
+        """
+        dtype = pn_data_type(self._data)
+        if dtype == -1:
+            return None
+        else:
+            return dtype
+
+    def encoded_size(self):
+        """
+        Returns the size in bytes needed to encode the data in AMQP format.
+        """
+        return pn_data_encoded_size(self._data)
+
+    def encode(self):
+        """
+        Returns a representation of the data encoded in AMQP format.
+        """
+        size = 1024
+        while True:
+            cd, enc = pn_data_encode(self._data, size)
+            if cd == PN_OVERFLOW:
+                size *= 2
+            elif cd >= 0:
+                return enc
+            else:
+                self._check(cd)
+
+    def decode(self, encoded):
+        """
+        Decodes the first value from supplied AMQP data and returns the
+        number of bytes consumed.
+
+        @type encoded: binary
+        @param encoded: AMQP encoded binary data
+        """
+        return self._check(pn_data_decode(self._data, encoded))
+
+    def put_list(self):
+        """
+        Puts a list value. Elements may be filled by entering the list
+        node and putting element values.
+
+          >>> data = Data()
+          >>> data.put_list()
+          >>> data.enter()
+          >>> data.put_int(1)
+          >>> data.put_int(2)
+          >>> data.put_int(3)
+          >>> data.exit()
+        """
+        self._check(pn_data_put_list(self._data))
+
+    def put_map(self):
+        """
+        Puts a map value. Elements may be filled by entering the map node
+        and putting alternating key value pairs.
+
+          >>> data = Data()
+          >>> data.put_map()
+          >>> data.enter()
+          >>> data.put_string("key")
+          >>> data.put_string("value")
+          >>> data.exit()
+        """
+        self._check(pn_data_put_map(self._data))
+
+    def put_array(self, described, element_type):
+        """
+        Puts an array value. Elements may be filled by entering the array
+        node and putting the element values. The values must all be of the
+        specified array element type. If an array is described then the
+        first child value of the array is the descriptor and may be of any
+        type.
+
+          >>> data = Data()
+          >>>
+          >>> data.put_array(False, Data.INT)
+          >>> data.enter()
+          >>> data.put_int(1)
+          >>> data.put_int(2)
+          >>> data.put_int(3)
+          >>> data.exit()
+          >>>
+          >>> data.put_array(True, Data.DOUBLE)
+          >>> data.enter()
+          >>> data.put_symbol("array-descriptor")
+          >>> data.put_double(1.1)
+          >>> data.put_double(1.2)
+          >>> data.put_double(1.3)
+          >>> data.exit()
+
+        @type described: bool
+        @param described: specifies whether the array is described
+        @type element_type: int
+        @param element_type: the type of the array elements
+        """
+        self._check(pn_data_put_array(self._data, described, element_type))
+
+    def put_described(self):
+        """
+        Puts a described value. A described node has two children, the
+        descriptor and the value. These are specified by entering the node
+        and putting the desired values.
+
+          >>> data = Data()
+          >>> data.put_described()
+          >>> data.enter()
+          >>> data.put_symbol("value-descriptor")
+          >>> data.put_string("the value")
+          >>> data.exit()
+        """
+        self._check(pn_data_put_described(self._data))
+
+    def put_null(self):
+        """
+        Puts a null value.
+        """
+        self._check(pn_data_put_null(self._data))
+
+    def put_bool(self, b):
+        """
+        Puts a boolean value.
+
+        @param b: a boolean value
+        """
+        self._check(pn_data_put_bool(self._data, b))
+
+    def put_ubyte(self, ub):
+        """
+        Puts an unsigned byte value.
+
+        @param ub: an integral value
+        """
+        self._check(pn_data_put_ubyte(self._data, ub))
+
+    def put_byte(self, b):
+        """
+        Puts a signed byte value.
+
+        @param b: an integral value
+        """
+        self._check(pn_data_put_byte(self._data, b))
+
+    def put_ushort(self, us):
+        """
+        Puts an unsigned short value.
+
+        @param us: an integral value.
+        """
+        self._check(pn_data_put_ushort(self._data, us))
+
+    def put_short(self, s):
+        """
+        Puts a signed short value.
+
+        @param s: an integral value
+        """
+        self._check(pn_data_put_short(self._data, s))
+
+    def put_uint(self, ui):
+        """
+        Puts an unsigned int value.
+
+        @param ui: an integral value
+        """
+        self._check(pn_data_put_uint(self._data, ui))
+
+    def put_int(self, i):
+        """
+        Puts a signed int value.
+
+        @param i: an integral value
+        """
+        self._check(pn_data_put_int(self._data, i))
+
+    def put_char(self, c):
+        """
+        Puts a char value.
+
+        @param c: a single character
+        """
+        self._check(pn_data_put_char(self._data, ord(c)))
+
+    def put_ulong(self, ul):
+        """
+        Puts an unsigned long value.
+
+        @param ul: an integral value
+        """
+        self._check(pn_data_put_ulong(self._data, ul))
+
+    def put_long(self, l):
+        """
+        Puts a signed long value.
+
+        @param l: an integral value
+        """
+        self._check(pn_data_put_long(self._data, l))
+
+    def put_timestamp(self, t):
+        """
+        Puts a timestamp value.
+
+        @param t: an integral value
+        """
+        self._check(pn_data_put_timestamp(self._data, t))
+
+    def put_float(self, f):
+        """
+        Puts a float value.
+
+        @param f: a floating point value
+        """
+        self._check(pn_data_put_float(self._data, f))
+
+    def put_double(self, d):
+        """
+        Puts a double value.
+
+        @param d: a floating point value.
+        """
+        self._check(pn_data_put_double(self._data, d))
+
+    def put_decimal32(self, d):
+        """
+        Puts a decimal32 value.
+
+        @param d: a decimal32 value
+        """
+        self._check(pn_data_put_decimal32(self._data, d))
+
+    def put_decimal64(self, d):
+        """
+        Puts a decimal64 value.
+
+        @param d: a decimal64 value
+        """
+        self._check(pn_data_put_decimal64(self._data, d))
+
+    def put_decimal128(self, d):
+        """
+        Puts a decimal128 value.
+
+        @param d: a decimal128 value
+        """
+        self._check(pn_data_put_decimal128(self._data, d))
+
+    def put_uuid(self, u):
+        """
+        Puts a UUID value.
+
+        @param u: a uuid value
+        """
+        self._check(pn_data_put_uuid(self._data, u.bytes))
+
+    def put_binary(self, b):
+        """
+        Puts a binary value.
+
+        @type b: binary
+        @param b: a binary value
+        """
+        self._check(pn_data_put_binary(self._data, b))
+
+    def put_memoryview(self, mv):
+        """Put a python memoryview object as an AMQP binary value"""
+        self.put_binary(mv.tobytes())
+
+    def put_buffer(self, buff):
+        """Put a python buffer object as an AMQP binary value"""
+        self.put_binary(bytes(buff))
+
+    def put_string(self, s):
+        """
+        Puts a unicode value.
+
+        @type s: unicode
+        @param s: a unicode value
+        """
+        self._check(pn_data_put_string(self._data, s.encode("utf8")))
+
+    def put_symbol(self, s):
+        """
+        Puts a symbolic value.
+
+        @type s: string
+        @param s: the symbol name
+        """
+        self._check(pn_data_put_symbol(self._data, s.encode('ascii')))
+
+    def get_list(self):
+        """
+        If the current node is a list, return the number of elements,
+        otherwise return zero. List elements can be accessed by entering
+        the list.
+
+          >>> count = data.get_list()
+          >>> data.enter()
+          >>> for i in range(count):
+          ...   type = data.next()
+          ...   if type == Data.STRING:
+          ...     print data.get_string()
+          ...   elif type == ...:
+          ...     ...
+          >>> data.exit()
+        """
+        return pn_data_get_list(self._data)
+
+    def get_map(self):
+        """
+        If the current node is a map, return the number of child elements,
+        otherwise return zero. Key value pairs can be accessed by entering
+        the map.
+
+          >>> count = data.get_map()
+          >>> data.enter()
+          >>> for i in range(count/2):
+          ...   type = data.next()
+          ...   if type == Data.STRING:
+          ...     print data.get_string()
+          ...   elif type == ...:
+          ...     ...
+          >>> data.exit()
+        """
+        return pn_data_get_map(self._data)
+
+    def get_array(self):
+        """
+        If the current node is an array, return a tuple of the element
+        count, a boolean indicating whether the array is described, and
+        the type of each element, otherwise return (0, False, None). Array
+        data can be accessed by entering the array.
+
+          >>> # read an array of strings with a symbolic descriptor
+          >>> count, described, type = data.get_array()
+          >>> data.enter()
+          >>> data.next()
+          >>> print "Descriptor:", data.get_symbol()
+          >>> for i in range(count):
+          ...    data.next()
+          ...    print "Element:", data.get_string()
+          >>> data.exit()
+        """
+        count = pn_data_get_array(self._data)
+        described = pn_data_is_array_described(self._data)
+        type = pn_data_get_array_type(self._data)
+        if type == -1:
+            type = None
+        return count, described, type
+
+    def is_described(self):
+        """
+        Checks if the current node is a described value. The descriptor
+        and value may be accessed by entering the described value.
+
+          >>> # read a symbolically described string
+          >>> assert data.is_described() # will error if the current node is not described
+          >>> data.enter()
+          >>> data.next()
+          >>> print data.get_symbol()
+          >>> data.next()
+          >>> print data.get_string()
+          >>> data.exit()
+        """
+        return pn_data_is_described(self._data)
+
+    def is_null(self):
+        """
+        Checks if the current node is a null.
+        """
+        return pn_data_is_null(self._data)
+
+    def get_bool(self):
+        """
+        If the current node is a boolean, returns its value, returns False
+        otherwise.
+        """
+        return pn_data_get_bool(self._data)
+
+    def get_ubyte(self):
+        """
+        If the current node is an unsigned byte, returns its value,
+        returns 0 otherwise.
+        """
+        return ubyte(pn_data_get_ubyte(self._data))
+
+    def get_byte(self):
+        """
+        If the current node is a signed byte, returns its value, returns 0
+        otherwise.
+        """
+        return byte(pn_data_get_byte(self._data))
+
+    def get_ushort(self):
+        """
+        If the current node is an unsigned short, returns its value,
+        returns 0 otherwise.
+        """
+        return ushort(pn_data_get_ushort(self._data))
+
+    def get_short(self):
+        """
+        If the current node is a signed short, returns its value, returns
+        0 otherwise.
+        """
+        return short(pn_data_get_short(self._data))
+
+    def get_uint(self):
+        """
+        If the current node is an unsigned int, returns its value, returns
+        0 otherwise.
+        """
+        return uint(pn_data_get_uint(self._data))
+
+    def get_int(self):
+        """
+        If the current node is a signed int, returns its value, returns 0
+        otherwise.
+        """
+        return int32(pn_data_get_int(self._data))
+
+    def get_char(self):
+        """
+        If the current node is a char, returns its value, returns 0
+        otherwise.
+        """
+        return char(_compat.unichr(pn_data_get_char(self._data)))
+
+    def get_ulong(self):
+        """
+        If the current node is an unsigned long, returns its value,
+        returns 0 otherwise.
+        """
+        return ulong(pn_data_get_ulong(self._data))
+
+    def get_long(self):
+        """
+        If the current node is an signed long, returns its value, returns
+        0 otherwise.
+        """
+        return long(pn_data_get_long(self._data))
+
+    def get_timestamp(self):
+        """
+        If the current node is a timestamp, returns its value, returns 0
+        otherwise.
+        """
+        return timestamp(pn_data_get_timestamp(self._data))
+
+    def get_float(self):
+        """
+        If the current node is a float, returns its value, raises 0
+        otherwise.
+        """
+        return float32(pn_data_get_float(self._data))
+
+    def get_double(self):
+        """
+        If the current node is a double, returns its value, returns 0
+        otherwise.
+        """
+        return pn_data_get_double(self._data)
+
+    # XXX: need to convert
+    def get_decimal32(self):
+        """
+        If the current node is a decimal32, returns its value, returns 0
+        otherwise.
+        """
+        return decimal32(pn_data_get_decimal32(self._data))
+
+    # XXX: need to convert
+    def get_decimal64(self):
+        """
+        If the current node is a decimal64, returns its value, returns 0
+        otherwise.
+        """
+        return decimal64(pn_data_get_decimal64(self._data))
+
+    # XXX: need to convert
+    def get_decimal128(self):
+        """
+        If the current node is a decimal128, returns its value, returns 0
+        otherwise.
+        """
+        return decimal128(pn_data_get_decimal128(self._data))
+
+    def get_uuid(self):
+        """
+        If the current node is a UUID, returns its value, returns None
+        otherwise.
+        """
+        if pn_data_type(self._data) == Data.UUID:
+            return uuid.UUID(bytes=pn_data_get_uuid(self._data))
+        else:
+            return None
+
+    def get_binary(self):
+        """
+        If the current node is binary, returns its value, returns ""
+        otherwise.
+        """
+        return pn_data_get_binary(self._data)
+
+    def get_string(self):
+        """
+        If the current node is a string, returns its value, returns ""
+        otherwise.
+        """
+        return pn_data_get_string(self._data).decode("utf8")
+
+    def get_symbol(self):
+        """
+        If the current node is a symbol, returns its value, returns ""
+        otherwise.
+        """
+        return symbol(pn_data_get_symbol(self._data).decode('ascii'))
+
+    def copy(self, src):
+        self._check(pn_data_copy(self._data, src._data))
+
+    def format(self):
+        sz = 16
+        while True:
+            err, result = pn_data_format(self._data, sz)
+            if err == PN_OVERFLOW:
+                sz *= 2
+                continue
+            else:
+                self._check(err)
+                return result
+
+    def dump(self):
+        pn_data_dump(self._data)
+
+    def put_dict(self, d):
+        self.put_map()
+        self.enter()
+        try:
+            for k, v in d.items():
+                self.put_object(k)
+                self.put_object(v)
+        finally:
+            self.exit()
+
+    def get_dict(self):
+        if self.enter():
+            try:
+                result = {}
+                while self.next():
+                    k = self.get_object()
+                    if self.next():
+                        v = self.get_object()
+                    else:
+                        v = None
+                    result[k] = v
+            finally:
+                self.exit()
+            return result
+
+    def put_sequence(self, s):
+        self.put_list()
+        self.enter()
+        try:
+            for o in s:
+                self.put_object(o)
+        finally:
+            self.exit()
+
+    def get_sequence(self):
+        if self.enter():
+            try:
+                result = []
+                while self.next():
+                    result.append(self.get_object())
+            finally:
+                self.exit()
+            return result
+
+    def get_py_described(self):
+        if self.enter():
+            try:
+                self.next()
+                descriptor = self.get_object()
+                self.next()
+                value = self.get_object()
+            finally:
+                self.exit()
+            return Described(descriptor, value)
+
+    def put_py_described(self, d):
+        self.put_described()
+        self.enter()
+        try:
+            self.put_object(d.descriptor)
+            self.put_object(d.value)
+        finally:
+            self.exit()
+
+    def get_py_array(self):
+        """
+        If the current node is an array, return an Array object
+        representing the array and its contents. Otherwise return None.
+        This is a convenience wrapper around get_array, enter, etc.
+        """
+
+        count, described, type = self.get_array()
+        if type is None: return None
+        if self.enter():
+            try:
+                if described:
+                    self.next()
+                    descriptor = self.get_object()
+                else:
+                    descriptor = UNDESCRIBED
+                elements = []
+                while self.next():
+                    elements.append(self.get_object())
+            finally:
+                self.exit()
+            return Array(descriptor, type, *elements)
+
+    def put_py_array(self, a):
+        described = a.descriptor != UNDESCRIBED
+        self.put_array(described, a.type)
+        self.enter()
+        try:
+            if described:
+                self.put_object(a.descriptor)
+            for e in a.elements:
+                self.put_object(e)
+        finally:
+            self.exit()
+
+    put_mappings = {
+        None.__class__: lambda s, _: s.put_null(),
+        bool: put_bool,
+        ubyte: put_ubyte,
+        ushort: put_ushort,
+        uint: put_uint,
+        ulong: put_ulong,
+        byte: put_byte,
+        short: put_short,
+        int32: put_int,
+        long: put_long,
+        float32: put_float,
+        float: put_double,
+        decimal32: put_decimal32,
+        decimal64: put_decimal64,
+        decimal128: put_decimal128,
+        char: put_char,
+        timestamp: put_timestamp,
+        uuid.UUID: put_uuid,
+        bytes: put_binary,
+        unicode: put_string,
+        symbol: put_symbol,
+        list: put_sequence,
+        tuple: put_sequence,
+        dict: put_dict,
+        Described: put_py_described,
+        Array: put_py_array
+    }
+    # for python 3.x, long is merely an alias for int, but for python 2.x
+    # we need to add an explicit int since it is a different type
+    if int not in put_mappings:
+        put_mappings[int] = put_int
+    # Python >=3.0 has 'memoryview', <=2.5 has 'buffer', >=2.6 has both.
+    try:
+        put_mappings[memoryview] = put_memoryview
+    except NameError:
+        pass
+    try:
+        put_mappings[buffer] = put_buffer
+    except NameError:
+        pass
+    get_mappings = {
+        NULL: lambda s: None,
+        BOOL: get_bool,
+        BYTE: get_byte,
+        UBYTE: get_ubyte,
+        SHORT: get_short,
+        USHORT: get_ushort,
+        INT: get_int,
+        UINT: get_uint,
+        CHAR: get_char,
+        LONG: get_long,
+        ULONG: get_ulong,
+        TIMESTAMP: get_timestamp,
+        FLOAT: get_float,
+        DOUBLE: get_double,
+        DECIMAL32: get_decimal32,
+        DECIMAL64: get_decimal64,
+        DECIMAL128: get_decimal128,
+        UUID: get_uuid,
+        BINARY: get_binary,
+        STRING: get_string,
+        SYMBOL: get_symbol,
+        DESCRIBED: get_py_described,
+        ARRAY: get_py_array,
+        LIST: get_sequence,
+        MAP: get_dict
+    }
+
+    def put_object(self, obj):
+        putter = self.put_mappings[obj.__class__]
+        putter(self, obj)
+
+    def get_object(self):
+        type = self.type()
+        if type is None: return None
+        getter = self.get_mappings.get(type)
+        if getter:
+            return getter(self)
+        else:
+            return UnmappedType(str(type))
+
+
+def dat2obj(dimpl):
+    if dimpl:
+        d = Data(dimpl)
+        d.rewind()
+        d.next()
+        obj = d.get_object()
+        d.rewind()
+        return obj
+
+
+def obj2dat(obj, dimpl):
+    if obj is not None:
+        d = Data(dimpl)
+        d.put_object(obj)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_delivery.py
----------------------------------------------------------------------
diff --git a/python/proton/_delivery.py b/python/proton/_delivery.py
new file mode 100644
index 0000000..e609451
--- /dev/null
+++ b/python/proton/_delivery.py
@@ -0,0 +1,293 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+from cproton import PN_REJECTED, PN_RELEASED, PN_MODIFIED, PN_RECEIVED, PN_ACCEPTED, \
+    pn_disposition_is_undeliverable, pn_disposition_set_section_number, pn_disposition_get_section_number, \
+    pn_disposition_set_undeliverable, pn_disposition_set_failed, pn_disposition_condition, \
+    pn_disposition_set_section_offset, pn_disposition_data, pn_disposition_get_section_offset, \
+    pn_disposition_is_failed, pn_disposition_annotations, \
+    pn_delivery_partial, pn_delivery_aborted, pn_disposition_type, pn_delivery_pending, pn_delivery_updated, \
+    pn_delivery_readable, pn_delivery_abort, pn_delivery_remote, pn_delivery_tag, pn_delivery_link, pn_delivery_local, \
+    pn_delivery_update, pn_delivery_attachments, pn_delivery_local_state, pn_delivery_settled, pn_delivery_settle, \
+    pn_delivery_writable, pn_delivery_remote_state, \
+    pn_work_next
+
+from ._condition import cond2obj, obj2cond
+from ._data import dat2obj, obj2dat
+from ._wrapper import Wrapper
+
+
+class NamedInt(int):
+    values = {}  # type: Dict[int, str]
+
+    def __new__(cls, i, name):
+        ni = super(NamedInt, cls).__new__(cls, i)
+        cls.values[i] = ni
+        return ni
+
+    def __init__(self, i, name):
+        self.name = name
+
+    def __repr__(self):
+        return self.name
+
+    def __str__(self):
+        return self.name
+
+    @classmethod
+    def get(cls, i):
+        return cls.values.get(i, i)
+
+
+class DispositionType(NamedInt):
+    values = {}
+
+
+class Disposition(object):
+    RECEIVED = DispositionType(PN_RECEIVED, "RECEIVED")
+    ACCEPTED = DispositionType(PN_ACCEPTED, "ACCEPTED")
+    REJECTED = DispositionType(PN_REJECTED, "REJECTED")
+    RELEASED = DispositionType(PN_RELEASED, "RELEASED")
+    MODIFIED = DispositionType(PN_MODIFIED, "MODIFIED")
+
+    def __init__(self, impl, local):
+        self._impl = impl
+        self.local = local
+        self._data = None
+        self._condition = None
+        self._annotations = None
+
+    @property
+    def type(self):
+        return DispositionType.get(pn_disposition_type(self._impl))
+
+    def _get_section_number(self):
+        return pn_disposition_get_section_number(self._impl)
+
+    def _set_section_number(self, n):
+        pn_disposition_set_section_number(self._impl, n)
+
+    section_number = property(_get_section_number, _set_section_number)
+
+    def _get_section_offset(self):
+        return pn_disposition_get_section_offset(self._impl)
+
+    def _set_section_offset(self, n):
+        pn_disposition_set_section_offset(self._impl, n)
+
+    section_offset = property(_get_section_offset, _set_section_offset)
+
+    def _get_failed(self):
+        return pn_disposition_is_failed(self._impl)
+
+    def _set_failed(self, b):
+        pn_disposition_set_failed(self._impl, b)
+
+    failed = property(_get_failed, _set_failed)
+
+    def _get_undeliverable(self):
+        return pn_disposition_is_undeliverable(self._impl)
+
+    def _set_undeliverable(self, b):
+        pn_disposition_set_undeliverable(self._impl, b)
+
+    undeliverable = property(_get_undeliverable, _set_undeliverable)
+
+    def _get_data(self):
+        if self.local:
+            return self._data
+        else:
+            return dat2obj(pn_disposition_data(self._impl))
+
+    def _set_data(self, obj):
+        if self.local:
+            self._data = obj
+        else:
+            raise AttributeError("data attribute is read-only")
+
+    data = property(_get_data, _set_data)
+
+    def _get_annotations(self):
+        if self.local:
+            return self._annotations
+        else:
+            return dat2obj(pn_disposition_annotations(self._impl))
+
+    def _set_annotations(self, obj):
+        if self.local:
+            self._annotations = obj
+        else:
+            raise AttributeError("annotations attribute is read-only")
+
+    annotations = property(_get_annotations, _set_annotations)
+
+    def _get_condition(self):
+        if self.local:
+            return self._condition
+        else:
+            return cond2obj(pn_disposition_condition(self._impl))
+
+    def _set_condition(self, obj):
+        if self.local:
+            self._condition = obj
+        else:
+            raise AttributeError("condition attribute is read-only")
+
+    condition = property(_get_condition, _set_condition)
+
+
+class Delivery(Wrapper):
+    """
+    Tracks and/or records the delivery of a message over a link.
+    """
+
+    RECEIVED = Disposition.RECEIVED
+    ACCEPTED = Disposition.ACCEPTED
+    REJECTED = Disposition.REJECTED
+    RELEASED = Disposition.RELEASED
+    MODIFIED = Disposition.MODIFIED
+
+    @staticmethod
+    def wrap(impl):
+        if impl is None:
+            return None
+        else:
+            return Delivery(impl)
+
+    def __init__(self, impl):
+        Wrapper.__init__(self, impl, pn_delivery_attachments)
+
+    def _init(self):
+        self.local = Disposition(pn_delivery_local(self._impl), True)
+        self.remote = Disposition(pn_delivery_remote(self._impl), False)
+
+    @property
+    def tag(self):
+        """The identifier for the delivery."""
+        return pn_delivery_tag(self._impl)
+
+    @property
+    def writable(self):
+        """Returns true for an outgoing delivery to which data can now be written."""
+        return pn_delivery_writable(self._impl)
+
+    @property
+    def readable(self):
+        """Returns true for an incoming delivery that has data to read."""
+        return pn_delivery_readable(self._impl)
+
+    @property
+    def updated(self):
+        """Returns true if the state of the delivery has been updated
+        (e.g. it has been settled and/or accepted, rejected etc)."""
+        return pn_delivery_updated(self._impl)
+
+    def update(self, state):
+        """
+        Set the local state of the delivery e.g. ACCEPTED, REJECTED, RELEASED.
+        """
+        obj2dat(self.local._data, pn_disposition_data(self.local._impl))
+        obj2dat(self.local._annotations, pn_disposition_annotations(self.local._impl))
+        obj2cond(self.local._condition, pn_disposition_condition(self.local._impl))
+        pn_delivery_update(self._impl, state)
+
+    @property
+    def pending(self):
+        return pn_delivery_pending(self._impl)
+
+    @property
+    def partial(self):
+        """
+        Returns true for an incoming delivery if not all the data is
+        yet available.
+        """
+        return pn_delivery_partial(self._impl)
+
+    @property
+    def local_state(self):
+        """Returns the local state of the delivery."""
+        return DispositionType.get(pn_delivery_local_state(self._impl))
+
+    @property
+    def remote_state(self):
+        """
+        Returns the state of the delivery as indicated by the remote
+        peer.
+        """
+        return DispositionType.get(pn_delivery_remote_state(self._impl))
+
+    @property
+    def settled(self):
+        """
+        Returns true if the delivery has been settled by the remote peer.
+        """
+        return pn_delivery_settled(self._impl)
+
+    def settle(self):
+        """
+        Settles the delivery locally. This indicates the application
+        considers the delivery complete and does not wish to receive any
+        further events about it. Every delivery should be settled locally.
+        """
+        pn_delivery_settle(self._impl)
+
+    @property
+    def aborted(self):
+        """Returns true if the delivery has been aborted."""
+        return pn_delivery_aborted(self._impl)
+
+    def abort(self):
+        """
+        Aborts the delivery.  This indicates the application wishes to
+        invalidate any data that may have already been sent on this delivery.
+        The delivery cannot be aborted after it has been completely delivered.
+        """
+        pn_delivery_abort(self._impl)
+
+    @property
+    def work_next(self):
+        return Delivery.wrap(pn_work_next(self._impl))
+
+    @property
+    def link(self):
+        """
+        Returns the link on which the delivery was sent or received.
+        """
+        from . import _endpoints
+        return _endpoints.Link.wrap(pn_delivery_link(self._impl))
+
+    @property
+    def session(self):
+        """
+        Returns the session over which the delivery was sent or received.
+        """
+        return self.link.session
+
+    @property
+    def connection(self):
+        """
+        Returns the connection over which the delivery was sent or received.
+        """
+        return self.session.connection
+
+    @property
+    def transport(self):
+        return self.connection.transport

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_endpoints.py
----------------------------------------------------------------------
diff --git a/python/proton/_endpoints.py b/python/proton/_endpoints.py
new file mode 100644
index 0000000..bfa9880
--- /dev/null
+++ b/python/proton/_endpoints.py
@@ -0,0 +1,765 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+"""
+The proton.endpoints module
+"""
+
+from __future__ import absolute_import
+
+import weakref
+
+from cproton import PN_LOCAL_UNINIT, PN_REMOTE_UNINIT, PN_LOCAL_ACTIVE, PN_REMOTE_ACTIVE, PN_LOCAL_CLOSED, \
+    PN_REMOTE_CLOSED, \
+    pn_object_reactor, pn_record_get_handler, pn_record_set_handler, pn_decref, \
+    pn_connection, pn_connection_attachments, pn_connection_transport, pn_connection_error, pn_connection_condition, \
+    pn_connection_remote_condition, pn_connection_collect, pn_connection_set_container, pn_connection_get_container, \
+    pn_connection_get_hostname, pn_connection_set_hostname, pn_connection_get_user, pn_connection_set_user, \
+    pn_connection_set_password, pn_connection_remote_container, pn_connection_remote_hostname, \
+    pn_connection_remote_offered_capabilities, pn_connection_remote_desired_capabilities, \
+    pn_connection_remote_properties, pn_connection_offered_capabilities, pn_connection_desired_capabilities, \
+    pn_connection_properties, pn_connection_open, pn_connection_close, pn_connection_state, pn_connection_release, \
+    pn_session, pn_session_head, pn_session_attachments, pn_session_condition, pn_session_remote_condition, \
+    pn_session_get_incoming_capacity, pn_session_set_incoming_capacity, pn_session_get_outgoing_window, \
+    pn_session_set_outgoing_window, pn_session_incoming_bytes, pn_session_outgoing_bytes, pn_session_open, \
+    pn_session_close, pn_session_next, pn_session_state, pn_session_connection, pn_session_free, \
+    PN_SND_UNSETTLED, PN_SND_SETTLED, PN_SND_MIXED, PN_RCV_FIRST, PN_RCV_SECOND, \
+    pn_link_head, pn_link_is_sender, pn_link_attachments, pn_link_error, pn_link_condition, pn_link_remote_condition, \
+    pn_link_open, pn_link_close, pn_link_state, pn_link_source, pn_link_target, pn_link_remote_source, \
+    pn_link_remote_target, pn_link_session, pn_link_current, pn_link_advance, pn_link_unsettled, pn_link_credit, \
+    pn_link_available, pn_link_queued, pn_link_next, pn_link_name, pn_link_is_receiver, pn_link_remote_snd_settle_mode, \
+    pn_link_remote_rcv_settle_mode, pn_link_snd_settle_mode, pn_link_set_snd_settle_mode, pn_link_rcv_settle_mode, \
+    pn_link_set_rcv_settle_mode, pn_link_get_drain, pn_link_set_drain, pn_link_drained, pn_link_remote_max_message_size, \
+    pn_link_max_message_size, pn_link_set_max_message_size, pn_link_detach, pn_link_free, pn_link_offered, pn_link_send, \
+    pn_link_flow, pn_link_recv, pn_link_drain, pn_link_draining, \
+    pn_sender, pn_receiver, \
+    PN_UNSPECIFIED, PN_SOURCE, PN_TARGET, PN_COORDINATOR, PN_NONDURABLE, PN_CONFIGURATION, \
+    PN_DELIVERIES, PN_DIST_MODE_UNSPECIFIED, PN_DIST_MODE_COPY, PN_DIST_MODE_MOVE, PN_EXPIRE_WITH_LINK, \
+    PN_EXPIRE_WITH_SESSION, PN_EXPIRE_WITH_CONNECTION, PN_EXPIRE_NEVER, \
+    pn_terminus_set_durability, pn_terminus_set_timeout, pn_terminus_set_dynamic, pn_terminus_get_type, \
+    pn_terminus_get_durability, pn_terminus_set_type, pn_terminus_get_address, pn_terminus_capabilities, \
+    pn_terminus_set_address, pn_terminus_get_timeout, pn_terminus_filter, pn_terminus_properties, \
+    pn_terminus_get_expiry_policy, pn_terminus_set_expiry_policy, pn_terminus_set_distribution_mode, \
+    pn_terminus_get_distribution_mode, pn_terminus_copy, pn_terminus_outcomes, pn_terminus_is_dynamic, \
+    PN_EOS, \
+    pn_delivery, \
+    pn_work_head, \
+    pn_error_code, pn_error_text
+
+from ._common import utf82unicode, unicode2utf8
+from ._condition import obj2cond, cond2obj
+from ._data import Data, obj2dat, dat2obj
+from ._delivery import Delivery
+from ._exceptions import EXCEPTIONS, LinkException, SessionException, ConnectionException
+from ._transport import Transport
+from ._wrapper import Wrapper
+
+
+class Endpoint(object):
+    LOCAL_UNINIT = PN_LOCAL_UNINIT
+    REMOTE_UNINIT = PN_REMOTE_UNINIT
+    LOCAL_ACTIVE = PN_LOCAL_ACTIVE
+    REMOTE_ACTIVE = PN_REMOTE_ACTIVE
+    LOCAL_CLOSED = PN_LOCAL_CLOSED
+    REMOTE_CLOSED = PN_REMOTE_CLOSED
+
+    def _init(self):
+        self.condition = None
+
+    def _update_cond(self):
+        obj2cond(self.condition, self._get_cond_impl())
+
+    @property
+    def remote_condition(self):
+        return cond2obj(self._get_remote_cond_impl())
+
+    # the following must be provided by subclasses
+    def _get_cond_impl(self):
+        assert False, "Subclass must override this!"
+
+    def _get_remote_cond_impl(self):
+        assert False, "Subclass must override this!"
+
+    def _get_handler(self):
+        from . import reactor
+        from . import _reactor_impl
+        ractor = reactor.Reactor.wrap(pn_object_reactor(self._impl))
+        if ractor:
+            on_error = ractor.on_error_delegate()
+        else:
+            on_error = None
+        record = self._get_attachments()
+        return _reactor_impl.WrappedHandler.wrap(pn_record_get_handler(record), on_error)
+
+    def _set_handler(self, handler):
+        from . import reactor
+        from . import _reactor_impl
+        ractor = reactor.Reactor.wrap(pn_object_reactor(self._impl))
+        if ractor:
+            on_error = ractor.on_error_delegate()
+        else:
+            on_error = None
+        impl = _reactor_impl._chandler(handler, on_error)
+        record = self._get_attachments()
+        pn_record_set_handler(record, impl)
+        pn_decref(impl)
+
+    handler = property(_get_handler, _set_handler)
+
+    @property
+    def transport(self):
+        return self.connection.transport
+
+
+class Connection(Wrapper, Endpoint):
+    """
+    A representation of an AMQP connection
+    """
+
+    @staticmethod
+    def wrap(impl):
+        if impl is None:
+            return None
+        else:
+            return Connection(impl)
+
+    def __init__(self, impl=pn_connection):
+        Wrapper.__init__(self, impl, pn_connection_attachments)
+
+    def _init(self):
+        Endpoint._init(self)
+        self.offered_capabilities = None
+        self.desired_capabilities = None
+        self.properties = None
+
+    def _get_attachments(self):
+        return pn_connection_attachments(self._impl)
+
+    @property
+    def connection(self):
+        return self
+
+    @property
+    def transport(self):
+        return Transport.wrap(pn_connection_transport(self._impl))
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, ConnectionException)
+            raise exc("[%s]: %s" % (err, pn_connection_error(self._impl)))
+        else:
+            return err
+
+    def _get_cond_impl(self):
+        return pn_connection_condition(self._impl)
+
+    def _get_remote_cond_impl(self):
+        return pn_connection_remote_condition(self._impl)
+
+    def collect(self, collector):
+        if collector is None:
+            pn_connection_collect(self._impl, None)
+        else:
+            pn_connection_collect(self._impl, collector._impl)
+        self._collector = weakref.ref(collector)
+
+    def _get_container(self):
+        return utf82unicode(pn_connection_get_container(self._impl))
+
+    def _set_container(self, name):
+        return pn_connection_set_container(self._impl, unicode2utf8(name))
+
+    container = property(_get_container, _set_container)
+
+    def _get_hostname(self):
+        return utf82unicode(pn_connection_get_hostname(self._impl))
+
+    def _set_hostname(self, name):
+        return pn_connection_set_hostname(self._impl, unicode2utf8(name))
+
+    hostname = property(_get_hostname, _set_hostname,
+                        doc="""
+Set the name of the host (either fully qualified or relative) to which this
+connection is connecting to.  This information may be used by the remote
+peer to determine the correct back-end service to connect the client to.
+This value will be sent in the Open performative, and will be used by SSL
+and SASL layers to identify the peer.
+""")
+
+    def _get_user(self):
+        return utf82unicode(pn_connection_get_user(self._impl))
+
+    def _set_user(self, name):
+        return pn_connection_set_user(self._impl, unicode2utf8(name))
+
+    user = property(_get_user, _set_user)
+
+    def _get_password(self):
+        return None
+
+    def _set_password(self, name):
+        return pn_connection_set_password(self._impl, unicode2utf8(name))
+
+    password = property(_get_password, _set_password)
+
+    @property
+    def remote_container(self):
+        """The container identifier specified by the remote peer for this connection."""
+        return pn_connection_remote_container(self._impl)
+
+    @property
+    def remote_hostname(self):
+        """The hostname specified by the remote peer for this connection."""
+        return pn_connection_remote_hostname(self._impl)
+
+    @property
+    def remote_offered_capabilities(self):
+        """The capabilities offered by the remote peer for this connection."""
+        return dat2obj(pn_connection_remote_offered_capabilities(self._impl))
+
+    @property
+    def remote_desired_capabilities(self):
+        """The capabilities desired by the remote peer for this connection."""
+        return dat2obj(pn_connection_remote_desired_capabilities(self._impl))
+
+    @property
+    def remote_properties(self):
+        """The properties specified by the remote peer for this connection."""
+        return dat2obj(pn_connection_remote_properties(self._impl))
+
+    def open(self):
+        """
+        Opens the connection.
+
+        In more detail, this moves the local state of the connection to
+        the ACTIVE state and triggers an open frame to be sent to the
+        peer. A connection is fully active once both peers have opened it.
+        """
+        obj2dat(self.offered_capabilities,
+                pn_connection_offered_capabilities(self._impl))
+        obj2dat(self.desired_capabilities,
+                pn_connection_desired_capabilities(self._impl))
+        obj2dat(self.properties, pn_connection_properties(self._impl))
+        pn_connection_open(self._impl)
+
+    def close(self):
+        """
+        Closes the connection.
+
+        In more detail, this moves the local state of the connection to
+        the CLOSED state and triggers a close frame to be sent to the
+        peer. A connection is fully closed once both peers have closed it.
+        """
+        self._update_cond()
+        pn_connection_close(self._impl)
+        if hasattr(self, '_session_policy'):
+            # break circular ref
+            del self._session_policy
+
+    @property
+    def state(self):
+        """
+        The state of the connection as a bit field. The state has a local
+        and a remote component. Each of these can be in one of three
+        states: UNINIT, ACTIVE or CLOSED. These can be tested by masking
+        against LOCAL_UNINIT, LOCAL_ACTIVE, LOCAL_CLOSED, REMOTE_UNINIT,
+        REMOTE_ACTIVE and REMOTE_CLOSED.
+        """
+        return pn_connection_state(self._impl)
+
+    def session(self):
+        """
+        Returns a new session on this connection.
+        """
+        ssn = pn_session(self._impl)
+        if ssn is None:
+            raise (SessionException("Session allocation failed."))
+        else:
+            return Session(ssn)
+
+    def session_head(self, mask):
+        return Session.wrap(pn_session_head(self._impl, mask))
+
+    def link_head(self, mask):
+        return Link.wrap(pn_link_head(self._impl, mask))
+
+    @property
+    def work_head(self):
+        return Delivery.wrap(pn_work_head(self._impl))
+
+    @property
+    def error(self):
+        return pn_error_code(pn_connection_error(self._impl))
+
+    def free(self):
+        pn_connection_release(self._impl)
+
+
+class Session(Wrapper, Endpoint):
+
+    @staticmethod
+    def wrap(impl):
+        if impl is None:
+            return None
+        else:
+            return Session(impl)
+
+    def __init__(self, impl):
+        Wrapper.__init__(self, impl, pn_session_attachments)
+
+    def _get_attachments(self):
+        return pn_session_attachments(self._impl)
+
+    def _get_cond_impl(self):
+        return pn_session_condition(self._impl)
+
+    def _get_remote_cond_impl(self):
+        return pn_session_remote_condition(self._impl)
+
+    def _get_incoming_capacity(self):
+        return pn_session_get_incoming_capacity(self._impl)
+
+    def _set_incoming_capacity(self, capacity):
+        pn_session_set_incoming_capacity(self._impl, capacity)
+
+    incoming_capacity = property(_get_incoming_capacity, _set_incoming_capacity)
+
+    def _get_outgoing_window(self):
+        return pn_session_get_outgoing_window(self._impl)
+
+    def _set_outgoing_window(self, window):
+        pn_session_set_outgoing_window(self._impl, window)
+
+    outgoing_window = property(_get_outgoing_window, _set_outgoing_window)
+
+    @property
+    def outgoing_bytes(self):
+        return pn_session_outgoing_bytes(self._impl)
+
+    @property
+    def incoming_bytes(self):
+        return pn_session_incoming_bytes(self._impl)
+
+    def open(self):
+        pn_session_open(self._impl)
+
+    def close(self):
+        self._update_cond()
+        pn_session_close(self._impl)
+
+    def next(self, mask):
+        return Session.wrap(pn_session_next(self._impl, mask))
+
+    @property
+    def state(self):
+        return pn_session_state(self._impl)
+
+    @property
+    def connection(self):
+        return Connection.wrap(pn_session_connection(self._impl))
+
+    def sender(self, name):
+        return Sender(pn_sender(self._impl, unicode2utf8(name)))
+
+    def receiver(self, name):
+        return Receiver(pn_receiver(self._impl, unicode2utf8(name)))
+
+    def free(self):
+        pn_session_free(self._impl)
+
+
+class Link(Wrapper, Endpoint):
+    """
+    A representation of an AMQP link, of which there are two concrete
+    implementations, Sender and Receiver.
+    """
+
+    SND_UNSETTLED = PN_SND_UNSETTLED
+    SND_SETTLED = PN_SND_SETTLED
+    SND_MIXED = PN_SND_MIXED
+
+    RCV_FIRST = PN_RCV_FIRST
+    RCV_SECOND = PN_RCV_SECOND
+
+    @staticmethod
+    def wrap(impl):
+        if impl is None: return None
+        if pn_link_is_sender(impl):
+            return Sender(impl)
+        else:
+            return Receiver(impl)
+
+    def __init__(self, impl):
+        Wrapper.__init__(self, impl, pn_link_attachments)
+
+    def _get_attachments(self):
+        return pn_link_attachments(self._impl)
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, LinkException)
+            raise exc("[%s]: %s" % (err, pn_error_text(pn_link_error(self._impl))))
+        else:
+            return err
+
+    def _get_cond_impl(self):
+        return pn_link_condition(self._impl)
+
+    def _get_remote_cond_impl(self):
+        return pn_link_remote_condition(self._impl)
+
+    def open(self):
+        """
+        Opens the link.
+
+        In more detail, this moves the local state of the link to the
+        ACTIVE state and triggers an attach frame to be sent to the
+        peer. A link is fully active once both peers have attached it.
+        """
+        pn_link_open(self._impl)
+
+    def close(self):
+        """
+        Closes the link.
+
+        In more detail, this moves the local state of the link to the
+        CLOSED state and triggers an detach frame (with the closed flag
+        set) to be sent to the peer. A link is fully closed once both
+        peers have detached it.
+        """
+        self._update_cond()
+        pn_link_close(self._impl)
+
+    @property
+    def state(self):
+        """
+        The state of the link as a bit field. The state has a local
+        and a remote component. Each of these can be in one of three
+        states: UNINIT, ACTIVE or CLOSED. These can be tested by masking
+        against LOCAL_UNINIT, LOCAL_ACTIVE, LOCAL_CLOSED, REMOTE_UNINIT,
+        REMOTE_ACTIVE and REMOTE_CLOSED.
+        """
+        return pn_link_state(self._impl)
+
+    @property
+    def source(self):
+        """The source of the link as described by the local peer."""
+        return Terminus(pn_link_source(self._impl))
+
+    @property
+    def target(self):
+        """The target of the link as described by the local peer."""
+        return Terminus(pn_link_target(self._impl))
+
+    @property
+    def remote_source(self):
+        """The source of the link as described by the remote peer."""
+        return Terminus(pn_link_remote_source(self._impl))
+
+    @property
+    def remote_target(self):
+        """The target of the link as described by the remote peer."""
+        return Terminus(pn_link_remote_target(self._impl))
+
+    @property
+    def session(self):
+        return Session.wrap(pn_link_session(self._impl))
+
+    @property
+    def connection(self):
+        """The connection on which this link was attached."""
+        return self.session.connection
+
+    def delivery(self, tag):
+        return Delivery(pn_delivery(self._impl, tag))
+
+    @property
+    def current(self):
+        return Delivery.wrap(pn_link_current(self._impl))
+
+    def advance(self):
+        return pn_link_advance(self._impl)
+
+    @property
+    def unsettled(self):
+        return pn_link_unsettled(self._impl)
+
+    @property
+    def credit(self):
+        """The amount of outstanding credit on this link."""
+        return pn_link_credit(self._impl)
+
+    @property
+    def available(self):
+        return pn_link_available(self._impl)
+
+    @property
+    def queued(self):
+        return pn_link_queued(self._impl)
+
+    def next(self, mask):
+        return Link.wrap(pn_link_next(self._impl, mask))
+
+    @property
+    def name(self):
+        """Returns the name of the link"""
+        return utf82unicode(pn_link_name(self._impl))
+
+    @property
+    def is_sender(self):
+        """Returns true if this link is a sender."""
+        return pn_link_is_sender(self._impl)
+
+    @property
+    def is_receiver(self):
+        """Returns true if this link is a receiver."""
+        return pn_link_is_receiver(self._impl)
+
+    @property
+    def remote_snd_settle_mode(self):
+        return pn_link_remote_snd_settle_mode(self._impl)
+
+    @property
+    def remote_rcv_settle_mode(self):
+        return pn_link_remote_rcv_settle_mode(self._impl)
+
+    def _get_snd_settle_mode(self):
+        return pn_link_snd_settle_mode(self._impl)
+
+    def _set_snd_settle_mode(self, mode):
+        pn_link_set_snd_settle_mode(self._impl, mode)
+
+    snd_settle_mode = property(_get_snd_settle_mode, _set_snd_settle_mode)
+
+    def _get_rcv_settle_mode(self):
+        return pn_link_rcv_settle_mode(self._impl)
+
+    def _set_rcv_settle_mode(self, mode):
+        pn_link_set_rcv_settle_mode(self._impl, mode)
+
+    rcv_settle_mode = property(_get_rcv_settle_mode, _set_rcv_settle_mode)
+
+    def _get_drain(self):
+        return pn_link_get_drain(self._impl)
+
+    def _set_drain(self, b):
+        pn_link_set_drain(self._impl, bool(b))
+
+    drain_mode = property(_get_drain, _set_drain)
+
+    def drained(self):
+        return pn_link_drained(self._impl)
+
+    @property
+    def remote_max_message_size(self):
+        return pn_link_remote_max_message_size(self._impl)
+
+    def _get_max_message_size(self):
+        return pn_link_max_message_size(self._impl)
+
+    def _set_max_message_size(self, mode):
+        pn_link_set_max_message_size(self._impl, mode)
+
+    max_message_size = property(_get_max_message_size, _set_max_message_size)
+
+    def detach(self):
+        return pn_link_detach(self._impl)
+
+    def free(self):
+        pn_link_free(self._impl)
+
+
+class Sender(Link):
+    """
+    A link over which messages are sent.
+    """
+
+    def offered(self, n):
+        pn_link_offered(self._impl, n)
+
+    def stream(self, data):
+        """
+        Send specified data as part of the current delivery
+
+        @type data: binary
+        @param data: data to send
+        """
+        return self._check(pn_link_send(self._impl, data))
+
+    def send(self, obj, tag=None):
+        """
+        Send specified object over this sender; the object is expected to
+        have a send() method on it that takes the sender and an optional
+        tag as arguments.
+
+        Where the object is a Message, this will send the message over
+        this link, creating a new delivery for the purpose.
+        """
+        if hasattr(obj, 'send'):
+            return obj.send(self, tag=tag)
+        else:
+            # treat object as bytes
+            return self.stream(obj)
+
+    def delivery_tag(self):
+        if not hasattr(self, 'tag_generator'):
+            def simple_tags():
+                count = 1
+                while True:
+                    yield str(count)
+                    count += 1
+
+            self.tag_generator = simple_tags()
+        return next(self.tag_generator)
+
+
+class Receiver(Link):
+    """
+    A link over which messages are received.
+    """
+
+    def flow(self, n):
+        """Increases the credit issued to the remote sender by the specified number of messages."""
+        pn_link_flow(self._impl, n)
+
+    def recv(self, limit):
+        n, binary = pn_link_recv(self._impl, limit)
+        if n == PN_EOS:
+            return None
+        else:
+            self._check(n)
+            return binary
+
+    def drain(self, n):
+        pn_link_drain(self._impl, n)
+
+    def draining(self):
+        return pn_link_draining(self._impl)
+
+
+class Terminus(object):
+    UNSPECIFIED = PN_UNSPECIFIED
+    SOURCE = PN_SOURCE
+    TARGET = PN_TARGET
+    COORDINATOR = PN_COORDINATOR
+
+    NONDURABLE = PN_NONDURABLE
+    CONFIGURATION = PN_CONFIGURATION
+    DELIVERIES = PN_DELIVERIES
+
+    DIST_MODE_UNSPECIFIED = PN_DIST_MODE_UNSPECIFIED
+    DIST_MODE_COPY = PN_DIST_MODE_COPY
+    DIST_MODE_MOVE = PN_DIST_MODE_MOVE
+
+    EXPIRE_WITH_LINK = PN_EXPIRE_WITH_LINK
+    EXPIRE_WITH_SESSION = PN_EXPIRE_WITH_SESSION
+    EXPIRE_WITH_CONNECTION = PN_EXPIRE_WITH_CONNECTION
+    EXPIRE_NEVER = PN_EXPIRE_NEVER
+
+    def __init__(self, impl):
+        self._impl = impl
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, LinkException)
+            raise exc("[%s]" % err)
+        else:
+            return err
+
+    def _get_type(self):
+        return pn_terminus_get_type(self._impl)
+
+    def _set_type(self, type):
+        self._check(pn_terminus_set_type(self._impl, type))
+
+    type = property(_get_type, _set_type)
+
+    def _get_address(self):
+        """The address that identifies the source or target node"""
+        return utf82unicode(pn_terminus_get_address(self._impl))
+
+    def _set_address(self, address):
+        self._check(pn_terminus_set_address(self._impl, unicode2utf8(address)))
+
+    address = property(_get_address, _set_address)
+
+    def _get_durability(self):
+        return pn_terminus_get_durability(self._impl)
+
+    def _set_durability(self, seconds):
+        self._check(pn_terminus_set_durability(self._impl, seconds))
+
+    durability = property(_get_durability, _set_durability)
+
+    def _get_expiry_policy(self):
+        return pn_terminus_get_expiry_policy(self._impl)
+
+    def _set_expiry_policy(self, seconds):
+        self._check(pn_terminus_set_expiry_policy(self._impl, seconds))
+
+    expiry_policy = property(_get_expiry_policy, _set_expiry_policy)
+
+    def _get_timeout(self):
+        return pn_terminus_get_timeout(self._impl)
+
+    def _set_timeout(self, seconds):
+        self._check(pn_terminus_set_timeout(self._impl, seconds))
+
+    timeout = property(_get_timeout, _set_timeout)
+
+    def _is_dynamic(self):
+        """Indicates whether the source or target node was dynamically
+        created"""
+        return pn_terminus_is_dynamic(self._impl)
+
+    def _set_dynamic(self, dynamic):
+        self._check(pn_terminus_set_dynamic(self._impl, dynamic))
+
+    dynamic = property(_is_dynamic, _set_dynamic)
+
+    def _get_distribution_mode(self):
+        return pn_terminus_get_distribution_mode(self._impl)
+
+    def _set_distribution_mode(self, mode):
+        self._check(pn_terminus_set_distribution_mode(self._impl, mode))
+
+    distribution_mode = property(_get_distribution_mode, _set_distribution_mode)
+
+    @property
+    def properties(self):
+        """Properties of a dynamic source or target."""
+        return Data(pn_terminus_properties(self._impl))
+
+    @property
+    def capabilities(self):
+        """Capabilities of the source or target."""
+        return Data(pn_terminus_capabilities(self._impl))
+
+    @property
+    def outcomes(self):
+        return Data(pn_terminus_outcomes(self._impl))
+
+    @property
+    def filter(self):
+        """A filter on a source allows the set of messages transfered over
+        the link to be restricted"""
+        return Data(pn_terminus_filter(self._impl))
+
+    def copy(self, src):
+        self._check(pn_terminus_copy(self._impl, src._impl))

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_events.py
----------------------------------------------------------------------
diff --git a/python/proton/_events.py b/python/proton/_events.py
new file mode 100644
index 0000000..c8af8e2
--- /dev/null
+++ b/python/proton/_events.py
@@ -0,0 +1,333 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+import threading
+
+from cproton import PN_SESSION_REMOTE_CLOSE, PN_SESSION_FINAL, pn_event_context, pn_collector_put, \
+    PN_SELECTABLE_UPDATED, pn_collector, PN_CONNECTION_REMOTE_OPEN, pn_event_attachments, pn_event_type, \
+    pn_collector_free, pn_handler_dispatch, PN_SELECTABLE_WRITABLE, PN_SELECTABLE_INIT, PN_SESSION_REMOTE_OPEN, \
+    pn_collector_peek, PN_CONNECTION_BOUND, PN_LINK_FLOW, pn_event_connection, PN_LINK_LOCAL_CLOSE, \
+    PN_TRANSPORT_ERROR, PN_CONNECTION_LOCAL_OPEN, PN_CONNECTION_LOCAL_CLOSE, pn_event_delivery, \
+    PN_LINK_REMOTE_OPEN, PN_TRANSPORT_CLOSED, PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT, pn_event_reactor, \
+    PN_CONNECTION_REMOTE_CLOSE, pn_collector_pop, PN_LINK_INIT, pn_event_link, PN_CONNECTION_UNBOUND, \
+    pn_event_type_name, pn_event_session, PN_LINK_FINAL, pn_py2void, PN_REACTOR_INIT, PN_REACTOR_QUIESCED, \
+    PN_LINK_LOCAL_DETACH, PN_SESSION_INIT, PN_CONNECTION_FINAL, PN_TIMER_TASK, pn_class_name, PN_SELECTABLE_READABLE, \
+    pn_event_transport, PN_TRANSPORT_TAIL_CLOSED, PN_SELECTABLE_FINAL, PN_SESSION_LOCAL_OPEN, PN_DELIVERY, \
+    PN_SESSION_LOCAL_CLOSE, pn_event_copy, PN_REACTOR_FINAL, PN_LINK_LOCAL_OPEN, PN_SELECTABLE_EXPIRED, \
+    PN_LINK_REMOTE_DETACH, PN_PYREF, PN_LINK_REMOTE_CLOSE, pn_event_root, PN_SELECTABLE_ERROR, \
+    PN_CONNECTION_INIT, pn_event_class, pn_void2py, pn_cast_pn_session, pn_cast_pn_link, pn_cast_pn_delivery, \
+    pn_cast_pn_transport, pn_cast_pn_connection, pn_cast_pn_selectable
+
+from ._common import Constant
+from ._delivery import Delivery
+from ._endpoints import Connection, Session, Link
+from ._reactor_impl import Selectable, WrappedHandler
+from ._transport import Transport
+from ._wrapper import Wrapper
+
+
+class Collector:
+
+    def __init__(self):
+        self._impl = pn_collector()
+
+    def put(self, obj, etype):
+        pn_collector_put(self._impl, PN_PYREF, pn_py2void(obj), etype.number)
+
+    def peek(self):
+        return Event.wrap(pn_collector_peek(self._impl))
+
+    def pop(self):
+        ev = self.peek()
+        pn_collector_pop(self._impl)
+
+    def __del__(self):
+        pn_collector_free(self._impl)
+        del self._impl
+
+
+if "TypeExtender" not in globals():
+    class TypeExtender:
+        def __init__(self, number):
+            self.number = number
+
+        def next(self):
+            try:
+                return self.number
+            finally:
+                self.number += 1
+
+
+class EventType(object):
+    _lock = threading.Lock()
+    _extended = TypeExtender(10000)
+    TYPES = {}
+
+    def __init__(self, name=None, number=None, method=None):
+        if name is None and number is None:
+            raise TypeError("extended events require a name")
+        try:
+            self._lock.acquire()
+            if name is None:
+                name = pn_event_type_name(number)
+
+            if number is None:
+                number = self._extended.next()
+
+            if method is None:
+                method = "on_%s" % name
+
+            self.name = name
+            self.number = number
+            self.method = method
+
+            self.TYPES[number] = self
+        finally:
+            self._lock.release()
+
+    def __repr__(self):
+        return self.name
+
+
+def dispatch(handler, method, *args):
+    m = getattr(handler, method, None)
+    if m:
+        return m(*args)
+    elif hasattr(handler, "on_unhandled"):
+        return handler.on_unhandled(method, *args)
+
+
+class EventBase(object):
+
+    def __init__(self, clazz, context, type):
+        self.clazz = clazz
+        self.context = context
+        self.type = type
+
+    def dispatch(self, handler):
+        return dispatch(handler, self.type.method, self)
+
+
+def _none(x): return None
+
+
+DELEGATED = Constant("DELEGATED")
+
+
+def _core(number, method):
+    return EventType(number=number, method=method)
+
+
+wrappers = {
+    "pn_void": lambda x: pn_void2py(x),
+    "pn_pyref": lambda x: pn_void2py(x),
+    "pn_connection": lambda x: Connection.wrap(pn_cast_pn_connection(x)),
+    "pn_session": lambda x: Session.wrap(pn_cast_pn_session(x)),
+    "pn_link": lambda x: Link.wrap(pn_cast_pn_link(x)),
+    "pn_delivery": lambda x: Delivery.wrap(pn_cast_pn_delivery(x)),
+    "pn_transport": lambda x: Transport.wrap(pn_cast_pn_transport(x)),
+    "pn_selectable": lambda x: Selectable.wrap(pn_cast_pn_selectable(x))
+}
+
+
+class Event(Wrapper, EventBase):
+    REACTOR_INIT = _core(PN_REACTOR_INIT, "on_reactor_init")
+    REACTOR_QUIESCED = _core(PN_REACTOR_QUIESCED, "on_reactor_quiesced")
+    REACTOR_FINAL = _core(PN_REACTOR_FINAL, "on_reactor_final")
+
+    TIMER_TASK = _core(PN_TIMER_TASK, "on_timer_task")
+
+    CONNECTION_INIT = _core(PN_CONNECTION_INIT, "on_connection_init")
+    CONNECTION_BOUND = _core(PN_CONNECTION_BOUND, "on_connection_bound")
+    CONNECTION_UNBOUND = _core(PN_CONNECTION_UNBOUND, "on_connection_unbound")
+    CONNECTION_LOCAL_OPEN = _core(PN_CONNECTION_LOCAL_OPEN, "on_connection_local_open")
+    CONNECTION_LOCAL_CLOSE = _core(PN_CONNECTION_LOCAL_CLOSE, "on_connection_local_close")
+    CONNECTION_REMOTE_OPEN = _core(PN_CONNECTION_REMOTE_OPEN, "on_connection_remote_open")
+    CONNECTION_REMOTE_CLOSE = _core(PN_CONNECTION_REMOTE_CLOSE, "on_connection_remote_close")
+    CONNECTION_FINAL = _core(PN_CONNECTION_FINAL, "on_connection_final")
+
+    SESSION_INIT = _core(PN_SESSION_INIT, "on_session_init")
+    SESSION_LOCAL_OPEN = _core(PN_SESSION_LOCAL_OPEN, "on_session_local_open")
+    SESSION_LOCAL_CLOSE = _core(PN_SESSION_LOCAL_CLOSE, "on_session_local_close")
+    SESSION_REMOTE_OPEN = _core(PN_SESSION_REMOTE_OPEN, "on_session_remote_open")
+    SESSION_REMOTE_CLOSE = _core(PN_SESSION_REMOTE_CLOSE, "on_session_remote_close")
+    SESSION_FINAL = _core(PN_SESSION_FINAL, "on_session_final")
+
+    LINK_INIT = _core(PN_LINK_INIT, "on_link_init")
+    LINK_LOCAL_OPEN = _core(PN_LINK_LOCAL_OPEN, "on_link_local_open")
+    LINK_LOCAL_CLOSE = _core(PN_LINK_LOCAL_CLOSE, "on_link_local_close")
+    LINK_LOCAL_DETACH = _core(PN_LINK_LOCAL_DETACH, "on_link_local_detach")
+    LINK_REMOTE_OPEN = _core(PN_LINK_REMOTE_OPEN, "on_link_remote_open")
+    LINK_REMOTE_CLOSE = _core(PN_LINK_REMOTE_CLOSE, "on_link_remote_close")
+    LINK_REMOTE_DETACH = _core(PN_LINK_REMOTE_DETACH, "on_link_remote_detach")
+    LINK_FLOW = _core(PN_LINK_FLOW, "on_link_flow")
+    LINK_FINAL = _core(PN_LINK_FINAL, "on_link_final")
+
+    DELIVERY = _core(PN_DELIVERY, "on_delivery")
+
+    TRANSPORT = _core(PN_TRANSPORT, "on_transport")
+    TRANSPORT_ERROR = _core(PN_TRANSPORT_ERROR, "on_transport_error")
+    TRANSPORT_HEAD_CLOSED = _core(PN_TRANSPORT_HEAD_CLOSED, "on_transport_head_closed")
+    TRANSPORT_TAIL_CLOSED = _core(PN_TRANSPORT_TAIL_CLOSED, "on_transport_tail_closed")
+    TRANSPORT_CLOSED = _core(PN_TRANSPORT_CLOSED, "on_transport_closed")
+
+    SELECTABLE_INIT = _core(PN_SELECTABLE_INIT, "on_selectable_init")
+    SELECTABLE_UPDATED = _core(PN_SELECTABLE_UPDATED, "on_selectable_updated")
+    SELECTABLE_READABLE = _core(PN_SELECTABLE_READABLE, "on_selectable_readable")
+    SELECTABLE_WRITABLE = _core(PN_SELECTABLE_WRITABLE, "on_selectable_writable")
+    SELECTABLE_EXPIRED = _core(PN_SELECTABLE_EXPIRED, "on_selectable_expired")
+    SELECTABLE_ERROR = _core(PN_SELECTABLE_ERROR, "on_selectable_error")
+    SELECTABLE_FINAL = _core(PN_SELECTABLE_FINAL, "on_selectable_final")
+
+    @staticmethod
+    def wrap(impl, number=None):
+        if impl is None:
+            return None
+
+        if number is None:
+            number = pn_event_type(impl)
+
+        event = Event(impl, number)
+
+        # check for an application defined ApplicationEvent and return that.  This
+        # avoids an expensive wrap operation invoked by event.context
+        if pn_event_class(impl) == PN_PYREF and \
+                isinstance(event.context, EventBase):
+            return event.context
+        else:
+            return event
+
+    def __init__(self, impl, number):
+        Wrapper.__init__(self, impl, pn_event_attachments)
+        self.__dict__["type"] = EventType.TYPES[number]
+
+    def _init(self):
+        pass
+
+    def copy(self):
+        copy = pn_event_copy(self._impl)
+        return Event.wrap(copy)
+
+    @property
+    def clazz(self):
+        cls = pn_event_class(self._impl)
+        if cls:
+            return pn_class_name(cls)
+        else:
+            return None
+
+    @property
+    def root(self):
+        return WrappedHandler.wrap(pn_event_root(self._impl))
+
+    @property
+    def context(self):
+        """Returns the context object associated with the event. The type of this depend on the type of event."""
+        return wrappers[self.clazz](pn_event_context(self._impl))
+
+    def dispatch(self, handler, type=None):
+        type = type or self.type
+        if isinstance(handler, WrappedHandler):
+            pn_handler_dispatch(handler._impl, self._impl, type.number)
+        else:
+            result = dispatch(handler, type.method, self)
+            if result != DELEGATED and hasattr(handler, "handlers"):
+                for h in handler.handlers:
+                    self.dispatch(h, type)
+
+    @property
+    def reactor(self):
+        """Returns the reactor associated with the event."""
+        return wrappers.get("pn_reactor", _none)(pn_event_reactor(self._impl))
+
+    def __getattr__(self, name):
+        r = self.reactor
+        if r and hasattr(r, 'subclass') and r.subclass.__name__.lower() == name:
+            return r
+        else:
+            return super(Event, self).__getattr__(name)
+
+    @property
+    def transport(self):
+        """Returns the transport associated with the event, or null if none is associated with it."""
+        return Transport.wrap(pn_event_transport(self._impl))
+
+    @property
+    def connection(self):
+        """Returns the connection associated with the event, or null if none is associated with it."""
+        return Connection.wrap(pn_event_connection(self._impl))
+
+    @property
+    def session(self):
+        """Returns the session associated with the event, or null if none is associated with it."""
+        return Session.wrap(pn_event_session(self._impl))
+
+    @property
+    def link(self):
+        """Returns the link associated with the event, or null if none is associated with it."""
+        return Link.wrap(pn_event_link(self._impl))
+
+    @property
+    def sender(self):
+        """Returns the sender link associated with the event, or null if
+           none is associated with it. This is essentially an alias for
+           link(), that does an additional checkon the type of the
+           link."""
+        l = self.link
+        if l and l.is_sender:
+            return l
+        else:
+            return None
+
+    @property
+    def receiver(self):
+        """Returns the receiver link associated with the event, or null if
+           none is associated with it. This is essentially an alias for
+           link(), that does an additional checkon the type of the link."""
+        l = self.link
+        if l and l.is_receiver:
+            return l
+        else:
+            return None
+
+    @property
+    def delivery(self):
+        """Returns the delivery associated with the event, or null if none is associated with it."""
+        return Delivery.wrap(pn_event_delivery(self._impl))
+
+    def __repr__(self):
+        return "%s(%s)" % (self.type, self.context)
+
+
+class LazyHandlers(object):
+    def __get__(self, obj, clazz):
+        if obj is None:
+            return self
+        ret = []
+        obj.__dict__['handlers'] = ret
+        return ret
+
+
+class Handler(object):
+    handlers = LazyHandlers()
+
+    def on_unhandled(self, method, *args):
+        pass


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[2/6] qpid-proton git commit: PROTON-1850: Split up proton __init__.py into multiple files - Reformatted python source to (mostly) PEP-8 standards - Control what gets exported from __init__ by restricting what it imports - Move most of the reactor implem

Posted by as...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/wrapper.py
----------------------------------------------------------------------
diff --git a/python/proton/wrapper.py b/python/proton/wrapper.py
deleted file mode 100644
index f009de5..0000000
--- a/python/proton/wrapper.py
+++ /dev/null
@@ -1,112 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-from cproton import *
-
-class EmptyAttrs:
-
-    def __contains__(self, name):
-        return False
-
-    def __getitem__(self, name):
-        raise KeyError(name)
-
-    def __setitem__(self, name, value):
-        raise TypeError("does not support item assignment")
-
-EMPTY_ATTRS = EmptyAttrs()
-
-class Wrapper(object):
-
-    def __init__(self, impl_or_constructor, get_context=None):
-        init = False
-        if callable(impl_or_constructor):
-            # we are constructing a new object
-            impl = impl_or_constructor()
-            if impl is None:
-                self.__dict__["_impl"] = impl
-                self.__dict__["_attrs"] = EMPTY_ATTRS
-                self.__dict__["_record"] = None
-                from proton import ProtonException
-                raise ProtonException("Wrapper failed to create wrapped object. Check for file descriptor or memory exhaustion.")
-            init = True
-        else:
-            # we are wrapping an existing object
-            impl = impl_or_constructor
-            pn_incref(impl)
-
-        if get_context:
-            record = get_context(impl)
-            attrs = pn_void2py(pn_record_get(record, PYCTX))
-            if attrs is None:
-                attrs = {}
-                pn_record_def(record, PYCTX, PN_PYREF)
-                pn_record_set(record, PYCTX, pn_py2void(attrs))
-                init = True
-        else:
-            attrs = EMPTY_ATTRS
-            init = False
-            record = None
-        self.__dict__["_impl"] = impl
-        self.__dict__["_attrs"] = attrs
-        self.__dict__["_record"] = record
-        if init: self._init()
-
-    def __getattr__(self, name):
-        attrs = self.__dict__["_attrs"]
-        if name in attrs:
-            return attrs[name]
-        else:
-            raise AttributeError(name + " not in _attrs")
-
-    def __setattr__(self, name, value):
-        if hasattr(self.__class__, name):
-            object.__setattr__(self, name, value)
-        else:
-            attrs = self.__dict__["_attrs"]
-            attrs[name] = value
-
-    def __delattr__(self, name):
-        attrs = self.__dict__["_attrs"]
-        if attrs:
-            del attrs[name]
-
-    def __hash__(self):
-        return hash(addressof(self._impl))
-
-    def __eq__(self, other):
-        if isinstance(other, Wrapper):
-            return addressof(self._impl) == addressof(other._impl)
-        return False
-
-    def __ne__(self, other):
-        if isinstance(other, Wrapper):
-            return addressof(self._impl) != addressof(other._impl)
-        return True
-
-    def __del__(self):
-        pn_decref(self._impl)
-
-    def __repr__(self):
-        return '<%s.%s 0x%x ~ 0x%x>' % (self.__class__.__module__,
-                                               self.__class__.__name__,
-                                               id(self), addressof(self._impl))
-
-
-PYCTX = int(pn_py2void(Wrapper))
-addressof = int


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[6/6] qpid-proton git commit: PROTON-1850: Split up proton __init__.py into multiple files - Reformatted python source to (mostly) PEP-8 standards - Control what gets exported from __init__ by restricting what it imports - Move most of the reactor implem

Posted by as...@apache.org.
PROTON-1850: Split up proton __init__.py into multiple files
- Reformatted python source to (mostly) PEP-8 standards
- Control what gets exported from __init__ by restricting what it imports
- Move most of the reactor implementation specific code into _reactor_impl.py


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/d28fecf5
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/d28fecf5
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/d28fecf5

Branch: refs/heads/master
Commit: d28fecf5d5388734508fa79e3ec33fe975bd060e
Parents: 5193403
Author: Andrew Stitcher <as...@apache.org>
Authored: Wed May 2 19:20:38 2018 -0400
Committer: Andrew Stitcher <as...@apache.org>
Committed: Wed May 23 16:27:49 2018 -0400

----------------------------------------------------------------------
 python/CMakeLists.txt          |   17 +-
 python/proton/__init__.py      | 3726 +----------------------------------
 python/proton/_common.py       |   91 +
 python/proton/_compat.py       |   13 +-
 python/proton/_condition.py    |   63 +
 python/proton/_data.py         | 1129 +++++++++++
 python/proton/_delivery.py     |  293 +++
 python/proton/_endpoints.py    |  765 +++++++
 python/proton/_events.py       |  333 ++++
 python/proton/_exceptions.py   |   92 +
 python/proton/_message.py      |  465 +++++
 python/proton/_reactor_impl.py |  217 ++
 python/proton/_transport.py    |  524 +++++
 python/proton/_url.py          |  161 ++
 python/proton/_wrapper.py      |  120 ++
 python/proton/handlers.py      |   66 +-
 python/proton/reactor.py       |  138 +-
 python/proton/utils.py         |   54 +-
 python/proton/wrapper.py       |  112 --
 19 files changed, 4552 insertions(+), 3827 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index bdd1af7..2c3f4d9 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -64,11 +64,24 @@ endif()
 set (pysrc-generated cproton.py)
 set (pysrc
     proton/__init__.py
+    proton/_compat.py
+    proton/_common.py
+    proton/_condition.py
+    proton/_data.py
+    proton/_delivery.py
+    proton/_endpoints.py
+    proton/_events.py
+    proton/_exceptions.py
+    proton/_message.py
+    proton/_transport.py
+    proton/_url.py
+    proton/_wrapper.py
+
     proton/handlers.py
     proton/reactor.py
     proton/utils.py
-    proton/wrapper.py
-    proton/_compat.py
+
+    proton/_reactor_impl.py
     )
 # extra files included in the source distribution
 set(py_dist_files


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[3/6] qpid-proton git commit: PROTON-1850: Split up proton __init__.py into multiple files - Reformatted python source to (mostly) PEP-8 standards - Control what gets exported from __init__ by restricting what it imports - Move most of the reactor implem

Posted by as...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_exceptions.py
----------------------------------------------------------------------
diff --git a/python/proton/_exceptions.py b/python/proton/_exceptions.py
new file mode 100644
index 0000000..47420c2
--- /dev/null
+++ b/python/proton/_exceptions.py
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+from cproton import PN_TIMEOUT, PN_INTR
+
+
+class ProtonException(Exception):
+    """
+    The root of the proton exception hierarchy. All proton exception
+    classes derive from this exception.
+    """
+    pass
+
+
+class Timeout(ProtonException):
+    """
+    A timeout exception indicates that a blocking operation has timed
+    out.
+    """
+    pass
+
+
+class Interrupt(ProtonException):
+    """
+    An interrupt exception indicates that a blocking operation was interrupted.
+    """
+    pass
+
+
+EXCEPTIONS = {
+    PN_TIMEOUT: Timeout,
+    PN_INTR: Interrupt
+}
+
+
+class MessageException(ProtonException):
+    """
+    The MessageException class is the root of the message exception
+    hierarchy. All exceptions generated by the Message class derive from
+    this exception.
+    """
+    pass
+
+
+class DataException(ProtonException):
+    """
+    The DataException class is the root of the Data exception hierarchy.
+    All exceptions raised by the Data class extend this exception.
+    """
+    pass
+
+
+class TransportException(ProtonException):
+    pass
+
+
+class SSLException(TransportException):
+    pass
+
+
+class SSLUnavailable(SSLException):
+    pass
+
+
+class ConnectionException(ProtonException):
+    pass
+
+
+class SessionException(ProtonException):
+    pass
+
+
+class LinkException(ProtonException):
+    pass

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_message.py
----------------------------------------------------------------------
diff --git a/python/proton/_message.py b/python/proton/_message.py
new file mode 100644
index 0000000..32a8c72
--- /dev/null
+++ b/python/proton/_message.py
@@ -0,0 +1,465 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+from cproton import PN_STATUS_SETTLED, PN_DEFAULT_PRIORITY, PN_STATUS_MODIFIED, PN_STATUS_RELEASED, PN_STATUS_ABORTED, \
+    PN_STATUS_REJECTED, PN_STATUS_PENDING, PN_STATUS_UNKNOWN, PN_STATUS_ACCEPTED, \
+    PN_OVERFLOW, \
+    pn_message_set_delivery_count, pn_message_set_address, pn_message_properties, \
+    pn_message_get_user_id, pn_message_set_content_encoding, pn_message_get_subject, pn_message_get_priority, \
+    pn_message_get_content_encoding, pn_message_body, \
+    pn_message_correlation_id, pn_message_get_address, pn_message_set_content_type, pn_message_get_group_id, \
+    pn_message_set_expiry_time, pn_message_set_creation_time, pn_message_error, \
+    pn_message_is_first_acquirer, pn_message_set_priority, \
+    pn_message_free, pn_message_get_creation_time, pn_message_is_inferred, pn_message_set_subject, \
+    pn_message_set_user_id, pn_message_set_group_id, \
+    pn_message_id, pn_message_clear, pn_message_set_durable, \
+    pn_message_set_first_acquirer, pn_message_get_delivery_count, \
+    pn_message_decode, pn_message_set_reply_to_group_id, \
+    pn_message_get_group_sequence, pn_message_set_reply_to, \
+    pn_message_set_ttl, pn_message_get_reply_to, pn_message, pn_message_annotations, pn_message_is_durable, \
+    pn_message_instructions, pn_message_get_content_type, \
+    pn_message_get_reply_to_group_id, pn_message_get_ttl, pn_message_encode, pn_message_get_expiry_time, \
+    pn_message_set_group_sequence, pn_message_set_inferred, \
+    pn_inspect, pn_string, pn_string_get, pn_free, pn_error_text
+
+from . import _compat
+from ._common import Constant, isinteger, secs2millis, millis2secs, unicode2utf8, utf82unicode
+from ._data import Data, ulong, symbol
+from ._endpoints import Link
+from ._exceptions import EXCEPTIONS, MessageException
+
+PENDING = Constant("PENDING")
+ACCEPTED = Constant("ACCEPTED")
+REJECTED = Constant("REJECTED")
+RELEASED = Constant("RELEASED")
+MODIFIED = Constant("MODIFIED")
+ABORTED = Constant("ABORTED")
+SETTLED = Constant("SETTLED")
+
+STATUSES = {
+    PN_STATUS_ABORTED: ABORTED,
+    PN_STATUS_ACCEPTED: ACCEPTED,
+    PN_STATUS_REJECTED: REJECTED,
+    PN_STATUS_RELEASED: RELEASED,
+    PN_STATUS_MODIFIED: MODIFIED,
+    PN_STATUS_PENDING: PENDING,
+    PN_STATUS_SETTLED: SETTLED,
+    PN_STATUS_UNKNOWN: None
+}
+
+
+class Message(object):
+    """The L{Message} class is a mutable holder of message content.
+
+    @ivar instructions: delivery instructions for the message
+    @type instructions: dict
+    @ivar annotations: infrastructure defined message annotations
+    @type annotations: dict
+    @ivar properties: application defined message properties
+    @type properties: dict
+    @ivar body: message body
+    @type body: bytes | unicode | dict | list | int | long | float | UUID
+    """
+
+    DEFAULT_PRIORITY = PN_DEFAULT_PRIORITY
+
+    def __init__(self, body=None, **kwargs):
+        """
+        @param kwargs: Message property name/value pairs to initialise the Message
+        """
+        self._msg = pn_message()
+        self._id = Data(pn_message_id(self._msg))
+        self._correlation_id = Data(pn_message_correlation_id(self._msg))
+        self.instructions = None
+        self.annotations = None
+        self.properties = None
+        self.body = body
+        for k, v in _compat.iteritems(kwargs):
+            getattr(self, k)  # Raise exception if it's not a valid attribute.
+            setattr(self, k, v)
+
+    def __del__(self):
+        if hasattr(self, "_msg"):
+            pn_message_free(self._msg)
+            del self._msg
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, MessageException)
+            raise exc("[%s]: %s" % (err, pn_error_text(pn_message_error(self._msg))))
+        else:
+            return err
+
+    def _check_property_keys(self):
+        for k in self.properties.keys():
+            if isinstance(k, unicode):
+                # py2 unicode, py3 str (via hack definition)
+                continue
+            # If key is binary then change to string
+            elif isinstance(k, str):
+                # py2 str
+                self.properties[k.encode('utf-8')] = self.properties.pop(k)
+            else:
+                raise MessageException('Application property key is not string type: key=%s %s' % (str(k), type(k)))
+
+    def _pre_encode(self):
+        inst = Data(pn_message_instructions(self._msg))
+        ann = Data(pn_message_annotations(self._msg))
+        props = Data(pn_message_properties(self._msg))
+        body = Data(pn_message_body(self._msg))
+
+        inst.clear()
+        if self.instructions is not None:
+            inst.put_object(self.instructions)
+        ann.clear()
+        if self.annotations is not None:
+            ann.put_object(self.annotations)
+        props.clear()
+        if self.properties is not None:
+            self._check_property_keys()
+            props.put_object(self.properties)
+        body.clear()
+        if self.body is not None:
+            body.put_object(self.body)
+
+    def _post_decode(self):
+        inst = Data(pn_message_instructions(self._msg))
+        ann = Data(pn_message_annotations(self._msg))
+        props = Data(pn_message_properties(self._msg))
+        body = Data(pn_message_body(self._msg))
+
+        if inst.next():
+            self.instructions = inst.get_object()
+        else:
+            self.instructions = None
+        if ann.next():
+            self.annotations = ann.get_object()
+        else:
+            self.annotations = None
+        if props.next():
+            self.properties = props.get_object()
+        else:
+            self.properties = None
+        if body.next():
+            self.body = body.get_object()
+        else:
+            self.body = None
+
+    def clear(self):
+        """
+        Clears the contents of the L{Message}. All fields will be reset to
+        their default values.
+        """
+        pn_message_clear(self._msg)
+        self.instructions = None
+        self.annotations = None
+        self.properties = None
+        self.body = None
+
+    def _is_inferred(self):
+        return pn_message_is_inferred(self._msg)
+
+    def _set_inferred(self, value):
+        self._check(pn_message_set_inferred(self._msg, bool(value)))
+
+    inferred = property(_is_inferred, _set_inferred, doc="""
+The inferred flag for a message indicates how the message content
+is encoded into AMQP sections. If inferred is true then binary and
+list values in the body of the message will be encoded as AMQP DATA
+and AMQP SEQUENCE sections, respectively. If inferred is false,
+then all values in the body of the message will be encoded as AMQP
+VALUE sections regardless of their type.
+""")
+
+    def _is_durable(self):
+        return pn_message_is_durable(self._msg)
+
+    def _set_durable(self, value):
+        self._check(pn_message_set_durable(self._msg, bool(value)))
+
+    durable = property(_is_durable, _set_durable,
+                       doc="""
+The durable property indicates that the message should be held durably
+by any intermediaries taking responsibility for the message.
+""")
+
+    def _get_priority(self):
+        return pn_message_get_priority(self._msg)
+
+    def _set_priority(self, value):
+        self._check(pn_message_set_priority(self._msg, value))
+
+    priority = property(_get_priority, _set_priority,
+                        doc="""
+The priority of the message.
+""")
+
+    def _get_ttl(self):
+        return millis2secs(pn_message_get_ttl(self._msg))
+
+    def _set_ttl(self, value):
+        self._check(pn_message_set_ttl(self._msg, secs2millis(value)))
+
+    ttl = property(_get_ttl, _set_ttl,
+                   doc="""
+The time to live of the message measured in seconds. Expired messages
+may be dropped.
+""")
+
+    def _is_first_acquirer(self):
+        return pn_message_is_first_acquirer(self._msg)
+
+    def _set_first_acquirer(self, value):
+        self._check(pn_message_set_first_acquirer(self._msg, bool(value)))
+
+    first_acquirer = property(_is_first_acquirer, _set_first_acquirer,
+                              doc="""
+True iff the recipient is the first to acquire the message.
+""")
+
+    def _get_delivery_count(self):
+        return pn_message_get_delivery_count(self._msg)
+
+    def _set_delivery_count(self, value):
+        self._check(pn_message_set_delivery_count(self._msg, value))
+
+    delivery_count = property(_get_delivery_count, _set_delivery_count,
+                              doc="""
+The number of delivery attempts made for this message.
+""")
+
+    def _get_id(self):
+        return self._id.get_object()
+
+    def _set_id(self, value):
+        if isinteger(value):
+            value = ulong(value)
+        self._id.rewind()
+        self._id.put_object(value)
+
+    id = property(_get_id, _set_id,
+                  doc="""
+The id of the message.
+""")
+
+    def _get_user_id(self):
+        return pn_message_get_user_id(self._msg)
+
+    def _set_user_id(self, value):
+        self._check(pn_message_set_user_id(self._msg, value))
+
+    user_id = property(_get_user_id, _set_user_id,
+                       doc="""
+The user id of the message creator.
+""")
+
+    def _get_address(self):
+        return utf82unicode(pn_message_get_address(self._msg))
+
+    def _set_address(self, value):
+        self._check(pn_message_set_address(self._msg, unicode2utf8(value)))
+
+    address = property(_get_address, _set_address,
+                       doc="""
+The address of the message.
+""")
+
+    def _get_subject(self):
+        return utf82unicode(pn_message_get_subject(self._msg))
+
+    def _set_subject(self, value):
+        self._check(pn_message_set_subject(self._msg, unicode2utf8(value)))
+
+    subject = property(_get_subject, _set_subject,
+                       doc="""
+The subject of the message.
+""")
+
+    def _get_reply_to(self):
+        return utf82unicode(pn_message_get_reply_to(self._msg))
+
+    def _set_reply_to(self, value):
+        self._check(pn_message_set_reply_to(self._msg, unicode2utf8(value)))
+
+    reply_to = property(_get_reply_to, _set_reply_to,
+                        doc="""
+The reply-to address for the message.
+""")
+
+    def _get_correlation_id(self):
+        return self._correlation_id.get_object()
+
+    def _set_correlation_id(self, value):
+        if isinteger(value):
+            value = ulong(value)
+        self._correlation_id.rewind()
+        self._correlation_id.put_object(value)
+
+    correlation_id = property(_get_correlation_id, _set_correlation_id,
+                              doc="""
+The correlation-id for the message.
+""")
+
+    def _get_content_type(self):
+        return symbol(utf82unicode(pn_message_get_content_type(self._msg)))
+
+    def _set_content_type(self, value):
+        self._check(pn_message_set_content_type(self._msg, unicode2utf8(value)))
+
+    content_type = property(_get_content_type, _set_content_type,
+                            doc="""
+The content-type of the message.
+""")
+
+    def _get_content_encoding(self):
+        return symbol(utf82unicode(pn_message_get_content_encoding(self._msg)))
+
+    def _set_content_encoding(self, value):
+        self._check(pn_message_set_content_encoding(self._msg, unicode2utf8(value)))
+
+    content_encoding = property(_get_content_encoding, _set_content_encoding,
+                                doc="""
+The content-encoding of the message.
+""")
+
+    def _get_expiry_time(self):
+        return millis2secs(pn_message_get_expiry_time(self._msg))
+
+    def _set_expiry_time(self, value):
+        self._check(pn_message_set_expiry_time(self._msg, secs2millis(value)))
+
+    expiry_time = property(_get_expiry_time, _set_expiry_time,
+                           doc="""
+The expiry time of the message.
+""")
+
+    def _get_creation_time(self):
+        return millis2secs(pn_message_get_creation_time(self._msg))
+
+    def _set_creation_time(self, value):
+        self._check(pn_message_set_creation_time(self._msg, secs2millis(value)))
+
+    creation_time = property(_get_creation_time, _set_creation_time,
+                             doc="""
+The creation time of the message.
+""")
+
+    def _get_group_id(self):
+        return utf82unicode(pn_message_get_group_id(self._msg))
+
+    def _set_group_id(self, value):
+        self._check(pn_message_set_group_id(self._msg, unicode2utf8(value)))
+
+    group_id = property(_get_group_id, _set_group_id,
+                        doc="""
+The group id of the message.
+""")
+
+    def _get_group_sequence(self):
+        return pn_message_get_group_sequence(self._msg)
+
+    def _set_group_sequence(self, value):
+        self._check(pn_message_set_group_sequence(self._msg, value))
+
+    group_sequence = property(_get_group_sequence, _set_group_sequence,
+                              doc="""
+The sequence of the message within its group.
+""")
+
+    def _get_reply_to_group_id(self):
+        return utf82unicode(pn_message_get_reply_to_group_id(self._msg))
+
+    def _set_reply_to_group_id(self, value):
+        self._check(pn_message_set_reply_to_group_id(self._msg, unicode2utf8(value)))
+
+    reply_to_group_id = property(_get_reply_to_group_id, _set_reply_to_group_id,
+                                 doc="""
+The group-id for any replies.
+""")
+
+    def encode(self):
+        self._pre_encode()
+        sz = 16
+        while True:
+            err, data = pn_message_encode(self._msg, sz)
+            if err == PN_OVERFLOW:
+                sz *= 2
+                continue
+            else:
+                self._check(err)
+                return data
+
+    def decode(self, data):
+        self._check(pn_message_decode(self._msg, data))
+        self._post_decode()
+
+    def send(self, sender, tag=None):
+        dlv = sender.delivery(tag or sender.delivery_tag())
+        encoded = self.encode()
+        sender.stream(encoded)
+        sender.advance()
+        if sender.snd_settle_mode == Link.SND_SETTLED:
+            dlv.settle()
+        return dlv
+
+    def recv(self, link):
+        """
+        Receives and decodes the message content for the current delivery
+        from the link. Upon success it will return the current delivery
+        for the link. If there is no current delivery, or if the current
+        delivery is incomplete, or if the link is not a receiver, it will
+        return None.
+
+        @type link: Link
+        @param link: the link to receive a message from
+        @return the delivery associated with the decoded message (or None)
+
+        """
+        if link.is_sender: return None
+        dlv = link.current
+        if not dlv or dlv.partial: return None
+        dlv.encoded = link.recv(dlv.pending)
+        link.advance()
+        # the sender has already forgotten about the delivery, so we might
+        # as well too
+        if link.remote_snd_settle_mode == Link.SND_SETTLED:
+            dlv.settle()
+        self.decode(dlv.encoded)
+        return dlv
+
+    def __repr2__(self):
+        props = []
+        for attr in ("inferred", "address", "reply_to", "durable", "ttl",
+                     "priority", "first_acquirer", "delivery_count", "id",
+                     "correlation_id", "user_id", "group_id", "group_sequence",
+                     "reply_to_group_id", "instructions", "annotations",
+                     "properties", "body"):
+            value = getattr(self, attr)
+            if value: props.append("%s=%r" % (attr, value))
+        return "Message(%s)" % ", ".join(props)
+
+    def __repr__(self):
+        tmp = pn_string(None)
+        err = pn_inspect(self._msg, tmp)
+        result = pn_string_get(tmp)
+        pn_free(tmp)
+        self._check(err)
+        return result

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_reactor_impl.py
----------------------------------------------------------------------
diff --git a/python/proton/_reactor_impl.py b/python/proton/_reactor_impl.py
new file mode 100644
index 0000000..39986ff
--- /dev/null
+++ b/python/proton/_reactor_impl.py
@@ -0,0 +1,217 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+import weakref
+
+from cproton import PN_INVALID_SOCKET, \
+    pn_incref, pn_decref, \
+    pn_handler_add, pn_handler_clear, pn_pyhandler, \
+    pn_selectable_is_reading, pn_selectable_attachments, pn_selectable_set_reading, \
+    pn_selectable_expired, pn_selectable_set_fd, pn_selectable_set_registered, pn_selectable_writable, \
+    pn_selectable_is_writing, pn_selectable_set_deadline, pn_selectable_is_registered, pn_selectable_terminate, \
+    pn_selectable_get_deadline, pn_selectable_is_terminal, pn_selectable_readable, \
+    pn_selectable_release, pn_selectable_set_writing, pn_selectable_get_fd
+
+from ._common import millis2secs, secs2millis
+from ._wrapper import Wrapper
+
+from . import _compat
+
+_DEFAULT = object()
+
+
+class Selectable(Wrapper):
+
+    @staticmethod
+    def wrap(impl):
+        if impl is None:
+            return None
+        else:
+            return Selectable(impl)
+
+    def __init__(self, impl):
+        Wrapper.__init__(self, impl, pn_selectable_attachments)
+
+    def _init(self):
+        pass
+
+    def fileno(self, fd=_DEFAULT):
+        if fd is _DEFAULT:
+            return pn_selectable_get_fd(self._impl)
+        elif fd is None:
+            pn_selectable_set_fd(self._impl, PN_INVALID_SOCKET)
+        else:
+            pn_selectable_set_fd(self._impl, fd)
+
+    def _is_reading(self):
+        return pn_selectable_is_reading(self._impl)
+
+    def _set_reading(self, val):
+        pn_selectable_set_reading(self._impl, bool(val))
+
+    reading = property(_is_reading, _set_reading)
+
+    def _is_writing(self):
+        return pn_selectable_is_writing(self._impl)
+
+    def _set_writing(self, val):
+        pn_selectable_set_writing(self._impl, bool(val))
+
+    writing = property(_is_writing, _set_writing)
+
+    def _get_deadline(self):
+        tstamp = pn_selectable_get_deadline(self._impl)
+        if tstamp:
+            return millis2secs(tstamp)
+        else:
+            return None
+
+    def _set_deadline(self, deadline):
+        pn_selectable_set_deadline(self._impl, secs2millis(deadline))
+
+    deadline = property(_get_deadline, _set_deadline)
+
+    def readable(self):
+        pn_selectable_readable(self._impl)
+
+    def writable(self):
+        pn_selectable_writable(self._impl)
+
+    def expired(self):
+        pn_selectable_expired(self._impl)
+
+    def _is_registered(self):
+        return pn_selectable_is_registered(self._impl)
+
+    def _set_registered(self, registered):
+        pn_selectable_set_registered(self._impl, registered)
+
+    registered = property(_is_registered, _set_registered,
+                          doc="""
+The registered property may be get/set by an I/O polling system to
+indicate whether the fd has been registered or not.
+""")
+
+    @property
+    def is_terminal(self):
+        return pn_selectable_is_terminal(self._impl)
+
+    def terminate(self):
+        pn_selectable_terminate(self._impl)
+
+    def release(self):
+        pn_selectable_release(self._impl)
+
+
+class _cadapter:
+
+    def __init__(self, handler, on_error=None):
+        self.handler = handler
+        self.on_error = on_error
+
+    def dispatch(self, cevent, ctype):
+        from ._events import Event
+        ev = Event.wrap(cevent, ctype)
+        ev.dispatch(self.handler)
+
+    def exception(self, exc, val, tb):
+        if self.on_error is None:
+            _compat.raise_(exc, val, tb)
+        else:
+            self.on_error((exc, val, tb))
+
+
+class WrappedHandlersChildSurrogate:
+    def __init__(self, delegate):
+        self.handlers = []
+        self.delegate = weakref.ref(delegate)
+
+    def on_unhandled(self, method, event):
+        from ._events import dispatch
+        delegate = self.delegate()
+        if delegate:
+            dispatch(delegate, method, event)
+
+
+class WrappedHandlersProperty(object):
+    def __get__(self, obj, clazz):
+        if obj is None:
+            return None
+        return self.surrogate(obj).handlers
+
+    def __set__(self, obj, value):
+        self.surrogate(obj).handlers = value
+
+    def surrogate(self, obj):
+        key = "_surrogate"
+        objdict = obj.__dict__
+        surrogate = objdict.get(key, None)
+        if surrogate is None:
+            objdict[key] = surrogate = WrappedHandlersChildSurrogate(obj)
+            obj.add(surrogate)
+        return surrogate
+
+
+class WrappedHandler(Wrapper):
+    handlers = WrappedHandlersProperty()
+
+    @classmethod
+    def wrap(cls, impl, on_error=None):
+        if impl is None:
+            return None
+        else:
+            handler = cls(impl)
+            handler.__dict__["on_error"] = on_error
+            return handler
+
+    def __init__(self, impl_or_constructor):
+        Wrapper.__init__(self, impl_or_constructor)
+        if list(self.__class__.__mro__).index(WrappedHandler) > 1:
+            # instantiate the surrogate
+            self.handlers.extend([])
+
+    def _on_error(self, info):
+        on_error = getattr(self, "on_error", None)
+        if on_error is None:
+            _compat.raise_(info[0], info[1], info[2])
+        else:
+            on_error(info)
+
+    def add(self, handler, on_error=None):
+        if handler is None: return
+        if on_error is None: on_error = self._on_error
+        impl = _chandler(handler, on_error)
+        pn_handler_add(self._impl, impl)
+        pn_decref(impl)
+
+    def clear(self):
+        pn_handler_clear(self._impl)
+
+
+def _chandler(obj, on_error=None):
+    if obj is None:
+        return None
+    elif isinstance(obj, WrappedHandler):
+        impl = obj._impl
+        pn_incref(impl)
+        return impl
+    else:
+        return pn_pyhandler(_cadapter(obj, on_error))

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_transport.py
----------------------------------------------------------------------
diff --git a/python/proton/_transport.py b/python/proton/_transport.py
new file mode 100644
index 0000000..3db0078
--- /dev/null
+++ b/python/proton/_transport.py
@@ -0,0 +1,524 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+from cproton import PN_SASL_AUTH, PN_SASL_PERM, PN_SASL_SYS, PN_SSL_RESUME_REUSED, PN_SASL_NONE, PN_SSL_SHA1, \
+    PN_SSL_CERT_SUBJECT_COUNTRY_NAME, PN_SASL_OK, PN_SSL_RESUME_UNKNOWN, PN_EOS, PN_SSL_ANONYMOUS_PEER, PN_SSL_MD5, \
+    PN_SSL_CERT_SUBJECT_COMMON_NAME, PN_SSL_VERIFY_PEER, PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY, PN_SSL_MODE_SERVER, \
+    PN_TRACE_DRV, PN_TRACE_RAW, pn_transport, PN_SSL_SHA256, PN_TRACE_FRM, PN_SSL_MODE_CLIENT, PN_SASL_TEMP, \
+    PN_SSL_SHA512, PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT, PN_OK, PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE, \
+    PN_SSL_VERIFY_PEER_NAME, PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME, PN_SSL_RESUME_NEW, PN_TRACE_OFF, \
+    pn_transport_get_channel_max, pn_transport_capacity, pn_transport_push, pn_transport_get_user, pn_transport_tick, \
+    pn_transport_set_max_frame, pn_transport_attachments, pn_transport_unbind, pn_transport_peek, \
+    pn_transport_set_channel_max, pn_transport_close_tail, pn_transport_condition, pn_transport_is_encrypted, \
+    pn_transport_get_frames_input, pn_transport_bind, pn_transport_closed, pn_transport_get_idle_timeout, \
+    pn_transport_get_remote_idle_timeout, pn_transport_get_frames_output, pn_transport_pending, \
+    pn_transport_set_pytracer, pn_transport_close_head, pn_transport_get_remote_max_frame, \
+    pn_transport_is_authenticated, pn_transport_set_idle_timeout, pn_transport_log, pn_transport_get_pytracer, \
+    pn_transport_require_auth, pn_transport_get_max_frame, pn_transport_set_server, pn_transport_remote_channel_max, \
+    pn_transport_require_encryption, pn_transport_pop, pn_transport_connection, \
+    pn_sasl, pn_sasl_set_allow_insecure_mechs, pn_sasl_outcome, pn_transport_error, pn_sasl_get_user, \
+    pn_sasl_extended, pn_sasl_done, pn_sasl_get_allow_insecure_mechs, pn_sasl_allowed_mechs, \
+    pn_sasl_config_name, pn_sasl_config_path, \
+    pn_ssl, pn_ssl_init, pn_ssl_domain_allow_unsecured_client, pn_ssl_domain_free, \
+    pn_ssl_domain, pn_transport_trace, pn_ssl_resume_status, pn_sasl_get_mech, \
+    pn_ssl_domain_set_trusted_ca_db, pn_ssl_get_remote_subject_subfield, pn_ssl_present, \
+    pn_ssl_get_remote_subject, pn_ssl_domain_set_credentials, pn_ssl_domain_set_peer_authentication, \
+    pn_ssl_get_peer_hostname, pn_ssl_set_peer_hostname, pn_ssl_get_cipher_name, pn_ssl_get_cert_fingerprint, \
+    pn_ssl_get_protocol_name, \
+    pn_error_text
+
+from ._common import millis2secs, secs2millis, unicode2utf8, utf82unicode
+from ._condition import cond2obj
+from ._exceptions import EXCEPTIONS, TransportException, SessionException, SSLException, SSLUnavailable
+from ._wrapper import Wrapper
+
+
+class TraceAdapter:
+
+    def __init__(self, tracer):
+        self.tracer = tracer
+
+    def __call__(self, trans_impl, message):
+        self.tracer(Transport.wrap(trans_impl), message)
+
+
+class Transport(Wrapper):
+    TRACE_OFF = PN_TRACE_OFF
+    TRACE_DRV = PN_TRACE_DRV
+    TRACE_FRM = PN_TRACE_FRM
+    TRACE_RAW = PN_TRACE_RAW
+
+    CLIENT = 1
+    SERVER = 2
+
+    @staticmethod
+    def wrap(impl):
+        if impl is None:
+            return None
+        else:
+            return Transport(_impl=impl)
+
+    def __init__(self, mode=None, _impl=pn_transport):
+        Wrapper.__init__(self, _impl, pn_transport_attachments)
+        if mode == Transport.SERVER:
+            pn_transport_set_server(self._impl)
+        elif mode is None or mode == Transport.CLIENT:
+            pass
+        else:
+            raise TransportException("Cannot initialise Transport from mode: %s" % str(mode))
+
+    def _init(self):
+        self._sasl = None
+        self._ssl = None
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, TransportException)
+            raise exc("[%s]: %s" % (err, pn_error_text(pn_transport_error(self._impl))))
+        else:
+            return err
+
+    def _set_tracer(self, tracer):
+        pn_transport_set_pytracer(self._impl, TraceAdapter(tracer))
+
+    def _get_tracer(self):
+        adapter = pn_transport_get_pytracer(self._impl)
+        if adapter:
+            return adapter.tracer
+        else:
+            return None
+
+    tracer = property(_get_tracer, _set_tracer,
+                      doc="""
+A callback for trace logging. The callback is passed the transport and log message.
+""")
+
+    def log(self, message):
+        pn_transport_log(self._impl, message)
+
+    def require_auth(self, bool):
+        pn_transport_require_auth(self._impl, bool)
+
+    @property
+    def authenticated(self):
+        return pn_transport_is_authenticated(self._impl)
+
+    def require_encryption(self, bool):
+        pn_transport_require_encryption(self._impl, bool)
+
+    @property
+    def encrypted(self):
+        return pn_transport_is_encrypted(self._impl)
+
+    @property
+    def user(self):
+        return pn_transport_get_user(self._impl)
+
+    def bind(self, connection):
+        """Assign a connection to the transport"""
+        self._check(pn_transport_bind(self._impl, connection._impl))
+
+    def unbind(self):
+        """Release the connection"""
+        self._check(pn_transport_unbind(self._impl))
+
+    def trace(self, n):
+        pn_transport_trace(self._impl, n)
+
+    def tick(self, now):
+        """Process any timed events (like heartbeat generation).
+        now = seconds since epoch (float).
+        """
+        return millis2secs(pn_transport_tick(self._impl, secs2millis(now)))
+
+    def capacity(self):
+        c = pn_transport_capacity(self._impl)
+        if c >= PN_EOS:
+            return c
+        else:
+            return self._check(c)
+
+    def push(self, binary):
+        n = self._check(pn_transport_push(self._impl, binary))
+        if n != len(binary):
+            raise OverflowError("unable to process all bytes: %s, %s" % (n, len(binary)))
+
+    def close_tail(self):
+        self._check(pn_transport_close_tail(self._impl))
+
+    def pending(self):
+        p = pn_transport_pending(self._impl)
+        if p >= PN_EOS:
+            return p
+        else:
+            return self._check(p)
+
+    def peek(self, size):
+        cd, out = pn_transport_peek(self._impl, size)
+        if cd == PN_EOS:
+            return None
+        else:
+            self._check(cd)
+            return out
+
+    def pop(self, size):
+        pn_transport_pop(self._impl, size)
+
+    def close_head(self):
+        self._check(pn_transport_close_head(self._impl))
+
+    @property
+    def closed(self):
+        return pn_transport_closed(self._impl)
+
+    # AMQP 1.0 max-frame-size
+    def _get_max_frame_size(self):
+        return pn_transport_get_max_frame(self._impl)
+
+    def _set_max_frame_size(self, value):
+        pn_transport_set_max_frame(self._impl, value)
+
+    max_frame_size = property(_get_max_frame_size, _set_max_frame_size,
+                              doc="""
+Sets the maximum size for received frames (in bytes).
+""")
+
+    @property
+    def remote_max_frame_size(self):
+        return pn_transport_get_remote_max_frame(self._impl)
+
+    def _get_channel_max(self):
+        return pn_transport_get_channel_max(self._impl)
+
+    def _set_channel_max(self, value):
+        if pn_transport_set_channel_max(self._impl, value):
+            raise SessionException("Too late to change channel max.")
+
+    channel_max = property(_get_channel_max, _set_channel_max,
+                           doc="""
+Sets the maximum channel that may be used on the transport.
+""")
+
+    @property
+    def remote_channel_max(self):
+        return pn_transport_remote_channel_max(self._impl)
+
+    # AMQP 1.0 idle-time-out
+    def _get_idle_timeout(self):
+        return millis2secs(pn_transport_get_idle_timeout(self._impl))
+
+    def _set_idle_timeout(self, sec):
+        pn_transport_set_idle_timeout(self._impl, secs2millis(sec))
+
+    idle_timeout = property(_get_idle_timeout, _set_idle_timeout,
+                            doc="""
+The idle timeout of the connection (float, in seconds).
+""")
+
+    @property
+    def remote_idle_timeout(self):
+        return millis2secs(pn_transport_get_remote_idle_timeout(self._impl))
+
+    @property
+    def frames_output(self):
+        return pn_transport_get_frames_output(self._impl)
+
+    @property
+    def frames_input(self):
+        return pn_transport_get_frames_input(self._impl)
+
+    def sasl(self):
+        return SASL(self)
+
+    def ssl(self, domain=None, session_details=None):
+        # SSL factory (singleton for this transport)
+        if not self._ssl:
+            self._ssl = SSL(self, domain, session_details)
+        return self._ssl
+
+    @property
+    def condition(self):
+        return cond2obj(pn_transport_condition(self._impl))
+
+    @property
+    def connection(self):
+        from . import _endpoints
+        return _endpoints.Connection.wrap(pn_transport_connection(self._impl))
+
+
+class SASLException(TransportException):
+    pass
+
+
+class SASL(Wrapper):
+    OK = PN_SASL_OK
+    AUTH = PN_SASL_AUTH
+    SYS = PN_SASL_SYS
+    PERM = PN_SASL_PERM
+    TEMP = PN_SASL_TEMP
+
+    @staticmethod
+    def extended():
+        return pn_sasl_extended()
+
+    def __init__(self, transport):
+        Wrapper.__init__(self, transport._impl, pn_transport_attachments)
+        self._sasl = pn_sasl(transport._impl)
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, SASLException)
+            raise exc("[%s]" % (err))
+        else:
+            return err
+
+    @property
+    def user(self):
+        return pn_sasl_get_user(self._sasl)
+
+    @property
+    def mech(self):
+        return pn_sasl_get_mech(self._sasl)
+
+    @property
+    def outcome(self):
+        outcome = pn_sasl_outcome(self._sasl)
+        if outcome == PN_SASL_NONE:
+            return None
+        else:
+            return outcome
+
+    def allowed_mechs(self, mechs):
+        pn_sasl_allowed_mechs(self._sasl, unicode2utf8(mechs))
+
+    def _get_allow_insecure_mechs(self):
+        return pn_sasl_get_allow_insecure_mechs(self._sasl)
+
+    def _set_allow_insecure_mechs(self, insecure):
+        pn_sasl_set_allow_insecure_mechs(self._sasl, insecure)
+
+    allow_insecure_mechs = property(_get_allow_insecure_mechs, _set_allow_insecure_mechs,
+                                    doc="""
+Allow unencrypted cleartext passwords (PLAIN mech)
+""")
+
+    def done(self, outcome):
+        pn_sasl_done(self._sasl, outcome)
+
+    def config_name(self, name):
+        pn_sasl_config_name(self._sasl, name)
+
+    def config_path(self, path):
+        pn_sasl_config_path(self._sasl, path)
+
+
+class SSLDomain(object):
+    MODE_CLIENT = PN_SSL_MODE_CLIENT
+    MODE_SERVER = PN_SSL_MODE_SERVER
+    VERIFY_PEER = PN_SSL_VERIFY_PEER
+    VERIFY_PEER_NAME = PN_SSL_VERIFY_PEER_NAME
+    ANONYMOUS_PEER = PN_SSL_ANONYMOUS_PEER
+
+    def __init__(self, mode):
+        self._domain = pn_ssl_domain(mode)
+        if self._domain is None:
+            raise SSLUnavailable()
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, SSLException)
+            raise exc("SSL failure.")
+        else:
+            return err
+
+    def set_credentials(self, cert_file, key_file, password):
+        return self._check(pn_ssl_domain_set_credentials(self._domain,
+                                                         cert_file, key_file,
+                                                         password))
+
+    def set_trusted_ca_db(self, certificate_db):
+        return self._check(pn_ssl_domain_set_trusted_ca_db(self._domain,
+                                                           certificate_db))
+
+    def set_peer_authentication(self, verify_mode, trusted_CAs=None):
+        return self._check(pn_ssl_domain_set_peer_authentication(self._domain,
+                                                                 verify_mode,
+                                                                 trusted_CAs))
+
+    def allow_unsecured_client(self):
+        return self._check(pn_ssl_domain_allow_unsecured_client(self._domain))
+
+    def __del__(self):
+        pn_ssl_domain_free(self._domain)
+
+
+class SSL(object):
+
+    @staticmethod
+    def present():
+        return pn_ssl_present()
+
+    def _check(self, err):
+        if err < 0:
+            exc = EXCEPTIONS.get(err, SSLException)
+            raise exc("SSL failure.")
+        else:
+            return err
+
+    def __new__(cls, transport, domain, session_details=None):
+        """Enforce a singleton SSL object per Transport"""
+        if transport._ssl:
+            # unfortunately, we've combined the allocation and the configuration in a
+            # single step.  So catch any attempt by the application to provide what
+            # may be a different configuration than the original (hack)
+            ssl = transport._ssl
+            if (domain and (ssl._domain is not domain) or
+                    session_details and (ssl._session_details is not session_details)):
+                raise SSLException("Cannot re-configure existing SSL object!")
+        else:
+            obj = super(SSL, cls).__new__(cls)
+            obj._domain = domain
+            obj._session_details = session_details
+            session_id = None
+            if session_details:
+                session_id = session_details.get_session_id()
+            obj._ssl = pn_ssl(transport._impl)
+            if obj._ssl is None:
+                raise SSLUnavailable()
+            if domain:
+                pn_ssl_init(obj._ssl, domain._domain, session_id)
+            transport._ssl = obj
+        return transport._ssl
+
+    def cipher_name(self):
+        rc, name = pn_ssl_get_cipher_name(self._ssl, 128)
+        if rc:
+            return name
+        return None
+
+    def protocol_name(self):
+        rc, name = pn_ssl_get_protocol_name(self._ssl, 128)
+        if rc:
+            return name
+        return None
+
+    SHA1 = PN_SSL_SHA1
+    SHA256 = PN_SSL_SHA256
+    SHA512 = PN_SSL_SHA512
+    MD5 = PN_SSL_MD5
+
+    CERT_COUNTRY_NAME = PN_SSL_CERT_SUBJECT_COUNTRY_NAME
+    CERT_STATE_OR_PROVINCE = PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE
+    CERT_CITY_OR_LOCALITY = PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY
+    CERT_ORGANIZATION_NAME = PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME
+    CERT_ORGANIZATION_UNIT = PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT
+    CERT_COMMON_NAME = PN_SSL_CERT_SUBJECT_COMMON_NAME
+
+    def get_cert_subject_subfield(self, subfield_name):
+        subfield_value = pn_ssl_get_remote_subject_subfield(self._ssl, subfield_name)
+        return subfield_value
+
+    def get_cert_subject(self):
+        subject = pn_ssl_get_remote_subject(self._ssl)
+        return subject
+
+    def _get_cert_subject_unknown_subfield(self):
+        # Pass in an unhandled enum
+        return self.get_cert_subject_subfield(10)
+
+    # Convenience functions for obtaining the subfields of the subject field.
+    def get_cert_common_name(self):
+        return self.get_cert_subject_subfield(SSL.CERT_COMMON_NAME)
+
+    def get_cert_organization(self):
+        return self.get_cert_subject_subfield(SSL.CERT_ORGANIZATION_NAME)
+
+    def get_cert_organization_unit(self):
+        return self.get_cert_subject_subfield(SSL.CERT_ORGANIZATION_UNIT)
+
+    def get_cert_locality_or_city(self):
+        return self.get_cert_subject_subfield(SSL.CERT_CITY_OR_LOCALITY)
+
+    def get_cert_country(self):
+        return self.get_cert_subject_subfield(SSL.CERT_COUNTRY_NAME)
+
+    def get_cert_state_or_province(self):
+        return self.get_cert_subject_subfield(SSL.CERT_STATE_OR_PROVINCE)
+
+    def get_cert_fingerprint(self, fingerprint_length, digest_name):
+        rc, fingerprint_str = pn_ssl_get_cert_fingerprint(self._ssl, fingerprint_length, digest_name)
+        if rc == PN_OK:
+            return fingerprint_str
+        return None
+
+    # Convenience functions for obtaining fingerprint for specific hashing algorithms
+    def _get_cert_fingerprint_unknown_hash_alg(self):
+        return self.get_cert_fingerprint(41, 10)
+
+    def get_cert_fingerprint_sha1(self):
+        return self.get_cert_fingerprint(41, SSL.SHA1)
+
+    def get_cert_fingerprint_sha256(self):
+        # sha256 produces a fingerprint that is 64 characters long
+        return self.get_cert_fingerprint(65, SSL.SHA256)
+
+    def get_cert_fingerprint_sha512(self):
+        # sha512 produces a fingerprint that is 128 characters long
+        return self.get_cert_fingerprint(129, SSL.SHA512)
+
+    def get_cert_fingerprint_md5(self):
+        return self.get_cert_fingerprint(33, SSL.MD5)
+
+    @property
+    def remote_subject(self):
+        return pn_ssl_get_remote_subject(self._ssl)
+
+    RESUME_UNKNOWN = PN_SSL_RESUME_UNKNOWN
+    RESUME_NEW = PN_SSL_RESUME_NEW
+    RESUME_REUSED = PN_SSL_RESUME_REUSED
+
+    def resume_status(self):
+        return pn_ssl_resume_status(self._ssl)
+
+    def _set_peer_hostname(self, hostname):
+        self._check(pn_ssl_set_peer_hostname(self._ssl, unicode2utf8(hostname)))
+
+    def _get_peer_hostname(self):
+        err, name = pn_ssl_get_peer_hostname(self._ssl, 1024)
+        self._check(err)
+        return utf82unicode(name)
+
+    peer_hostname = property(_get_peer_hostname, _set_peer_hostname,
+                             doc="""
+Manage the expected name of the remote peer.  Used to authenticate the remote.
+""")
+
+
+class SSLSessionDetails(object):
+    """ Unique identifier for the SSL session.  Used to resume previous session on a new
+    SSL connection.
+    """
+
+    def __init__(self, session_id):
+        self._session_id = session_id
+
+    def get_session_id(self):
+        return self._session_id

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_url.py
----------------------------------------------------------------------
diff --git a/python/proton/_url.py b/python/proton/_url.py
new file mode 100644
index 0000000..b4a9a6a
--- /dev/null
+++ b/python/proton/_url.py
@@ -0,0 +1,161 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import absolute_import
+
+import socket
+
+from cproton import pn_url, pn_url_free, pn_url_parse, pn_url_str, pn_url_get_port, pn_url_get_scheme, \
+    pn_url_get_host, pn_url_get_username, pn_url_get_password, pn_url_get_path, pn_url_set_scheme, pn_url_set_host, \
+    pn_url_set_username, pn_url_set_password, pn_url_set_port, pn_url_set_path
+
+from ._common import unicode2utf8
+
+
+class Url(object):
+    """
+    Simple URL parser/constructor, handles URLs of the form:
+
+    <scheme>://<user>:<password>@<host>:<port>/<path>
+
+    All components can be None if not specified in the URL string.
+
+    The port can be specified as a service name, e.g. 'amqp' in the
+    URL string but Url.port always gives the integer value.
+
+    Warning: The placement of user and password in URLs is not
+    recommended.  It can result in credentials leaking out in program
+    logs.  Use connection configuration attributes instead.
+
+    @ivar scheme: Url scheme e.g. 'amqp' or 'amqps'
+    @ivar user: Username
+    @ivar password: Password
+    @ivar host: Host name, ipv6 literal or ipv4 dotted quad.
+    @ivar port: Integer port.
+    @ivar host_port: Returns host:port
+    """
+
+    AMQPS = "amqps"
+    AMQP = "amqp"
+
+    class Port(int):
+        """An integer port number that can be constructed from a service name string"""
+
+        def __new__(cls, value):
+            """@param value: integer port number or string service name."""
+            port = super(Url.Port, cls).__new__(cls, cls._port_int(value))
+            setattr(port, 'name', str(value))
+            return port
+
+        def __eq__(self, x):
+            return str(self) == x or int(self) == x
+
+        def __ne__(self, x):
+            return not self == x
+
+        def __str__(self):
+            return str(self.name)
+
+        @staticmethod
+        def _port_int(value):
+            """Convert service, an integer or a service name, into an integer port number."""
+            try:
+                return int(value)
+            except ValueError:
+                try:
+                    return socket.getservbyname(value)
+                except socket.error:
+                    # Not every system has amqp/amqps defined as a service
+                    if value == Url.AMQPS:
+                        return 5671
+                    elif value == Url.AMQP:
+                        return 5672
+                    else:
+                        raise ValueError("Not a valid port number or service name: '%s'" % value)
+
+    def __init__(self, url=None, defaults=True, **kwargs):
+        """
+        @param url: URL string to parse.
+        @param defaults: If true, fill in missing default values in the URL.
+            If false, you can fill them in later by calling self.defaults()
+        @param kwargs: scheme, user, password, host, port, path.
+          If specified, replaces corresponding part in url string.
+        """
+        if url:
+            self._url = pn_url_parse(unicode2utf8(str(url)))
+            if not self._url: raise ValueError("Invalid URL '%s'" % url)
+        else:
+            self._url = pn_url()
+        for k in kwargs:  # Let kwargs override values parsed from url
+            getattr(self, k)  # Check for invalid kwargs
+            setattr(self, k, kwargs[k])
+        if defaults: self.defaults()
+
+    class PartDescriptor(object):
+        def __init__(self, part):
+            self.getter = globals()["pn_url_get_%s" % part]
+            self.setter = globals()["pn_url_set_%s" % part]
+
+        def __get__(self, obj, type=None): return self.getter(obj._url)
+
+        def __set__(self, obj, value): return self.setter(obj._url, str(value))
+
+    scheme = PartDescriptor('scheme')
+    username = PartDescriptor('username')
+    password = PartDescriptor('password')
+    host = PartDescriptor('host')
+    path = PartDescriptor('path')
+
+    def _get_port(self):
+        portstr = pn_url_get_port(self._url)
+        return portstr and Url.Port(portstr)
+
+    def _set_port(self, value):
+        if value is None:
+            pn_url_set_port(self._url, None)
+        else:
+            pn_url_set_port(self._url, str(Url.Port(value)))
+
+    port = property(_get_port, _set_port)
+
+    def __str__(self):
+        return pn_url_str(self._url)
+
+    def __repr__(self):
+        return "Url(%s://%s/%s)" % (self.scheme, self.host, self.path)
+
+    def __eq__(self, x):
+        return str(self) == str(x)
+
+    def __ne__(self, x):
+        return not self == x
+
+    def __del__(self):
+        pn_url_free(self._url)
+        del self._url
+
+    def defaults(self):
+        """
+        Fill in missing values (scheme, host or port) with defaults
+        @return: self
+        """
+        self.scheme = self.scheme or self.AMQP
+        self.host = self.host or '0.0.0.0'
+        self.port = self.port or self.Port(self.scheme)
+        return self

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/_wrapper.py
----------------------------------------------------------------------
diff --git a/python/proton/_wrapper.py b/python/proton/_wrapper.py
new file mode 100644
index 0000000..805ecb1
--- /dev/null
+++ b/python/proton/_wrapper.py
@@ -0,0 +1,120 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from cproton import pn_incref, pn_decref, \
+    pn_py2void, pn_void2py, \
+    pn_record_get, pn_record_def, pn_record_set, \
+    PN_PYREF
+
+
+class EmptyAttrs:
+
+    def __contains__(self, name):
+        return False
+
+    def __getitem__(self, name):
+        raise KeyError(name)
+
+    def __setitem__(self, name, value):
+        raise TypeError("does not support item assignment")
+
+
+EMPTY_ATTRS = EmptyAttrs()
+
+
+class Wrapper(object):
+
+    def __init__(self, impl_or_constructor, get_context=None):
+        init = False
+        if callable(impl_or_constructor):
+            # we are constructing a new object
+            impl = impl_or_constructor()
+            if impl is None:
+                self.__dict__["_impl"] = impl
+                self.__dict__["_attrs"] = EMPTY_ATTRS
+                self.__dict__["_record"] = None
+                from proton import ProtonException
+                raise ProtonException(
+                    "Wrapper failed to create wrapped object. Check for file descriptor or memory exhaustion.")
+            init = True
+        else:
+            # we are wrapping an existing object
+            impl = impl_or_constructor
+            pn_incref(impl)
+
+        if get_context:
+            record = get_context(impl)
+            attrs = pn_void2py(pn_record_get(record, PYCTX))
+            if attrs is None:
+                attrs = {}
+                pn_record_def(record, PYCTX, PN_PYREF)
+                pn_record_set(record, PYCTX, pn_py2void(attrs))
+                init = True
+        else:
+            attrs = EMPTY_ATTRS
+            init = False
+            record = None
+        self.__dict__["_impl"] = impl
+        self.__dict__["_attrs"] = attrs
+        self.__dict__["_record"] = record
+        if init: self._init()
+
+    def __getattr__(self, name):
+        attrs = self.__dict__["_attrs"]
+        if name in attrs:
+            return attrs[name]
+        else:
+            raise AttributeError(name + " not in _attrs")
+
+    def __setattr__(self, name, value):
+        if hasattr(self.__class__, name):
+            object.__setattr__(self, name, value)
+        else:
+            attrs = self.__dict__["_attrs"]
+            attrs[name] = value
+
+    def __delattr__(self, name):
+        attrs = self.__dict__["_attrs"]
+        if attrs:
+            del attrs[name]
+
+    def __hash__(self):
+        return hash(addressof(self._impl))
+
+    def __eq__(self, other):
+        if isinstance(other, Wrapper):
+            return addressof(self._impl) == addressof(other._impl)
+        return False
+
+    def __ne__(self, other):
+        if isinstance(other, Wrapper):
+            return addressof(self._impl) != addressof(other._impl)
+        return True
+
+    def __del__(self):
+        pn_decref(self._impl)
+
+    def __repr__(self):
+        return '<%s.%s 0x%x ~ 0x%x>' % (self.__class__.__module__,
+                                        self.__class__.__name__,
+                                        id(self), addressof(self._impl))
+
+
+PYCTX = int(pn_py2void(Wrapper))
+addressof = int

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/handlers.py
----------------------------------------------------------------------
diff --git a/python/proton/handlers.py b/python/proton/handlers.py
index 76c9e51..1e61f44 100644
--- a/python/proton/handlers.py
+++ b/python/proton/handlers.py
@@ -16,28 +16,35 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-import heapq, logging, os, re, socket, time, types, weakref
 
-from proton import dispatch, generate_uuid, PN_ACCEPTED, SASL, symbol, ulong, Url
-from proton import Collector, Connection, Delivery, Described, Endpoint, Event, Link, Terminus, Timeout
-from proton import Message, Handler, ProtonException, Transport, TransportException, ConnectionException
+from __future__ import absolute_import
+
+import logging
+import time
+import weakref
 from select import select
 
+from proton import Delivery, Endpoint
+from proton import Message, Handler, ProtonException
+from ._events import dispatch
+
 log = logging.getLogger("proton")
 
+
 class OutgoingMessageHandler(Handler):
     """
     A utility for simpler and more intuitive handling of delivery
     events related to outgoing i.e. sent messages.
     """
+
     def __init__(self, auto_settle=True, delegate=None):
         self.auto_settle = auto_settle
         self.delegate = delegate
 
     def on_link_flow(self, event):
         if event.link.is_sender and event.link.credit \
-           and event.link.state & Endpoint.LOCAL_ACTIVE \
-           and event.link.state & Endpoint.REMOTE_ACTIVE :
+                and event.link.state & Endpoint.LOCAL_ACTIVE \
+                and event.link.state & Endpoint.REMOTE_ACTIVE:
             self.on_sendable(event)
 
     def on_delivery(self, event):
@@ -94,23 +101,27 @@ class OutgoingMessageHandler(Handler):
         if self.delegate != None:
             dispatch(self.delegate, 'on_settled', event)
 
+
 def recv_msg(delivery):
     msg = Message()
     msg.decode(delivery.link.recv(delivery.pending))
     delivery.link.advance()
     return msg
 
+
 class Reject(ProtonException):
-  """
-  An exception that indicate a message should be rejected
-  """
-  pass
+    """
+    An exception that indicate a message should be rejected
+    """
+    pass
+
 
 class Release(ProtonException):
-  """
-  An exception that indicate a message should be rejected
-  """
-  pass
+    """
+    An exception that indicate a message should be rejected
+    """
+    pass
+
 
 class Acking(object):
     def accept(self, delivery):
@@ -146,6 +157,7 @@ class Acking(object):
             delivery.update(state)
         delivery.settle()
 
+
 class IncomingMessageHandler(Handler, Acking):
     """
     A utility for simpler and more intuitive handling of delivery
@@ -202,6 +214,7 @@ class IncomingMessageHandler(Handler, Acking):
         if self.delegate != None:
             dispatch(self.delegate, 'on_aborted', event)
 
+
 class EndpointStateHandler(Handler):
     """
     A utility that exposes 'endpoint' events i.e. the open/close for
@@ -272,7 +285,7 @@ class EndpointStateHandler(Handler):
                 return
             self.on_connection_error(event)
         elif self.is_local_closed(event.connection):
-           self.on_connection_closed(event)
+            self.on_connection_closed(event)
         else:
             self.on_connection_closing(event)
         event.connection.close()
@@ -391,12 +404,14 @@ class EndpointStateHandler(Handler):
         if self.delegate != None and event.connection and self.is_local_open(event.connection):
             dispatch(self.delegate, 'on_disconnected', event)
 
+
 class MessagingHandler(Handler, Acking):
     """
     A general purpose handler that makes the proton-c events somewhat
     simpler to deal with and/or avoids repetitive tasks for common use
     cases.
     """
+
     def __init__(self, prefetch=10, auto_accept=True, auto_settle=True, peer_close_is_error=False):
         self.handlers = []
         if prefetch:
@@ -414,7 +429,8 @@ class MessagingHandler(Handler, Acking):
         """
         if event.transport.condition:
             if event.transport.condition.info:
-                log.error("%s: %s: %s" % (event.transport.condition.name, event.transport.condition.description, event.transport.condition.info))
+                log.error("%s: %s: %s" % (
+                event.transport.condition.name, event.transport.condition.description, event.transport.condition.info))
             else:
                 log.error("%s: %s" % (event.transport.condition.name, event.transport.condition.description))
             if event.transport.condition.name in self.fatal_conditions:
@@ -455,36 +471,43 @@ class MessagingHandler(Handler, Acking):
         Called when the event loop starts. (Just an alias for on_reactor_init)
         """
         pass
+
     def on_connection_closed(self, event):
         """
         Called when the connection is closed.
         """
         pass
+
     def on_session_closed(self, event):
         """
         Called when the session is closed.
         """
         pass
+
     def on_link_closed(self, event):
         """
         Called when the link is closed.
         """
         pass
+
     def on_connection_closing(self, event):
         """
         Called when the peer initiates the closing of the connection.
         """
         pass
+
     def on_session_closing(self, event):
         """
         Called when the peer initiates the closing of the session.
         """
         pass
+
     def on_link_closing(self, event):
         """
         Called when the peer initiates the closing of the link.
         """
         pass
+
     def on_disconnected(self, event):
         """
         Called when the socket is disconnected.
@@ -525,6 +548,7 @@ class MessagingHandler(Handler, Acking):
         retransmitted.
         """
         pass
+
     def on_message(self, event):
         """
         Called when a message is received. The message itself can be
@@ -535,11 +559,13 @@ class MessagingHandler(Handler, Acking):
         """
         pass
 
+
 class TransactionHandler(object):
     """
     The interface for transaction handlers, i.e. objects that want to
     be notified of state changes related to a transaction.
     """
+
     def on_transaction_declared(self, event):
         pass
 
@@ -555,6 +581,7 @@ class TransactionHandler(object):
     def on_transaction_commit_failed(self, event):
         pass
 
+
 class TransactionalClientHandler(MessagingHandler, TransactionHandler):
     """
     An extension to the MessagingHandler for applications using
@@ -570,24 +597,29 @@ class TransactionalClientHandler(MessagingHandler, TransactionHandler):
         else:
             super(TransactionalClientHandler, self).accept(delivery)
 
-from proton import WrappedHandler
+
+from ._events import WrappedHandler
 from cproton import pn_flowcontroller, pn_handshaker, pn_iohandler
 
+
 class CFlowController(WrappedHandler):
 
     def __init__(self, window=1024):
         WrappedHandler.__init__(self, lambda: pn_flowcontroller(window))
 
+
 class CHandshaker(WrappedHandler):
 
     def __init__(self):
         WrappedHandler.__init__(self, pn_handshaker)
 
+
 class IOHandler(WrappedHandler):
 
     def __init__(self):
         WrappedHandler.__init__(self, pn_iohandler)
 
+
 class PythonIO:
 
     def __init__(self):

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/reactor.py
----------------------------------------------------------------------
diff --git a/python/proton/reactor.py b/python/proton/reactor.py
index d5d5183..ccdbf94 100644
--- a/python/proton/reactor.py
+++ b/python/proton/reactor.py
@@ -17,22 +17,35 @@ from __future__ import absolute_import
 # specific language governing permissions and limitations
 # under the License.
 #
-import logging, os, socket, time, types
-from heapq import heappush, heappop, nsmallest
+from __future__ import absolute_import
 
+import os
+import logging
 import traceback
 
-from proton import Collector, Connection, ConnectionException, Delivery, Described, dispatch
-from proton import Endpoint, Event, EventBase, EventType, generate_uuid, Handler, Link, Message
-from proton import ProtonException, PN_ACCEPTED, PN_PYREF, SASL, Session, SSL, SSLDomain, SSLUnavailable, symbol
-from proton import Terminus, Timeout, Transport, TransportException, ulong, Url
-from select import select
+from proton import Connection, Delivery, Described
+from proton import Endpoint, EventType, Handler, Link, Message
+from proton import Session, SSL, SSLDomain, SSLUnavailable, symbol
+from proton import Terminus, Transport, ulong, Url
 from proton.handlers import OutgoingMessageHandler
-from proton import unicode2utf8, utf82unicode
 
-from proton import WrappedHandler, _chandler, secs2millis, millis2secs, timeout2millis, millis2timeout, Selectable
-from .wrapper import Wrapper, PYCTX
-from cproton import *
+from proton import generate_uuid
+
+from ._common import isstring, secs2millis, millis2secs, unicode2utf8, utf82unicode
+
+from ._events import EventBase
+from ._reactor_impl import Selectable, WrappedHandler, _chandler
+from ._wrapper import Wrapper, PYCTX
+
+from cproton import PN_MILLIS_MAX, PN_PYREF, PN_ACCEPTED, \
+    pn_reactor_stop, pn_selectable_attachments, pn_reactor_quiesced, pn_reactor_acceptor, \
+    pn_record_set_handler, pn_collector_put, pn_reactor_get_timeout, pn_task_cancel, pn_acceptor_set_ssl_domain, \
+    pn_record_get, pn_reactor_selectable, pn_task_attachments, pn_reactor_schedule, pn_acceptor_close, pn_py2void, \
+    pn_reactor_error, pn_reactor_attachments, pn_reactor_get_global_handler, pn_reactor_process, pn_reactor, \
+    pn_reactor_set_handler, pn_reactor_set_global_handler, pn_reactor_yield, pn_error_text, pn_reactor_connection, \
+    pn_cast_pn_reactor, pn_reactor_get_connection_address, pn_reactor_update, pn_reactor_collector, pn_void2py, \
+    pn_reactor_start, pn_reactor_set_connection_host, pn_cast_pn_task, pn_decref, pn_reactor_set_timeout, \
+    pn_reactor_mark, pn_reactor_get_handler, pn_reactor_wakeup
 
 from . import _compat
 
@@ -40,6 +53,17 @@ from ._compat import queue
 
 log = logging.getLogger("proton")
 
+
+def _timeout2millis(secs):
+    if secs is None: return PN_MILLIS_MAX
+    return secs2millis(secs)
+
+
+def _millis2timeout(millis):
+    if millis == PN_MILLIS_MAX: return None
+    return millis2secs(millis)
+
+
 class Task(Wrapper):
 
     @staticmethod
@@ -58,6 +82,7 @@ class Task(Wrapper):
     def cancel(self):
         pn_task_cancel(self._impl)
 
+
 class Acceptor(Wrapper):
 
     def __init__(self, impl):
@@ -69,6 +94,7 @@ class Acceptor(Wrapper):
     def close(self):
         pn_acceptor_close(self._impl)
 
+
 class Reactor(Wrapper):
 
     @staticmethod
@@ -95,11 +121,12 @@ class Reactor(Wrapper):
     # error will always be generated from a callback from this reactor.
     # Needed to prevent reference cycles and be compatible with wrappers.
     class ErrorDelegate(object):
-      def __init__(self, reactor):
-        self.reactor_impl = reactor._impl
-      def on_error(self, info):
-        ractor = Reactor.wrap(self.reactor_impl)
-        ractor.on_error(info)
+        def __init__(self, reactor):
+            self.reactor_impl = reactor._impl
+
+        def on_error(self, info):
+            ractor = Reactor.wrap(self.reactor_impl)
+            ractor.on_error(info)
 
     def on_error_delegate(self):
         return Reactor.ErrorDelegate(self).on_error
@@ -119,10 +146,10 @@ class Reactor(Wrapper):
     global_handler = property(_get_global, _set_global)
 
     def _get_timeout(self):
-        return millis2timeout(pn_reactor_get_timeout(self._impl))
+        return _millis2timeout(pn_reactor_get_timeout(self._impl))
 
     def _set_timeout(self, secs):
-        return pn_reactor_set_timeout(self._impl, timeout2millis(secs))
+        return pn_reactor_set_timeout(self._impl, _timeout2millis(secs))
 
     timeout = property(_get_timeout, _set_timeout)
 
@@ -244,7 +271,9 @@ class Reactor(Wrapper):
     def push_event(self, obj, etype):
         pn_collector_put(pn_reactor_collector(self._impl), PN_PYREF, pn_py2void(obj), etype.number)
 
-from proton import wrappers as _wrappers
+
+from ._events import wrappers as _wrappers
+
 _wrappers["pn_reactor"] = lambda x: Reactor.wrap(pn_cast_pn_reactor(x))
 _wrappers["pn_task"] = lambda x: Task.wrap(pn_cast_pn_task(x))
 
@@ -258,6 +287,7 @@ class EventInjector(object):
     it. The close() method should be called when it is no longer
     needed, to allow the event loop to end if needed.
     """
+
     def __init__(self):
         self.queue = queue.Queue()
         self.pipe = os.pipe()
@@ -305,6 +335,7 @@ class ApplicationEvent(EventBase):
     Application defined event, which can optionally be associated with
     an engine object and or an arbitrary subject
     """
+
     def __init__(self, typename, connection=None, session=None, link=None, delivery=None, subject=None):
         super(ApplicationEvent, self).__init__(PN_PYREF, self, EventType(typename))
         self.connection = connection
@@ -323,10 +354,12 @@ class ApplicationEvent(EventBase):
         objects = [self.connection, self.session, self.link, self.delivery, self.subject]
         return "%s(%s)" % (self.type, ", ".join([str(o) for o in objects if o is not None]))
 
+
 class Transaction(object):
     """
     Class to track state of an AMQP 1.0 transaction.
     """
+
     def __init__(self, txn_ctrl, handler, settle_before_discharge=False):
         self.txn_ctrl = txn_ctrl
         self.handler = handler
@@ -397,7 +430,7 @@ class Transaction(object):
             if event.delivery.remote_state == Delivery.REJECTED:
                 if not self.failed:
                     self.handler.on_transaction_commit_failed(event)
-                    self._release_pending() # make this optional?
+                    self._release_pending()  # make this optional?
             else:
                 if self.failed:
                     self.handler.on_transaction_aborted(event)
@@ -406,16 +439,19 @@ class Transaction(object):
                     self.handler.on_transaction_committed(event)
             self._clear_pending()
 
+
 class LinkOption(object):
     """
     Abstract interface for link configuration options
     """
+
     def apply(self, link):
         """
         Subclasses will implement any configuration logic in this
         method
         """
         pass
+
     def test(self, link):
         """
         Subclasses can override this to selectively apply an option
@@ -423,23 +459,30 @@ class LinkOption(object):
         """
         return True
 
+
 class AtMostOnce(LinkOption):
     def apply(self, link):
         link.snd_settle_mode = Link.SND_SETTLED
 
+
 class AtLeastOnce(LinkOption):
     def apply(self, link):
         link.snd_settle_mode = Link.SND_UNSETTLED
         link.rcv_settle_mode = Link.RCV_FIRST
 
+
 class SenderOption(LinkOption):
     def apply(self, sender): pass
+
     def test(self, link): return link.is_sender
 
+
 class ReceiverOption(LinkOption):
     def apply(self, receiver): pass
+
     def test(self, link): return link.is_receiver
 
+
 class DynamicNodeProperties(LinkOption):
     def __init__(self, props={}):
         self.properties = {}
@@ -455,6 +498,7 @@ class DynamicNodeProperties(LinkOption):
         else:
             link.target.properties.put_dict(self.properties)
 
+
 class Filter(ReceiverOption):
     def __init__(self, filter_set={}):
         self.filter_set = filter_set
@@ -462,26 +506,32 @@ class Filter(ReceiverOption):
     def apply(self, receiver):
         receiver.source.filter.put_dict(self.filter_set)
 
+
 class Selector(Filter):
     """
     Configures a link with a message selector filter
     """
+
     def __init__(self, value, name='selector'):
         super(Selector, self).__init__({symbol(name): Described(symbol('apache.org:selector-filter:string'), value)})
 
+
 class DurableSubscription(ReceiverOption):
     def apply(self, receiver):
         receiver.source.durability = Terminus.DELIVERIES
         receiver.source.expiry_policy = Terminus.EXPIRE_NEVER
 
+
 class Move(ReceiverOption):
     def apply(self, receiver):
         receiver.source.distribution_mode = Terminus.DIST_MODE_MOVE
 
+
 class Copy(ReceiverOption):
     def apply(self, receiver):
         receiver.source.distribution_mode = Terminus.DIST_MODE_COPY
 
+
 def _apply_link_options(options, link):
     if options:
         if isinstance(options, list):
@@ -490,6 +540,7 @@ def _apply_link_options(options, link):
         else:
             if options.test(link): options.apply(link)
 
+
 def _create_session(connection, handler=None):
     session = connection.session()
     session.open()
@@ -502,6 +553,7 @@ def _get_attr(target, name):
     else:
         return None
 
+
 class SessionPerConnection(object):
     def __init__(self):
         self._default_session = None
@@ -511,11 +563,13 @@ class SessionPerConnection(object):
             self._default_session = _create_session(connection)
         return self._default_session
 
+
 class GlobalOverrides(object):
     """
     Internal handler that triggers the necessary socket connect for an
     opened connection.
     """
+
     def __init__(self, base):
         self.base = base
 
@@ -527,11 +581,13 @@ class GlobalOverrides(object):
         conn = event.connection
         return conn and hasattr(conn, '_overrides') and event.dispatch(conn._overrides)
 
+
 class Connector(Handler):
     """
     Internal handler that triggers the necessary socket connect for an
     opened connection.
     """
+
     def __init__(self, connection):
         self.connection = connection
         self.address = None
@@ -548,7 +604,7 @@ class Connector(Handler):
         self.max_frame_size = None
 
     def _connect(self, connection, reactor):
-        assert(reactor is not None)
+        assert (reactor is not None)
         url = self.address.next()
         reactor.set_connection_host(connection, url.host, str(url.port))
         # if virtual-host not set, use host from address as default
@@ -615,11 +671,13 @@ class Connector(Handler):
     def on_timer_task(self, event):
         self._connect(self.connection, event.reactor)
 
+
 class Backoff(object):
     """
     A reconnect strategy involving an increasing delay between
     retries, up to a maximum or 10 seconds.
     """
+
     def __init__(self):
         self.delay = 0
 
@@ -631,9 +689,10 @@ class Backoff(object):
         if current == 0:
             self.delay = 0.1
         else:
-            self.delay = min(10, 2*current)
+            self.delay = min(10, 2 * current)
         return current
 
+
 class Urls(object):
     def __init__(self, values):
         self.values = [Url(v) for v in values]
@@ -649,6 +708,7 @@ class Urls(object):
             self.i = iter(self.values)
             return next(self.i)
 
+
 class SSLConfig(object):
     def __init__(self):
         self.client = SSLDomain(SSLDomain.MODE_CLIENT)
@@ -670,6 +730,7 @@ class Container(Reactor):
        an extension to the Reactor class that adds convenience methods
        for creating connections and sender- or receiver- links.
     """
+
     def __init__(self, *handlers, **kwargs):
         super(Container, self).__init__(*handlers, **kwargs)
         if "impl" not in kwargs:
@@ -687,7 +748,8 @@ class Container(Reactor):
             self.password = None
             Wrapper.__setattr__(self, 'subclass', self.__class__)
 
-    def connect(self, url=None, urls=None, address=None, handler=None, reconnect=None, heartbeat=None, ssl_domain=None, **kwargs):
+    def connect(self, url=None, urls=None, address=None, handler=None, reconnect=None, heartbeat=None, ssl_domain=None,
+                **kwargs):
         """
         Initiates the establishment of an AMQP connection. Returns an
         instance of proton.Connection.
@@ -748,10 +810,14 @@ class Container(Reactor):
         connector.max_frame_size = kwargs.get('max_frame_size')
 
         conn._overrides = connector
-        if url: connector.address = Urls([url])
-        elif urls: connector.address = Urls(urls)
-        elif address: connector.address = address
-        else: raise ValueError("One of url, urls or address required")
+        if url:
+            connector.address = Urls([url])
+        elif urls:
+            connector.address = Urls(urls)
+        elif address:
+            connector.address = address
+        else:
+            raise ValueError("One of url, urls or address required")
         if heartbeat:
             connector.heartbeat = heartbeat
         if reconnect:
@@ -761,15 +827,19 @@ class Container(Reactor):
         # use container's default client domain if none specified.  This is
         # only necessary of the URL specifies the "amqps:" scheme
         connector.ssl_domain = ssl_domain or (self.ssl and self.ssl.client)
-        conn._session_policy = SessionPerConnection() #todo: make configurable
+        conn._session_policy = SessionPerConnection()  # todo: make configurable
         conn.open()
         return conn
 
     def _get_id(self, container, remote, local):
-        if local and remote: "%s-%s-%s" % (container, remote, local)
-        elif local: return "%s-%s" % (container, local)
-        elif remote: return "%s-%s" % (container, remote)
-        else: return "%s-%s" % (container, str(generate_uuid()))
+        if local and remote:
+            "%s-%s-%s" % (container, remote, local)
+        elif local:
+            return "%s-%s" % (container, local)
+        elif remote:
+            return "%s-%s" % (container, remote)
+        else:
+            return "%s-%s" % (container, str(generate_uuid()))
 
     def _get_session(self, context):
         if isinstance(context, Url):
@@ -806,7 +876,7 @@ class Container(Reactor):
         Various LinkOptions can be specified to further control the
         attachment.
         """
-        if isinstance(context, _compat.string_types):
+        if isstring(context):
             context = Url(context)
         if isinstance(context, Url) and not target:
             target = context.path
@@ -847,7 +917,7 @@ class Container(Reactor):
         Various LinkOptions can be specified to further control the
         attachment.
         """
-        if isinstance(context, _compat.string_types):
+        if isstring(context):
             context = Url(context)
         if isinstance(context, Url) and not source:
             source = context.path

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/utils.py
----------------------------------------------------------------------
diff --git a/python/proton/utils.py b/python/proton/utils.py
index 1d052d0..c6f8cb4 100644
--- a/python/proton/utils.py
+++ b/python/proton/utils.py
@@ -38,7 +38,8 @@ class BlockingLink(object):
             self.connection.wait(lambda: self.link.state & Endpoint.REMOTE_CLOSED,
                                  timeout=timeout,
                                  msg="Opening link %s" % self.link.name)
-        except Timeout as e: pass
+        except Timeout as e:
+            pass
         self._checkClosed()
 
     def _checkClosed(self):
@@ -53,31 +54,37 @@ class BlockingLink(object):
                              msg="Closing link %s" % self.link.name)
 
     # Access to other link attributes.
-    def __getattr__(self, name): return getattr(self.link, name)
+    def __getattr__(self, name):
+        return getattr(self.link, name)
+
 
 class SendException(ProtonException):
     """
     Exception used to indicate an exceptional state/condition on a send request
     """
+
     def __init__(self, state):
         self.state = state
 
+
 def _is_settled(delivery):
     return delivery.settled or delivery.link.snd_settle_mode == Link.SND_SETTLED
 
+
 class BlockingSender(BlockingLink):
     def __init__(self, connection, sender):
         super(BlockingSender, self).__init__(connection, sender)
         if self.link.target and self.link.target.address and self.link.target.address != self.link.remote_target.address:
-            #this may be followed by a detach, which may contain an error condition, so wait a little...
+            # this may be followed by a detach, which may contain an error condition, so wait a little...
             self._waitForClose()
-            #...but close ourselves if peer does not
+            # ...but close ourselves if peer does not
             self.link.close()
             raise LinkException("Failed to open sender %s, target does not match" % self.link.name)
 
     def send(self, msg, timeout=False, error_states=None):
         delivery = self.link.send(msg)
-        self.connection.wait(lambda: _is_settled(delivery), msg="Sending on sender %s" % self.link.name, timeout=timeout)
+        self.connection.wait(lambda: _is_settled(delivery), msg="Sending on sender %s" % self.link.name,
+                             timeout=timeout)
         if delivery.link.snd_settle_mode != Link.SND_SETTLED:
             delivery.settle()
         bad = error_states
@@ -87,6 +94,7 @@ class BlockingSender(BlockingLink):
             raise SendException(delivery.remote_state)
         return delivery
 
+
 class Fetcher(MessagingHandler):
     def __init__(self, connection, prefetch):
         super(Fetcher, self).__init__(prefetch=prefetch, auto_accept=False)
@@ -96,7 +104,7 @@ class Fetcher(MessagingHandler):
 
     def on_message(self, event):
         self.incoming.append((event.message, event.delivery))
-        self.connection.container.yield_() # Wake up the wait() loop to handle the message.
+        self.connection.container.yield_()  # Wake up the wait() loop to handle the message.
 
     def on_link_error(self, event):
         if event.link.state & Endpoint.LOCAL_ACTIVE:
@@ -129,9 +137,9 @@ class BlockingReceiver(BlockingLink):
     def __init__(self, connection, receiver, fetcher, credit=1):
         super(BlockingReceiver, self).__init__(connection, receiver)
         if self.link.source and self.link.source.address and self.link.source.address != self.link.remote_source.address:
-            #this may be followed by a detach, which may contain an error condition, so wait a little...
+            # this may be followed by a detach, which may contain an error condition, so wait a little...
             self._waitForClose()
-            #...but close ourselves if peer does not
+            # ...but close ourselves if peer does not
             self.link.close()
             raise LinkException("Failed to open receiver %s, source does not match" % self.link.name)
         if credit: receiver.flow(credit)
@@ -151,7 +159,8 @@ class BlockingReceiver(BlockingLink):
             raise Exception("Can't call receive on this receiver as a handler was provided")
         if not self.link.credit:
             self.link.flow(1)
-        self.connection.wait(lambda: self.fetcher.has_message, msg="Receiving on receiver %s" % self.link.name, timeout=timeout)
+        self.connection.wait(lambda: self.fetcher.has_message, msg="Receiving on receiver %s" % self.link.name,
+                             timeout=timeout)
         return self.fetcher.pop()
 
     def accept(self):
@@ -210,6 +219,7 @@ class BlockingConnection(Handler):
     object operations are enclosed in a try block and that close() is
     always executed on exit.
     """
+
     def __init__(self, url, timeout=None, container=None, ssl_domain=None, heartbeat=None, **kwargs):
         self.disconnected = False
         self.timeout = timeout or 60
@@ -221,7 +231,8 @@ class BlockingConnection(Handler):
         self.closing = False
         failed = True
         try:
-            self.conn = self.container.connect(url=self.url, handler=self, ssl_domain=ssl_domain, reconnect=False, heartbeat=heartbeat, **kwargs)
+            self.conn = self.container.connect(url=self.url, handler=self, ssl_domain=ssl_domain, reconnect=False,
+                                               heartbeat=heartbeat, **kwargs)
             self.wait(lambda: not (self.conn.state & Endpoint.REMOTE_UNINIT),
                       msg="Opening connection")
             failed = False
@@ -230,7 +241,8 @@ class BlockingConnection(Handler):
                 self.close()
 
     def create_sender(self, address, handler=None, name=None, options=None):
-        return BlockingSender(self, self.container.create_sender(self.conn, address, name=name, handler=handler, options=options))
+        return BlockingSender(self, self.container.create_sender(self.conn, address, name=name, handler=handler,
+                                                                 options=options))
 
     def create_receiver(self, address, credit=None, dynamic=False, handler=None, name=None, options=None):
         prefetch = credit
@@ -241,7 +253,9 @@ class BlockingConnection(Handler):
         else:
             fetcher = Fetcher(self, credit)
         return BlockingReceiver(
-            self, self.container.create_receiver(self.conn, address, name=name, dynamic=dynamic, handler=handler or fetcher, options=options), fetcher, credit=prefetch)
+            self,
+            self.container.create_receiver(self.conn, address, name=name, dynamic=dynamic, handler=handler or fetcher,
+                                           options=options), fetcher, credit=prefetch)
 
     def close(self):
         # TODO: provide stronger interrupt protection on cleanup.  See PEP 419
@@ -259,8 +273,8 @@ class BlockingConnection(Handler):
             # Nothing left to block on.  Allow reactor to clean up.
             self.run()
             self.conn = None
-            self.container.global_handler = None # break circular ref: container to cadapter.on_error
-            pn_collector_release(pn_reactor_collector(self.container._impl)) # straggling event may keep reactor alive
+            self.container.global_handler = None  # break circular ref: container to cadapter.on_error
+            pn_collector_release(pn_reactor_collector(self.container._impl))  # straggling event may keep reactor alive
             self.container = None
 
     def _is_closed(self):
@@ -294,7 +308,7 @@ class BlockingConnection(Handler):
                 self.container.timeout = container_timeout
         if self.disconnected or self._is_closed():
             self.container.stop()
-            self.conn.handler = None # break cyclical reference
+            self.conn.handler = None  # break cyclical reference
         if self.disconnected and not self._is_closed():
             raise ConnectionException(
                 "Connection %s disconnected: %s" % (self.url, self.disconnected))
@@ -320,6 +334,7 @@ class BlockingConnection(Handler):
     def on_transport_closed(self, event):
         self.disconnected = event.transport.condition or "unknown"
 
+
 class AtomicCount(object):
     def __init__(self, start=0, step=1):
         """Thread-safe atomic counter. Start at start, increment by step."""
@@ -334,6 +349,7 @@ class AtomicCount(object):
         self.lock.release()
         return result
 
+
 class SyncRequestResponse(IncomingMessageHandler):
     """
     Implementation of the synchronous request-response (aka RPC) pattern.
@@ -374,12 +390,14 @@ class SyncRequestResponse(IncomingMessageHandler):
         request.reply_to = self.reply_to
         request.correlation_id = correlation_id = str(self.correlation_id.next())
         self.sender.send(request)
+
         def wakeup():
             return self.response and (self.response.correlation_id == correlation_id)
+
         self.connection.wait(wakeup, msg="Waiting for response")
         response = self.response
-        self.response = None    # Ready for next response.
-        self.receiver.flow(1)   # Set up credit for the next response.
+        self.response = None  # Ready for next response.
+        self.receiver.flow(1)  # Set up credit for the next response.
         return response
 
     @property
@@ -390,4 +408,4 @@ class SyncRequestResponse(IncomingMessageHandler):
     def on_message(self, event):
         """Called when we receive a message for our receiver."""
         self.response = event.message
-        self.connection.container.yield_() # Wake up the wait() loop to handle the message.
+        self.connection.container.yield_()  # Wake up the wait() loop to handle the message.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[5/6] qpid-proton git commit: PROTON-1850: Split up proton __init__.py into multiple files - Reformatted python source to (mostly) PEP-8 standards - Control what gets exported from __init__ by restricting what it imports - Move most of the reactor implem

Posted by as...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d28fecf5/python/proton/__init__.py
----------------------------------------------------------------------
diff --git a/python/proton/__init__.py b/python/proton/__init__.py
index 6ee0d68..be8e247 100644
--- a/python/proton/__init__.py
+++ b/python/proton/__init__.py
@@ -30,54 +30,84 @@ The proton APIs consist of the following classes:
 """
 from __future__ import absolute_import
 
-from cproton import *
-from .wrapper import Wrapper
-from . import _compat
-
 import logging
-import socket
-import sys
-import threading
-import uuid
-import weakref
-
-# This private NullHandler is required for Python 2.6,
-# when we no longer support 2.6 this replace NullHandler class definition and assignment with:
-#  handler = logging.NullHandler()
-class NullHandler(logging.Handler):
-  def handle(self, record):
-      pass
-
-  def emit(self, record):
-      pass
-
-  def createLock(self):
-      self.lock = None
-
-handler = NullHandler()
-
-log = logging.getLogger("proton")
-log.addHandler(handler)
 
-def generate_uuid():
-  return uuid.uuid4()
+from cproton import PN_VERSION_MAJOR, PN_VERSION_MINOR, PN_VERSION_POINT
 
-#
-# Hacks to provide Python2 <---> Python3 compatibility
-#
-# The results are
-# |       |long|unicode|
-# |python2|long|unicode|
-# |python3| int|    str|
-try:
-  long()
-except NameError:
-  long = int
-try:
-  unicode()
-except NameError:
-  unicode = str
+from ._condition import Condition
+from ._data import UNDESCRIBED, Array, Data, Described, char, symbol, timestamp, ubyte, ushort, uint, ulong, \
+    byte, short, int32, float32, decimal32, decimal64, decimal128
+from ._delivery import Delivery, Disposition
+from ._endpoints import Endpoint, Connection, Session, Link, Receiver, Sender, Terminus
+from ._events import Collector, Event, EventType, Handler
+from ._exceptions import ProtonException, MessageException, DataException, TransportException, \
+    SSLException, SSLUnavailable, ConnectionException, SessionException, LinkException, Timeout, Interrupt
+from ._message import Message, ABORTED, ACCEPTED, PENDING, REJECTED, RELEASED, MODIFIED, SETTLED
+from ._transport import Transport, SASL, SSL, SSLDomain, SSLSessionDetails
+from ._url import Url
 
+__all__ = [
+    "API_LANGUAGE",
+    "IMPLEMENTATION_LANGUAGE",
+    "ABORTED",
+    "ACCEPTED",
+    "PENDING",
+    "REJECTED",
+    "RELEASED",
+    "MODIFIED",
+    "SETTLED",
+    "UNDESCRIBED",
+    "Array",
+    "Collector",
+    "Condition",
+    "Connection",
+    "Data",
+    "DataException",
+    "Delivery",
+    "Disposition",
+    "Described",
+    "Endpoint",
+    "Event",
+    "EventType",
+    "Handler",
+    "Link",
+    "LinkException",
+    "Message",
+    "MessageException",
+    "ProtonException",
+    "VERSION_MAJOR",
+    "VERSION_MINOR",
+    "Receiver",
+    "SASL",
+    "Sender",
+    "Session",
+    "SessionException",
+    "SSL",
+    "SSLDomain",
+    "SSLSessionDetails",
+    "SSLUnavailable",
+    "SSLException",
+    "Terminus",
+    "Timeout",
+    "Interrupt",
+    "Transport",
+    "TransportException",
+    "Url",
+    "char",
+    "symbol",
+    "timestamp",
+    "ulong",
+    "byte",
+    "short",
+    "int32",
+    "ubyte",
+    "ushort",
+    "uint",
+    "float32",
+    "decimal32",
+    "decimal64",
+    "decimal128"
+]
 
 VERSION_MAJOR = PN_VERSION_MAJOR
 VERSION_MINOR = PN_VERSION_MINOR
@@ -86,3603 +116,27 @@ VERSION = (VERSION_MAJOR, VERSION_MINOR, VERSION_POINT)
 API_LANGUAGE = "C"
 IMPLEMENTATION_LANGUAGE = "C"
 
-class Constant(object):
-
-  def __init__(self, name):
-    self.name = name
-
-  def __repr__(self):
-    return self.name
-
-class ProtonException(Exception):
-  """
-  The root of the proton exception hierarchy. All proton exception
-  classes derive from this exception.
-  """
-  pass
-
-class Timeout(ProtonException):
-  """
-  A timeout exception indicates that a blocking operation has timed
-  out.
-  """
-  pass
-
-class Interrupt(ProtonException):
-  """
-  An interrupt exception indicates that a blocking operation was interrupted.
-  """
-  pass
-
-class MessageException(ProtonException):
-  """
-  The MessageException class is the root of the message exception
-  hierarchy. All exceptions generated by the Message class derive from
-  this exception.
-  """
-  pass
-
-EXCEPTIONS = {
-  PN_TIMEOUT: Timeout,
-  PN_INTR: Interrupt
-  }
-
-PENDING = Constant("PENDING")
-ACCEPTED = Constant("ACCEPTED")
-REJECTED = Constant("REJECTED")
-RELEASED = Constant("RELEASED")
-MODIFIED = Constant("MODIFIED")
-ABORTED = Constant("ABORTED")
-SETTLED = Constant("SETTLED")
-
-STATUSES = {
-  PN_STATUS_ABORTED: ABORTED,
-  PN_STATUS_ACCEPTED: ACCEPTED,
-  PN_STATUS_REJECTED: REJECTED,
-  PN_STATUS_RELEASED: RELEASED,
-  PN_STATUS_MODIFIED: MODIFIED,
-  PN_STATUS_PENDING: PENDING,
-  PN_STATUS_SETTLED: SETTLED,
-  PN_STATUS_UNKNOWN: None
-  }
-
-class Message(object):
-  """The L{Message} class is a mutable holder of message content.
-
-  @ivar instructions: delivery instructions for the message
-  @type instructions: dict
-  @ivar annotations: infrastructure defined message annotations
-  @type annotations: dict
-  @ivar properties: application defined message properties
-  @type properties: dict
-  @ivar body: message body
-  @type body: bytes | unicode | dict | list | int | long | float | UUID
-  """
-
-  DEFAULT_PRIORITY = PN_DEFAULT_PRIORITY
-
-  def __init__(self, body=None, **kwargs):
-    """
-    @param kwargs: Message property name/value pairs to initialise the Message
-    """
-    self._msg = pn_message()
-    self._id = Data(pn_message_id(self._msg))
-    self._correlation_id = Data(pn_message_correlation_id(self._msg))
-    self.instructions = None
-    self.annotations = None
-    self.properties = None
-    self.body = body
-    for k,v in _compat.iteritems(kwargs):
-      getattr(self, k)          # Raise exception if it's not a valid attribute.
-      setattr(self, k, v)
-
-  def __del__(self):
-    if hasattr(self, "_msg"):
-      pn_message_free(self._msg)
-      del self._msg
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, MessageException)
-      raise exc("[%s]: %s" % (err, pn_error_text(pn_message_error(self._msg))))
-    else:
-      return err
-
-  def _check_property_keys(self):
-      for k in self.properties.keys():
-        if isinstance(k, unicode):
-          # py2 unicode, py3 str (via hack definition)
-          continue
-        # If key is binary then change to string
-        elif isinstance(k, str):
-          # py2 str
-          self.properties[k.encode('utf-8')] = self.properties.pop(k)
-        else:
-          raise MessageException('Application property key is not string type: key=%s %s' % (str(k), type(k)))
-
-  def _pre_encode(self):
-    inst = Data(pn_message_instructions(self._msg))
-    ann = Data(pn_message_annotations(self._msg))
-    props = Data(pn_message_properties(self._msg))
-    body = Data(pn_message_body(self._msg))
-
-    inst.clear()
-    if self.instructions is not None:
-      inst.put_object(self.instructions)
-    ann.clear()
-    if self.annotations is not None:
-      ann.put_object(self.annotations)
-    props.clear()
-    if self.properties is not None:
-      self._check_property_keys()
-      props.put_object(self.properties)
-    body.clear()
-    if self.body is not None:
-      body.put_object(self.body)
-
-  def _post_decode(self):
-    inst = Data(pn_message_instructions(self._msg))
-    ann = Data(pn_message_annotations(self._msg))
-    props = Data(pn_message_properties(self._msg))
-    body = Data(pn_message_body(self._msg))
-
-    if inst.next():
-      self.instructions = inst.get_object()
-    else:
-      self.instructions = None
-    if ann.next():
-      self.annotations = ann.get_object()
-    else:
-      self.annotations = None
-    if props.next():
-      self.properties = props.get_object()
-    else:
-      self.properties = None
-    if body.next():
-      self.body = body.get_object()
-    else:
-      self.body = None
-
-  def clear(self):
-    """
-    Clears the contents of the L{Message}. All fields will be reset to
-    their default values.
-    """
-    pn_message_clear(self._msg)
-    self.instructions = None
-    self.annotations = None
-    self.properties = None
-    self.body = None
-
-  def _is_inferred(self):
-    return pn_message_is_inferred(self._msg)
-
-  def _set_inferred(self, value):
-    self._check(pn_message_set_inferred(self._msg, bool(value)))
-
-  inferred = property(_is_inferred, _set_inferred, doc="""
-The inferred flag for a message indicates how the message content
-is encoded into AMQP sections. If inferred is true then binary and
-list values in the body of the message will be encoded as AMQP DATA
-and AMQP SEQUENCE sections, respectively. If inferred is false,
-then all values in the body of the message will be encoded as AMQP
-VALUE sections regardless of their type.
-""")
-
-  def _is_durable(self):
-    return pn_message_is_durable(self._msg)
-
-  def _set_durable(self, value):
-    self._check(pn_message_set_durable(self._msg, bool(value)))
-
-  durable = property(_is_durable, _set_durable,
-                     doc="""
-The durable property indicates that the message should be held durably
-by any intermediaries taking responsibility for the message.
-""")
-
-  def _get_priority(self):
-    return pn_message_get_priority(self._msg)
-
-  def _set_priority(self, value):
-    self._check(pn_message_set_priority(self._msg, value))
-
-  priority = property(_get_priority, _set_priority,
-                      doc="""
-The priority of the message.
-""")
-
-  def _get_ttl(self):
-    return millis2secs(pn_message_get_ttl(self._msg))
-
-  def _set_ttl(self, value):
-    self._check(pn_message_set_ttl(self._msg, secs2millis(value)))
-
-  ttl = property(_get_ttl, _set_ttl,
-                 doc="""
-The time to live of the message measured in seconds. Expired messages
-may be dropped.
-""")
-
-  def _is_first_acquirer(self):
-    return pn_message_is_first_acquirer(self._msg)
-
-  def _set_first_acquirer(self, value):
-    self._check(pn_message_set_first_acquirer(self._msg, bool(value)))
-
-  first_acquirer = property(_is_first_acquirer, _set_first_acquirer,
-                            doc="""
-True iff the recipient is the first to acquire the message.
-""")
-
-  def _get_delivery_count(self):
-    return pn_message_get_delivery_count(self._msg)
-
-  def _set_delivery_count(self, value):
-    self._check(pn_message_set_delivery_count(self._msg, value))
-
-  delivery_count = property(_get_delivery_count, _set_delivery_count,
-                            doc="""
-The number of delivery attempts made for this message.
-""")
-
-
-  def _get_id(self):
-    return self._id.get_object()
-  def _set_id(self, value):
-    if type(value) in (int, long):
-      value = ulong(value)
-    self._id.rewind()
-    self._id.put_object(value)
-  id = property(_get_id, _set_id,
-                doc="""
-The id of the message.
-""")
-
-  def _get_user_id(self):
-    return pn_message_get_user_id(self._msg)
-
-  def _set_user_id(self, value):
-    self._check(pn_message_set_user_id(self._msg, value))
-
-  user_id = property(_get_user_id, _set_user_id,
-                     doc="""
-The user id of the message creator.
-""")
-
-  def _get_address(self):
-    return utf82unicode(pn_message_get_address(self._msg))
-
-  def _set_address(self, value):
-    self._check(pn_message_set_address(self._msg, unicode2utf8(value)))
-
-  address = property(_get_address, _set_address,
-                     doc="""
-The address of the message.
-""")
-
-  def _get_subject(self):
-    return utf82unicode(pn_message_get_subject(self._msg))
-
-  def _set_subject(self, value):
-    self._check(pn_message_set_subject(self._msg, unicode2utf8(value)))
-
-  subject = property(_get_subject, _set_subject,
-                     doc="""
-The subject of the message.
-""")
-
-  def _get_reply_to(self):
-    return utf82unicode(pn_message_get_reply_to(self._msg))
-
-  def _set_reply_to(self, value):
-    self._check(pn_message_set_reply_to(self._msg, unicode2utf8(value)))
-
-  reply_to = property(_get_reply_to, _set_reply_to,
-                      doc="""
-The reply-to address for the message.
-""")
-
-  def _get_correlation_id(self):
-    return self._correlation_id.get_object()
-  def _set_correlation_id(self, value):
-    if type(value) in (int, long):
-      value = ulong(value)
-    self._correlation_id.rewind()
-    self._correlation_id.put_object(value)
-
-  correlation_id = property(_get_correlation_id, _set_correlation_id,
-                            doc="""
-The correlation-id for the message.
-""")
-
-  def _get_content_type(self):
-    return symbol(utf82unicode(pn_message_get_content_type(self._msg)))
-
-  def _set_content_type(self, value):
-    self._check(pn_message_set_content_type(self._msg, unicode2utf8(value)))
-
-  content_type = property(_get_content_type, _set_content_type,
-                          doc="""
-The content-type of the message.
-""")
-
-  def _get_content_encoding(self):
-    return symbol(utf82unicode(pn_message_get_content_encoding(self._msg)))
-
-  def _set_content_encoding(self, value):
-    self._check(pn_message_set_content_encoding(self._msg, unicode2utf8(value)))
-
-  content_encoding = property(_get_content_encoding, _set_content_encoding,
-                              doc="""
-The content-encoding of the message.
-""")
-
-  def _get_expiry_time(self):
-    return millis2secs(pn_message_get_expiry_time(self._msg))
-
-  def _set_expiry_time(self, value):
-    self._check(pn_message_set_expiry_time(self._msg, secs2millis(value)))
-
-  expiry_time = property(_get_expiry_time, _set_expiry_time,
-                         doc="""
-The expiry time of the message.
-""")
-
-  def _get_creation_time(self):
-    return millis2secs(pn_message_get_creation_time(self._msg))
-
-  def _set_creation_time(self, value):
-    self._check(pn_message_set_creation_time(self._msg, secs2millis(value)))
-
-  creation_time = property(_get_creation_time, _set_creation_time,
-                           doc="""
-The creation time of the message.
-""")
-
-  def _get_group_id(self):
-    return utf82unicode(pn_message_get_group_id(self._msg))
-
-  def _set_group_id(self, value):
-    self._check(pn_message_set_group_id(self._msg, unicode2utf8(value)))
-
-  group_id = property(_get_group_id, _set_group_id,
-                      doc="""
-The group id of the message.
-""")
-
-  def _get_group_sequence(self):
-    return pn_message_get_group_sequence(self._msg)
-
-  def _set_group_sequence(self, value):
-    self._check(pn_message_set_group_sequence(self._msg, value))
-
-  group_sequence = property(_get_group_sequence, _set_group_sequence,
-                            doc="""
-The sequence of the message within its group.
-""")
-
-  def _get_reply_to_group_id(self):
-    return utf82unicode(pn_message_get_reply_to_group_id(self._msg))
-
-  def _set_reply_to_group_id(self, value):
-    self._check(pn_message_set_reply_to_group_id(self._msg, unicode2utf8(value)))
-
-  reply_to_group_id = property(_get_reply_to_group_id, _set_reply_to_group_id,
-                               doc="""
-The group-id for any replies.
-""")
-
-  def encode(self):
-    self._pre_encode()
-    sz = 16
-    while True:
-      err, data = pn_message_encode(self._msg, sz)
-      if err == PN_OVERFLOW:
-        sz *= 2
-        continue
-      else:
-        self._check(err)
-        return data
-
-  def decode(self, data):
-    self._check(pn_message_decode(self._msg, data))
-    self._post_decode()
-
-  def send(self, sender, tag=None):
-    dlv = sender.delivery(tag or sender.delivery_tag())
-    encoded = self.encode()
-    sender.stream(encoded)
-    sender.advance()
-    if sender.snd_settle_mode == Link.SND_SETTLED:
-      dlv.settle()
-    return dlv
-
-  def recv(self, link):
-    """
-    Receives and decodes the message content for the current delivery
-    from the link. Upon success it will return the current delivery
-    for the link. If there is no current delivery, or if the current
-    delivery is incomplete, or if the link is not a receiver, it will
-    return None.
-
-    @type link: Link
-    @param link: the link to receive a message from
-    @return the delivery associated with the decoded message (or None)
-
-    """
-    if link.is_sender: return None
-    dlv = link.current
-    if not dlv or dlv.partial: return None
-    dlv.encoded = link.recv(dlv.pending)
-    link.advance()
-    # the sender has already forgotten about the delivery, so we might
-    # as well too
-    if link.remote_snd_settle_mode == Link.SND_SETTLED:
-      dlv.settle()
-    self.decode(dlv.encoded)
-    return dlv
-
-  def __repr2__(self):
-    props = []
-    for attr in ("inferred", "address", "reply_to", "durable", "ttl",
-                 "priority", "first_acquirer", "delivery_count", "id",
-                 "correlation_id", "user_id", "group_id", "group_sequence",
-                 "reply_to_group_id", "instructions", "annotations",
-                 "properties", "body"):
-      value = getattr(self, attr)
-      if value: props.append("%s=%r" % (attr, value))
-    return "Message(%s)" % ", ".join(props)
-
-  def __repr__(self):
-    tmp = pn_string(None)
-    err = pn_inspect(self._msg, tmp)
-    result = pn_string_get(tmp)
-    pn_free(tmp)
-    self._check(err)
-    return result
-
-_DEFAULT = object()
-
-class Selectable(Wrapper):
-
-  @staticmethod
-  def wrap(impl):
-    if impl is None:
-      return None
-    else:
-      return Selectable(impl)
-
-  def __init__(self, impl):
-    Wrapper.__init__(self, impl, pn_selectable_attachments)
-
-  def _init(self):
-    pass
-
-  def fileno(self, fd = _DEFAULT):
-    if fd is _DEFAULT:
-      return pn_selectable_get_fd(self._impl)
-    elif fd is None:
-      pn_selectable_set_fd(self._impl, PN_INVALID_SOCKET)
-    else:
-      pn_selectable_set_fd(self._impl, fd)
-
-  def _is_reading(self):
-    return pn_selectable_is_reading(self._impl)
-
-  def _set_reading(self, val):
-    pn_selectable_set_reading(self._impl, bool(val))
-
-  reading = property(_is_reading, _set_reading)
-
-  def _is_writing(self):
-    return pn_selectable_is_writing(self._impl)
-
-  def _set_writing(self, val):
-    pn_selectable_set_writing(self._impl, bool(val))
-
-  writing = property(_is_writing, _set_writing)
-
-  def _get_deadline(self):
-    tstamp = pn_selectable_get_deadline(self._impl)
-    if tstamp:
-      return millis2secs(tstamp)
-    else:
-      return None
-
-  def _set_deadline(self, deadline):
-    pn_selectable_set_deadline(self._impl, secs2millis(deadline))
-
-  deadline = property(_get_deadline, _set_deadline)
-
-  def readable(self):
-    pn_selectable_readable(self._impl)
-
-  def writable(self):
-    pn_selectable_writable(self._impl)
-
-  def expired(self):
-    pn_selectable_expired(self._impl)
-
-  def _is_registered(self):
-    return pn_selectable_is_registered(self._impl)
-
-  def _set_registered(self, registered):
-    pn_selectable_set_registered(self._impl, registered)
-
-  registered = property(_is_registered, _set_registered,
-                    doc="""
-The registered property may be get/set by an I/O polling system to
-indicate whether the fd has been registered or not.
-""")
-
-  @property
-  def is_terminal(self):
-    return pn_selectable_is_terminal(self._impl)
-
-  def terminate(self):
-    pn_selectable_terminate(self._impl)
-
-  def release(self):
-    pn_selectable_release(self._impl)
-
-class DataException(ProtonException):
-  """
-  The DataException class is the root of the Data exception hierarchy.
-  All exceptions raised by the Data class extend this exception.
-  """
-  pass
-
-class UnmappedType:
-
-  def __init__(self, msg):
-    self.msg = msg
-
-  def __repr__(self):
-    return "UnmappedType(%s)" % self.msg
 
-class ulong(long):
-
-  def __repr__(self):
-    return "ulong(%s)" % long.__repr__(self)
-
-class timestamp(long):
-
-  def __repr__(self):
-    return "timestamp(%s)" % long.__repr__(self)
-
-class symbol(unicode):
-
-  def __repr__(self):
-    return "symbol(%s)" % unicode.__repr__(self)
-
-class char(unicode):
-
-  def __repr__(self):
-    return "char(%s)" % unicode.__repr__(self)
-
-class byte(int):
-
-  def __repr__(self):
-    return "byte(%s)" % int.__repr__(self)
-
-class short(int):
-
-  def __repr__(self):
-    return "short(%s)" % int.__repr__(self)
-
-class int32(int):
-
-  def __repr__(self):
-      return "int32(%s)" % int.__repr__(self)
-
-class ubyte(int):
-
-  def __repr__(self):
-    return "ubyte(%s)" % int.__repr__(self)
-
-class ushort(int):
-
-  def __repr__(self):
-    return "ushort(%s)" % int.__repr__(self)
-
-class uint(long):
-
-  def __repr__(self):
-      return "uint(%s)" % long.__repr__(self)
-
-class float32(float):
-
-  def __repr__(self):
-    return "float32(%s)" % float.__repr__(self)
-
-class decimal32(int):
-
-  def __repr__(self):
-    return "decimal32(%s)" % int.__repr__(self)
-
-class decimal64(long):
-
-  def __repr__(self):
-    return "decimal64(%s)" % long.__repr__(self)
-
-class decimal128(bytes):
-
-  def __repr__(self):
-    return "decimal128(%s)" % bytes.__repr__(self)
-
-class Described(object):
-
-  def __init__(self, descriptor, value):
-    self.descriptor = descriptor
-    self.value = value
-
-  def __repr__(self):
-    return "Described(%r, %r)" % (self.descriptor, self.value)
-
-  def __eq__(self, o):
-    if isinstance(o, Described):
-      return self.descriptor == o.descriptor and self.value == o.value
-    else:
-      return False
-
-UNDESCRIBED = Constant("UNDESCRIBED")
-
-class Array(object):
-
-  def __init__(self, descriptor, type, *elements):
-    self.descriptor = descriptor
-    self.type = type
-    self.elements = elements
-
-  def __iter__(self):
-    return iter(self.elements)
-
-  def __repr__(self):
-    if self.elements:
-      els = ", %s"  % (", ".join(map(repr, self.elements)))
-    else:
-      els = ""
-    return "Array(%r, %r%s)" % (self.descriptor, self.type, els)
-
-  def __eq__(self, o):
-    if isinstance(o, Array):
-      return self.descriptor == o.descriptor and \
-          self.type == o.type and self.elements == o.elements
-    else:
-      return False
-
-class Data:
-  """
-  The L{Data} class provides an interface for decoding, extracting,
-  creating, and encoding arbitrary AMQP data. A L{Data} object
-  contains a tree of AMQP values. Leaf nodes in this tree correspond
-  to scalars in the AMQP type system such as L{ints<INT>} or
-  L{strings<STRING>}. Non-leaf nodes in this tree correspond to
-  compound values in the AMQP type system such as L{lists<LIST>},
-  L{maps<MAP>}, L{arrays<ARRAY>}, or L{described values<DESCRIBED>}.
-  The root node of the tree is the L{Data} object itself and can have
-  an arbitrary number of children.
-
-  A L{Data} object maintains the notion of the current sibling node
-  and a current parent node. Siblings are ordered within their parent.
-  Values are accessed and/or added by using the L{next}, L{prev},
-  L{enter}, and L{exit} methods to navigate to the desired location in
-  the tree and using the supplied variety of put_*/get_* methods to
-  access or add a value of the desired type.
-
-  The put_* methods will always add a value I{after} the current node
-  in the tree. If the current node has a next sibling the put_* method
-  will overwrite the value on this node. If there is no current node
-  or the current node has no next sibling then one will be added. The
-  put_* methods always set the added/modified node to the current
-  node. The get_* methods read the value of the current node and do
-  not change which node is current.
-
-  The following types of scalar values are supported:
-
-   - L{NULL}
-   - L{BOOL}
-   - L{UBYTE}
-   - L{USHORT}
-   - L{SHORT}
-   - L{UINT}
-   - L{INT}
-   - L{ULONG}
-   - L{LONG}
-   - L{FLOAT}
-   - L{DOUBLE}
-   - L{BINARY}
-   - L{STRING}
-   - L{SYMBOL}
-
-  The following types of compound values are supported:
-
-   - L{DESCRIBED}
-   - L{ARRAY}
-   - L{LIST}
-   - L{MAP}
-  """
-
-  NULL = PN_NULL; "A null value."
-  BOOL = PN_BOOL; "A boolean value."
-  UBYTE = PN_UBYTE; "An unsigned byte value."
-  BYTE = PN_BYTE; "A signed byte value."
-  USHORT = PN_USHORT; "An unsigned short value."
-  SHORT = PN_SHORT; "A short value."
-  UINT = PN_UINT; "An unsigned int value."
-  INT = PN_INT; "A signed int value."
-  CHAR = PN_CHAR; "A character value."
-  ULONG = PN_ULONG; "An unsigned long value."
-  LONG = PN_LONG; "A signed long value."
-  TIMESTAMP = PN_TIMESTAMP; "A timestamp value."
-  FLOAT = PN_FLOAT; "A float value."
-  DOUBLE = PN_DOUBLE; "A double value."
-  DECIMAL32 = PN_DECIMAL32; "A DECIMAL32 value."
-  DECIMAL64 = PN_DECIMAL64; "A DECIMAL64 value."
-  DECIMAL128 = PN_DECIMAL128; "A DECIMAL128 value."
-  UUID = PN_UUID; "A UUID value."
-  BINARY = PN_BINARY; "A binary string."
-  STRING = PN_STRING; "A unicode string."
-  SYMBOL = PN_SYMBOL; "A symbolic string."
-  DESCRIBED = PN_DESCRIBED; "A described value."
-  ARRAY = PN_ARRAY; "An array value."
-  LIST = PN_LIST; "A list value."
-  MAP = PN_MAP; "A map value."
-
-  type_names = {
-    NULL: "null",
-    BOOL: "bool",
-    BYTE: "byte",
-    UBYTE: "ubyte",
-    SHORT: "short",
-    USHORT: "ushort",
-    INT: "int",
-    UINT: "uint",
-    CHAR: "char",
-    LONG: "long",
-    ULONG: "ulong",
-    TIMESTAMP: "timestamp",
-    FLOAT: "float",
-    DOUBLE: "double",
-    DECIMAL32: "decimal32",
-    DECIMAL64: "decimal64",
-    DECIMAL128: "decimal128",
-    UUID: "uuid",
-    BINARY: "binary",
-    STRING: "string",
-    SYMBOL: "symbol",
-    DESCRIBED: "described",
-    ARRAY: "array",
-    LIST: "list",
-    MAP: "map"
-    }
-
-  @classmethod
-  def type_name(type): return Data.type_names[type]
-
-  def __init__(self, capacity=16):
-    if type(capacity) in (int, long):
-      self._data = pn_data(capacity)
-      self._free = True
-    else:
-      self._data = capacity
-      self._free = False
-
-  def __del__(self):
-    if self._free and hasattr(self, "_data"):
-      pn_data_free(self._data)
-      del self._data
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, DataException)
-      raise exc("[%s]: %s" % (err, pn_error_text(pn_data_error(self._data))))
-    else:
-      return err
-
-  def clear(self):
-    """
-    Clears the data object.
-    """
-    pn_data_clear(self._data)
-
-  def rewind(self):
-    """
-    Clears current node and sets the parent to the root node.  Clearing the
-    current node sets it _before_ the first node, calling next() will advance to
-    the first node.
-    """
-    assert self._data is not None
-    pn_data_rewind(self._data)
-
-  def next(self):
-    """
-    Advances the current node to its next sibling and returns its
-    type. If there is no next sibling the current node remains
-    unchanged and None is returned.
-    """
-    found = pn_data_next(self._data)
-    if found:
-      return self.type()
-    else:
-      return None
-
-  def prev(self):
-    """
-    Advances the current node to its previous sibling and returns its
-    type. If there is no previous sibling the current node remains
-    unchanged and None is returned.
-    """
-    found = pn_data_prev(self._data)
-    if found:
-      return self.type()
-    else:
-      return None
-
-  def enter(self):
-    """
-    Sets the parent node to the current node and clears the current node.
-    Clearing the current node sets it _before_ the first child,
-    call next() advances to the first child.
-    """
-    return pn_data_enter(self._data)
-
-  def exit(self):
-    """
-    Sets the current node to the parent node and the parent node to
-    its own parent.
-    """
-    return pn_data_exit(self._data)
-
-  def lookup(self, name):
-    return pn_data_lookup(self._data, name)
-
-  def narrow(self):
-    pn_data_narrow(self._data)
-
-  def widen(self):
-    pn_data_widen(self._data)
-
-  def type(self):
-    """
-    Returns the type of the current node.
-    """
-    dtype = pn_data_type(self._data)
-    if dtype == -1:
-      return None
-    else:
-      return dtype
-
-  def encoded_size(self):
-    """
-    Returns the size in bytes needed to encode the data in AMQP format.
-    """
-    return pn_data_encoded_size(self._data)
-
-  def encode(self):
-    """
-    Returns a representation of the data encoded in AMQP format.
-    """
-    size = 1024
-    while True:
-      cd, enc = pn_data_encode(self._data, size)
-      if cd == PN_OVERFLOW:
-        size *= 2
-      elif cd >= 0:
-        return enc
-      else:
-        self._check(cd)
-
-  def decode(self, encoded):
-    """
-    Decodes the first value from supplied AMQP data and returns the
-    number of bytes consumed.
-
-    @type encoded: binary
-    @param encoded: AMQP encoded binary data
-    """
-    return self._check(pn_data_decode(self._data, encoded))
-
-  def put_list(self):
-    """
-    Puts a list value. Elements may be filled by entering the list
-    node and putting element values.
-
-      >>> data = Data()
-      >>> data.put_list()
-      >>> data.enter()
-      >>> data.put_int(1)
-      >>> data.put_int(2)
-      >>> data.put_int(3)
-      >>> data.exit()
-    """
-    self._check(pn_data_put_list(self._data))
-
-  def put_map(self):
-    """
-    Puts a map value. Elements may be filled by entering the map node
-    and putting alternating key value pairs.
-
-      >>> data = Data()
-      >>> data.put_map()
-      >>> data.enter()
-      >>> data.put_string("key")
-      >>> data.put_string("value")
-      >>> data.exit()
-    """
-    self._check(pn_data_put_map(self._data))
-
-  def put_array(self, described, element_type):
-    """
-    Puts an array value. Elements may be filled by entering the array
-    node and putting the element values. The values must all be of the
-    specified array element type. If an array is described then the
-    first child value of the array is the descriptor and may be of any
-    type.
-
-      >>> data = Data()
-      >>>
-      >>> data.put_array(False, Data.INT)
-      >>> data.enter()
-      >>> data.put_int(1)
-      >>> data.put_int(2)
-      >>> data.put_int(3)
-      >>> data.exit()
-      >>>
-      >>> data.put_array(True, Data.DOUBLE)
-      >>> data.enter()
-      >>> data.put_symbol("array-descriptor")
-      >>> data.put_double(1.1)
-      >>> data.put_double(1.2)
-      >>> data.put_double(1.3)
-      >>> data.exit()
-
-    @type described: bool
-    @param described: specifies whether the array is described
-    @type element_type: int
-    @param element_type: the type of the array elements
-    """
-    self._check(pn_data_put_array(self._data, described, element_type))
-
-  def put_described(self):
-    """
-    Puts a described value. A described node has two children, the
-    descriptor and the value. These are specified by entering the node
-    and putting the desired values.
-
-      >>> data = Data()
-      >>> data.put_described()
-      >>> data.enter()
-      >>> data.put_symbol("value-descriptor")
-      >>> data.put_string("the value")
-      >>> data.exit()
-    """
-    self._check(pn_data_put_described(self._data))
-
-  def put_null(self):
-    """
-    Puts a null value.
-    """
-    self._check(pn_data_put_null(self._data))
-
-  def put_bool(self, b):
-    """
-    Puts a boolean value.
-
-    @param b: a boolean value
-    """
-    self._check(pn_data_put_bool(self._data, b))
-
-  def put_ubyte(self, ub):
-    """
-    Puts an unsigned byte value.
-
-    @param ub: an integral value
-    """
-    self._check(pn_data_put_ubyte(self._data, ub))
-
-  def put_byte(self, b):
-    """
-    Puts a signed byte value.
-
-    @param b: an integral value
-    """
-    self._check(pn_data_put_byte(self._data, b))
-
-  def put_ushort(self, us):
-    """
-    Puts an unsigned short value.
-
-    @param us: an integral value.
-    """
-    self._check(pn_data_put_ushort(self._data, us))
-
-  def put_short(self, s):
-    """
-    Puts a signed short value.
-
-    @param s: an integral value
-    """
-    self._check(pn_data_put_short(self._data, s))
-
-  def put_uint(self, ui):
-    """
-    Puts an unsigned int value.
-
-    @param ui: an integral value
-    """
-    self._check(pn_data_put_uint(self._data, ui))
-
-  def put_int(self, i):
-    """
-    Puts a signed int value.
-
-    @param i: an integral value
-    """
-    self._check(pn_data_put_int(self._data, i))
-
-  def put_char(self, c):
-    """
-    Puts a char value.
-
-    @param c: a single character
-    """
-    self._check(pn_data_put_char(self._data, ord(c)))
-
-  def put_ulong(self, ul):
-    """
-    Puts an unsigned long value.
-
-    @param ul: an integral value
-    """
-    self._check(pn_data_put_ulong(self._data, ul))
-
-  def put_long(self, l):
-    """
-    Puts a signed long value.
-
-    @param l: an integral value
-    """
-    self._check(pn_data_put_long(self._data, l))
-
-  def put_timestamp(self, t):
-    """
-    Puts a timestamp value.
-
-    @param t: an integral value
-    """
-    self._check(pn_data_put_timestamp(self._data, t))
-
-  def put_float(self, f):
-    """
-    Puts a float value.
-
-    @param f: a floating point value
-    """
-    self._check(pn_data_put_float(self._data, f))
-
-  def put_double(self, d):
-    """
-    Puts a double value.
-
-    @param d: a floating point value.
-    """
-    self._check(pn_data_put_double(self._data, d))
-
-  def put_decimal32(self, d):
-    """
-    Puts a decimal32 value.
-
-    @param d: a decimal32 value
-    """
-    self._check(pn_data_put_decimal32(self._data, d))
-
-  def put_decimal64(self, d):
-    """
-    Puts a decimal64 value.
-
-    @param d: a decimal64 value
-    """
-    self._check(pn_data_put_decimal64(self._data, d))
-
-  def put_decimal128(self, d):
-    """
-    Puts a decimal128 value.
-
-    @param d: a decimal128 value
-    """
-    self._check(pn_data_put_decimal128(self._data, d))
-
-  def put_uuid(self, u):
-    """
-    Puts a UUID value.
-
-    @param u: a uuid value
-    """
-    self._check(pn_data_put_uuid(self._data, u.bytes))
-
-  def put_binary(self, b):
-    """
-    Puts a binary value.
-
-    @type b: binary
-    @param b: a binary value
-    """
-    self._check(pn_data_put_binary(self._data, b))
-
-  def put_memoryview(self, mv):
-    """Put a python memoryview object as an AMQP binary value"""
-    self.put_binary(mv.tobytes())
-
-  def put_buffer(self, buff):
-    """Put a python buffer object as an AMQP binary value"""
-    self.put_binary(bytes(buff))
-
-  def put_string(self, s):
-    """
-    Puts a unicode value.
-
-    @type s: unicode
-    @param s: a unicode value
-    """
-    self._check(pn_data_put_string(self._data, s.encode("utf8")))
-
-  def put_symbol(self, s):
-    """
-    Puts a symbolic value.
-
-    @type s: string
-    @param s: the symbol name
-    """
-    self._check(pn_data_put_symbol(self._data, s.encode('ascii')))
-
-  def get_list(self):
-    """
-    If the current node is a list, return the number of elements,
-    otherwise return zero. List elements can be accessed by entering
-    the list.
-
-      >>> count = data.get_list()
-      >>> data.enter()
-      >>> for i in range(count):
-      ...   type = data.next()
-      ...   if type == Data.STRING:
-      ...     print data.get_string()
-      ...   elif type == ...:
-      ...     ...
-      >>> data.exit()
-    """
-    return pn_data_get_list(self._data)
-
-  def get_map(self):
-    """
-    If the current node is a map, return the number of child elements,
-    otherwise return zero. Key value pairs can be accessed by entering
-    the map.
-
-      >>> count = data.get_map()
-      >>> data.enter()
-      >>> for i in range(count/2):
-      ...   type = data.next()
-      ...   if type == Data.STRING:
-      ...     print data.get_string()
-      ...   elif type == ...:
-      ...     ...
-      >>> data.exit()
-    """
-    return pn_data_get_map(self._data)
-
-  def get_array(self):
-    """
-    If the current node is an array, return a tuple of the element
-    count, a boolean indicating whether the array is described, and
-    the type of each element, otherwise return (0, False, None). Array
-    data can be accessed by entering the array.
-
-      >>> # read an array of strings with a symbolic descriptor
-      >>> count, described, type = data.get_array()
-      >>> data.enter()
-      >>> data.next()
-      >>> print "Descriptor:", data.get_symbol()
-      >>> for i in range(count):
-      ...    data.next()
-      ...    print "Element:", data.get_string()
-      >>> data.exit()
-    """
-    count = pn_data_get_array(self._data)
-    described = pn_data_is_array_described(self._data)
-    type = pn_data_get_array_type(self._data)
-    if type == -1:
-      type = None
-    return count, described, type
+# This private NullHandler is required for Python 2.6,
+# when we no longer support 2.6 replace this NullHandler class definition and assignment with:
+#  handler = logging.NullHandler()
+class NullHandler(logging.Handler):
+    def handle(self, record):
+        pass
 
-  def is_described(self):
-    """
-    Checks if the current node is a described value. The descriptor
-    and value may be accessed by entering the described value.
+    def emit(self, record):
+        pass
 
-      >>> # read a symbolically described string
-      >>> assert data.is_described() # will error if the current node is not described
-      >>> data.enter()
-      >>> data.next()
-      >>> print data.get_symbol()
-      >>> data.next()
-      >>> print data.get_string()
-      >>> data.exit()
-    """
-    return pn_data_is_described(self._data)
+    def createLock(self):
+        self.lock = None
 
-  def is_null(self):
-    """
-    Checks if the current node is a null.
-    """
-    return pn_data_is_null(self._data)
 
-  def get_bool(self):
-    """
-    If the current node is a boolean, returns its value, returns False
-    otherwise.
-    """
-    return pn_data_get_bool(self._data)
+handler = NullHandler()
 
-  def get_ubyte(self):
-    """
-    If the current node is an unsigned byte, returns its value,
-    returns 0 otherwise.
-    """
-    return ubyte(pn_data_get_ubyte(self._data))
+log = logging.getLogger("proton")
+log.addHandler(handler)
 
-  def get_byte(self):
-    """
-    If the current node is a signed byte, returns its value, returns 0
-    otherwise.
-    """
-    return byte(pn_data_get_byte(self._data))
 
-  def get_ushort(self):
-    """
-    If the current node is an unsigned short, returns its value,
-    returns 0 otherwise.
-    """
-    return ushort(pn_data_get_ushort(self._data))
-
-  def get_short(self):
-    """
-    If the current node is a signed short, returns its value, returns
-    0 otherwise.
-    """
-    return short(pn_data_get_short(self._data))
-
-  def get_uint(self):
-    """
-    If the current node is an unsigned int, returns its value, returns
-    0 otherwise.
-    """
-    return uint(pn_data_get_uint(self._data))
-
-  def get_int(self):
-    """
-    If the current node is a signed int, returns its value, returns 0
-    otherwise.
-    """
-    return int32(pn_data_get_int(self._data))
-
-  def get_char(self):
-    """
-    If the current node is a char, returns its value, returns 0
-    otherwise.
-    """
-    return char(_compat.unichr(pn_data_get_char(self._data)))
-
-  def get_ulong(self):
-    """
-    If the current node is an unsigned long, returns its value,
-    returns 0 otherwise.
-    """
-    return ulong(pn_data_get_ulong(self._data))
-
-  def get_long(self):
-    """
-    If the current node is an signed long, returns its value, returns
-    0 otherwise.
-    """
-    return long(pn_data_get_long(self._data))
-
-  def get_timestamp(self):
-    """
-    If the current node is a timestamp, returns its value, returns 0
-    otherwise.
-    """
-    return timestamp(pn_data_get_timestamp(self._data))
-
-  def get_float(self):
-    """
-    If the current node is a float, returns its value, raises 0
-    otherwise.
-    """
-    return float32(pn_data_get_float(self._data))
-
-  def get_double(self):
-    """
-    If the current node is a double, returns its value, returns 0
-    otherwise.
-    """
-    return pn_data_get_double(self._data)
-
-  # XXX: need to convert
-  def get_decimal32(self):
-    """
-    If the current node is a decimal32, returns its value, returns 0
-    otherwise.
-    """
-    return decimal32(pn_data_get_decimal32(self._data))
-
-  # XXX: need to convert
-  def get_decimal64(self):
-    """
-    If the current node is a decimal64, returns its value, returns 0
-    otherwise.
-    """
-    return decimal64(pn_data_get_decimal64(self._data))
-
-  # XXX: need to convert
-  def get_decimal128(self):
-    """
-    If the current node is a decimal128, returns its value, returns 0
-    otherwise.
-    """
-    return decimal128(pn_data_get_decimal128(self._data))
-
-  def get_uuid(self):
-    """
-    If the current node is a UUID, returns its value, returns None
-    otherwise.
-    """
-    if pn_data_type(self._data) == Data.UUID:
-      return uuid.UUID(bytes=pn_data_get_uuid(self._data))
-    else:
-      return None
-
-  def get_binary(self):
-    """
-    If the current node is binary, returns its value, returns ""
-    otherwise.
-    """
-    return pn_data_get_binary(self._data)
-
-  def get_string(self):
-    """
-    If the current node is a string, returns its value, returns ""
-    otherwise.
-    """
-    return pn_data_get_string(self._data).decode("utf8")
-
-  def get_symbol(self):
-    """
-    If the current node is a symbol, returns its value, returns ""
-    otherwise.
-    """
-    return symbol(pn_data_get_symbol(self._data).decode('ascii'))
-
-  def copy(self, src):
-    self._check(pn_data_copy(self._data, src._data))
-
-  def format(self):
-    sz = 16
-    while True:
-      err, result = pn_data_format(self._data, sz)
-      if err == PN_OVERFLOW:
-        sz *= 2
-        continue
-      else:
-        self._check(err)
-        return result
-
-  def dump(self):
-    pn_data_dump(self._data)
-
-  def put_dict(self, d):
-    self.put_map()
-    self.enter()
-    try:
-      for k, v in d.items():
-        self.put_object(k)
-        self.put_object(v)
-    finally:
-      self.exit()
-
-  def get_dict(self):
-    if self.enter():
-      try:
-        result = {}
-        while self.next():
-          k = self.get_object()
-          if self.next():
-            v = self.get_object()
-          else:
-            v = None
-          result[k] = v
-      finally:
-        self.exit()
-      return result
-
-  def put_sequence(self, s):
-    self.put_list()
-    self.enter()
-    try:
-      for o in s:
-        self.put_object(o)
-    finally:
-      self.exit()
-
-  def get_sequence(self):
-    if self.enter():
-      try:
-        result = []
-        while self.next():
-          result.append(self.get_object())
-      finally:
-        self.exit()
-      return result
-
-  def get_py_described(self):
-    if self.enter():
-      try:
-        self.next()
-        descriptor = self.get_object()
-        self.next()
-        value = self.get_object()
-      finally:
-        self.exit()
-      return Described(descriptor, value)
-
-  def put_py_described(self, d):
-    self.put_described()
-    self.enter()
-    try:
-      self.put_object(d.descriptor)
-      self.put_object(d.value)
-    finally:
-      self.exit()
-
-  def get_py_array(self):
-    """
-    If the current node is an array, return an Array object
-    representing the array and its contents. Otherwise return None.
-    This is a convenience wrapper around get_array, enter, etc.
-    """
-
-    count, described, type = self.get_array()
-    if type is None: return None
-    if self.enter():
-      try:
-        if described:
-          self.next()
-          descriptor = self.get_object()
-        else:
-          descriptor = UNDESCRIBED
-        elements = []
-        while self.next():
-          elements.append(self.get_object())
-      finally:
-        self.exit()
-      return Array(descriptor, type, *elements)
-
-  def put_py_array(self, a):
-    described = a.descriptor != UNDESCRIBED
-    self.put_array(described, a.type)
-    self.enter()
-    try:
-      if described:
-        self.put_object(a.descriptor)
-      for e in a.elements:
-        self.put_object(e)
-    finally:
-      self.exit()
-
-  put_mappings = {
-    None.__class__: lambda s, _: s.put_null(),
-    bool: put_bool,
-    ubyte: put_ubyte,
-    ushort: put_ushort,
-    uint: put_uint,
-    ulong: put_ulong,
-    byte: put_byte,
-    short: put_short,
-    int32: put_int,
-    long: put_long,
-    float32: put_float,
-    float: put_double,
-    decimal32: put_decimal32,
-    decimal64: put_decimal64,
-    decimal128: put_decimal128,
-    char: put_char,
-    timestamp: put_timestamp,
-    uuid.UUID: put_uuid,
-    bytes: put_binary,
-    unicode: put_string,
-    symbol: put_symbol,
-    list: put_sequence,
-    tuple: put_sequence,
-    dict: put_dict,
-    Described: put_py_described,
-    Array: put_py_array
-    }
-  # for python 3.x, long is merely an alias for int, but for python 2.x
-  # we need to add an explicit int since it is a different type
-  if int not in put_mappings:
-    put_mappings[int] = put_int
-  # Python >=3.0 has 'memoryview', <=2.5 has 'buffer', >=2.6 has both.
-  try: put_mappings[memoryview] = put_memoryview
-  except NameError: pass
-  try: put_mappings[buffer] = put_buffer
-  except NameError: pass
-  get_mappings = {
-    NULL: lambda s: None,
-    BOOL: get_bool,
-    BYTE: get_byte,
-    UBYTE: get_ubyte,
-    SHORT: get_short,
-    USHORT: get_ushort,
-    INT: get_int,
-    UINT: get_uint,
-    CHAR: get_char,
-    LONG: get_long,
-    ULONG: get_ulong,
-    TIMESTAMP: get_timestamp,
-    FLOAT: get_float,
-    DOUBLE: get_double,
-    DECIMAL32: get_decimal32,
-    DECIMAL64: get_decimal64,
-    DECIMAL128: get_decimal128,
-    UUID: get_uuid,
-    BINARY: get_binary,
-    STRING: get_string,
-    SYMBOL: get_symbol,
-    DESCRIBED: get_py_described,
-    ARRAY: get_py_array,
-    LIST: get_sequence,
-    MAP: get_dict
-    }
-
-
-  def put_object(self, obj):
-    putter = self.put_mappings[obj.__class__]
-    putter(self, obj)
-
-  def get_object(self):
-    type = self.type()
-    if type is None: return None
-    getter = self.get_mappings.get(type)
-    if getter:
-      return getter(self)
-    else:
-      return UnmappedType(str(type))
-
-class ConnectionException(ProtonException):
-  pass
-
-class Endpoint(object):
-
-  LOCAL_UNINIT = PN_LOCAL_UNINIT
-  REMOTE_UNINIT = PN_REMOTE_UNINIT
-  LOCAL_ACTIVE = PN_LOCAL_ACTIVE
-  REMOTE_ACTIVE = PN_REMOTE_ACTIVE
-  LOCAL_CLOSED = PN_LOCAL_CLOSED
-  REMOTE_CLOSED  = PN_REMOTE_CLOSED
-
-  def _init(self):
-    self.condition = None
-
-  def _update_cond(self):
-    obj2cond(self.condition, self._get_cond_impl())
-
-  @property
-  def remote_condition(self):
-    return cond2obj(self._get_remote_cond_impl())
-
-  # the following must be provided by subclasses
-  def _get_cond_impl(self):
-      assert False, "Subclass must override this!"
-
-  def _get_remote_cond_impl(self):
-      assert False, "Subclass must override this!"
-
-  def _get_handler(self):
-    from . import reactor
-    ractor = reactor.Reactor.wrap(pn_object_reactor(self._impl))
-    if ractor:
-      on_error = ractor.on_error_delegate()
-    else:
-      on_error = None
-    record = self._get_attachments()
-    return WrappedHandler.wrap(pn_record_get_handler(record), on_error)
-
-  def _set_handler(self, handler):
-    from . import reactor
-    ractor = reactor.Reactor.wrap(pn_object_reactor(self._impl))
-    if ractor:
-      on_error = ractor.on_error_delegate()
-    else:
-      on_error = None
-    impl = _chandler(handler, on_error)
-    record = self._get_attachments()
-    pn_record_set_handler(record, impl)
-    pn_decref(impl)
-
-  handler = property(_get_handler, _set_handler)
-
-  @property
-  def transport(self):
-    return self.connection.transport
-
-class Condition:
-
-  def __init__(self, name, description=None, info=None):
-    self.name = name
-    self.description = description
-    self.info = info
-
-  def __repr__(self):
-    return "Condition(%s)" % ", ".join([repr(x) for x in
-                                        (self.name, self.description, self.info)
-                                        if x])
-
-  def __eq__(self, o):
-    if not isinstance(o, Condition): return False
-    return self.name == o.name and \
-        self.description == o.description and \
-        self.info == o.info
-
-def obj2cond(obj, cond):
-  pn_condition_clear(cond)
-  if obj:
-    pn_condition_set_name(cond, str(obj.name))
-    pn_condition_set_description(cond, obj.description)
-    info = Data(pn_condition_info(cond))
-    if obj.info:
-      info.put_object(obj.info)
-
-def cond2obj(cond):
-  if pn_condition_is_set(cond):
-    return Condition(pn_condition_get_name(cond),
-                     pn_condition_get_description(cond),
-                     dat2obj(pn_condition_info(cond)))
-  else:
-    return None
-
-def dat2obj(dimpl):
-  if dimpl:
-    d = Data(dimpl)
-    d.rewind()
-    d.next()
-    obj = d.get_object()
-    d.rewind()
-    return obj
-
-def obj2dat(obj, dimpl):
-  if obj is not None:
-    d = Data(dimpl)
-    d.put_object(obj)
-
-def secs2millis(secs):
-  return long(secs*1000)
-
-def millis2secs(millis):
-  return float(millis)/1000.0
-
-def timeout2millis(secs):
-  if secs is None: return PN_MILLIS_MAX
-  return secs2millis(secs)
-
-def millis2timeout(millis):
-  if millis == PN_MILLIS_MAX: return None
-  return millis2secs(millis)
-
-def unicode2utf8(string):
-  """Some Proton APIs expect a null terminated string. Convert python text
-  types to UTF8 to avoid zero bytes introduced by other multi-byte encodings.
-  This method will throw if the string cannot be converted.
-  """
-  if string is None:
-    return None
-  elif isinstance(string, str):
-    # Must be py2 or py3 str
-    # The swig binding converts py3 str -> utf8 char* and back sutomatically
-    return string
-  elif isinstance(string, unicode):
-    # This must be python2 unicode as we already detected py3 str above
-    return string.encode('utf-8')
-  # Anything else illegal - specifically python3 bytes
-  raise TypeError("Unrecognized string type: %r (%s)" % (string, type(string)))
-
-def utf82unicode(string):
-  """Convert C strings returned from proton-c into python unicode"""
-  if string is None:
-    return None
-  elif isinstance(string, unicode):
-    # py2 unicode, py3 str (via hack definition)
-    return string
-  elif isinstance(string, bytes):
-    # py2 str (via hack definition), py3 bytes
-    return string.decode('utf8')
-  raise TypeError("Unrecognized string type")
-
-class Connection(Wrapper, Endpoint):
-  """
-  A representation of an AMQP connection
-  """
-
-  @staticmethod
-  def wrap(impl):
-    if impl is None:
-      return None
-    else:
-      return Connection(impl)
-
-  def __init__(self, impl = pn_connection):
-    Wrapper.__init__(self, impl, pn_connection_attachments)
-
-  def _init(self):
-    Endpoint._init(self)
-    self.offered_capabilities = None
-    self.desired_capabilities = None
-    self.properties = None
-
-  def _get_attachments(self):
-    return pn_connection_attachments(self._impl)
-
-  @property
-  def connection(self):
-    return self
-
-  @property
-  def transport(self):
-    return Transport.wrap(pn_connection_transport(self._impl))
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, ConnectionException)
-      raise exc("[%s]: %s" % (err, pn_connection_error(self._impl)))
-    else:
-      return err
-
-  def _get_cond_impl(self):
-    return pn_connection_condition(self._impl)
-
-  def _get_remote_cond_impl(self):
-    return pn_connection_remote_condition(self._impl)
-
-  def collect(self, collector):
-    if collector is None:
-      pn_connection_collect(self._impl, None)
-    else:
-      pn_connection_collect(self._impl, collector._impl)
-    self._collector = weakref.ref(collector)
-
-  def _get_container(self):
-    return utf82unicode(pn_connection_get_container(self._impl))
-  def _set_container(self, name):
-    return pn_connection_set_container(self._impl, unicode2utf8(name))
-
-  container = property(_get_container, _set_container)
-
-  def _get_hostname(self):
-    return utf82unicode(pn_connection_get_hostname(self._impl))
-  def _set_hostname(self, name):
-    return pn_connection_set_hostname(self._impl, unicode2utf8(name))
-
-  hostname = property(_get_hostname, _set_hostname,
-                      doc="""
-Set the name of the host (either fully qualified or relative) to which this
-connection is connecting to.  This information may be used by the remote
-peer to determine the correct back-end service to connect the client to.
-This value will be sent in the Open performative, and will be used by SSL
-and SASL layers to identify the peer.
-""")
-
-  def _get_user(self):
-    return utf82unicode(pn_connection_get_user(self._impl))
-  def _set_user(self, name):
-    return pn_connection_set_user(self._impl, unicode2utf8(name))
-
-  user = property(_get_user, _set_user)
-
-  def _get_password(self):
-    return None
-  def _set_password(self, name):
-    return pn_connection_set_password(self._impl, unicode2utf8(name))
-
-  password = property(_get_password, _set_password)
-
-  @property
-  def remote_container(self):
-    """The container identifier specified by the remote peer for this connection."""
-    return pn_connection_remote_container(self._impl)
-
-  @property
-  def remote_hostname(self):
-    """The hostname specified by the remote peer for this connection."""
-    return pn_connection_remote_hostname(self._impl)
-
-  @property
-  def remote_offered_capabilities(self):
-    """The capabilities offered by the remote peer for this connection."""
-    return dat2obj(pn_connection_remote_offered_capabilities(self._impl))
-
-  @property
-  def remote_desired_capabilities(self):
-    """The capabilities desired by the remote peer for this connection."""
-    return dat2obj(pn_connection_remote_desired_capabilities(self._impl))
-
-  @property
-  def remote_properties(self):
-    """The properties specified by the remote peer for this connection."""
-    return dat2obj(pn_connection_remote_properties(self._impl))
-
-  def open(self):
-    """
-    Opens the connection.
-
-    In more detail, this moves the local state of the connection to
-    the ACTIVE state and triggers an open frame to be sent to the
-    peer. A connection is fully active once both peers have opened it.
-    """
-    obj2dat(self.offered_capabilities,
-            pn_connection_offered_capabilities(self._impl))
-    obj2dat(self.desired_capabilities,
-            pn_connection_desired_capabilities(self._impl))
-    obj2dat(self.properties, pn_connection_properties(self._impl))
-    pn_connection_open(self._impl)
-
-  def close(self):
-    """
-    Closes the connection.
-
-    In more detail, this moves the local state of the connection to
-    the CLOSED state and triggers a close frame to be sent to the
-    peer. A connection is fully closed once both peers have closed it.
-    """
-    self._update_cond()
-    pn_connection_close(self._impl)
-    if hasattr(self, '_session_policy'):
-      # break circular ref
-      del self._session_policy
-
-  @property
-  def state(self):
-    """
-    The state of the connection as a bit field. The state has a local
-    and a remote component. Each of these can be in one of three
-    states: UNINIT, ACTIVE or CLOSED. These can be tested by masking
-    against LOCAL_UNINIT, LOCAL_ACTIVE, LOCAL_CLOSED, REMOTE_UNINIT,
-    REMOTE_ACTIVE and REMOTE_CLOSED.
-    """
-    return pn_connection_state(self._impl)
-
-  def session(self):
-    """
-    Returns a new session on this connection.
-    """
-    ssn = pn_session(self._impl)
-    if ssn is None:
-      raise(SessionException("Session allocation failed."))
-    else:
-      return Session(ssn)
-
-  def session_head(self, mask):
-    return Session.wrap(pn_session_head(self._impl, mask))
-
-  def link_head(self, mask):
-    return Link.wrap(pn_link_head(self._impl, mask))
-
-  @property
-  def work_head(self):
-    return Delivery.wrap(pn_work_head(self._impl))
-
-  @property
-  def error(self):
-    return pn_error_code(pn_connection_error(self._impl))
-
-  def free(self):
-    pn_connection_release(self._impl)
-
-class SessionException(ProtonException):
-  pass
-
-class Session(Wrapper, Endpoint):
-
-  @staticmethod
-  def wrap(impl):
-    if impl is None:
-      return None
-    else:
-      return Session(impl)
-
-  def __init__(self, impl):
-    Wrapper.__init__(self, impl, pn_session_attachments)
-
-  def _get_attachments(self):
-    return pn_session_attachments(self._impl)
-
-  def _get_cond_impl(self):
-    return pn_session_condition(self._impl)
-
-  def _get_remote_cond_impl(self):
-    return pn_session_remote_condition(self._impl)
-
-  def _get_incoming_capacity(self):
-    return pn_session_get_incoming_capacity(self._impl)
-
-  def _set_incoming_capacity(self, capacity):
-    pn_session_set_incoming_capacity(self._impl, capacity)
-
-  incoming_capacity = property(_get_incoming_capacity, _set_incoming_capacity)
-
-  def _get_outgoing_window(self):
-    return pn_session_get_outgoing_window(self._impl)
-
-  def _set_outgoing_window(self, window):
-    pn_session_set_outgoing_window(self._impl, window)
-
-  outgoing_window = property(_get_outgoing_window, _set_outgoing_window)
-
-  @property
-  def outgoing_bytes(self):
-    return pn_session_outgoing_bytes(self._impl)
-
-  @property
-  def incoming_bytes(self):
-    return pn_session_incoming_bytes(self._impl)
-
-  def open(self):
-    pn_session_open(self._impl)
-
-  def close(self):
-    self._update_cond()
-    pn_session_close(self._impl)
-
-  def next(self, mask):
-    return Session.wrap(pn_session_next(self._impl, mask))
-
-  @property
-  def state(self):
-    return pn_session_state(self._impl)
-
-  @property
-  def connection(self):
-    return Connection.wrap(pn_session_connection(self._impl))
-
-  def sender(self, name):
-    return Sender(pn_sender(self._impl, unicode2utf8(name)))
-
-  def receiver(self, name):
-    return Receiver(pn_receiver(self._impl, unicode2utf8(name)))
-
-  def free(self):
-    pn_session_free(self._impl)
-
-class LinkException(ProtonException):
-  pass
-
-class Link(Wrapper, Endpoint):
-  """
-  A representation of an AMQP link, of which there are two concrete
-  implementations, Sender and Receiver.
-  """
-
-  SND_UNSETTLED = PN_SND_UNSETTLED
-  SND_SETTLED = PN_SND_SETTLED
-  SND_MIXED = PN_SND_MIXED
-
-  RCV_FIRST = PN_RCV_FIRST
-  RCV_SECOND = PN_RCV_SECOND
-
-  @staticmethod
-  def wrap(impl):
-    if impl is None: return None
-    if pn_link_is_sender(impl):
-      return Sender(impl)
-    else:
-      return Receiver(impl)
-
-  def __init__(self, impl):
-    Wrapper.__init__(self, impl, pn_link_attachments)
-
-  def _get_attachments(self):
-    return pn_link_attachments(self._impl)
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, LinkException)
-      raise exc("[%s]: %s" % (err, pn_error_text(pn_link_error(self._impl))))
-    else:
-      return err
-
-  def _get_cond_impl(self):
-    return pn_link_condition(self._impl)
-
-  def _get_remote_cond_impl(self):
-    return pn_link_remote_condition(self._impl)
-
-  def open(self):
-    """
-    Opens the link.
-
-    In more detail, this moves the local state of the link to the
-    ACTIVE state and triggers an attach frame to be sent to the
-    peer. A link is fully active once both peers have attached it.
-    """
-    pn_link_open(self._impl)
-
-  def close(self):
-    """
-    Closes the link.
-
-    In more detail, this moves the local state of the link to the
-    CLOSED state and triggers an detach frame (with the closed flag
-    set) to be sent to the peer. A link is fully closed once both
-    peers have detached it.
-    """
-    self._update_cond()
-    pn_link_close(self._impl)
-
-  @property
-  def state(self):
-    """
-    The state of the link as a bit field. The state has a local
-    and a remote component. Each of these can be in one of three
-    states: UNINIT, ACTIVE or CLOSED. These can be tested by masking
-    against LOCAL_UNINIT, LOCAL_ACTIVE, LOCAL_CLOSED, REMOTE_UNINIT,
-    REMOTE_ACTIVE and REMOTE_CLOSED.
-    """
-    return pn_link_state(self._impl)
-
-  @property
-  def source(self):
-    """The source of the link as described by the local peer."""
-    return Terminus(pn_link_source(self._impl))
-
-  @property
-  def target(self):
-    """The target of the link as described by the local peer."""
-    return Terminus(pn_link_target(self._impl))
-
-  @property
-  def remote_source(self):
-    """The source of the link as described by the remote peer."""
-    return Terminus(pn_link_remote_source(self._impl))
-  @property
-  def remote_target(self):
-    """The target of the link as described by the remote peer."""
-    return Terminus(pn_link_remote_target(self._impl))
-
-  @property
-  def session(self):
-    return Session.wrap(pn_link_session(self._impl))
-
-  @property
-  def connection(self):
-    """The connection on which this link was attached."""
-    return self.session.connection
-
-  def delivery(self, tag):
-    return Delivery(pn_delivery(self._impl, tag))
-
-  @property
-  def current(self):
-    return Delivery.wrap(pn_link_current(self._impl))
-
-  def advance(self):
-    return pn_link_advance(self._impl)
-
-  @property
-  def unsettled(self):
-    return pn_link_unsettled(self._impl)
-
-  @property
-  def credit(self):
-    """The amount of outstanding credit on this link."""
-    return pn_link_credit(self._impl)
-
-  @property
-  def available(self):
-    return pn_link_available(self._impl)
-
-  @property
-  def queued(self):
-    return pn_link_queued(self._impl)
-
-  def next(self, mask):
-    return Link.wrap(pn_link_next(self._impl, mask))
-
-  @property
-  def name(self):
-    """Returns the name of the link"""
-    return utf82unicode(pn_link_name(self._impl))
-
-  @property
-  def is_sender(self):
-    """Returns true if this link is a sender."""
-    return pn_link_is_sender(self._impl)
-
-  @property
-  def is_receiver(self):
-    """Returns true if this link is a receiver."""
-    return pn_link_is_receiver(self._impl)
-
-  @property
-  def remote_snd_settle_mode(self):
-    return pn_link_remote_snd_settle_mode(self._impl)
-
-  @property
-  def remote_rcv_settle_mode(self):
-    return pn_link_remote_rcv_settle_mode(self._impl)
-
-  def _get_snd_settle_mode(self):
-    return pn_link_snd_settle_mode(self._impl)
-  def _set_snd_settle_mode(self, mode):
-    pn_link_set_snd_settle_mode(self._impl, mode)
-  snd_settle_mode = property(_get_snd_settle_mode, _set_snd_settle_mode)
-
-  def _get_rcv_settle_mode(self):
-    return pn_link_rcv_settle_mode(self._impl)
-  def _set_rcv_settle_mode(self, mode):
-    pn_link_set_rcv_settle_mode(self._impl, mode)
-  rcv_settle_mode = property(_get_rcv_settle_mode, _set_rcv_settle_mode)
-
-  def _get_drain(self):
-    return pn_link_get_drain(self._impl)
-
-  def _set_drain(self, b):
-    pn_link_set_drain(self._impl, bool(b))
-
-  drain_mode = property(_get_drain, _set_drain)
-
-  def drained(self):
-    return pn_link_drained(self._impl)
-
-  @property
-  def remote_max_message_size(self):
-    return pn_link_remote_max_message_size(self._impl)
-
-  def _get_max_message_size(self):
-    return pn_link_max_message_size(self._impl)
-  def _set_max_message_size(self, mode):
-    pn_link_set_max_message_size(self._impl, mode)
-  max_message_size = property(_get_max_message_size, _set_max_message_size)
-
-  def detach(self):
-    return pn_link_detach(self._impl)
-
-  def free(self):
-    pn_link_free(self._impl)
-
-class Terminus(object):
-
-  UNSPECIFIED = PN_UNSPECIFIED
-  SOURCE = PN_SOURCE
-  TARGET = PN_TARGET
-  COORDINATOR = PN_COORDINATOR
-
-  NONDURABLE = PN_NONDURABLE
-  CONFIGURATION = PN_CONFIGURATION
-  DELIVERIES = PN_DELIVERIES
-
-  DIST_MODE_UNSPECIFIED = PN_DIST_MODE_UNSPECIFIED
-  DIST_MODE_COPY = PN_DIST_MODE_COPY
-  DIST_MODE_MOVE = PN_DIST_MODE_MOVE
-
-  EXPIRE_WITH_LINK = PN_EXPIRE_WITH_LINK
-  EXPIRE_WITH_SESSION = PN_EXPIRE_WITH_SESSION
-  EXPIRE_WITH_CONNECTION = PN_EXPIRE_WITH_CONNECTION
-  EXPIRE_NEVER = PN_EXPIRE_NEVER
-
-  def __init__(self, impl):
-    self._impl = impl
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, LinkException)
-      raise exc("[%s]" % err)
-    else:
-      return err
-
-  def _get_type(self):
-    return pn_terminus_get_type(self._impl)
-  def _set_type(self, type):
-    self._check(pn_terminus_set_type(self._impl, type))
-  type = property(_get_type, _set_type)
-
-  def _get_address(self):
-    """The address that identifies the source or target node"""
-    return utf82unicode(pn_terminus_get_address(self._impl))
-  def _set_address(self, address):
-    self._check(pn_terminus_set_address(self._impl, unicode2utf8(address)))
-  address = property(_get_address, _set_address)
-
-  def _get_durability(self):
-    return pn_terminus_get_durability(self._impl)
-  def _set_durability(self, seconds):
-    self._check(pn_terminus_set_durability(self._impl, seconds))
-  durability = property(_get_durability, _set_durability)
-
-  def _get_expiry_policy(self):
-    return pn_terminus_get_expiry_policy(self._impl)
-  def _set_expiry_policy(self, seconds):
-    self._check(pn_terminus_set_expiry_policy(self._impl, seconds))
-  expiry_policy = property(_get_expiry_policy, _set_expiry_policy)
-
-  def _get_timeout(self):
-    return pn_terminus_get_timeout(self._impl)
-  def _set_timeout(self, seconds):
-    self._check(pn_terminus_set_timeout(self._impl, seconds))
-  timeout = property(_get_timeout, _set_timeout)
-
-  def _is_dynamic(self):
-    """Indicates whether the source or target node was dynamically
-    created"""
-    return pn_terminus_is_dynamic(self._impl)
-  def _set_dynamic(self, dynamic):
-    self._check(pn_terminus_set_dynamic(self._impl, dynamic))
-  dynamic = property(_is_dynamic, _set_dynamic)
-
-  def _get_distribution_mode(self):
-    return pn_terminus_get_distribution_mode(self._impl)
-  def _set_distribution_mode(self, mode):
-    self._check(pn_terminus_set_distribution_mode(self._impl, mode))
-  distribution_mode = property(_get_distribution_mode, _set_distribution_mode)
-
-  @property
-  def properties(self):
-    """Properties of a dynamic source or target."""
-    return Data(pn_terminus_properties(self._impl))
-
-  @property
-  def capabilities(self):
-    """Capabilities of the source or target."""
-    return Data(pn_terminus_capabilities(self._impl))
-
-  @property
-  def outcomes(self):
-    return Data(pn_terminus_outcomes(self._impl))
-
-  @property
-  def filter(self):
-    """A filter on a source allows the set of messages transfered over
-    the link to be restricted"""
-    return Data(pn_terminus_filter(self._impl))
-
-  def copy(self, src):
-    self._check(pn_terminus_copy(self._impl, src._impl))
-
-class Sender(Link):
-  """
-  A link over which messages are sent.
-  """
-
-  def offered(self, n):
-    pn_link_offered(self._impl, n)
-
-  def stream(self, data):
-    """
-    Send specified data as part of the current delivery
-
-    @type data: binary
-    @param data: data to send
-    """
-    return self._check(pn_link_send(self._impl, data))
-
-  def send(self, obj, tag=None):
-    """
-    Send specified object over this sender; the object is expected to
-    have a send() method on it that takes the sender and an optional
-    tag as arguments.
-
-    Where the object is a Message, this will send the message over
-    this link, creating a new delivery for the purpose.
-    """
-    if hasattr(obj, 'send'):
-      return obj.send(self, tag=tag)
-    else:
-      # treat object as bytes
-      return self.stream(obj)
-
-  def delivery_tag(self):
-    if not hasattr(self, 'tag_generator'):
-      def simple_tags():
-        count = 1
-        while True:
-          yield str(count)
-          count += 1
-      self.tag_generator = simple_tags()
-    return next(self.tag_generator)
-
-class Receiver(Link):
-  """
-  A link over which messages are received.
-  """
-
-  def flow(self, n):
-    """Increases the credit issued to the remote sender by the specified number of messages."""
-    pn_link_flow(self._impl, n)
-
-  def recv(self, limit):
-    n, binary = pn_link_recv(self._impl, limit)
-    if n == PN_EOS:
-      return None
-    else:
-      self._check(n)
-      return binary
-
-  def drain(self, n):
-    pn_link_drain(self._impl, n)
-
-  def draining(self):
-    return pn_link_draining(self._impl)
-
-class NamedInt(int):
-
-  values = {}
-
-  def __new__(cls, i, name):
-    ni = super(NamedInt, cls).__new__(cls, i)
-    cls.values[i] = ni
-    return ni
-
-  def __init__(self, i, name):
-    self.name = name
-
-  def __repr__(self):
-    return self.name
-
-  def __str__(self):
-    return self.name
-
-  @classmethod
-  def get(cls, i):
-    return cls.values.get(i, i)
-
-class DispositionType(NamedInt):
-  values = {}
-
-class Disposition(object):
-
-  RECEIVED = DispositionType(PN_RECEIVED, "RECEIVED")
-  ACCEPTED = DispositionType(PN_ACCEPTED, "ACCEPTED")
-  REJECTED = DispositionType(PN_REJECTED, "REJECTED")
-  RELEASED = DispositionType(PN_RELEASED, "RELEASED")
-  MODIFIED = DispositionType(PN_MODIFIED, "MODIFIED")
-
-  def __init__(self, impl, local):
-    self._impl = impl
-    self.local = local
-    self._data = None
-    self._condition = None
-    self._annotations = None
-
-  @property
-  def type(self):
-    return DispositionType.get(pn_disposition_type(self._impl))
-
-  def _get_section_number(self):
-    return pn_disposition_get_section_number(self._impl)
-  def _set_section_number(self, n):
-    pn_disposition_set_section_number(self._impl, n)
-  section_number = property(_get_section_number, _set_section_number)
-
-  def _get_section_offset(self):
-    return pn_disposition_get_section_offset(self._impl)
-  def _set_section_offset(self, n):
-    pn_disposition_set_section_offset(self._impl, n)
-  section_offset = property(_get_section_offset, _set_section_offset)
-
-  def _get_failed(self):
-    return pn_disposition_is_failed(self._impl)
-  def _set_failed(self, b):
-    pn_disposition_set_failed(self._impl, b)
-  failed = property(_get_failed, _set_failed)
-
-  def _get_undeliverable(self):
-    return pn_disposition_is_undeliverable(self._impl)
-  def _set_undeliverable(self, b):
-    pn_disposition_set_undeliverable(self._impl, b)
-  undeliverable = property(_get_undeliverable, _set_undeliverable)
-
-  def _get_data(self):
-    if self.local:
-      return self._data
-    else:
-      return dat2obj(pn_disposition_data(self._impl))
-  def _set_data(self, obj):
-    if self.local:
-      self._data = obj
-    else:
-      raise AttributeError("data attribute is read-only")
-  data = property(_get_data, _set_data)
-
-  def _get_annotations(self):
-    if self.local:
-      return self._annotations
-    else:
-      return dat2obj(pn_disposition_annotations(self._impl))
-  def _set_annotations(self, obj):
-    if self.local:
-      self._annotations = obj
-    else:
-      raise AttributeError("annotations attribute is read-only")
-  annotations = property(_get_annotations, _set_annotations)
-
-  def _get_condition(self):
-    if self.local:
-      return self._condition
-    else:
-      return cond2obj(pn_disposition_condition(self._impl))
-  def _set_condition(self, obj):
-    if self.local:
-      self._condition = obj
-    else:
-      raise AttributeError("condition attribute is read-only")
-  condition = property(_get_condition, _set_condition)
-
-class Delivery(Wrapper):
-  """
-  Tracks and/or records the delivery of a message over a link.
-  """
-
-  RECEIVED = Disposition.RECEIVED
-  ACCEPTED = Disposition.ACCEPTED
-  REJECTED = Disposition.REJECTED
-  RELEASED = Disposition.RELEASED
-  MODIFIED = Disposition.MODIFIED
-
-  @staticmethod
-  def wrap(impl):
-    if impl is None:
-      return None
-    else:
-      return Delivery(impl)
-
-  def __init__(self, impl):
-    Wrapper.__init__(self, impl, pn_delivery_attachments)
-
-  def _init(self):
-    self.local = Disposition(pn_delivery_local(self._impl), True)
-    self.remote = Disposition(pn_delivery_remote(self._impl), False)
-
-  @property
-  def tag(self):
-    """The identifier for the delivery."""
-    return pn_delivery_tag(self._impl)
-
-  @property
-  def writable(self):
-    """Returns true for an outgoing delivery to which data can now be written."""
-    return pn_delivery_writable(self._impl)
-
-  @property
-  def readable(self):
-    """Returns true for an incoming delivery that has data to read."""
-    return pn_delivery_readable(self._impl)
-
-  @property
-  def updated(self):
-    """Returns true if the state of the delivery has been updated
-    (e.g. it has been settled and/or accepted, rejected etc)."""
-    return pn_delivery_updated(self._impl)
-
-  def update(self, state):
-    """
-    Set the local state of the delivery e.g. ACCEPTED, REJECTED, RELEASED.
-    """
-    obj2dat(self.local._data, pn_disposition_data(self.local._impl))
-    obj2dat(self.local._annotations, pn_disposition_annotations(self.local._impl))
-    obj2cond(self.local._condition, pn_disposition_condition(self.local._impl))
-    pn_delivery_update(self._impl, state)
-
-  @property
-  def pending(self):
-    return pn_delivery_pending(self._impl)
-
-  @property
-  def partial(self):
-    """
-    Returns true for an incoming delivery if not all the data is
-    yet available.
-    """
-    return pn_delivery_partial(self._impl)
-
-  @property
-  def local_state(self):
-    """Returns the local state of the delivery."""
-    return DispositionType.get(pn_delivery_local_state(self._impl))
-
-  @property
-  def remote_state(self):
-    """
-    Returns the state of the delivery as indicated by the remote
-    peer.
-    """
-    return DispositionType.get(pn_delivery_remote_state(self._impl))
-
-  @property
-  def settled(self):
-    """
-    Returns true if the delivery has been settled by the remote peer.
-    """
-    return pn_delivery_settled(self._impl)
-
-  def settle(self):
-    """
-    Settles the delivery locally. This indicates the application
-    considers the delivery complete and does not wish to receive any
-    further events about it. Every delivery should be settled locally.
-    """
-    pn_delivery_settle(self._impl)
-
-  @property
-  def aborted(self):
-    """Returns true if the delivery has been aborted."""
-    return pn_delivery_aborted(self._impl)
-
-  def abort(self):
-    """
-    Aborts the delivery.  This indicates the application wishes to
-    invalidate any data that may have already been sent on this delivery.
-    The delivery cannot be aborted after it has been completely delivered.
-    """
-    pn_delivery_abort(self._impl)
-
-  @property
-  def work_next(self):
-    return Delivery.wrap(pn_work_next(self._impl))
-
-  @property
-  def link(self):
-    """
-    Returns the link on which the delivery was sent or received.
-    """
-    return Link.wrap(pn_delivery_link(self._impl))
-
-  @property
-  def session(self):
-    """
-    Returns the session over which the delivery was sent or received.
-    """
-    return self.link.session
-
-  @property
-  def connection(self):
-    """
-    Returns the connection over which the delivery was sent or received.
-    """
-    return self.session.connection
-
-  @property
-  def transport(self):
-    return self.connection.transport
-
-class TransportException(ProtonException):
-  pass
-
-class TraceAdapter:
-
-  def __init__(self, tracer):
-    self.tracer = tracer
-
-  def __call__(self, trans_impl, message):
-    self.tracer(Transport.wrap(trans_impl), message)
-
-class Transport(Wrapper):
-
-  TRACE_OFF = PN_TRACE_OFF
-  TRACE_DRV = PN_TRACE_DRV
-  TRACE_FRM = PN_TRACE_FRM
-  TRACE_RAW = PN_TRACE_RAW
-
-  CLIENT = 1
-  SERVER = 2
-
-  @staticmethod
-  def wrap(impl):
-    if impl is None:
-      return None
-    else:
-      return Transport(_impl=impl)
-
-  def __init__(self, mode=None, _impl = pn_transport):
-    Wrapper.__init__(self, _impl, pn_transport_attachments)
-    if mode == Transport.SERVER:
-      pn_transport_set_server(self._impl)
-    elif mode is None or mode==Transport.CLIENT:
-      pass
-    else:
-      raise TransportException("Cannot initialise Transport from mode: %s" % str(mode))
-
-  def _init(self):
-    self._sasl = None
-    self._ssl = None
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, TransportException)
-      raise exc("[%s]: %s" % (err, pn_error_text(pn_transport_error(self._impl))))
-    else:
-      return err
-
-  def _set_tracer(self, tracer):
-    pn_transport_set_pytracer(self._impl, TraceAdapter(tracer));
-
-  def _get_tracer(self):
-    adapter = pn_transport_get_pytracer(self._impl)
-    if adapter:
-      return adapter.tracer
-    else:
-      return None
-
-  tracer = property(_get_tracer, _set_tracer,
-                            doc="""
-A callback for trace logging. The callback is passed the transport and log message.
-""")
-
-  def log(self, message):
-    pn_transport_log(self._impl, message)
-
-  def require_auth(self, bool):
-    pn_transport_require_auth(self._impl, bool)
-
-  @property
-  def authenticated(self):
-    return pn_transport_is_authenticated(self._impl)
-
-  def require_encryption(self, bool):
-    pn_transport_require_encryption(self._impl, bool)
-
-  @property
-  def encrypted(self):
-    return pn_transport_is_encrypted(self._impl)
-
-  @property
-  def user(self):
-    return pn_transport_get_user(self._impl)
-
-  def bind(self, connection):
-    """Assign a connection to the transport"""
-    self._check(pn_transport_bind(self._impl, connection._impl))
-
-  def unbind(self):
-    """Release the connection"""
-    self._check(pn_transport_unbind(self._impl))
-
-  def trace(self, n):
-    pn_transport_trace(self._impl, n)
-
-  def tick(self, now):
-    """Process any timed events (like heartbeat generation).
-    now = seconds since epoch (float).
-    """
-    return millis2secs(pn_transport_tick(self._impl, secs2millis(now)))
-
-  def capacity(self):
-    c = pn_transport_capacity(self._impl)
-    if c >= PN_EOS:
-      return c
-    else:
-      return self._check(c)
-
-  def push(self, binary):
-    n = self._check(pn_transport_push(self._impl, binary))
-    if n != len(binary):
-      raise OverflowError("unable to process all bytes: %s, %s" % (n, len(binary)))
-
-  def close_tail(self):
-    self._check(pn_transport_close_tail(self._impl))
-
-  def pending(self):
-    p = pn_transport_pending(self._impl)
-    if p >= PN_EOS:
-      return p
-    else:
-      return self._check(p)
-
-  def peek(self, size):
-    cd, out = pn_transport_peek(self._impl, size)
-    if cd == PN_EOS:
-      return None
-    else:
-      self._check(cd)
-      return out
-
-  def pop(self, size):
-    pn_transport_pop(self._impl, size)
-
-  def close_head(self):
-    self._check(pn_transport_close_head(self._impl))
-
-  @property
-  def closed(self):
-    return pn_transport_closed(self._impl)
-
-  # AMQP 1.0 max-frame-size
-  def _get_max_frame_size(self):
-    return pn_transport_get_max_frame(self._impl)
-
-  def _set_max_frame_size(self, value):
-    pn_transport_set_max_frame(self._impl, value)
-
-  max_frame_size = property(_get_max_frame_size, _set_max_frame_size,
-                            doc="""
-Sets the maximum size for received frames (in bytes).
-""")
-
-  @property
-  def remote_max_frame_size(self):
-    return pn_transport_get_remote_max_frame(self._impl)
-
-  def _get_channel_max(self):
-    return pn_transport_get_channel_max(self._impl)
-
-  def _set_channel_max(self, value):
-    if pn_transport_set_channel_max(self._impl, value):
-      raise SessionException("Too late to change channel max.")
-
-  channel_max = property(_get_channel_max, _set_channel_max,
-                         doc="""
-Sets the maximum channel that may be used on the transport.
-""")
-
-  @property
-  def remote_channel_max(self):
-    return pn_transport_remote_channel_max(self._impl)
-
-  # AMQP 1.0 idle-time-out
-  def _get_idle_timeout(self):
-    return millis2secs(pn_transport_get_idle_timeout(self._impl))
-
-  def _set_idle_timeout(self, sec):
-    pn_transport_set_idle_timeout(self._impl, secs2millis(sec))
-
-  idle_timeout = property(_get_idle_timeout, _set_idle_timeout,
-                          doc="""
-The idle timeout of the connection (float, in seconds).
-""")
-
-  @property
-  def remote_idle_timeout(self):
-    return millis2secs(pn_transport_get_remote_idle_timeout(self._impl))
-
-  @property
-  def frames_output(self):
-    return pn_transport_get_frames_output(self._impl)
-
-  @property
-  def frames_input(self):
-    return pn_transport_get_frames_input(self._impl)
-
-  def sasl(self):
-    return SASL(self)
-
-  def ssl(self, domain=None, session_details=None):
-    # SSL factory (singleton for this transport)
-    if not self._ssl:
-      self._ssl = SSL(self, domain, session_details)
-    return self._ssl
-
-  @property
-  def condition(self):
-    return cond2obj(pn_transport_condition(self._impl))
-
-  @property
-  def connection(self):
-    return Connection.wrap(pn_transport_connection(self._impl))
-
-class SASLException(TransportException):
-  pass
-
-class SASL(Wrapper):
-
-  OK = PN_SASL_OK
-  AUTH = PN_SASL_AUTH
-  SYS = PN_SASL_SYS
-  PERM = PN_SASL_PERM
-  TEMP = PN_SASL_TEMP
-
-  @staticmethod
-  def extended():
-    return pn_sasl_extended()
-
-  def __init__(self, transport):
-    Wrapper.__init__(self, transport._impl, pn_transport_attachments)
-    self._sasl = pn_sasl(transport._impl)
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, SASLException)
-      raise exc("[%s]" % (err))
-    else:
-      return err
-
-  @property
-  def user(self):
-    return pn_sasl_get_user(self._sasl)
-
-  @property
-  def mech(self):
-    return pn_sasl_get_mech(self._sasl)
-
-  @property
-  def outcome(self):
-    outcome = pn_sasl_outcome(self._sasl)
-    if outcome == PN_SASL_NONE:
-      return None
-    else:
-      return outcome
-
-  def allowed_mechs(self, mechs):
-    pn_sasl_allowed_mechs(self._sasl, unicode2utf8(mechs))
-
-  def _get_allow_insecure_mechs(self):
-    return pn_sasl_get_allow_insecure_mechs(self._sasl)
-
-  def _set_allow_insecure_mechs(self, insecure):
-    pn_sasl_set_allow_insecure_mechs(self._sasl, insecure)
-
-  allow_insecure_mechs = property(_get_allow_insecure_mechs, _set_allow_insecure_mechs,
-                                  doc="""
-Allow unencrypted cleartext passwords (PLAIN mech)
-""")
-
-  def done(self, outcome):
-    pn_sasl_done(self._sasl, outcome)
-
-  def config_name(self, name):
-    pn_sasl_config_name(self._sasl, name)
-
-  def config_path(self, path):
-    pn_sasl_config_path(self._sasl, path)
-
-class SSLException(TransportException):
-  pass
-
-class SSLUnavailable(SSLException):
-  pass
-
-class SSLDomain(object):
-
-  MODE_CLIENT = PN_SSL_MODE_CLIENT
-  MODE_SERVER = PN_SSL_MODE_SERVER
-  VERIFY_PEER = PN_SSL_VERIFY_PEER
-  VERIFY_PEER_NAME = PN_SSL_VERIFY_PEER_NAME
-  ANONYMOUS_PEER = PN_SSL_ANONYMOUS_PEER
-
-  def __init__(self, mode):
-    self._domain = pn_ssl_domain(mode)
-    if self._domain is None:
-      raise SSLUnavailable()
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, SSLException)
-      raise exc("SSL failure.")
-    else:
-      return err
-
-  def set_credentials(self, cert_file, key_file, password):
-    return self._check( pn_ssl_domain_set_credentials(self._domain,
-                                                      cert_file, key_file,
-                                                      password) )
-  def set_trusted_ca_db(self, certificate_db):
-    return self._check( pn_ssl_domain_set_trusted_ca_db(self._domain,
-                                                        certificate_db) )
-  def set_peer_authentication(self, verify_mode, trusted_CAs=None):
-    return self._check( pn_ssl_domain_set_peer_authentication(self._domain,
-                                                              verify_mode,
-                                                              trusted_CAs) )
-
-  def allow_unsecured_client(self):
-    return self._check( pn_ssl_domain_allow_unsecured_client(self._domain) )
-
-  def __del__(self):
-    pn_ssl_domain_free(self._domain)
-
-class SSL(object):
-
-  @staticmethod
-  def present():
-    return pn_ssl_present()
-
-  def _check(self, err):
-    if err < 0:
-      exc = EXCEPTIONS.get(err, SSLException)
-      raise exc("SSL failure.")
-    else:
-      return err
-
-  def __new__(cls, transport, domain, session_details=None):
-    """Enforce a singleton SSL object per Transport"""
-    if transport._ssl:
-      # unfortunately, we've combined the allocation and the configuration in a
-      # single step.  So catch any attempt by the application to provide what
-      # may be a different configuration than the original (hack)
-      ssl = transport._ssl
-      if (domain and (ssl._domain is not domain) or
-          session_details and (ssl._session_details is not session_details)):
-        raise SSLException("Cannot re-configure existing SSL object!")
-    else:
-      obj = super(SSL, cls).__new__(cls)
-      obj._domain = domain
-      obj._session_details = session_details
-      session_id = None
-      if session_details:
-        session_id = session_details.get_session_id()
-      obj._ssl = pn_ssl( transport._impl )
-      if obj._ssl is None:
-        raise SSLUnavailable()
-      if domain:
-        pn_ssl_init( obj._ssl, domain._domain, session_id )
-      transport._ssl = obj
-    return transport._ssl
-
-  def cipher_name(self):
-    rc, name = pn_ssl_get_cipher_name( self._ssl, 128 )
-    if rc:
-      return name
-    return None
-
-  def protocol_name(self):
-    rc, name = pn_ssl_get_protocol_name( self._ssl, 128 )
-    if rc:
-      return name
-    return None
-
-  SHA1 = PN_SSL_SHA1
-  SHA256 = PN_SSL_SHA256
-  SHA512 = PN_SSL_SHA512
-  MD5 = PN_SSL_MD5
-
-  CERT_COUNTRY_NAME = PN_SSL_CERT_SUBJECT_COUNTRY_NAME
-  CERT_STATE_OR_PROVINCE = PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE
-  CERT_CITY_OR_LOCALITY = PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY
-  CERT_ORGANIZATION_NAME = PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME
-  CERT_ORGANIZATION_UNIT = PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT
-  CERT_COMMON_NAME = PN_SSL_CERT_SUBJECT_COMMON_NAME
-
-  def get_cert_subject_subfield(self, subfield_name):
-    subfield_value = pn_ssl_get_remote_subject_subfield(self._ssl, subfield_name)
-    return subfield_value
-
-  def get_cert_subject(self):
-    subject = pn_ssl_get_remote_subject(self._ssl)
-    return subject
-
-  def _get_cert_subject_unknown_subfield(self):
-    # Pass in an unhandled enum
-    return self.get_cert_subject_subfield(10)
-
-  # Convenience functions for obtaining the subfields of the subject field.
-  def get_cert_common_name(self):
-    return self.get_cert_subject_subfield(SSL.CERT_COMMON_NAME)
-
-  def get_cert_organization(self):
-    return self.get_cert_subject_subfield(SSL.CERT_ORGANIZATION_NAME)
-
-  def get_cert_organization_unit(self):
-    return self.get_cert_subject_subfield(SSL.CERT_ORGANIZATION_UNIT)
-
-  def get_cert_locality_or_city(self):
-    return self.get_cert_subject_subfield(SSL.CERT_CITY_OR_LOCALITY)
-
-  def get_cert_country(self):
-    return self.get_cert_subject_subfield(SSL.CERT_COUNTRY_NAME)
-
-  def get_cert_state_or_province(self):
-    return self.get_cert_subject_subfield(SSL.CERT_STATE_OR_PROVINCE)
-
-  def get_cert_fingerprint(self, fingerprint_length, digest_name):
-    rc, fingerprint_str = pn_ssl_get_cert_fingerprint(self._ssl, fingerprint_length, digest_name)
-    if rc == PN_OK:
-      return fingerprint_str
-    return None
-
-  # Convenience functions for obtaining fingerprint for specific hashing algorithms
-  def _get_cert_fingerprint_unknown_hash_alg(self):
-    return self.get_cert_fingerprint(41, 10)
-
-  def get_cert_fingerprint_sha1(self):
-    return self.get_cert_fingerprint(41, SSL.SHA1)
-
-  def get_cert_fingerprint_sha256(self):
-    # sha256 produces a fingerprint that is 64 characters long
-    return self.get_cert_fingerprint(65, SSL.SHA256)
-
-  def get_cert_fingerprint_sha512(self):
-    # sha512 produces a fingerprint that is 128 characters long
-    return self.get_cert_fingerprint(129, SSL.SHA512)
-
-  def get_cert_fingerprint_md5(self):
-    return self.get_cert_fingerprint(33, SSL.MD5)
-
-  @property
-  def remote_subject(self):
-    return pn_ssl_get_remote_subject( self._ssl )
-
-  RESUME_UNKNOWN = PN_SSL_RESUME_UNKNOWN
-  RESUME_NEW = PN_SSL_RESUME_NEW
-  RESUME_REUSED = PN_SSL_RESUME_REUSED
-
-  def resume_status(self):
-    return pn_ssl_resume_status( self._ssl )
-
-  def _set_peer_hostname(self, hostname):
-    self._check(pn_ssl_set_peer_hostname( self._ssl, unicode2utf8(hostname) ))
-  def 

<TRUNCATED>

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org