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 2023/01/27 18:06:15 UTC

[qpid-proton] 01/02: PROTON-2095: Use CFFI instead of SWIG for python binding

This is an automated email from the ASF dual-hosted git repository.

astitcher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git

commit 66cac760950779a8655a12337ba4fe663800a94e
Author: Andrew Stitcher <as...@apache.org>
AuthorDate: Thu Jan 5 18:02:49 2023 -0500

    PROTON-2095: Use CFFI instead of SWIG for python binding
    
    Build an extension library cproton_ffi which can be used by a cproton
    module to interface to libqpid_proton_core.
    
    The intention is that the higher level code continue to use cproton
    as before but the cproton module is now written manually to
    interface to the ffi code instead if being written by swig.
---
 CMakeLists.txt                      |  18 +-
 python/CMakeLists.txt               |  39 +-
 python/MANIFEST.in                  |   4 +-
 python/cproton.h                    | 682 +++++++++++++++++++++++++++++++++
 python/cproton.i                    | 497 ------------------------
 python/cproton.py                   | 729 ++++++++++++++++++++++++++++++++++++
 python/ext_build.py                 | 195 ++++++++++
 python/proton/_common.py            |  35 +-
 python/proton/_condition.py         |   2 +-
 python/proton/_data.py              |  12 +-
 python/proton/_delivery.py          |   5 +-
 python/proton/_endpoints.py         |  45 +--
 python/proton/_events.py            |  29 +-
 python/proton/_message.py           |  38 +-
 python/proton/_reactor.py           |  10 +-
 python/proton/_transport.py         |  37 +-
 python/proton/_wrapper.py           |  28 +-
 python/pyproject.toml               |  44 +++
 python/setup.cfg                    |   3 +
 python/setup.py                     | 177 +--------
 python/setuputils/PYZMQ_LICENSE.BSD |  32 --
 python/setuputils/__init__.py       |   0
 python/setuputils/log.py            |  46 ---
 python/setuputils/misc.py           |  85 -----
 24 files changed, 1768 insertions(+), 1024 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 30d59bdf9..58624b587 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -345,7 +345,7 @@ add_subdirectory(c)
 
 # Add bindings that do not require swig here - the directory name must be the same as the binding name
 # See below for swig bindings
-set(BINDINGS cpp go)
+set(BINDINGS cpp go python)
 
 if (CMAKE_CXX_COMPILER)
   set (DEFAULT_CPP ON)
@@ -363,9 +363,14 @@ if (GO_EXE)
   endif()
 endif (GO_EXE)
 
+# Prerequisites for Python wrapper:
+if (Python_Development_FOUND)
+  set (DEFAULT_PYTHON ON)
+endif ()
+
 if(SWIG_FOUND)
   # Add any new swig bindings here - the directory name must be the same as the binding name
-  list(APPEND BINDINGS python ruby)
+  list(APPEND BINDINGS ruby)
 
   include(UseSWIG)
 
@@ -380,11 +385,6 @@ if(SWIG_FOUND)
   # If the prerequisites for the binding are present set a variable called
   # DEFAULT_{uppercase name of binding} to ON
 
-  # Prerequisites for Python wrapper:
-  if (Python_Development_FOUND)
-    set (DEFAULT_PYTHON ON)
-  endif ()
-
   # Prerequisites for Ruby:
   find_package(Ruby)
   if (RUBY_FOUND)
@@ -400,7 +400,7 @@ endif()
 # BUILD_XXX variables to enable/disable bindings.
 #
 if (NOT DEFINED BUILD_BINDINGS)
-    set(BUILD_BINDINGS "${BINDINGS}")
+  set(BUILD_BINDINGS "${BINDINGS}")
 endif()
 
 foreach(BINDING ${BINDINGS})
@@ -411,7 +411,7 @@ foreach(BINDING ${BINDINGS})
   endif()
   option(BUILD_${UBINDING} "Build ${BINDING} language binding" ${DEFAULT_${UBINDING}})
   if (BUILD_${UBINDING})
-      add_subdirectory(${BINDING})
+    add_subdirectory(${BINDING})
   endif ()
 endforeach(BINDING)
 
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 305255415..af4b17631 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -17,39 +17,8 @@
 # under the License.
 #
 
-# NB For python the SWIG module name must have the same name as the input .i file for CMake to generate the
-# correct dependencies
-
-set(CMAKE_SWIG_FLAGS "-threads" "-DUINTPTR_SIZE=${CMAKE_SIZEOF_VOID_P}")
-
 include_directories (${PN_C_INCLUDE_DIR} ${Python_INCLUDE_DIRS})
 
-set_source_files_properties(cproton.i PROPERTIES CPLUSPLUS NO)
-
-# Suppress warnings in swig generated code.
-set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
-
-# Set Compiler extra flags for Solaris when using SunStudio
-if (CMAKE_C_COMPILER_ID STREQUAL "SunPro")
-    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSWIGEXPORT=__global")
-endif ()
-
-
-list(APPEND SWIG_MODULE_cproton_EXTRA_DEPS
-    ${CMAKE_SOURCE_DIR}/c/include/proton/cproton.i
-    ${PROTON_HEADERS}
-)
-
-set(SWIG_OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
-
-swig_add_library(cproton LANGUAGE python SOURCES cproton.i)
-swig_link_libraries(cproton ${BINDING_DEPS} ${Python_LIBRARIES} -lm)
-set_target_properties(${SWIG_MODULE_cproton_REAL_NAME}
-    PROPERTIES
-    LINK_FLAGS "${CATCH_UNDEFINED}")
-
-set (cproton-generated cprotonPYTHON_wrap.c cproton.py)
-set (pysrc-generated cproton.py)
 set (pysrc
     proton/__init__.py
     proton/_common.py
@@ -80,12 +49,12 @@ set (pysrc
 set(py_dist_files
     setup.py
     setup.cfg
+    pyproject.toml
     README.rst
     MANIFEST.in
-    setuputils/__init__.py
-    setuputils/log.py
-    setuputils/misc.py
-    setuputils/PYZMQ_LICENSE.BSD
+    ext_build.py
+    cproton.h
+    cproton.py
     docs/conf.py
     docs/index.rst
     docs/overview.rst
diff --git a/python/MANIFEST.in b/python/MANIFEST.in
index 35558b54d..b99a0e8dc 100644
--- a/python/MANIFEST.in
+++ b/python/MANIFEST.in
@@ -1,6 +1,8 @@
 include VERSION.txt
+include ext_build.py
+include cproton.h
+include cproton.py
 graft docs
-graft setuputils
 graft src
 graft include
 global-exclude *.pyc *.pyo
diff --git a/python/cproton.h b/python/cproton.h
new file mode 100644
index 000000000..3f6b7ad39
--- /dev/null
+++ b/python/cproton.h
@@ -0,0 +1,682 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+typedef struct pn_bytes_t
+{
+  size_t size;
+  const char *start;
+} pn_bytes_t;
+typedef uint32_t pn_char_t;
+typedef struct pn_collector_t pn_collector_t;
+typedef struct pn_condition_t pn_condition_t;
+typedef struct pn_connection_t pn_connection_t;
+typedef struct pn_data_t pn_data_t;
+typedef struct 
+{
+  char bytes[16];
+} pn_decimal128_t;
+typedef uint32_t pn_decimal32_t;
+typedef uint64_t pn_decimal64_t;
+typedef struct pn_delivery_t pn_delivery_t;
+typedef pn_bytes_t pn_delivery_tag_t;
+typedef struct pn_disposition_t pn_disposition_t;
+typedef enum 
+{
+  PN_DIST_MODE_UNSPECIFIED = 0,
+  PN_DIST_MODE_COPY = 1,
+  PN_DIST_MODE_MOVE = 2
+} pn_distribution_mode_t;
+typedef enum 
+{
+  PN_NONDURABLE = 0,
+  PN_CONFIGURATION = 1,
+  PN_DELIVERIES = 2
+} pn_durability_t;
+typedef struct pn_error_t pn_error_t;
+typedef struct pn_event_t pn_event_t;
+typedef enum 
+{
+  PN_EVENT_NONE = 0,
+  PN_REACTOR_INIT,
+  PN_REACTOR_QUIESCED,
+  PN_REACTOR_FINAL,
+  PN_TIMER_TASK,
+  PN_CONNECTION_INIT,
+  PN_CONNECTION_BOUND,
+  PN_CONNECTION_UNBOUND,
+  PN_CONNECTION_LOCAL_OPEN,
+  PN_CONNECTION_REMOTE_OPEN,
+  PN_CONNECTION_LOCAL_CLOSE,
+  PN_CONNECTION_REMOTE_CLOSE,
+  PN_CONNECTION_FINAL,
+  PN_SESSION_INIT,
+  PN_SESSION_LOCAL_OPEN,
+  PN_SESSION_REMOTE_OPEN,
+  PN_SESSION_LOCAL_CLOSE,
+  PN_SESSION_REMOTE_CLOSE,
+  PN_SESSION_FINAL,
+  PN_LINK_INIT,
+  PN_LINK_LOCAL_OPEN,
+  PN_LINK_REMOTE_OPEN,
+  PN_LINK_LOCAL_CLOSE,
+  PN_LINK_REMOTE_CLOSE,
+  PN_LINK_LOCAL_DETACH,
+  PN_LINK_REMOTE_DETACH,
+  PN_LINK_FLOW,
+  PN_LINK_FINAL,
+  PN_DELIVERY,
+  PN_TRANSPORT,
+  PN_TRANSPORT_AUTHENTICATED,
+  PN_TRANSPORT_ERROR,
+  PN_TRANSPORT_HEAD_CLOSED,
+  PN_TRANSPORT_TAIL_CLOSED,
+  PN_TRANSPORT_CLOSED,
+  PN_SELECTABLE_INIT,
+  PN_SELECTABLE_UPDATED,
+  PN_SELECTABLE_READABLE,
+  PN_SELECTABLE_WRITABLE,
+  PN_SELECTABLE_ERROR,
+  PN_SELECTABLE_EXPIRED,
+  PN_SELECTABLE_FINAL,
+  PN_CONNECTION_WAKE,
+  PN_LISTENER_ACCEPT,
+  PN_LISTENER_CLOSE,
+  PN_PROACTOR_INTERRUPT,
+  PN_PROACTOR_TIMEOUT,
+  PN_PROACTOR_INACTIVE,
+  PN_LISTENER_OPEN,
+  PN_RAW_CONNECTION_CONNECTED,
+  PN_RAW_CONNECTION_CLOSED_READ,
+  PN_RAW_CONNECTION_CLOSED_WRITE,
+  PN_RAW_CONNECTION_DISCONNECTED,
+  PN_RAW_CONNECTION_NEED_READ_BUFFERS,
+  PN_RAW_CONNECTION_NEED_WRITE_BUFFERS,
+  PN_RAW_CONNECTION_READ,
+  PN_RAW_CONNECTION_WRITTEN,
+  PN_RAW_CONNECTION_WAKE,
+  PN_RAW_CONNECTION_DRAIN_BUFFERS
+} pn_event_type_t;
+typedef enum 
+{
+  PN_EXPIRE_WITH_LINK,
+  PN_EXPIRE_WITH_SESSION,
+  PN_EXPIRE_WITH_CONNECTION,
+  PN_EXPIRE_NEVER
+} pn_expiry_policy_t;
+typedef struct pn_link_t pn_link_t;
+typedef struct pn_message_t pn_message_t;
+typedef uint32_t pn_millis_t;
+typedef enum
+{
+  PN_RCV_FIRST = 0,
+  PN_RCV_SECOND = 1
+} pn_rcv_settle_mode_t;
+typedef struct pn_record_t pn_record_t;
+typedef enum 
+{
+  PN_SASL_NONE = -1,
+  PN_SASL_OK = 0,
+  PN_SASL_AUTH = 1,
+  PN_SASL_SYS = 2,
+  PN_SASL_PERM = 3,
+  PN_SASL_TEMP = 4
+} pn_sasl_outcome_t;
+typedef struct pn_sasl_t pn_sasl_t;
+typedef uint32_t pn_seconds_t;
+typedef uint32_t pn_sequence_t;
+typedef struct pn_session_t pn_session_t;
+typedef enum 
+{
+  PN_SND_UNSETTLED = 0,
+  PN_SND_SETTLED = 1,
+  PN_SND_MIXED = 2
+} pn_snd_settle_mode_t;
+typedef enum 
+{
+  PN_SSL_CERT_SUBJECT_COUNTRY_NAME,
+  PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE,
+  PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY,
+  PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME,
+  PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT,
+  PN_SSL_CERT_SUBJECT_COMMON_NAME
+} pn_ssl_cert_subject_subfield;
+typedef struct pn_ssl_domain_t pn_ssl_domain_t;
+typedef enum 
+{
+  PN_SSL_SHA1,
+  PN_SSL_SHA256,
+  PN_SSL_SHA512,
+  PN_SSL_MD5
+} pn_ssl_hash_alg;
+typedef enum 
+{
+  PN_SSL_MODE_CLIENT = 1,
+  PN_SSL_MODE_SERVER
+} pn_ssl_mode_t;
+typedef enum 
+{
+  PN_SSL_RESUME_UNKNOWN,
+  PN_SSL_RESUME_NEW,
+  PN_SSL_RESUME_REUSED
+} pn_ssl_resume_status_t;
+typedef struct pn_ssl_t pn_ssl_t;
+typedef enum 
+{
+  PN_SSL_VERIFY_NULL = 0,
+  PN_SSL_VERIFY_PEER,
+  PN_SSL_ANONYMOUS_PEER,
+  PN_SSL_VERIFY_PEER_NAME
+} pn_ssl_verify_mode_t;
+typedef int pn_state_t;
+typedef struct pn_terminus_t pn_terminus_t;
+typedef enum 
+{
+  PN_UNSPECIFIED = 0,
+  PN_SOURCE = 1,
+  PN_TARGET = 2,
+  PN_COORDINATOR = 3
+} pn_terminus_type_t;
+typedef int64_t pn_timestamp_t;
+typedef int pn_trace_t;
+typedef struct pn_transport_t pn_transport_t;
+typedef enum 
+{
+  PN_NULL = 1,
+  PN_BOOL = 2,
+  PN_UBYTE = 3,
+  PN_BYTE = 4,
+  PN_USHORT = 5,
+  PN_SHORT = 6,
+  PN_UINT = 7,
+  PN_INT = 8,
+  PN_CHAR = 9,
+  PN_ULONG = 10,
+  PN_LONG = 11,
+  PN_TIMESTAMP = 12,
+  PN_FLOAT = 13,
+  PN_DOUBLE = 14,
+  PN_DECIMAL32 = 15,
+  PN_DECIMAL64 = 16,
+  PN_DECIMAL128 = 17,
+  PN_UUID = 18,
+  PN_BINARY = 19,
+  PN_STRING = 20,
+  PN_SYMBOL = 21,
+  PN_DESCRIBED = 22,
+  PN_ARRAY = 23,
+  PN_LIST = 24,
+  PN_MAP = 25,
+  PN_INVALID = -1
+} pn_type_t;
+typedef struct 
+{
+  char bytes[16];
+} pn_uuid_t;
+typedef struct {
+  pn_type_t type;
+  union {
+    _Bool as_bool;
+    uint8_t as_ubyte;
+    int8_t as_byte;
+    uint16_t as_ushort;
+    int16_t as_short;
+    uint32_t as_uint;
+    int32_t as_int;
+    pn_char_t as_char;
+    uint64_t as_ulong;
+    int64_t as_long;
+    pn_timestamp_t as_timestamp;
+    float as_float;
+    double as_double;
+    pn_decimal32_t as_decimal32;
+    pn_decimal64_t as_decimal64;
+    pn_decimal128_t as_decimal128;
+    pn_uuid_t as_uuid;
+    pn_bytes_t as_bytes;
+  } u;
+} pn_atom_t;
+typedef pn_atom_t pn_msgid_t;
+typedef void (*pn_tracer_t)(pn_transport_t *transport, const char *message);
+
+pn_collector_t *pn_collector(void);
+void pn_collector_free(pn_collector_t *collector);
+_Bool pn_collector_more(pn_collector_t *collector);
+pn_event_t *pn_collector_peek(pn_collector_t *collector);
+_Bool pn_collector_pop(pn_collector_t *collector);
+void pn_collector_release(pn_collector_t *collector);
+
+void pn_condition_clear(pn_condition_t *condition);
+const char *pn_condition_get_description(pn_condition_t *condition);
+const char *pn_condition_get_name(pn_condition_t *condition);
+pn_data_t *pn_condition_info(pn_condition_t *condition);
+_Bool pn_condition_is_set(pn_condition_t *condition);
+int pn_condition_set_description(pn_condition_t *condition, const char *description);
+int pn_condition_set_name(pn_condition_t *condition, const char *name);
+
+pn_connection_t *pn_connection(void);
+pn_record_t *pn_connection_attachments(pn_connection_t *connection);
+void pn_connection_close(pn_connection_t *connection);
+void pn_connection_collect(pn_connection_t *connection, pn_collector_t *collector);
+pn_condition_t *pn_connection_condition(pn_connection_t *connection);
+pn_data_t *pn_connection_desired_capabilities(pn_connection_t *connection);
+pn_error_t *pn_connection_error(pn_connection_t *connection);
+const char *pn_connection_get_authorization(pn_connection_t *connection);
+const char *pn_connection_get_container(pn_connection_t *connection);
+const char *pn_connection_get_hostname(pn_connection_t *connection);
+const char *pn_connection_get_user(pn_connection_t *connection);
+pn_data_t *pn_connection_offered_capabilities(pn_connection_t *connection);
+void pn_connection_open(pn_connection_t *connection);
+pn_data_t *pn_connection_properties(pn_connection_t *connection);
+void pn_connection_release(pn_connection_t *connection);
+pn_condition_t *pn_connection_remote_condition(pn_connection_t *connection);
+const char *pn_connection_remote_container(pn_connection_t *connection);
+pn_data_t *pn_connection_remote_desired_capabilities(pn_connection_t *connection);
+const char *pn_connection_remote_hostname(pn_connection_t *connection);
+pn_data_t *pn_connection_remote_offered_capabilities(pn_connection_t *connection);
+pn_data_t *pn_connection_remote_properties(pn_connection_t *connection);
+void pn_connection_set_authorization(pn_connection_t *connection, const char *authzid);
+void pn_connection_set_container(pn_connection_t *connection, const char *container);
+void pn_connection_set_hostname(pn_connection_t *connection, const char *hostname);
+void pn_connection_set_password(pn_connection_t *connection, const char *password);
+void pn_connection_set_user(pn_connection_t *connection, const char *user);
+pn_state_t pn_connection_state(pn_connection_t *connection);
+pn_transport_t *pn_connection_transport(pn_connection_t *connection);
+
+pn_data_t *pn_data(size_t capacity);
+void pn_data_clear(pn_data_t *data);
+int pn_data_copy(pn_data_t *data, pn_data_t *src);
+ssize_t pn_data_decode(pn_data_t *data, const char *bytes, size_t size);
+void pn_data_dump(pn_data_t *data);
+ssize_t pn_data_encode(pn_data_t *data, char *bytes, size_t size);
+ssize_t pn_data_encoded_size(pn_data_t *data);
+_Bool pn_data_enter(pn_data_t *data);
+pn_error_t *pn_data_error(pn_data_t *data);
+_Bool pn_data_exit(pn_data_t *data);
+void pn_data_free(pn_data_t *data);
+size_t pn_data_get_array(pn_data_t *data);
+pn_type_t pn_data_get_array_type(pn_data_t *data);
+pn_bytes_t pn_data_get_binary(pn_data_t *data);
+_Bool pn_data_get_bool(pn_data_t *data);
+int8_t pn_data_get_byte(pn_data_t *data);
+pn_char_t pn_data_get_char(pn_data_t *data);
+pn_decimal128_t pn_data_get_decimal128(pn_data_t *data);
+pn_decimal32_t pn_data_get_decimal32(pn_data_t *data);
+pn_decimal64_t pn_data_get_decimal64(pn_data_t *data);
+double pn_data_get_double(pn_data_t *data);
+float pn_data_get_float(pn_data_t *data);
+int32_t pn_data_get_int(pn_data_t *data);
+size_t pn_data_get_list(pn_data_t *data);
+int64_t pn_data_get_long(pn_data_t *data);
+size_t pn_data_get_map(pn_data_t *data);
+int16_t pn_data_get_short(pn_data_t *data);
+pn_bytes_t pn_data_get_string(pn_data_t *data);
+pn_bytes_t pn_data_get_symbol(pn_data_t *data);
+pn_timestamp_t pn_data_get_timestamp(pn_data_t *data);
+uint8_t pn_data_get_ubyte(pn_data_t *data);
+uint32_t pn_data_get_uint(pn_data_t *data);
+uint64_t pn_data_get_ulong(pn_data_t *data);
+uint16_t pn_data_get_ushort(pn_data_t *data);
+pn_uuid_t pn_data_get_uuid(pn_data_t *data);
+_Bool pn_data_is_array_described(pn_data_t *data);
+_Bool pn_data_is_described(pn_data_t *data);
+_Bool pn_data_is_null(pn_data_t *data);
+_Bool pn_data_lookup(pn_data_t *data, const char *name);
+void pn_data_narrow(pn_data_t *data);
+_Bool pn_data_next(pn_data_t *data);
+_Bool pn_data_prev(pn_data_t *data);
+int pn_data_put_array(pn_data_t *data, _Bool described, pn_type_t type);
+int pn_data_put_binary(pn_data_t *data, pn_bytes_t bytes);
+int pn_data_put_bool(pn_data_t *data, _Bool b);
+int pn_data_put_byte(pn_data_t *data, int8_t b);
+int pn_data_put_char(pn_data_t *data, pn_char_t c);
+int pn_data_put_decimal128(pn_data_t *data, pn_decimal128_t d);
+int pn_data_put_decimal32(pn_data_t *data, pn_decimal32_t d);
+int pn_data_put_decimal64(pn_data_t *data, pn_decimal64_t d);
+int pn_data_put_described(pn_data_t *data);
+int pn_data_put_double(pn_data_t *data, double d);
+int pn_data_put_float(pn_data_t *data, float f);
+int pn_data_put_int(pn_data_t *data, int32_t i);
+int pn_data_put_list(pn_data_t *data);
+int pn_data_put_long(pn_data_t *data, int64_t l);
+int pn_data_put_map(pn_data_t *data);
+int pn_data_put_null(pn_data_t *data);
+int pn_data_put_short(pn_data_t *data, int16_t s);
+int pn_data_put_string(pn_data_t *data, pn_bytes_t string);
+int pn_data_put_symbol(pn_data_t *data, pn_bytes_t symbol);
+int pn_data_put_timestamp(pn_data_t *data, pn_timestamp_t t);
+int pn_data_put_ubyte(pn_data_t *data, uint8_t ub);
+int pn_data_put_uint(pn_data_t *data, uint32_t ui);
+int pn_data_put_ulong(pn_data_t *data, uint64_t ul);
+int pn_data_put_ushort(pn_data_t *data, uint16_t us);
+int pn_data_put_uuid(pn_data_t *data, pn_uuid_t u);
+void pn_data_rewind(pn_data_t *data);
+pn_type_t pn_data_type(pn_data_t *data);
+void pn_data_widen(pn_data_t *data);
+
+int pn_decref(void *object);
+
+pn_delivery_t *pn_delivery(pn_link_t *link, pn_delivery_tag_t tag);
+void pn_delivery_abort(pn_delivery_t *delivery);
+_Bool pn_delivery_aborted(pn_delivery_t *delivery);
+pn_record_t *pn_delivery_attachments(pn_delivery_t *delivery);
+pn_link_t *pn_delivery_link(pn_delivery_t *delivery);
+pn_disposition_t *pn_delivery_local(pn_delivery_t *delivery);
+uint64_t pn_delivery_local_state(pn_delivery_t *delivery);
+_Bool pn_delivery_partial(pn_delivery_t *delivery);
+size_t pn_delivery_pending(pn_delivery_t *delivery);
+_Bool pn_delivery_readable(pn_delivery_t *delivery);
+pn_disposition_t *pn_delivery_remote(pn_delivery_t *delivery);
+uint64_t pn_delivery_remote_state(pn_delivery_t *delivery);
+void pn_delivery_settle(pn_delivery_t *delivery);
+_Bool pn_delivery_settled(pn_delivery_t *delivery);
+pn_delivery_tag_t pn_delivery_tag(pn_delivery_t *delivery);
+void pn_delivery_update(pn_delivery_t *delivery, uint64_t state);
+_Bool pn_delivery_updated(pn_delivery_t *delivery);
+_Bool pn_delivery_writable(pn_delivery_t *delivery);
+
+pn_data_t *pn_disposition_annotations(pn_disposition_t *disposition);
+pn_condition_t *pn_disposition_condition(pn_disposition_t *disposition);
+pn_data_t *pn_disposition_data(pn_disposition_t *disposition);
+uint32_t pn_disposition_get_section_number(pn_disposition_t *disposition);
+uint64_t pn_disposition_get_section_offset(pn_disposition_t *disposition);
+_Bool pn_disposition_is_failed(pn_disposition_t *disposition);
+_Bool pn_disposition_is_undeliverable(pn_disposition_t *disposition);
+void pn_disposition_set_failed(pn_disposition_t *disposition, _Bool failed);
+void pn_disposition_set_section_number(pn_disposition_t *disposition, uint32_t section_number);
+void pn_disposition_set_section_offset(pn_disposition_t *disposition, uint64_t section_offset);
+void pn_disposition_set_undeliverable(pn_disposition_t *disposition, _Bool undeliverable);
+uint64_t pn_disposition_type(pn_disposition_t *disposition);
+
+int pn_error_code(pn_error_t *error);
+const char *pn_error_text(pn_error_t *error);
+
+pn_connection_t *pn_event_connection(pn_event_t *event);
+void *pn_event_context(pn_event_t *event);
+pn_delivery_t *pn_event_delivery(pn_event_t *event);
+pn_link_t *pn_event_link(pn_event_t *event);
+pn_session_t *pn_event_session(pn_event_t *event);
+pn_transport_t *pn_event_transport(pn_event_t *event);
+pn_event_type_t pn_event_type(pn_event_t *event);
+const char *pn_event_type_name(pn_event_type_t type);
+
+void *pn_incref(void *object);
+
+_Bool pn_link_advance(pn_link_t *link);
+pn_record_t *pn_link_attachments(pn_link_t *link);
+int pn_link_available(pn_link_t *link);
+void pn_link_close(pn_link_t *link);
+pn_condition_t *pn_link_condition(pn_link_t *link);
+int pn_link_credit(pn_link_t *link);
+pn_delivery_t *pn_link_current(pn_link_t *link);
+void pn_link_detach(pn_link_t *link);
+void pn_link_drain(pn_link_t *receiver, int credit);
+int pn_link_drained(pn_link_t *link);
+_Bool pn_link_draining(pn_link_t *receiver);
+pn_error_t *pn_link_error(pn_link_t *link);
+void pn_link_flow(pn_link_t *receiver, int credit);
+void pn_link_free(pn_link_t *link);
+_Bool pn_link_get_drain(pn_link_t *link);
+pn_link_t *pn_link_head(pn_connection_t *connection, pn_state_t state);
+_Bool pn_link_is_receiver(pn_link_t *link);
+_Bool pn_link_is_sender(pn_link_t *link);
+uint64_t pn_link_max_message_size(pn_link_t *link);
+const char *pn_link_name(pn_link_t *link);
+pn_link_t *pn_link_next(pn_link_t *link, pn_state_t state);
+void pn_link_offered(pn_link_t *sender, int credit);
+void pn_link_open(pn_link_t *link);
+pn_data_t *pn_link_properties(pn_link_t *link);
+int pn_link_queued(pn_link_t *link);
+pn_rcv_settle_mode_t pn_link_rcv_settle_mode(pn_link_t *link);
+ssize_t pn_link_recv(pn_link_t *receiver, char *bytes, size_t n);
+pn_condition_t *pn_link_remote_condition(pn_link_t *link);
+uint64_t pn_link_remote_max_message_size(pn_link_t *link);
+pn_data_t *pn_link_remote_properties(pn_link_t *link);
+pn_rcv_settle_mode_t pn_link_remote_rcv_settle_mode(pn_link_t *link);
+pn_snd_settle_mode_t pn_link_remote_snd_settle_mode(pn_link_t *link);
+pn_terminus_t *pn_link_remote_source(pn_link_t *link);
+pn_terminus_t *pn_link_remote_target(pn_link_t *link);
+ssize_t pn_link_send(pn_link_t *sender, const char *bytes, size_t n);
+pn_session_t *pn_link_session(pn_link_t *link);
+void pn_link_set_drain(pn_link_t *receiver, _Bool drain);
+void pn_link_set_max_message_size(pn_link_t *link, uint64_t size);
+void pn_link_set_rcv_settle_mode(pn_link_t *link, pn_rcv_settle_mode_t mode);
+void pn_link_set_snd_settle_mode(pn_link_t *link, pn_snd_settle_mode_t mode);
+pn_snd_settle_mode_t pn_link_snd_settle_mode(pn_link_t *link);
+pn_terminus_t *pn_link_source(pn_link_t *link);
+pn_state_t pn_link_state(pn_link_t *link);
+pn_terminus_t *pn_link_target(pn_link_t *link);
+int pn_link_unsettled(pn_link_t *link);
+
+pn_message_t *pn_message(void);
+pn_data_t *pn_message_annotations(pn_message_t *msg);
+pn_data_t *pn_message_body(pn_message_t *msg);
+void pn_message_clear(pn_message_t *msg);
+int pn_message_decode(pn_message_t *msg, const char *bytes, size_t size);
+pn_error_t *pn_message_error(pn_message_t *msg);
+void pn_message_free(pn_message_t *msg);
+const char *pn_message_get_address(pn_message_t *msg);
+const char *pn_message_get_content_encoding(pn_message_t *msg);
+const char *pn_message_get_content_type(pn_message_t *msg);
+pn_msgid_t pn_message_get_correlation_id(pn_message_t *msg);
+pn_timestamp_t pn_message_get_creation_time(pn_message_t *msg);
+uint32_t pn_message_get_delivery_count(pn_message_t *msg);
+pn_timestamp_t pn_message_get_expiry_time(pn_message_t *msg);
+const char *pn_message_get_group_id(pn_message_t *msg);
+pn_sequence_t pn_message_get_group_sequence(pn_message_t *msg);
+pn_msgid_t pn_message_get_id(pn_message_t *msg);
+uint8_t pn_message_get_priority(pn_message_t *msg);
+const char *pn_message_get_reply_to(pn_message_t *msg);
+const char *pn_message_get_reply_to_group_id(pn_message_t *msg);
+const char *pn_message_get_subject(pn_message_t *msg);
+pn_millis_t pn_message_get_ttl(pn_message_t *msg);
+pn_bytes_t pn_message_get_user_id(pn_message_t *msg);
+pn_data_t *pn_message_instructions(pn_message_t *msg);
+_Bool pn_message_is_durable(pn_message_t *msg);
+_Bool pn_message_is_first_acquirer(pn_message_t *msg);
+_Bool pn_message_is_inferred(pn_message_t *msg);
+pn_data_t *pn_message_properties(pn_message_t *msg);
+int pn_message_set_address(pn_message_t *msg, const char *address);
+int pn_message_set_content_encoding(pn_message_t *msg, const char *encoding);
+int pn_message_set_content_type(pn_message_t *msg, const char *type);
+int pn_message_set_correlation_id(pn_message_t *msg, pn_msgid_t id);
+int pn_message_set_creation_time(pn_message_t *msg, pn_timestamp_t time);
+int pn_message_set_delivery_count(pn_message_t *msg, uint32_t count);
+int pn_message_set_durable(pn_message_t *msg, _Bool durable);
+int pn_message_set_expiry_time(pn_message_t *msg, pn_timestamp_t time);
+int pn_message_set_first_acquirer(pn_message_t *msg, _Bool first);
+int pn_message_set_group_id(pn_message_t *msg, const char *group_id);
+int pn_message_set_group_sequence(pn_message_t *msg, pn_sequence_t n);
+int pn_message_set_id(pn_message_t *msg, pn_msgid_t id);
+int pn_message_set_inferred(pn_message_t *msg, _Bool inferred);
+int pn_message_set_priority(pn_message_t *msg, uint8_t priority);
+int pn_message_set_reply_to(pn_message_t *msg, const char *reply_to);
+int pn_message_set_reply_to_group_id(pn_message_t *msg, const char *reply_to_group_id);
+int pn_message_set_subject(pn_message_t *msg, const char *subject);
+int pn_message_set_ttl(pn_message_t *msg, pn_millis_t ttl);
+int pn_message_set_user_id(pn_message_t *msg, pn_bytes_t user_id);
+
+pn_link_t *pn_receiver(pn_session_t *session, const char *name);
+
+pn_sasl_t *pn_sasl(pn_transport_t *transport);
+void pn_sasl_allowed_mechs(pn_sasl_t *sasl, const char *mechs);
+void pn_sasl_config_name(pn_sasl_t *sasl, const char *name);
+void pn_sasl_config_path(pn_sasl_t *sasl, const char *path);
+void pn_sasl_done(pn_sasl_t *sasl, pn_sasl_outcome_t outcome);
+_Bool pn_sasl_extended(void);
+_Bool pn_sasl_get_allow_insecure_mechs(pn_sasl_t *sasl);
+const char *pn_sasl_get_authorization(pn_sasl_t *sasl);
+const char *pn_sasl_get_mech(pn_sasl_t *sasl);
+const char *pn_sasl_get_user(pn_sasl_t *sasl);
+pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl);
+void pn_sasl_set_allow_insecure_mechs(pn_sasl_t *sasl, _Bool insecure);
+
+pn_link_t *pn_sender(pn_session_t *session, const char *name);
+
+pn_session_t *pn_session(pn_connection_t *connection);
+pn_record_t *pn_session_attachments(pn_session_t *session);
+void pn_session_close(pn_session_t *session);
+pn_condition_t *pn_session_condition(pn_session_t *session);
+pn_connection_t *pn_session_connection(pn_session_t *session);
+void pn_session_free(pn_session_t *session);
+size_t pn_session_get_incoming_capacity(pn_session_t *session);
+size_t pn_session_get_outgoing_window(pn_session_t *session);
+pn_session_t *pn_session_head(pn_connection_t *connection, pn_state_t state);
+size_t pn_session_incoming_bytes(pn_session_t *session);
+pn_session_t *pn_session_next(pn_session_t *session, pn_state_t state);
+void pn_session_open(pn_session_t *session);
+size_t pn_session_outgoing_bytes(pn_session_t *session);
+pn_condition_t *pn_session_remote_condition(pn_session_t *session);
+void pn_session_set_incoming_capacity(pn_session_t *session, size_t capacity);
+void pn_session_set_outgoing_window(pn_session_t *session, size_t window);
+pn_state_t pn_session_state(pn_session_t *session);
+
+pn_ssl_t *pn_ssl(pn_transport_t *transport);
+pn_ssl_domain_t *pn_ssl_domain(pn_ssl_mode_t mode);
+int pn_ssl_domain_allow_unsecured_client(pn_ssl_domain_t *domain);
+void pn_ssl_domain_free(pn_ssl_domain_t *domain);
+int pn_ssl_domain_set_ciphers(pn_ssl_domain_t *domain, const char *ciphers);
+int pn_ssl_domain_set_credentials(pn_ssl_domain_t *domain, const char *credential_1, const char *credential_2, const char *password);
+int pn_ssl_domain_set_peer_authentication(pn_ssl_domain_t *domain, const pn_ssl_verify_mode_t mode, const char *trusted_CAs);
+int pn_ssl_domain_set_protocols(pn_ssl_domain_t *domain, const char *protocols);
+int pn_ssl_domain_set_trusted_ca_db(pn_ssl_domain_t *domain, const char *certificate_db);
+int pn_ssl_get_cert_fingerprint(pn_ssl_t *ssl0, char *fingerprint, size_t fingerprint_length, pn_ssl_hash_alg hash_alg);
+_Bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *buffer, size_t size);
+_Bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *buffer, size_t size);
+const char *pn_ssl_get_remote_subject(pn_ssl_t *ssl);
+const char *pn_ssl_get_remote_subject_subfield(pn_ssl_t *ssl0, pn_ssl_cert_subject_subfield field);
+int pn_ssl_init(pn_ssl_t *ssl, pn_ssl_domain_t *domain, const char *session_id);
+_Bool pn_ssl_present(void);
+pn_ssl_resume_status_t pn_ssl_resume_status(pn_ssl_t *ssl);
+int pn_ssl_set_peer_hostname(pn_ssl_t *ssl, const char *hostname);
+
+pn_data_t *pn_terminus_capabilities(pn_terminus_t *terminus);
+int pn_terminus_copy(pn_terminus_t *terminus, pn_terminus_t *src);
+pn_data_t *pn_terminus_filter(pn_terminus_t *terminus);
+const char *pn_terminus_get_address(pn_terminus_t *terminus);
+pn_distribution_mode_t pn_terminus_get_distribution_mode(const pn_terminus_t *terminus);
+pn_durability_t pn_terminus_get_durability(pn_terminus_t *terminus);
+pn_expiry_policy_t pn_terminus_get_expiry_policy(pn_terminus_t *terminus);
+pn_seconds_t pn_terminus_get_timeout(pn_terminus_t *terminus);
+pn_terminus_type_t pn_terminus_get_type(pn_terminus_t *terminus);
+_Bool pn_terminus_is_dynamic(pn_terminus_t *terminus);
+pn_data_t *pn_terminus_outcomes(pn_terminus_t *terminus);
+pn_data_t *pn_terminus_properties(pn_terminus_t *terminus);
+int pn_terminus_set_address(pn_terminus_t *terminus, const char *address);
+int pn_terminus_set_distribution_mode(pn_terminus_t *terminus, pn_distribution_mode_t mode);
+int pn_terminus_set_durability(pn_terminus_t *terminus, pn_durability_t durability);
+int pn_terminus_set_dynamic(pn_terminus_t *terminus, _Bool dynamic);
+int pn_terminus_set_expiry_policy(pn_terminus_t *terminus, pn_expiry_policy_t policy);
+int pn_terminus_set_timeout(pn_terminus_t *terminus, pn_seconds_t timeout);
+int pn_terminus_set_type(pn_terminus_t *terminus, pn_terminus_type_t type);
+
+pn_transport_t *pn_transport(void);
+pn_record_t *pn_transport_attachments(pn_transport_t *transport);
+int pn_transport_bind(pn_transport_t *transport, pn_connection_t *connection);
+ssize_t pn_transport_capacity(pn_transport_t *transport);
+int pn_transport_close_head(pn_transport_t *transport);
+int pn_transport_close_tail(pn_transport_t *transport);
+_Bool pn_transport_closed(pn_transport_t *transport);
+pn_condition_t *pn_transport_condition(pn_transport_t *transport);
+pn_connection_t *pn_transport_connection(pn_transport_t *transport);
+pn_error_t *pn_transport_error(pn_transport_t *transport);
+uint16_t pn_transport_get_channel_max(pn_transport_t *transport);
+uint64_t pn_transport_get_frames_input(const pn_transport_t *transport);
+uint64_t pn_transport_get_frames_output(const pn_transport_t *transport);
+pn_millis_t pn_transport_get_idle_timeout(pn_transport_t *transport);
+uint32_t pn_transport_get_max_frame(pn_transport_t *transport);
+pn_millis_t pn_transport_get_remote_idle_timeout(pn_transport_t *transport);
+uint32_t pn_transport_get_remote_max_frame(pn_transport_t *transport);
+const char *pn_transport_get_user(pn_transport_t *transport);
+_Bool pn_transport_is_authenticated(pn_transport_t *transport);
+_Bool pn_transport_is_encrypted(pn_transport_t *transport);
+void pn_transport_log(pn_transport_t *transport, const char *message);
+ssize_t pn_transport_peek(pn_transport_t *transport, char *dst, size_t size);
+ssize_t pn_transport_pending(pn_transport_t *transport);
+void pn_transport_pop(pn_transport_t *transport, size_t size);
+ssize_t pn_transport_push(pn_transport_t *transport, const char *src, size_t size);
+uint16_t pn_transport_remote_channel_max(pn_transport_t *transport);
+void pn_transport_require_auth(pn_transport_t *transport, _Bool required);
+void pn_transport_require_encryption(pn_transport_t *transport, _Bool required);
+int pn_transport_set_channel_max(pn_transport_t *transport, uint16_t channel_max);
+void pn_transport_set_idle_timeout(pn_transport_t *transport, pn_millis_t timeout);
+void pn_transport_set_max_frame(pn_transport_t *transport, uint32_t size);
+void pn_transport_set_server(pn_transport_t *transport);
+void pn_transport_set_tracer(pn_transport_t *transport, pn_tracer_t tracer);
+int64_t pn_transport_tick(pn_transport_t *transport, int64_t now);
+void pn_transport_trace(pn_transport_t *transport, pn_trace_t trace);
+int pn_transport_unbind(pn_transport_t *transport);
+
+// Dispositions defined in C macros
+// results of pn_disposition_type
+#define PN_RECEIVED ...
+#define PN_ACCEPTED ...
+#define PN_REJECTED ...
+#define PN_RELEASED ...
+#define PN_MODIFIED ...
+
+// Default message priority
+#define PN_DEFAULT_PRIORITY ...
+
+// Returned errors
+#define PN_OK ...
+#define PN_EOS ...
+#define PN_OVERFLOW ...
+#define PN_TIMEOUT ...
+#define PN_INTR ...
+
+#define PN_LOCAL_UNINIT  ...
+#define PN_LOCAL_ACTIVE  ...
+#define PN_LOCAL_CLOSED  ...
+#define PN_REMOTE_UNINIT ...
+#define PN_REMOTE_ACTIVE ...
+#define PN_REMOTE_CLOSED ...
+
+#define PN_TRACE_OFF ...
+#define PN_TRACE_RAW ...
+#define PN_TRACE_FRM ...
+#define PN_TRACE_DRV ...
+
+// Maybe need to get this from cmake, or modify how the binding does this
+#define PN_VERSION_MAJOR ...
+#define PN_VERSION_MINOR ...
+#define PN_VERSION_POINT ...
+
+// Initialization of library - probably a better way to do this than explicitly, but it works!
+void init();
+
+pn_connection_t *pn_cast_pn_connection(void *x);
+pn_session_t *pn_cast_pn_session(void *x);
+pn_link_t *pn_cast_pn_link(void *x);
+pn_delivery_t *pn_cast_pn_delivery(void *x);
+pn_transport_t *pn_cast_pn_transport(void *x);
+
+extern "Python" void pn_pyref_incref(void *object);
+extern "Python" void pn_pyref_decref(void *object);
+extern "Python" void pn_pytracer(pn_transport_t *transport, const char *message);
+
+pn_event_t *pn_collector_put_py(pn_collector_t *collector, void *context, pn_event_type_t type);
+ssize_t pn_data_format_py(pn_data_t *data, char *bytes, size_t size);
+const char *pn_event_class_name_py(pn_event_t *event);
+ssize_t pn_message_encode_py(pn_message_t *msg, char *bytes, size_t size);
+void pn_record_def_py(pn_record_t *record);
+void *pn_record_get_py(pn_record_t *record);
+void pn_record_set_py(pn_record_t *record, void *value);
+int pn_ssl_get_peer_hostname_py(pn_ssl_t *ssl, char *hostname, size_t size);
diff --git a/python/cproton.i b/python/cproton.i
deleted file mode 100644
index 125999ca8..000000000
--- a/python/cproton.i
+++ /dev/null
@@ -1,497 +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.
- */
-%module cproton
-%{
-/* Includes the header in the wrapper code */
-#if defined(_WIN32) && ! defined(__CYGWIN__)
-#include <winsock2.h>
-#endif
-
-/* TODO: Remove once pn_work_head() and related have been removed from Proton */
-#define PN_USE_DEPRECATED_API 1
-
-#include <proton/cid.h>
-#include <proton/engine.h>
-#include <proton/message.h>
-#include <proton/object.h>
-#include <proton/sasl.h>
-#include <proton/ssl.h>
-
-/*
-NOTE: According to ccache-swig man page: "Known problems are using
-preprocessor directives within %inline blocks and the use of ’#pragma SWIG’."
-This includes using macros in an %inline section.
-
-Keep preprocessor directives and macro expansions in the normal header section.
-*/
-
-PN_HANDLE(PNI_PYTRACER);
-%}
-
-%include <cstring.i>
-
-%cstring_output_allocate_size(char **ALLOC_OUTPUT, size_t *ALLOC_SIZE, free(*$1));
-%cstring_output_maxsize(char *OUTPUT, size_t MAX_OUTPUT_SIZE)
-
-%include <pybuffer.i>
-%pybuffer_binary(const char *BIN_IN, size_t BIN_LEN)
-
-// Typemap for methods that return binary data:
-// force the return type as binary - this is necessary for Python3
-%typemap(in,noblock=1,fragment=SWIG_AsVal_frag(size_t)) (char *BIN_OUT, size_t *BIN_SIZE)
-(int res, size_t n, char *buff = 0, $*2_ltype size) {
-  res = SWIG_AsVal(size_t)($input, &n);
-  if (!SWIG_IsOK(res)) {
-    %argument_fail(res, "(char *BIN_OUT, size_t *BIN_SIZE)", $symname, $argnum);
-  }
-  buff= %new_array(n+1, char);
-  $1 = %static_cast(buff, $1_ltype);
-  size = %numeric_cast(n,$*2_ltype);
-  $2 = &size;
-}
-%typemap(freearg,noblock=1,match="in")(char *BIN_OUT, size_t *BIN_SIZE) {
-  if (buff$argnum) %delete_array(buff$argnum);
-}
-%typemap(argout,noblock=1) (char *BIN_OUT, size_t *BIN_SIZE) {
-  %append_output(PyBytes_FromStringAndSize($1,*$2));
-}
-
-// Typemap for those methods that return variable length text data in a buffer
-// provided as a parameter.  If the method fails we must avoid attempting to
-// decode the contents of the buffer as it does not carry valid text data.
-%typemap(in,noblock=1,fragment=SWIG_AsVal_frag(size_t)) (char *VTEXT_OUT, size_t *VTEXT_SIZE)
-(int res, size_t n, char *buff = 0, $*2_ltype size) {
-  res = SWIG_AsVal(size_t)($input, &n);
-  if (!SWIG_IsOK(res)) {
-    %argument_fail(res, "(char *VTEXT_OUT, size_t *VTEXT_SIZE)", $symname, $argnum);
-  }
-  buff = %new_array(n+1, char);
-  $1 = %static_cast(buff, $1_ltype);
-  size = %numeric_cast(n,$*2_ltype);
-  $2 = &size;
-}
-%typemap(freearg,noblock=1,match="in")(char *VTEXT_OUT, size_t *VTEXT_SIZE) {
-  if (buff$argnum) %delete_array(buff$argnum);
-}
-%typemap(argout,noblock=1,fragment="SWIG_FromCharPtrAndSize") (char *VTEXT_OUT, size_t *VTEXT_SIZE) {
-  %append_output(SWIG_FromCharPtrAndSize($1,*$2));
-}
-
-
-// These are not used/needed in the python binding
-%ignore pn_dtag;
-%ignore pn_message_id;
-%ignore pn_message_correlation_id;
-
-%ignore pn_list;
-%ignore pn_list_size;
-%ignore pn_list_get;
-%ignore pn_list_set;
-%ignore pn_list_add;
-%ignore pn_list_pop;
-%ignore pn_list_index;
-%ignore pn_list_remove;
-%ignore pn_list_del;
-%ignore pn_list_clear;
-%ignore pn_list_iterator;
-%ignore pn_list_minpush;
-%ignore pn_list_minpop;
-
-%ignore pn_map;
-%ignore pn_map_size;
-%ignore pn_map_put;
-%ignore pn_map_get;
-%ignore pn_map_del;
-%ignore pn_map_head;
-%ignore pn_map_next;
-%ignore pn_map_key;
-%ignore pn_map_value;
-
-%ignore pn_hash;
-%ignore pn_hash_size;
-%ignore pn_hash_put;
-%ignore pn_hash_get;
-%ignore pn_hash_del;
-%ignore pn_hash_head;
-%ignore pn_hash_next;
-%ignore pn_hash_key;
-%ignore pn_hash_value;
-
-%ignore pn_stringn;
-%ignore pn_string_size;
-%ignore pn_string_set;
-%ignore pn_string_setn;
-%ignore pn_string_put;
-%ignore pn_string_clear;
-%ignore pn_string_format;
-%ignore pn_string_vformat;
-%ignore pn_string_addf;
-%ignore pn_string_vaddf;
-%ignore pn_string_grow;
-%ignore pn_string_buffer;
-%ignore pn_string_capacity;
-%ignore pn_string_resize;
-%ignore pn_string_copy;
-
-%ignore pn_iterator;
-%ignore pn_iterator_start;
-%ignore pn_iterator_next;
-
-%typemap(in) pn_handle_t {
-  $1 = PyLong_AsVoidPtr($input);
-}
-
-%typemap(out) pn_handle_t {
-  $result = PyLong_FromVoidPtr((void*)$1);
-}
-
-
-%typemap(in) pn_bytes_t {
-  if ($input == Py_None) {
-    $1.start = NULL;
-    $1.size = 0;
-  } else {
-    $1.start = PyBytes_AsString($input);
-
-    if (!$1.start) {
-      return NULL;
-    }
-    $1.size = PyBytes_Size($input);
-  }
-}
-
-%typemap(out) pn_bytes_t {
-  $result = PyBytes_FromStringAndSize($1.start, $1.size);
-}
-
-%typemap(in) pn_msgid_t {
-  if (PyTuple_Check($input)) {
-    pn_type_t type = PyLong_AsUnsignedLong(PyTuple_GetItem($input, 0));
-    PyObject* obj =  PyTuple_GetItem($input, 1);
-    switch (type) {
-      case PN_NULL:
-        break;
-      case PN_ULONG:
-        $1.u.as_ulong = PyLong_AsUnsignedLong(obj);
-        break;
-      case PN_UUID:
-        if (PyBytes_Check(obj)) {
-          memmove(&$1.u.as_uuid, PyBytes_AsString(obj), (PyBytes_Size(obj) < 16 ? PyBytes_Size(obj) : 16));
-          break;
-        }
-        PyErr_SetString(PyExc_TypeError, "Require bytes for uuid id");
-        SWIG_fail;
-      case PN_BINARY:
-        if (PyBytes_Check(obj)) {
-          $1.u.as_bytes = (pn_bytes_t){.size=PyBytes_Size(obj), .start=PyBytes_AsString(obj)};
-          break;
-        }
-        PyErr_SetString(PyExc_TypeError, "Require bytes for binary id");
-        SWIG_fail;
-      case PN_STRING:
-        if (PyBytes_Check(obj)) {
-          $1.u.as_bytes = (pn_bytes_t){.size=PyBytes_Size(obj), .start=PyBytes_AsString(obj)};
-          break;
-        }
-        PyErr_SetString(PyExc_TypeError, "Require bytes for string id");
-        SWIG_fail;
-      default:
-        PyErr_SetString(PyExc_ValueError, "Invalid type selector for id");
-        SWIG_fail;
-    }
-    $1.type = type;
-  } else if (PyLong_Check($input)) {
-    $1.type = PN_ULONG;
-    $1.u.as_ulong = PyLong_AsUnsignedLong($input);
-  } else if (PyBytes_Check($input)) {
-    $1.type = PN_BINARY;
-    $1.u.as_bytes = (pn_bytes_t){.size=PyBytes_Size($input), .start=PyBytes_AsString($input)};
-  } else if (PyUnicode_Check($input)) {
-    $1.type = PN_STRING;
-    Py_ssize_t utf8size;
-    const char *utf8 = PyUnicode_AsUTF8AndSize($input, &utf8size);
-    $1.u.as_bytes = (pn_bytes_t){.size=utf8size, .start=utf8};
-  } else if ($input == Py_None) {
-    $1.type = PN_NULL;
-  } else {
-    PyErr_SetString(PyExc_TypeError, "Cannot convert to id");
-    SWIG_fail;
-  }
-}
-
-%typemap(out) pn_msgid_t {
-  switch ($1.type) {
-    case PN_NULL:
-      Py_INCREF(Py_None);
-      $result = Py_None;
-      break;
-    case PN_ULONG:
-      $result = PyLong_FromUnsignedLong($1.u.as_ulong);
-      break;
-    case PN_BINARY:
-      $result = PyBytes_FromStringAndSize($1.u.as_bytes.start, $1.u.as_bytes.size);
-      break;
-    case PN_STRING:
-      $result = PyUnicode_FromStringAndSize($1.u.as_bytes.start, $1.u.as_bytes.size);
-      break;
-    case PN_UUID:
-      $result = PyTuple_New(2);
-      PyTuple_SetItem($result, 0, PyLong_FromUnsignedLong($1.type));
-      PyTuple_SetItem($result, 1, PyBytes_FromStringAndSize($1.u.as_uuid.bytes, 16));
-      break;
-    // Theses 2 cases are for compatibility with the broken ruby binding
-    case PN_INT: {
-      int32_t v = $1.u.as_int;
-      if (v>=0) {
-        $result = PyLong_FromLong(v);
-        break;
-      }
-      Py_INCREF(Py_None);
-      $result = Py_None;
-      break;
-    }
-    case PN_LONG: {
-      int64_t v = $1.u.as_long;
-      if (v>=0) {
-        $result = PyLong_FromLong(v);
-        break;
-      }
-    }
-    default:
-      Py_INCREF(Py_None);
-      $result = Py_None;
-      break;
-  }
-}
-
-%typemap(out) pn_delivery_tag_t {
-  $result = PyBytes_FromStringAndSize($1.bytes, $1.size);
-}
-
-%typemap(in) pn_uuid_t {
-  memset($1.bytes, 0, 16);
-  if ($input == Py_None) {
-    ; // Already zeroed out
-  } else {
-    const char* b = PyBytes_AsString($input);
-    if (b) {
-        memmove($1.bytes, b, (PyBytes_Size($input) < 16 ? PyBytes_Size($input) : 16));
-    } else {
-        return NULL;
-    }
-  }
-}
-
-%typemap(out) pn_uuid_t {
-  $result = PyBytes_FromStringAndSize($1.bytes, 16);
-}
-
-%apply pn_uuid_t { pn_decimal128_t };
-
-int pn_message_encode(pn_message_t *msg, char *BIN_OUT, size_t *BIN_SIZE);
-%ignore pn_message_encode;
-
-int pn_message_decode(pn_message_t *msg, const char *BIN_IN, size_t BIN_LEN);
-%ignore pn_message_decode;
-
-ssize_t pn_link_send(pn_link_t *transport, const char *BIN_IN, size_t BIN_LEN);
-%ignore pn_link_send;
-
-%rename(pn_link_recv) wrap_pn_link_recv;
-%inline %{
-  ssize_t wrap_pn_link_recv(pn_link_t *link, char *BIN_OUT, size_t *BIN_SIZE) {
-    ssize_t sz = pn_link_recv(link, BIN_OUT, *BIN_SIZE);
-    if (sz >= 0) {
-      *BIN_SIZE = sz;
-    } else {
-      *BIN_SIZE = 0;
-    }
-    return sz;
-  }
-%}
-%ignore pn_link_recv;
-
-ssize_t pn_transport_push(pn_transport_t *transport, const char *BIN_IN, size_t BIN_LEN);
-%ignore pn_transport_push;
-
-%rename(pn_transport_peek) wrap_pn_transport_peek;
-%inline %{
-  ssize_t wrap_pn_transport_peek(pn_transport_t *transport, char *BIN_OUT, size_t *BIN_SIZE) {
-    ssize_t sz = pn_transport_peek(transport, BIN_OUT, *BIN_SIZE);
-    if (sz >= 0) {
-      *BIN_SIZE = sz;
-    } else {
-      *BIN_SIZE = 0;
-    }
-    return sz;
-  }
-%}
-%ignore pn_transport_peek;
-
-%rename(pn_delivery) wrap_pn_delivery;
-%inline %{
-  pn_delivery_t *wrap_pn_delivery(pn_link_t *link, char *STRING, size_t LENGTH) {
-    return pn_delivery(link, pn_dtag(STRING, LENGTH));
-  }
-%}
-%ignore pn_delivery;
-
-%rename(pn_delivery_tag) wrap_pn_delivery_tag;
-%inline %{
-  void wrap_pn_delivery_tag(pn_delivery_t *delivery, char **ALLOC_OUTPUT, size_t *ALLOC_SIZE) {
-    pn_delivery_tag_t tag = pn_delivery_tag(delivery);
-    *ALLOC_OUTPUT = (char *) malloc(tag.size);
-    *ALLOC_SIZE = tag.size;
-    memcpy(*ALLOC_OUTPUT, tag.start, tag.size);
-  }
-%}
-%ignore pn_delivery_tag;
-
-ssize_t pn_data_decode(pn_data_t *data, const char *BIN_IN, size_t BIN_LEN);
-%ignore pn_data_decode;
-
-%rename(pn_data_encode) wrap_pn_data_encode;
-%inline %{
-  ssize_t wrap_pn_data_encode(pn_data_t *data, char *BIN_OUT, size_t *BIN_SIZE) {
-    ssize_t sz = pn_data_encode(data, BIN_OUT, *BIN_SIZE);
-    if (sz >= 0) {
-      *BIN_SIZE = sz;
-    } else {
-      *BIN_SIZE = 0;
-    }
-    return sz;
-  }
-%}
-%ignore pn_data_encode;
-
-%rename(pn_data_format) wrap_pn_data_format;
-%inline %{
-  int wrap_pn_data_format(pn_data_t *data, char *VTEXT_OUT, size_t *VTEXT_SIZE) {
-    int err = pn_data_format(data, VTEXT_OUT, VTEXT_SIZE);
-    if (err) *VTEXT_SIZE = 0;
-    return err;
-  }
-%}
-%ignore pn_data_format;
-
-bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE);
-%ignore pn_ssl_get_cipher_name;
-
-bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE);
-%ignore pn_ssl_get_protocol_name;
-
-char* pn_ssl_get_remote_subject_subfield(pn_ssl_t *ssl, pn_ssl_cert_subject_subfield field);
-%ignore pn_ssl_get_remote_subject_subfield;
-
-int pn_ssl_get_cert_fingerprint(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE, pn_ssl_hash_alg hash_alg);
-%ignore pn_ssl_get_cert_fingerprint;
-
-%rename(pn_ssl_get_peer_hostname) wrap_pn_ssl_get_peer_hostname;
-%inline %{
-  int wrap_pn_ssl_get_peer_hostname(pn_ssl_t *ssl, char *VTEXT_OUT, size_t *VTEXT_SIZE) {
-    int err = pn_ssl_get_peer_hostname(ssl, VTEXT_OUT, VTEXT_SIZE);
-    if (err) *VTEXT_SIZE = 0;
-    return err;
-  }
-%}
-%ignore pn_ssl_get_peer_hostname;
-
-
-%immutable PN_PYREF;
-%inline %{
-  pn_class_t* PN_PYREF;
-
-  static void pn_pyref_incref(void *object) {
-    PyObject* p = (PyObject*) object;
-    SWIG_PYTHON_THREAD_BEGIN_BLOCK;
-    Py_XINCREF(p);
-    SWIG_PYTHON_THREAD_END_BLOCK;
-  }
-
-  static void pn_pyref_decref(void *object) {
-    PyObject* p = (PyObject*) object;
-    SWIG_PYTHON_THREAD_BEGIN_BLOCK;
-    Py_XDECREF(p);
-    SWIG_PYTHON_THREAD_END_BLOCK;
-  }
-
-  static int pn_pyref_refcount(void *object) {
-    return 1;
-  }
-
-  void *pn_py2void(PyObject *object) {
-    return object;
-  }
-
-  PyObject *pn_void2py(void *object) {
-    if (object) {
-      PyObject* p = (PyObject*) object;
-      SWIG_PYTHON_THREAD_BEGIN_BLOCK;
-      Py_INCREF(p);
-      SWIG_PYTHON_THREAD_END_BLOCK;
-      return p;
-    } else {
-      Py_RETURN_NONE;
-    }
-  }
-
-  PyObject *pn_cast_pn_void(void *object) {
-    return pn_void2py(object);
-  }
-
-  void pn_pytracer(pn_transport_t *transport, const char *message) {
-    PyObject *pytracer = (PyObject *) pn_record_get(pn_transport_attachments(transport), PNI_PYTRACER);
-    SWIG_PYTHON_THREAD_BEGIN_BLOCK;
-    PyObject *pytrans = SWIG_NewPointerObj(transport, SWIGTYPE_p_pn_transport_t, 0);
-    PyObject *pymsg = PyString_FromString(message);
-    PyObject *result = PyObject_CallFunctionObjArgs(pytracer, pytrans, pymsg, NULL);
-    if (!result) {
-      PyErr_PrintEx(true);
-    }
-    Py_XDECREF(pytrans);
-    Py_XDECREF(pymsg);
-    Py_XDECREF(result);
-    SWIG_PYTHON_THREAD_END_BLOCK;
-  }
-
-  void pn_transport_set_pytracer(pn_transport_t *transport, PyObject *obj) {
-    pn_record_t *record = pn_transport_attachments(transport);
-    pn_record_def(record, PNI_PYTRACER, PN_PYREF);
-    pn_record_set(record, PNI_PYTRACER, obj);
-    pn_transport_set_tracer(transport, pn_pytracer);
-  }
-
-  PyObject *pn_transport_get_pytracer(pn_transport_t *transport) {
-    pn_record_t *record = pn_transport_attachments(transport);
-    PyObject *obj = (PyObject *)pn_record_get(record, PNI_PYTRACER);
-    if (obj) {
-      Py_XINCREF(obj);
-      return obj;
-    } else {
-      Py_RETURN_NONE;
-    }
-  }
-
-%}
-
-%init %{
-  PN_PYREF = pn_class_create("pn_pyref", NULL, NULL, pn_pyref_incref, pn_pyref_decref, pn_pyref_refcount);
-%}
-
-%include "proton/cproton.i"
diff --git a/python/cproton.py b/python/cproton.py
new file mode 100644
index 000000000..91efe043d
--- /dev/null
+++ b/python/cproton.py
@@ -0,0 +1,729 @@
+#
+# 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 uuid import UUID
+
+from cproton_ffi import ffi, lib
+from cproton_ffi.lib import (PN_ACCEPTED, PN_ARRAY, PN_BINARY, PN_BOOL, PN_BYTE, PN_CHAR,
+                             PN_CONFIGURATION, PN_CONNECTION_BOUND, PN_CONNECTION_FINAL,
+                             PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_CLOSE,
+                             PN_CONNECTION_LOCAL_OPEN, PN_CONNECTION_REMOTE_CLOSE,
+                             PN_CONNECTION_REMOTE_OPEN, PN_CONNECTION_UNBOUND, PN_COORDINATOR,
+                             PN_DECIMAL32, PN_DECIMAL64, PN_DECIMAL128, PN_DEFAULT_PRIORITY,
+                             PN_DELIVERIES, PN_DELIVERY, PN_DESCRIBED, PN_DIST_MODE_COPY,
+                             PN_DIST_MODE_MOVE, PN_DIST_MODE_UNSPECIFIED, PN_DOUBLE, PN_EOS,
+                             PN_EVENT_NONE, PN_EXPIRE_NEVER, PN_EXPIRE_WITH_CONNECTION,
+                             PN_EXPIRE_WITH_LINK, PN_EXPIRE_WITH_SESSION, PN_FLOAT, PN_INT, PN_INTR,
+                             PN_LINK_FINAL, PN_LINK_FLOW, PN_LINK_INIT, PN_LINK_LOCAL_CLOSE,
+                             PN_LINK_LOCAL_DETACH, PN_LINK_LOCAL_OPEN, PN_LINK_REMOTE_CLOSE,
+                             PN_LINK_REMOTE_DETACH, PN_LINK_REMOTE_OPEN, PN_LIST, PN_LOCAL_ACTIVE,
+                             PN_LOCAL_CLOSED, PN_LOCAL_UNINIT, PN_LONG, PN_MAP, PN_MODIFIED,
+                             PN_NONDURABLE, PN_NULL, PN_OK, PN_OVERFLOW, PN_RCV_FIRST,
+                             PN_RCV_SECOND, PN_RECEIVED, PN_REJECTED, PN_RELEASED, PN_REMOTE_ACTIVE,
+                             PN_REMOTE_CLOSED, PN_REMOTE_UNINIT, PN_SASL_AUTH, PN_SASL_NONE,
+                             PN_SASL_OK, PN_SASL_PERM, PN_SASL_SYS, PN_SASL_TEMP, PN_SESSION_FINAL,
+                             PN_SESSION_INIT, PN_SESSION_LOCAL_CLOSE, PN_SESSION_LOCAL_OPEN,
+                             PN_SESSION_REMOTE_CLOSE, PN_SESSION_REMOTE_OPEN, PN_SHORT,
+                             PN_SND_MIXED, PN_SND_SETTLED, PN_SND_UNSETTLED, PN_SOURCE,
+                             PN_SSL_ANONYMOUS_PEER, PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY,
+                             PN_SSL_CERT_SUBJECT_COMMON_NAME, PN_SSL_CERT_SUBJECT_COUNTRY_NAME,
+                             PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME,
+                             PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT,
+                             PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE, PN_SSL_MD5, PN_SSL_MODE_CLIENT,
+                             PN_SSL_MODE_SERVER, PN_SSL_RESUME_NEW, PN_SSL_RESUME_REUSED,
+                             PN_SSL_RESUME_UNKNOWN, PN_SSL_SHA1, PN_SSL_SHA256, PN_SSL_SHA512,
+                             PN_SSL_VERIFY_PEER, PN_SSL_VERIFY_PEER_NAME, PN_STRING, PN_SYMBOL,
+                             PN_TARGET, PN_TIMEOUT, PN_TIMER_TASK, PN_TIMESTAMP, PN_TRACE_DRV,
+                             PN_TRACE_FRM, PN_TRACE_OFF, PN_TRACE_RAW, PN_TRANSPORT,
+                             PN_TRANSPORT_CLOSED, PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED,
+                             PN_TRANSPORT_TAIL_CLOSED, PN_UBYTE, PN_UINT, PN_ULONG, PN_UNSPECIFIED,
+                             PN_USHORT, PN_UUID, PN_VERSION_MAJOR, PN_VERSION_MINOR,
+                             PN_VERSION_POINT, pn_cast_pn_connection, pn_cast_pn_delivery,
+                             pn_cast_pn_link, pn_cast_pn_session, pn_cast_pn_transport,
+                             pn_collector, pn_collector_free, pn_collector_more, pn_collector_peek,
+                             pn_collector_pop, pn_collector_release, pn_condition_clear,
+                             pn_condition_info, pn_condition_is_set, pn_connection,
+                             pn_connection_attachments, pn_connection_close, pn_connection_collect,
+                             pn_connection_condition, pn_connection_desired_capabilities,
+                             pn_connection_error, pn_connection_offered_capabilities,
+                             pn_connection_open, pn_connection_properties, pn_connection_release,
+                             pn_connection_remote_condition,
+                             pn_connection_remote_desired_capabilities,
+                             pn_connection_remote_offered_capabilities,
+                             pn_connection_remote_properties, pn_connection_state,
+                             pn_connection_transport, pn_data, pn_data_clear, pn_data_copy,
+                             pn_data_dump, pn_data_encoded_size, pn_data_enter, pn_data_error,
+                             pn_data_exit, pn_data_free, pn_data_get_array, pn_data_get_array_type,
+                             pn_data_get_bool, pn_data_get_byte, pn_data_get_char,
+                             pn_data_get_decimal32, pn_data_get_decimal64, pn_data_get_double,
+                             pn_data_get_float, pn_data_get_int, pn_data_get_list, pn_data_get_long,
+                             pn_data_get_map, pn_data_get_short, pn_data_get_timestamp,
+                             pn_data_get_ubyte, pn_data_get_uint, pn_data_get_ulong,
+                             pn_data_get_ushort, pn_data_is_array_described, pn_data_is_described,
+                             pn_data_is_null, pn_data_narrow, pn_data_next, pn_data_prev,
+                             pn_data_put_array, pn_data_put_bool, pn_data_put_byte,
+                             pn_data_put_char, pn_data_put_decimal32, pn_data_put_decimal64,
+                             pn_data_put_described, pn_data_put_double, pn_data_put_float,
+                             pn_data_put_int, pn_data_put_list, pn_data_put_long, pn_data_put_map,
+                             pn_data_put_null, pn_data_put_short, pn_data_put_timestamp,
+                             pn_data_put_ubyte, pn_data_put_uint, pn_data_put_ulong,
+                             pn_data_put_ushort, pn_data_rewind, pn_data_type, pn_data_widen,
+                             pn_decref, pn_delivery_abort, pn_delivery_aborted,
+                             pn_delivery_attachments, pn_delivery_link, pn_delivery_local,
+                             pn_delivery_local_state, pn_delivery_partial, pn_delivery_pending,
+                             pn_delivery_readable, pn_delivery_remote, pn_delivery_remote_state,
+                             pn_delivery_settle, pn_delivery_settled, pn_delivery_update,
+                             pn_delivery_updated, pn_delivery_writable, pn_disposition_annotations,
+                             pn_disposition_condition, pn_disposition_data,
+                             pn_disposition_get_section_number, pn_disposition_get_section_offset,
+                             pn_disposition_is_failed, pn_disposition_is_undeliverable,
+                             pn_disposition_set_failed, pn_disposition_set_section_number,
+                             pn_disposition_set_section_offset, pn_disposition_set_undeliverable,
+                             pn_disposition_type, pn_error_code, pn_event_connection,
+                             pn_event_context, pn_event_delivery, pn_event_link, pn_event_session,
+                             pn_event_transport, pn_event_type, pn_incref, pn_link_advance,
+                             pn_link_attachments, pn_link_available, pn_link_close,
+                             pn_link_condition, pn_link_credit, pn_link_current, pn_link_detach,
+                             pn_link_drain, pn_link_drained, pn_link_draining, pn_link_error,
+                             pn_link_flow, pn_link_free, pn_link_get_drain, pn_link_head,
+                             pn_link_is_receiver, pn_link_is_sender, pn_link_max_message_size,
+                             pn_link_next, pn_link_offered, pn_link_open, pn_link_properties,
+                             pn_link_queued, pn_link_rcv_settle_mode, pn_link_remote_condition,
+                             pn_link_remote_max_message_size, pn_link_remote_properties,
+                             pn_link_remote_rcv_settle_mode, pn_link_remote_snd_settle_mode,
+                             pn_link_remote_source, pn_link_remote_target, pn_link_session,
+                             pn_link_set_drain, pn_link_set_max_message_size,
+                             pn_link_set_rcv_settle_mode, pn_link_set_snd_settle_mode,
+                             pn_link_snd_settle_mode, pn_link_source, pn_link_state, pn_link_target,
+                             pn_link_unsettled, pn_message, pn_message_annotations, pn_message_body,
+                             pn_message_clear, pn_message_error, pn_message_free,
+                             pn_message_get_creation_time, pn_message_get_delivery_count,
+                             pn_message_get_expiry_time, pn_message_get_group_sequence,
+                             pn_message_get_priority, pn_message_get_ttl, pn_message_instructions,
+                             pn_message_is_durable, pn_message_is_first_acquirer,
+                             pn_message_is_inferred, pn_message_properties,
+                             pn_message_set_creation_time, pn_message_set_delivery_count,
+                             pn_message_set_durable, pn_message_set_expiry_time,
+                             pn_message_set_first_acquirer, pn_message_set_group_sequence,
+                             pn_message_set_inferred, pn_message_set_priority, pn_message_set_ttl,
+                             pn_sasl, pn_sasl_done, pn_sasl_extended,
+                             pn_sasl_get_allow_insecure_mechs, pn_sasl_outcome,
+                             pn_sasl_set_allow_insecure_mechs, pn_session, pn_session_attachments,
+                             pn_session_close, pn_session_condition, pn_session_connection,
+                             pn_session_free, pn_session_get_incoming_capacity,
+                             pn_session_get_outgoing_window, pn_session_head,
+                             pn_session_incoming_bytes, pn_session_next, pn_session_open,
+                             pn_session_outgoing_bytes, pn_session_remote_condition,
+                             pn_session_set_incoming_capacity, pn_session_set_outgoing_window,
+                             pn_session_state, pn_ssl, pn_ssl_domain,
+                             pn_ssl_domain_allow_unsecured_client, pn_ssl_domain_free,
+                             pn_ssl_present, pn_ssl_resume_status, pn_terminus_capabilities,
+                             pn_terminus_copy, pn_terminus_filter,
+                             pn_terminus_get_distribution_mode, pn_terminus_get_durability,
+                             pn_terminus_get_expiry_policy, pn_terminus_get_timeout,
+                             pn_terminus_get_type, pn_terminus_is_dynamic, pn_terminus_outcomes,
+                             pn_terminus_properties, pn_terminus_set_distribution_mode,
+                             pn_terminus_set_durability, pn_terminus_set_dynamic,
+                             pn_terminus_set_expiry_policy, pn_terminus_set_timeout,
+                             pn_terminus_set_type, pn_transport, pn_transport_attachments,
+                             pn_transport_bind, pn_transport_capacity, pn_transport_close_head,
+                             pn_transport_close_tail, pn_transport_closed, pn_transport_condition,
+                             pn_transport_connection, pn_transport_error,
+                             pn_transport_get_channel_max, pn_transport_get_frames_input,
+                             pn_transport_get_frames_output, pn_transport_get_idle_timeout,
+                             pn_transport_get_max_frame, pn_transport_get_remote_idle_timeout,
+                             pn_transport_get_remote_max_frame, pn_transport_is_authenticated,
+                             pn_transport_is_encrypted, pn_transport_pending, pn_transport_pop,
+                             pn_transport_remote_channel_max, pn_transport_require_auth,
+                             pn_transport_require_encryption, pn_transport_set_channel_max,
+                             pn_transport_set_idle_timeout, pn_transport_set_max_frame,
+                             pn_transport_set_server, pn_transport_tick, pn_transport_trace,
+                             pn_transport_unbind)
+
+
+def isnull(obj):
+    return obj is None or obj == ffi.NULL
+
+
+def addressof(obj):
+    return ffi.cast('uint64_t', obj)
+
+
+def void2py(void):
+    if void == ffi.NULL:
+        return None
+    return ffi.from_handle(void)
+
+
+def string2utf8(string):
+    """Convert python string into bytes compatible with char* C string"""
+    if string is None:
+        return ffi.NULL
+    elif isinstance(string, str):
+        return string.encode('utf8')
+    # Anything else illegal - specifically python3 bytes
+    raise TypeError("Unrecognized string type: %r (%s)" % (string, type(string)))
+
+
+def utf82string(string):
+    """Convert char* C strings returned from proton-c into python unicode"""
+    if string == ffi.NULL:
+        return None
+    return ffi.string(string).decode('utf8')
+
+
+def bytes2py(b):
+    return memoryview(ffi.buffer(b.start, b.size))
+
+
+def bytes2string(b, encoding='utf8'):
+    return ffi.unpack(b.start, b.size).decode(encoding)
+
+
+def py2bytes(py):
+    if isinstance(py, memoryview):
+        return len(py), py
+    elif isinstance(py, str):
+        s = ffi.from_buffer(py.encode('utf8'))
+        return len(s), s
+    elif isinstance(py, bytes):
+        s = ffi.from_buffer(py)
+        return len(s), s
+
+
+def string2bytes(py, encoding='utf8'):
+    s = ffi.from_buffer(py.encode(encoding))
+    return len(s), s
+
+
+def UUID2uuid(py):
+    u = ffi.new('pn_uuid_t*')
+    ffi.memmove(u.bytes, py.bytes, 16)
+    return u[0]
+
+
+def uuid2bytes(uuid):
+    return ffi.unpack(uuid.bytes, 16)
+
+
+def decimal1282py(decimal128):
+    return ffi.unpack(decimal128.bytes, 16)
+
+
+def py2decimal128(py):
+    d = ffi.new('pn_decimal128_t*')
+    ffi.memmove(d.bytes, py, 16)
+    return d[0]
+
+
+def msgid2py(msgid):
+    t = msgid.type
+    if t == PN_NULL:
+        return None
+    elif t == PN_ULONG:
+        return msgid.u.as_ulong
+    elif t == PN_BINARY:
+        return ffi.buffer(msgid.u.as_bytes.start, msgid.u.as_bytes.size)
+    elif t == PN_STRING:
+        return bytes2string(msgid.u.as_bytes)
+    elif t == PN_UUID:
+        return UUID(bytes=uuid2bytes(msgid.u.as_uuid))
+    # These two cases are for compatibility with the broken ruby binding
+    elif t == PN_INT:
+        v = msgid.u.as_int
+        if v >= 0:
+            return v
+        return None
+    elif t == PN_LONG:
+        v = msgid.u.as_long
+        if v >= 0:
+            return v
+        return None
+    return None
+
+
+def py2msgid(py):
+    if py is None:
+        return {'type': PN_NULL}
+    elif isinstance(py, int):
+        return {'type': PN_ULONG, 'u': {'as_ulong': py}}
+    elif isinstance(py, str):
+        return {'type': PN_STRING, 'u': {'as_bytes': py2bytes(py)}}
+    elif isinstance(py, bytes):
+        return {'type': PN_BINARY, 'u': {'as_bytes': py2bytes(py)}}
+    elif isinstance(py, UUID):
+        return {'type': PN_UUID, 'u': {'as_uuid': {'bytes': py.bytes}}}
+    elif isinstance(py, tuple):
+        if py[0] == PN_UUID:
+            return {'type': PN_UUID, 'u': {'as_uuid': {'bytes': py[1]}}}
+    return {'type': PN_NULL}
+
+
+@ffi.def_extern()
+def pn_pytracer(transport, message):
+    attrs = pn_record_get_py(lib.pn_transport_attachments(transport))
+    tracer = attrs['_tracer']
+    if tracer:
+        tracer(transport, utf82string(message))
+
+
+def pn_transport_get_pytracer(transport):
+    attrs = pn_record_get_py(lib.pn_transport_attachments(transport))
+    if '_tracer' in attrs:
+        return attrs['_tracer']
+    else:
+        return None
+
+
+def pn_transport_set_pytracer(transport, tracer):
+    attrs = pn_record_get_py(lib.pn_transport_attachments(transport))
+    attrs['_tracer'] = tracer
+    lib.pn_transport_set_tracer(transport, lib.pn_pytracer)
+
+
+lib.init()
+global_root = set()
+
+
+@ffi.def_extern()
+def pn_pyref_incref(obj):
+    global_root.add(obj)
+
+
+@ffi.def_extern()
+def pn_pyref_decref(obj):
+    global_root.discard(obj)
+
+
+def pn_collector_put_pyref(collector, obj, etype):
+    d = ffi.new_handle(obj)
+    global_root.add(d)
+    lib.pn_collector_put_py(collector, d, etype.number)
+
+
+def pn_record_def_py(record):
+    lib.pn_record_def_py(record)
+
+
+def pn_record_get_py(record):
+    d = lib.pn_record_get_py(record)
+    if d == ffi.NULL:
+        return None
+    return ffi.from_handle(d)
+
+
+def pn_record_set_py(record, value):
+    if value is None:
+        d = ffi.NULL
+    else:
+        d = ffi.new_handle(value)
+        global_root.add(d)
+    lib.pn_record_set_py(record, d)
+
+
+def pn_event_class_name(event):
+    return ffi.string(lib.pn_event_class_name_py(event)).decode('utf8')
+
+
+# size_t pn_transport_peek(pn_transport_t *transport, char *dst, size_t size);
+def pn_transport_peek(transport, size):
+    buff = bytearray(size)
+    cd = lib.pn_transport_peek(transport, ffi.from_buffer(buff), size)
+    if cd >= 0:
+        buff = buff[:cd]
+    return cd, buff
+
+
+# ssize_t pn_transport_push(pn_transport_t *transport, const char *src, size_t size);
+def pn_transport_push(transport, src):
+    return lib.pn_transport_push(transport, ffi.from_buffer(src), len(src))
+
+
+# int pn_message_decode(pn_message_t *msg, const char *bytes, size_t size);
+def pn_message_decode(msg, buff):
+    return lib.pn_message_decode(msg, ffi.from_buffer(buff), len(buff))
+
+
+# int pn_message_encode_py(pn_message_t *msg, char *bytes, size_t size);
+def pn_message_encode(msg, size):
+    buff = bytearray(size)
+    err = lib.pn_message_encode_py(msg, ffi.from_buffer(buff), size)
+    if err >= 0:
+        buff = buff[:err]
+    return err, buff
+
+
+# ssize_t pn_data_decode(pn_data_t *data, const char *bytes, size_t size);
+def pn_data_decode(data, buff):
+    return lib.pn_data_decode(data, ffi.from_buffer(buff), len(buff))
+
+
+# ssize_t pn_data_encode(pn_data_t *data, char *bytes, size_t size);
+def pn_data_encode(data, size):
+    buff = bytearray(size)
+    err = lib.pn_data_encode(data, ffi.from_buffer(buff), size)
+    if err >= 0:
+        buff = buff[:err]
+    return err, buff
+
+
+# int pn_data_format(pn_data_t *data, char *bytes, size_t *size);
+def pn_data_format(data, size):
+    buff = bytearray(size)
+    err = lib.pn_data_format_py(data, ffi.from_buffer(buff), size)
+    if err >= 0:
+        buff = buff[:err]
+    return err, buff
+
+
+# ssize_t pn_link_recv(pn_link_t *receiver, char *bytes, size_t n);
+def pn_link_recv(receiver, limit):
+    buff = bytearray(limit)
+    err = lib.pn_link_recv(receiver, ffi.from_buffer(buff), limit)
+    if err >= 0:
+        buff = buff[:err]
+    return err, buff
+
+
+# ssize_t pn_link_send(pn_link_t *sender, const char *bytes, size_t n);
+def pn_link_send(sender, buff):
+    return lib.pn_link_send(sender, ffi.from_buffer(buff), len(buff))
+
+
+# pn_condition bindings
+def pn_condition_set_name(cond, name):
+    return lib.pn_condition_set_name(cond, string2utf8(name))
+
+
+def pn_condition_set_description(cond, description):
+    return lib.pn_condition_set_description(cond, string2utf8(description))
+
+
+def pn_condition_get_name(cond):
+    return utf82string(lib.pn_condition_get_name(cond))
+
+
+def pn_condition_get_description(cond):
+    return utf82string(lib.pn_condition_get_description(cond))
+
+
+# pn_error bindings
+def pn_error_text(error):
+    return utf82string(lib.pn_error_text(error))
+
+# pn_data bindings
+def pn_data_lookup(data, name):
+    return lib.pn_data_lookup(data, string2utf8(name))
+
+def pn_data_put_decimal128(data, d):
+    return lib.pn_data_put_decimal128(data, py2decimal128(d))
+
+
+def pn_data_put_uuid(data, u):
+    return lib.pn_data_put_uuid(data, UUID2uuid(u))
+
+
+def pn_data_put_binary(data, b):
+    return lib.pn_data_put_binary(data, py2bytes(b))
+
+
+def pn_data_put_string(data, s):
+    return lib.pn_data_put_string(data, string2bytes(s))
+
+
+def pn_data_put_symbol(data, s):
+    return lib.pn_data_put_symbol(data, string2bytes(s, 'ascii'))
+
+def pn_data_get_decimal128(data):
+    return decimal1282py(lib.pn_data_get_decimal128(data))
+
+
+def pn_data_get_uuid(data):
+    return UUID(bytes=uuid2bytes(lib.pn_data_get_uuid(data)))
+
+
+def pn_data_get_binary(data):
+    return bytes2py(lib.pn_data_get_binary(data))
+
+
+def pn_data_get_string(data):
+    return bytes2string(lib.pn_data_get_string(data))
+
+
+def pn_data_get_symbol(data):
+    return bytes2string(lib.pn_data_get_symbol(data), 'ascii')
+
+
+def pn_delivery_tag(delivery):
+    return bytes2string(lib.pn_delivery_tag(delivery))
+
+
+def pn_connection_get_container(connection):
+    return utf82string(lib.pn_connection_get_container(connection))
+
+
+def pn_connection_set_container(connection, name):
+    lib.pn_connection_set_container(connection, string2utf8(name))
+
+
+def pn_connection_get_hostname(connection):
+    return utf82string(lib.pn_connection_get_hostname(connection))
+
+
+def pn_connection_set_hostname(connection, name):
+    lib.pn_connection_set_hostname(connection, string2utf8(name))
+
+
+def pn_connection_get_user(connection):
+    return utf82string(lib.pn_connection_get_user(connection))
+
+
+def pn_connection_set_user(connection, name):
+    lib.pn_connection_set_user(connection, string2utf8(name))
+
+
+def pn_connection_get_authorization(connection):
+    return utf82string(lib.pn_connection_get_authorization(connection))
+
+
+def pn_connection_set_authorization(connection, name):
+    lib.pn_connection_set_authorization(connection, string2utf8(name))
+
+
+def pn_connection_set_password(connection, name):
+    lib.pn_connection_set_password(connection, string2utf8(name))
+
+
+def pn_connection_remote_container(connection):
+    return utf82string(lib.pn_connection_remote_container(connection))
+
+
+def pn_connection_remote_hostname(connection):
+    return utf82string(lib.pn_connection_remote_hostname(connection))
+
+
+def pn_sender(session, name):
+    return lib.pn_sender(session, string2utf8(name))
+
+
+def pn_receiver(session, name):
+    return lib.pn_receiver(session, string2utf8(name))
+
+
+def pn_delivery(link, tag):
+    return lib.pn_delivery(link, py2bytes(tag))
+
+def pn_link_name(link):
+    return utf82string(lib.pn_link_name(link))
+
+
+def pn_terminus_get_address(terminus):
+    return utf82string(lib.pn_terminus_get_address(terminus))
+
+
+def pn_terminus_set_address(terminus, address):
+    return lib.pn_terminus_set_address(terminus, string2utf8(address))
+
+
+def pn_event_type_name(number):
+    return utf82string(lib.pn_event_type_name(number))
+
+
+def pn_message_get_id(message):
+    return msgid2py(lib.pn_message_get_id(message))
+
+
+def pn_message_set_id(message, value):
+    lib.pn_message_set_id(message, py2msgid(value))
+
+
+def pn_message_get_user_id(message):
+    return bytes2py(lib.pn_message_get_user_id(message))
+
+
+def pn_message_set_user_id(message, value):
+    return lib.pn_message_set_user_id(message, py2bytes(value))
+
+
+def pn_message_get_address(message):
+    return utf82string(lib.pn_message_get_address(message))
+
+
+def pn_message_set_address(message, value):
+    return lib.pn_message_set_address(message, string2utf8(value))
+
+
+def pn_message_get_subject(message):
+    return utf82string(lib.pn_message_get_subject(message))
+
+
+def pn_message_set_subject(message, value):
+    return lib.pn_message_set_subject(message, string2utf8(value))
+
+
+def pn_message_get_reply_to(message):
+    return utf82string(lib.pn_message_get_reply_to(message))
+
+
+def pn_message_set_reply_to(message, value):
+    return lib.pn_message_set_reply_to(message, string2utf8(value))
+
+
+def pn_message_get_correlation_id(message):
+    return msgid2py(lib.pn_message_get_correlation_id(message))
+
+
+def pn_message_set_correlation_id(message, value):
+    lib.pn_message_set_correlation_id(message, py2msgid(value))
+
+
+def pn_message_get_content_type(message):
+    return utf82string(lib.pn_message_get_content_type(message))
+
+
+def pn_message_set_content_type(message, value):
+    return lib.pn_message_set_content_type(message, string2utf8(value))
+
+
+def pn_message_get_content_encoding(message):
+    return utf82string(lib.pn_message_get_content_encoding(message))
+
+
+def pn_message_set_content_encoding(message, value):
+    return lib.pn_message_set_content_encoding(message, string2utf8(value))
+
+
+def pn_message_get_group_id(message):
+    return utf82string(lib.pn_message_get_group_id(message))
+
+
+def pn_message_set_group_id(message, value):
+    return lib.pn_message_set_group_id(message, string2utf8(value))
+
+
+def pn_message_get_reply_to_group_id(message):
+    return utf82string(lib.pn_message_get_reply_to_group_id(message))
+
+
+def pn_message_set_reply_to_group_id(message, value):
+    return lib.pn_message_set_reply_to_group_id(message, string2utf8(value))
+
+
+def pn_transport_log(transport, message):
+    lib.pn_transport_log(transport, string2utf8(message))
+
+def pn_transport_get_user(transport):
+    return utf82string(lib.pn_transport_get_user(transport))
+
+
+def pn_sasl_get_user(sasl):
+    return utf82string(lib.pn_sasl_get_user(sasl))
+
+
+def pn_sasl_get_authorization(sasl):
+    return utf82string(lib.pn_sasl_get_authorization(sasl))
+
+
+def pn_sasl_get_mech(sasl):
+    return utf82string(lib.pn_sasl_get_mech(sasl))
+
+
+def pn_sasl_allowed_mechs(sasl, mechs):
+    lib.pn_sasl_allowed_mechs(sasl, string2utf8(mechs))
+
+
+def pn_sasl_config_name(sasl, name):
+    lib.pn_sasl_config_name(sasl, string2utf8(name))
+
+
+def pn_sasl_config_path(sasl, path):
+    lib.pn_sasl_config_path(sasl, string2utf8(path))
+
+def pn_ssl_domain_set_credentials(domain, cert_file, key_file, password):
+    return lib.pn_ssl_domain_set_credentials(domain, string2utf8(cert_file), string2utf8(key_file), string2utf8(password))
+
+
+def pn_ssl_domain_set_trusted_ca_db(domain, certificate_db):
+    return lib.pn_ssl_domain_set_trusted_ca_db(domain, string2utf8(certificate_db))
+
+
+def pn_ssl_domain_set_peer_authentication(domain, verify_mode, trusted_CAs):
+    return lib.pn_ssl_domain_set_peer_authentication(domain, verify_mode, string2utf8(trusted_CAs))
+
+
+def pn_ssl_init(ssl, domain, session_id):
+    lib.pn_ssl_init(ssl, domain, string2utf8(session_id))
+
+
+def pn_ssl_get_remote_subject_subfield(ssl, subfield_name):
+    return utf82string(lib.pn_ssl_get_remote_subject_subfield(ssl, subfield_name))
+
+
+def pn_ssl_get_remote_subject(ssl):
+    return  utf82string(lib.pn_ssl_get_remote_subject(ssl))
+
+
+# int pn_ssl_domain_set_protocols(pn_ssl_domain_t *domain, const char *protocols);
+def pn_ssl_domain_set_protocols(domain, protocols):
+    return lib.pn_ssl_domain_set_protocols(domain, string2utf8(protocols))
+
+
+# int pn_ssl_domain_set_ciphers(pn_ssl_domain_t *domain, const char *ciphers);
+def pn_ssl_domain_set_ciphers(domain, ciphers):
+    return lib.pn_ssl_domain_set_ciphers(domain, string2utf8(ciphers))
+
+
+# _Bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *buffer, size_t size);
+def pn_ssl_get_cipher_name(ssl, size):
+    buff = ffi.new('char[]', size)
+    r = lib.pn_ssl_get_cipher_name(ssl, buff, size)
+    if r:
+        return utf82string(buff)
+    return None
+
+
+# _Bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *buffer, size_t size);
+def pn_ssl_get_protocol_name(ssl, size):
+    buff = ffi.new('char[]', size)
+    r = lib.pn_ssl_get_protocol_name(ssl, buff, size)
+    if r:
+        return utf82string(buff)
+    return None
+
+
+# int pn_ssl_get_cert_fingerprint(pn_ssl_t *ssl, char *fingerprint, size_t fingerprint_len, pn_ssl_hash_alg hash_alg);
+def pn_ssl_get_cert_fingerprint(ssl, fingerprint_len, hash_alg):
+    buff = ffi.new('char[]', fingerprint_len)
+    r = lib.pn_ssl_get_cert_fingerprint(ssl, buff, fingerprint_len, hash_alg)
+    if r==PN_OK:
+        return utf82string(buff)
+    return None
+
+
+# int pn_ssl_get_peer_hostname(pn_ssl_t *ssl, char *hostname, size_t *bufsize);
+def pn_ssl_get_peer_hostname(ssl, size):
+    buff = ffi.new('char[]', size)
+    r = lib.pn_ssl_get_peer_hostname_py(ssl, buff, size)
+    if r==PN_OK:
+        return r, utf82string(buff)
+    return r, None
+
+def pn_ssl_set_peer_hostname(ssl, hostname):
+    return lib.pn_ssl_set_peer_hostname(ssl, string2utf8(hostname))
+
diff --git a/python/ext_build.py b/python/ext_build.py
new file mode 100644
index 000000000..c6dc6e40e
--- /dev/null
+++ b/python/ext_build.py
@@ -0,0 +1,195 @@
+#
+# 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.
+#
+
+import os
+
+import cffi.pkgconfig
+
+from distutils import ccompiler
+from cffi import FFI
+
+
+ffibuilder = FFI()
+
+# cdef() expects a single string declaring the C types, functions and
+# globals needed to use the shared object. It must be in valid C syntax
+# with cffi extensions
+cdefs = open('cproton.h').read()
+ffibuilder.cdef(cdefs)
+
+proton_base = '.'
+proton_c_src = os.path.join(proton_base, 'src')
+proton_core_src = os.path.join(proton_c_src, 'core')
+proton_c_include = os.path.join(proton_base, 'include')
+
+sources = []
+extra = []
+libraries = []
+for root, _, files in os.walk(proton_core_src):
+    for file_ in files:
+        if file_.endswith(('.c', '.cpp')):
+            sources.append(os.path.join(root, file_))
+
+compiler_type = ccompiler.get_default_compiler()
+
+if compiler_type == 'msvc':
+    sources += [
+        os.path.join(proton_c_src, 'compiler', 'msvc', 'start.c')
+    ]
+elif compiler_type == 'unix':
+    sources += [
+        os.path.join(proton_c_src, 'compiler', 'gcc', 'start.c')
+    ]
+    extra += ['-std=c99']
+
+sources.append(os.path.join(proton_c_src, 'sasl', 'sasl.c'))
+sources.append(os.path.join(proton_c_src, 'sasl', 'default_sasl.c'))
+
+pkgconfig = []
+if os.name == 'nt':
+    libraries += ['crypt32', 'secur32']
+    sources.append(os.path.join(proton_c_src, 'ssl', 'schannel.cpp'))
+else:
+    try:
+        ssl_pkgcfg = cffi.pkgconfig.flags_from_pkgconfig(['openssl'])
+        sources.append(os.path.join(proton_c_src, 'ssl', 'openssl.c'))
+        pkgconfig.append('openssl')
+    except cffi.pkgconfig.PkgConfigError:
+        # Stub ssl
+        sources.append(os.path.join(proton_c_src, 'ssl', 'ssl_stub.c'))
+
+# Stub sasl
+try:
+    sasl_pkgcfg = cffi.pkgconfig.flags_from_pkgconfig(['libsasl2'])
+    sources.append(os.path.join(proton_c_src, 'sasl', 'cyrus_sasl.c'))
+    pkgconfig.append('libsasl2')
+except cffi.pkgconfig.PkgConfigError:
+    sources.append(os.path.join(proton_c_src, 'sasl', 'cyrus_stub.c'))
+
+include_dirs = [proton_c_include, proton_c_src]
+macros = [('PROTON_DECLARE_STATIC', None)]
+
+c_code = r"""
+#include "proton/version.h"
+#include "proton/types.h"
+#include "proton/object.h"
+#include "proton/error.h"
+#include "proton/condition.h"
+#include "proton/connection.h"
+#include "proton/session.h"
+#include "proton/link.h"
+#include "proton/terminus.h"
+#include "proton/delivery.h"
+#include "proton/disposition.h"
+#include "proton/transport.h"
+#include "proton/event.h"
+#include "proton/message.h"
+#include "proton/sasl.h"
+#include "proton/ssl.h"
+#include "proton/codec.h"
+#include "proton/connection_driver.h"
+#include "proton/cid.h"
+
+static void pn_pyref_incref(void *object);
+static void pn_pyref_decref(void *object);
+
+static int pn_pyref_refcount(void *object) {
+    return 1;
+}
+
+pn_connection_t *pn_cast_pn_connection(void *x) { return (pn_connection_t *) x; }
+pn_session_t *pn_cast_pn_session(void *x) { return (pn_session_t *) x; }
+pn_link_t *pn_cast_pn_link(void *x) { return (pn_link_t *) x; }
+pn_delivery_t *pn_cast_pn_delivery(void *x) { return (pn_delivery_t *) x; }
+pn_transport_t *pn_cast_pn_transport(void *x) { return (pn_transport_t *) x; }
+
+static pn_class_t* PN_PYREF;
+PN_HANDLE(PN_PYCTX);
+
+static pn_class_t* pn_create_pyref() {
+    return pn_class_create("pn_pyref", NULL, NULL, pn_pyref_incref, pn_pyref_decref, pn_pyref_refcount);
+}
+
+pn_event_t *pn_collector_put_py(pn_collector_t *collector, void *context, pn_event_type_t type) {
+    return pn_collector_put(collector, PN_PYREF, context, type);
+}
+
+void pn_record_def_py(pn_record_t *record) {
+    pn_record_def(record, PN_PYCTX, PN_PYREF);
+}
+
+void *pn_record_get_py(pn_record_t *record) {
+    return pn_record_get(record, PN_PYCTX);
+}
+
+void pn_record_set_py(pn_record_t *record, void *value) {
+    pn_record_set(record, PN_PYCTX, value);
+}
+
+ssize_t pn_message_encode_py(pn_message_t *msg, char *bytes, size_t size) {
+    int err = pn_message_encode(msg, bytes, &size);
+    if (err == 0) return size;
+    else return err;
+}
+
+ssize_t pn_data_format_py(pn_data_t *data, char *bytes, size_t size) {
+    int err = pn_data_format(data, bytes, &size);
+    if (err == 0) return size;
+    else return err;
+}
+
+int pn_ssl_get_peer_hostname_py(pn_ssl_t *ssl, char *hostname, size_t size) {
+    return pn_ssl_get_peer_hostname(ssl, hostname, &size);
+}
+
+const char *pn_event_class_name_py(pn_event_t *event) {
+    pn_class_t *class = pn_event_class(event);
+    return class ? pn_class_name(class) : class;
+}
+
+void init() {
+    PN_PYREF = pn_create_pyref();
+}
+
+
+"""
+
+if len(pkgconfig) == 0:
+    ffibuilder.set_source(
+        "cproton_ffi",
+        c_code,
+        define_macros=macros,
+        extra_compile_args=extra,
+        sources=sources,
+        include_dirs=include_dirs,
+        libraries=libraries
+    )
+else:
+    ffibuilder.set_source_pkgconfig(
+        "cproton_ffi",
+        pkgconfig,
+        c_code,
+        define_macros=macros,
+        extra_compile_args=extra,
+        sources=sources,
+        include_dirs=include_dirs
+    )
+
+if __name__ == "__main__":
+    ffibuilder.compile(verbose=True)
diff --git a/python/proton/_common.py b/python/proton/_common.py
index bc03b4646..134d398a8 100644
--- a/python/proton/_common.py
+++ b/python/proton/_common.py
@@ -17,15 +17,7 @@
 # under the License.
 #
 
-from typing import Optional, Union, Any
-
-
-def isinteger(value: Any) -> bool:
-    return isinstance(value, int)
-
-
-def isstring(value: Any) -> bool:
-    return isinstance(value, str)
+from typing import Union
 
 
 class Constant(object):
@@ -43,28 +35,3 @@ def secs2millis(secs: Union[float, int]) -> int:
 
 def millis2secs(millis: int) -> float:
     return float(millis) / 1000.0
-
-
-def unicode2utf8(string: Optional[str]) -> Optional[str]:
-    """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):
-        # The swig binding converts py3 str -> utf8 char* and back automatically
-        return string
-    # Anything else illegal - specifically python3 bytes
-    raise TypeError("Unrecognized string type: %r (%s)" % (string, type(string)))
-
-
-def utf82unicode(string: Optional[Union[str, bytes]]) -> Optional[str]:
-    """Convert C strings returned from proton-c into python unicode"""
-    if string is None:
-        return None
-    elif isinstance(string, str):
-        return string
-    elif isinstance(string, bytes):
-        return string.decode('utf8')
-    raise TypeError("Unrecognized string type")
diff --git a/python/proton/_condition.py b/python/proton/_condition.py
index ab390afb0..cf0c1c5e2 100644
--- a/python/proton/_condition.py
+++ b/python/proton/_condition.py
@@ -83,7 +83,7 @@ class Condition:
 def obj2cond(obj, cond: Condition) -> None:
     pn_condition_clear(cond)
     if obj:
-        pn_condition_set_name(cond, str(obj.name))
+        pn_condition_set_name(cond, obj.name)
         pn_condition_set_description(cond, obj.description)
         info = Data(pn_condition_info(cond))
         if obj.info:
diff --git a/python/proton/_data.py b/python/proton/_data.py
index 05294e05d..8e8355f7e 100644
--- a/python/proton/_data.py
+++ b/python/proton/_data.py
@@ -1067,7 +1067,7 @@ class Data:
         :param u: a uuid value.
         :raise: :exc:`DataException` if there is a Proton error.
         """
-        self._check(pn_data_put_uuid(self._data, u.bytes))
+        self._check(pn_data_put_uuid(self._data, u))
 
     def put_binary(self, b: bytes) -> None:
         """
@@ -1103,7 +1103,7 @@ class Data:
         :param s: a unicode string
         :raise: :exc:`DataException` if there is a Proton error.
         """
-        self._check(pn_data_put_string(self._data, s.encode("utf8")))
+        self._check(pn_data_put_string(self._data, s))
 
     def put_symbol(self, s: Union[str, symbol]) -> None:
         """
@@ -1112,7 +1112,7 @@ class Data:
         :param s: the symbol name
         :raise: :exc:`DataException` if there is a Proton error.
         """
-        self._check(pn_data_put_symbol(self._data, s.encode('ascii')))
+        self._check(pn_data_put_symbol(self._data, s))
 
     def get_list(self) -> int:
         """
@@ -1346,7 +1346,7 @@ class Data:
         :return: If the current node is a UUID, its value, ``None`` otherwise.
         """
         if pn_data_type(self._data) == Data.UUID:
-            return uuid.UUID(bytes=pn_data_get_uuid(self._data))
+            return pn_data_get_uuid(self._data)
         else:
             return None
 
@@ -1364,7 +1364,7 @@ class Data:
 
         :return: If the current node is a string, its value, ``""`` otherwise.
         """
-        return pn_data_get_string(self._data).decode("utf8")
+        return pn_data_get_string(self._data)
 
     def get_symbol(self) -> symbol:
         """
@@ -1372,7 +1372,7 @@ class Data:
 
         :return: If the current node is a symbol, its value, ``""`` otherwise.
         """
-        return symbol(pn_data_get_symbol(self._data).decode('ascii'))
+        return symbol(pn_data_get_symbol(self._data))
 
     def copy(self, src: 'Data') -> None:
         """
diff --git a/python/proton/_delivery.py b/python/proton/_delivery.py
index 40143c286..f78b23596 100644
--- a/python/proton/_delivery.py
+++ b/python/proton/_delivery.py
@@ -24,7 +24,8 @@ from cproton import PN_ACCEPTED, PN_MODIFIED, PN_RECEIVED, PN_REJECTED, PN_RELEA
     pn_delivery_writable, pn_disposition_annotations, pn_disposition_condition, pn_disposition_data, \
     pn_disposition_get_section_number, pn_disposition_get_section_offset, pn_disposition_is_failed, \
     pn_disposition_is_undeliverable, pn_disposition_set_failed, pn_disposition_set_section_number, \
-    pn_disposition_set_section_offset, pn_disposition_set_undeliverable, pn_disposition_type
+    pn_disposition_set_section_offset, pn_disposition_set_undeliverable, pn_disposition_type, \
+    isnull
 
 from ._condition import cond2obj, obj2cond
 from ._data import dat2obj, obj2dat
@@ -289,7 +290,7 @@ class Delivery(Wrapper):
 
     @staticmethod
     def wrap(impl):
-        if impl is None:
+        if isnull(impl):
             return None
         else:
             return Delivery(impl)
diff --git a/python/proton/_endpoints.py b/python/proton/_endpoints.py
index d2064bd93..4f6ac20ca 100644
--- a/python/proton/_endpoints.py
+++ b/python/proton/_endpoints.py
@@ -54,9 +54,9 @@ from cproton import PN_CONFIGURATION, PN_COORDINATOR, PN_DELIVERIES, PN_DIST_MOD
     pn_terminus_is_dynamic, pn_terminus_outcomes, pn_terminus_properties, pn_terminus_set_address, \
     pn_terminus_set_distribution_mode, pn_terminus_set_durability, pn_terminus_set_dynamic, \
     pn_terminus_set_expiry_policy, pn_terminus_set_timeout, pn_terminus_set_type, \
-    pn_link_properties, pn_link_remote_properties
+    pn_link_properties, pn_link_remote_properties, \
+    isnull
 
-from ._common import unicode2utf8, utf82unicode
 from ._condition import cond2obj, obj2cond
 from ._data import Data, dat2obj, obj2dat, PropertyDict, SymbolList
 from ._delivery import Delivery
@@ -161,13 +161,16 @@ class Connection(Wrapper, Endpoint):
 
     @staticmethod
     def wrap(impl):
-        if impl is None:
+        if isnull(impl):
             return None
         else:
             return Connection(impl)
 
-    def __init__(self, impl: Callable[[], Any] = pn_connection) -> None:
-        Wrapper.__init__(self, impl, pn_connection_attachments)
+    def __init__(self, impl: Any = None) -> None:
+        if impl is None:
+            Wrapper.__init__(self, constructor=pn_connection, get_context=pn_connection_attachments)
+        else:
+            Wrapper.__init__(self, impl, pn_connection_attachments)
 
     def _init(self) -> None:
         Endpoint._init(self)
@@ -219,11 +222,11 @@ class Connection(Wrapper, Endpoint):
     @property
     def container(self) -> str:
         """The container name for this connection object."""
-        return utf82unicode(pn_connection_get_container(self._impl))
+        return pn_connection_get_container(self._impl)
 
     @container.setter
     def container(self, name: str) -> None:
-        pn_connection_set_container(self._impl, unicode2utf8(name))
+        pn_connection_set_container(self._impl, name)
 
     @property
     def hostname(self) -> Optional[str]:
@@ -233,11 +236,11 @@ class Connection(Wrapper, Endpoint):
         This value will be sent in the Open performative, and will be used by SSL
         and SASL layers to identify the peer.
         """
-        return utf82unicode(pn_connection_get_hostname(self._impl))
+        return pn_connection_get_hostname(self._impl)
 
     @hostname.setter
     def hostname(self, name: str) -> None:
-        pn_connection_set_hostname(self._impl, unicode2utf8(name))
+        pn_connection_set_hostname(self._impl, name)
 
     @property
     def user(self) -> Optional[str]:
@@ -252,11 +255,11 @@ class Connection(Wrapper, Endpoint):
         like Kerberos where the credentials are implicit in the environment,
         or to explicitly use the ``ANONYMOUS`` SASL mechanism)
         """
-        return utf82unicode(pn_connection_get_user(self._impl))
+        return pn_connection_get_user(self._impl)
 
     @user.setter
     def user(self, name: str) -> None:
-        pn_connection_set_user(self._impl, unicode2utf8(name))
+        pn_connection_set_user(self._impl, name)
 
     @property
     def authorization(self) -> str:
@@ -269,11 +272,11 @@ class Connection(Wrapper, Endpoint):
         If not set then implicitly the requested authorization is the same as the
         authentication user.
         """
-        return utf82unicode(pn_connection_get_authorization(self._impl))
+        return pn_connection_get_authorization(self._impl)
 
     @authorization.setter
     def authorization(self, name: str) -> None:
-        pn_connection_set_authorization(self._impl, unicode2utf8(name))
+        pn_connection_set_authorization(self._impl, name)
 
     @property
     def password(self) -> None:
@@ -288,7 +291,7 @@ class Connection(Wrapper, Endpoint):
 
     @password.setter
     def password(self, name: str) -> None:
-        pn_connection_set_password(self._impl, unicode2utf8(name))
+        pn_connection_set_password(self._impl, name)
 
     @property
     def remote_container(self) -> Optional[str]:
@@ -553,7 +556,7 @@ class Session(Wrapper, Endpoint):
     """A container of links"""
     @staticmethod
     def wrap(impl):
-        if impl is None:
+        if isnull(impl):
             return None
         else:
             return Session(impl)
@@ -675,7 +678,7 @@ class Session(Wrapper, Endpoint):
 
         :param name: Name of sender
         """
-        return Sender(pn_sender(self._impl, unicode2utf8(name)))
+        return Sender(pn_sender(self._impl, name))
 
     def receiver(self, name: str) -> 'Receiver':
         """
@@ -683,7 +686,7 @@ class Session(Wrapper, Endpoint):
 
         :param name: Name of receiver
         """
-        return Receiver(pn_receiver(self._impl, unicode2utf8(name)))
+        return Receiver(pn_receiver(self._impl, name))
 
     def free(self) -> None:
         """
@@ -717,7 +720,7 @@ class Link(Wrapper, Endpoint):
 
     @staticmethod
     def wrap(impl):
-        if impl is None:
+        if isnull(impl):
             return None
         if pn_link_is_sender(impl):
             return Sender(impl)
@@ -974,7 +977,7 @@ class Link(Wrapper, Endpoint):
         """
         The name of the link.
         """
-        return utf82unicode(pn_link_name(self._impl))
+        return pn_link_name(self._impl)
 
     @property
     def is_sender(self) -> bool:
@@ -1312,11 +1315,11 @@ class Terminus(object):
     @property
     def address(self) -> Optional[str]:
         """The address that identifies the source or target node"""
-        return utf82unicode(pn_terminus_get_address(self._impl))
+        return pn_terminus_get_address(self._impl)
 
     @address.setter
     def address(self, address: str) -> None:
-        self._check(pn_terminus_set_address(self._impl, unicode2utf8(address)))
+        self._check(pn_terminus_set_address(self._impl, address))
 
     @property
     def durability(self) -> int:
diff --git a/python/proton/_events.py b/python/proton/_events.py
index b3c10c270..9cc721ad9 100644
--- a/python/proton/_events.py
+++ b/python/proton/_events.py
@@ -22,13 +22,14 @@ import threading
 from cproton import PN_CONNECTION_BOUND, PN_CONNECTION_FINAL, PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_CLOSE, \
     PN_CONNECTION_LOCAL_OPEN, PN_CONNECTION_REMOTE_CLOSE, PN_CONNECTION_REMOTE_OPEN, PN_CONNECTION_UNBOUND, PN_DELIVERY, \
     PN_LINK_FINAL, PN_LINK_FLOW, PN_LINK_INIT, PN_LINK_LOCAL_CLOSE, PN_LINK_LOCAL_DETACH, PN_LINK_LOCAL_OPEN, \
-    PN_LINK_REMOTE_CLOSE, PN_LINK_REMOTE_DETACH, PN_LINK_REMOTE_OPEN, PN_PYREF, PN_SESSION_FINAL, PN_SESSION_INIT, \
+    PN_LINK_REMOTE_CLOSE, PN_LINK_REMOTE_DETACH, PN_LINK_REMOTE_OPEN, PN_SESSION_FINAL, PN_SESSION_INIT, \
     PN_SESSION_LOCAL_CLOSE, PN_SESSION_LOCAL_OPEN, PN_SESSION_REMOTE_CLOSE, PN_SESSION_REMOTE_OPEN, PN_TIMER_TASK, \
     PN_TRANSPORT, PN_TRANSPORT_CLOSED, PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT_TAIL_CLOSED, \
     pn_cast_pn_connection, pn_cast_pn_delivery, pn_cast_pn_link, pn_cast_pn_session, pn_cast_pn_transport, \
-    pn_class_name, pn_collector, pn_collector_free, pn_collector_more, pn_collector_peek, pn_collector_pop, \
-    pn_collector_put, pn_collector_release, pn_event_class, pn_event_connection, pn_event_context, pn_event_delivery, \
-    pn_event_link, pn_event_session, pn_event_transport, pn_event_type, pn_event_type_name, pn_py2void, pn_void2py
+    pn_collector, pn_collector_free, pn_collector_more, pn_collector_peek, pn_collector_pop, \
+    pn_collector_put_pyref, pn_collector_release, pn_event_connection, pn_event_context, pn_event_delivery, pn_event_link, \
+    pn_event_session, pn_event_transport, pn_event_type, pn_event_class_name, pn_event_type_name, \
+    isnull, void2py
 
 from ._delivery import Delivery
 from ._endpoints import Connection, Link, Session
@@ -48,8 +49,8 @@ class Collector:
     def __init__(self) -> None:
         self._impl = pn_collector()
 
-    def put(self, obj: 'Selectable', etype: 'EventType') -> None:
-        pn_collector_put(self._impl, PN_PYREF, pn_py2void(obj), etype.number)
+    def put(self, obj: Any, etype: 'EventType') -> None:
+        pn_collector_put_pyref(self._impl, obj, etype)
 
     def peek(self) -> Optional['Event']:
         return Event.wrap(pn_collector_peek(self._impl))
@@ -173,8 +174,8 @@ def _internal(name: str) -> EventType:
 
 
 wrappers = {
-    "pn_void": lambda x: pn_void2py(x),
-    "pn_pyref": lambda x: pn_void2py(x),
+    "pn_void": lambda x: void2py(x),
+    "pn_pyref": lambda x: 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)),
@@ -399,22 +400,20 @@ class Event(EventBase):
 
     @staticmethod
     def wrap(impl):
-        if impl is None:
+        if isnull(impl):
             return None
 
         number = pn_event_type(impl)
-        cls = pn_event_class(impl)
-
-        if cls:
-            clsname = pn_class_name(cls)
+        clsname = pn_event_class_name(impl)
+        if clsname:
             context = wrappers[clsname](pn_event_context(impl))
 
             # check for an application defined ApplicationEvent and return that.  This
             # avoids an expensive wrap operation invoked by event.context
-            if cls == PN_PYREF and isinstance(context, EventBase):
+            if isinstance(context, EventBase):
                 return context
         else:
-            clsname = None
+            context = None
 
         event = Event(impl, number, clsname, context)
         return event
diff --git a/python/proton/_message.py b/python/proton/_message.py
index 82eaa58af..2b1032a0e 100644
--- a/python/proton/_message.py
+++ b/python/proton/_message.py
@@ -31,7 +31,7 @@ from cproton import PN_DEFAULT_PRIORITY, PN_STRING, PN_UUID, PN_OVERFLOW, pn_err
     pn_message_set_reply_to, pn_message_set_reply_to_group_id, pn_message_set_subject, \
     pn_message_set_ttl, pn_message_set_user_id
 
-from ._common import isinteger, millis2secs, secs2millis, unicode2utf8, utf82unicode
+from ._common import millis2secs, secs2millis
 from ._data import char, Data, symbol, ulong, AnnotationDict
 from ._endpoints import Link
 from ._exceptions import EXCEPTIONS, MessageException
@@ -277,10 +277,6 @@ class Message(object):
 
     @id.setter
     def id(self, value: Optional[Union[str, bytes, 'UUID', int]]) -> None:
-        if isinteger(value):
-            value = ulong(value)
-        elif isinstance(value, UUID):
-            value = (PN_UUID, value.bytes)
         pn_message_set_id(self._msg, value)
 
     @property
@@ -301,11 +297,11 @@ class Message(object):
 
         :raise: :exc:`MessageException` if there is any Proton error when using the setter.
         """
-        return utf82unicode(pn_message_get_address(self._msg))
+        return pn_message_get_address(self._msg)
 
     @address.setter
     def address(self, value: str) -> None:
-        self._check(pn_message_set_address(self._msg, unicode2utf8(value)))
+        self._check(pn_message_set_address(self._msg, value))
 
     @property
     def subject(self) -> Optional[str]:
@@ -313,11 +309,11 @@ class Message(object):
 
         :raise: :exc:`MessageException` if there is any Proton error when using the setter.
         """
-        return utf82unicode(pn_message_get_subject(self._msg))
+        return pn_message_get_subject(self._msg)
 
     @subject.setter
     def subject(self, value: str) -> None:
-        self._check(pn_message_set_subject(self._msg, unicode2utf8(value)))
+        self._check(pn_message_set_subject(self._msg, value))
 
     @property
     def reply_to(self) -> Optional[str]:
@@ -325,11 +321,11 @@ class Message(object):
 
         :raise: :exc:`MessageException` if there is any Proton error when using the setter.
         """
-        return utf82unicode(pn_message_get_reply_to(self._msg))
+        return pn_message_get_reply_to(self._msg)
 
     @reply_to.setter
     def reply_to(self, value: str) -> None:
-        self._check(pn_message_set_reply_to(self._msg, unicode2utf8(value)))
+        self._check(pn_message_set_reply_to(self._msg, value))
 
     @property
     def correlation_id(self) -> Optional[Union['UUID', ulong, str, bytes]]:
@@ -350,10 +346,6 @@ class Message(object):
 
     @correlation_id.setter
     def correlation_id(self, value: Optional[Union[str, bytes, 'UUID', int]]) -> None:
-        if isinteger(value):
-            value = ulong(value)
-        elif isinstance(value, UUID):
-            value = (PN_UUID, value.bytes)
         pn_message_set_correlation_id(self._msg, value)
 
     @property
@@ -362,11 +354,11 @@ class Message(object):
 
         :raise: :exc:`MessageException` if there is any Proton error when using the setter.
         """
-        return symbol(utf82unicode(pn_message_get_content_type(self._msg)))
+        return symbol(pn_message_get_content_type(self._msg))
 
     @content_type.setter
     def content_type(self, value: str) -> None:
-        self._check(pn_message_set_content_type(self._msg, unicode2utf8(value)))
+        self._check(pn_message_set_content_type(self._msg, value))
 
     @property
     def content_encoding(self) -> symbol:
@@ -374,11 +366,11 @@ class Message(object):
 
         :raise: :exc:`MessageException` if there is any Proton error when using the setter.
         """
-        return symbol(utf82unicode(pn_message_get_content_encoding(self._msg)))
+        return symbol(pn_message_get_content_encoding(self._msg))
 
     @content_encoding.setter
     def content_encoding(self, value: str) -> None:
-        self._check(pn_message_set_content_encoding(self._msg, unicode2utf8(value)))
+        self._check(pn_message_set_content_encoding(self._msg, value))
 
     @property
     def expiry_time(self) -> float:  # TODO doc said int
@@ -410,11 +402,11 @@ class Message(object):
 
         :raise: :exc:`MessageException` if there is any Proton error when using the setter.
         """
-        return utf82unicode(pn_message_get_group_id(self._msg))
+        return pn_message_get_group_id(self._msg)
 
     @group_id.setter
     def group_id(self, value: str) -> None:
-        self._check(pn_message_set_group_id(self._msg, unicode2utf8(value)))
+        self._check(pn_message_set_group_id(self._msg, value))
 
     @property
     def group_sequence(self) -> int:
@@ -434,11 +426,11 @@ class Message(object):
 
         :raise: :exc:`MessageException` if there is any Proton error when using the setter.
         """
-        return utf82unicode(pn_message_get_reply_to_group_id(self._msg))
+        return pn_message_get_reply_to_group_id(self._msg)
 
     @reply_to_group_id.setter
     def reply_to_group_id(self, value: str) -> None:
-        self._check(pn_message_set_reply_to_group_id(self._msg, unicode2utf8(value)))
+        self._check(pn_message_set_reply_to_group_id(self._msg, value))
 
     @property
     def instructions(self) -> Optional[AnnotationDict]:
diff --git a/python/proton/_reactor.py b/python/proton/_reactor.py
index 6696c7037..40939863e 100644
--- a/python/proton/_reactor.py
+++ b/python/proton/_reactor.py
@@ -41,9 +41,8 @@ import traceback
 import uuid
 from functools import total_ordering
 
-from cproton import PN_PYREF, PN_ACCEPTED, PN_EVENT_NONE
+from cproton import PN_ACCEPTED, PN_EVENT_NONE
 
-from ._common import isstring, unicode2utf8
 from ._data import Described, symbol, ulong
 from ._delivery import Delivery
 from ._endpoints import Connection, Endpoint, Link, Session, Terminus
@@ -335,7 +334,7 @@ class Reactor(object):
             handler: Optional[Handler] = None,
     ) -> 'Acceptor':
         impl = self._make_handler(handler)
-        a = Acceptor(self, unicode2utf8(host), int(port), impl)
+        a = Acceptor(self, host, int(port), impl)
         if a:
             return a
         else:
@@ -497,7 +496,6 @@ class ApplicationEvent(EventBase):
                 eventtype = EventType(typename)
                 self.TYPES[typename] = eventtype
         super(ApplicationEvent, self).__init__(eventtype)
-        self.clazz = PN_PYREF
         self.connection = connection
         self.session = session
         self.link = link
@@ -1466,7 +1464,7 @@ class Container(Reactor):
 
         :return: New sender instance.
         """
-        if isstring(context):
+        if isinstance(context, str):
             context = Url(context)
         if isinstance(context, Url) and not target:
             target = context.path
@@ -1527,7 +1525,7 @@ class Container(Reactor):
 
         :return: New receiver instance.
         """
-        if isstring(context):
+        if isinstance(context, str):
             context = Url(context)
         if isinstance(context, Url) and not source:
             source = context.path
diff --git a/python/proton/_transport.py b/python/proton/_transport.py
index 89a18f6bd..612857f6b 100644
--- a/python/proton/_transport.py
+++ b/python/proton/_transport.py
@@ -40,9 +40,10 @@ from cproton import PN_EOS, PN_OK, PN_SASL_AUTH, PN_SASL_NONE, PN_SASL_OK, PN_SA
     pn_transport_peek, pn_transport_pending, pn_transport_pop, pn_transport_push, pn_transport_remote_channel_max, \
     pn_transport_require_auth, pn_transport_require_encryption, pn_transport_set_channel_max, \
     pn_transport_set_idle_timeout, pn_transport_set_max_frame, pn_transport_set_pytracer, pn_transport_set_server, \
-    pn_transport_tick, pn_transport_trace, pn_transport_unbind
+    pn_transport_tick, pn_transport_trace, pn_transport_unbind, \
+    isnull
 
-from ._common import millis2secs, secs2millis, unicode2utf8, utf82unicode
+from ._common import millis2secs, secs2millis
 from ._condition import cond2obj, obj2cond
 from ._exceptions import EXCEPTIONS, SSLException, SSLUnavailable, SessionException, TransportException
 from ._wrapper import Wrapper
@@ -86,17 +87,20 @@ class Transport(Wrapper):
 
     @staticmethod
     def wrap(impl: Optional[Callable]) -> Optional['Transport']:
-        if impl is None:
+        if isnull(impl):
             return None
         else:
-            return Transport(_impl=impl)
+            return Transport(impl=impl)
 
     def __init__(
             self,
             mode: 'Optional[int]' = None,
-            _impl: 'Callable' = pn_transport,
+            impl: 'Callable' = None,
     ) -> None:
-        Wrapper.__init__(self, _impl, pn_transport_attachments)
+        if impl is None:
+            Wrapper.__init__(self, constructor=pn_transport, get_context=pn_transport_attachments)
+        else:
+            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:
@@ -643,7 +647,7 @@ class SASL(Wrapper):
         """
         if isinstance(mechs, list):
             mechs = " ".join(mechs)
-        pn_sasl_allowed_mechs(self._sasl, unicode2utf8(mechs))
+        pn_sasl_allowed_mechs(self._sasl, mechs)
 
     @property
     def allow_insecure_mechs(self) -> bool:
@@ -887,10 +891,7 @@ class SSL(object):
 
         :return: The cypher name, or ``None`` if no cipher in use.
         """
-        rc, name = pn_ssl_get_cipher_name(self._ssl, 128)
-        if rc:
-            return name
-        return None
+        return pn_ssl_get_cipher_name(self._ssl, 128)
 
     def protocol_name(self) -> Optional[str]:
         """
@@ -904,10 +905,7 @@ class SSL(object):
         :return: The protocol name if SSL is active, or ``None`` if SSL connection
                  is not ready or active.
         """
-        rc, name = pn_ssl_get_protocol_name(self._ssl, 128)
-        if rc:
-            return name
-        return None
+        return pn_ssl_get_protocol_name(self._ssl, 128)
 
     SHA1 = PN_SSL_SHA1
     """Produces hash that is 20 bytes long using SHA-1"""
@@ -1044,10 +1042,7 @@ class SSL(object):
                             :const:`SHA256`, :const:`SHA512`,  :const:`MD5`.
         :return: Hex fingerprint in a string, or ``None`` if an error occurred.
         """
-        rc, fingerprint_str = pn_ssl_get_cert_fingerprint(self._ssl, fingerprint_length, digest_name)
-        if rc == PN_OK:
-            return fingerprint_str
-        return None
+        return pn_ssl_get_cert_fingerprint(self._ssl, fingerprint_length, digest_name)
 
     # Convenience functions for obtaining fingerprint for specific hashing algorithms
     def _get_cert_fingerprint_unknown_hash_alg(self) -> None:
@@ -1146,11 +1141,11 @@ class SSL(object):
             :meth:`SSLDomain.set_peer_authentication`."""
         err, name = pn_ssl_get_peer_hostname(self._ssl, 1024)
         self._check(err)
-        return utf82unicode(name)
+        return name
 
     @peer_hostname.setter
     def peer_hostname(self, hostname: Optional[str]) -> None:
-        self._check(pn_ssl_set_peer_hostname(self._ssl, unicode2utf8(hostname)))
+        self._check(pn_ssl_set_peer_hostname(self._ssl, hostname))
 
 
 class SSLSessionDetails(object):
diff --git a/python/proton/_wrapper.py b/python/proton/_wrapper.py
index 4b3973f96..f22f3fce2 100644
--- a/python/proton/_wrapper.py
+++ b/python/proton/_wrapper.py
@@ -17,12 +17,10 @@
 # under the License.
 #
 
-from typing import Any, Callable, Optional, Union
+from typing import Any, Callable, Optional
 
-from cproton import pn_incref, pn_decref, \
-    pn_py2void, pn_void2py, \
-    pn_record_get, pn_record_def, pn_record_set, \
-    PN_PYREF
+from cproton import addressof, pn_incref, pn_decref, \
+    pn_record_get_py, pn_record_def_py, pn_record_set_py
 
 from ._exceptions import ProtonException
 
@@ -43,7 +41,7 @@ EMPTY_ATTRS = EmptyAttrs()
 
 
 class Wrapper(object):
-    """ Wrapper for python objects that need to be stored in event contexts and be retrived again from them
+    """ Wrapper for python objects that need to be stored in event contexts and be retrieved again from them
         Quick note on how this works:
         The actual *python* object has only 3 attributes which redirect into the wrapped C objects:
         _impl   The wrapped C object itself
@@ -61,13 +59,14 @@ class Wrapper(object):
 
     def __init__(
             self,
-            impl_or_constructor: Union[Any, Callable[[], Any]],
+            impl: Any = None,
             get_context: Optional[Callable[[Any], Any]] = None,
+            constructor: Callable[[], Any] = None
     ) -> None:
         init = False
-        if callable(impl_or_constructor):
+        if impl is None and constructor is not None:
             # we are constructing a new object
-            impl = impl_or_constructor()
+            impl = constructor()
             if impl is None:
                 self.__dict__["_impl"] = impl
                 self.__dict__["_attrs"] = EMPTY_ATTRS
@@ -77,16 +76,15 @@ class Wrapper(object):
             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))
+            attrs = pn_record_get_py(record)
             if attrs is None:
                 attrs = {}
-                pn_record_def(record, PYCTX, PN_PYREF)
-                pn_record_set(record, PYCTX, pn_py2void(attrs))
+                pn_record_def_py(record)
+                pn_record_set_py(record, attrs)
                 init = True
         else:
             attrs = EMPTY_ATTRS
@@ -137,7 +135,3 @@ class Wrapper(object):
         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
diff --git a/python/pyproject.toml b/python/pyproject.toml
new file mode 100644
index 000000000..f8074913f
--- /dev/null
+++ b/python/pyproject.toml
@@ -0,0 +1,44 @@
+[build-system]
+requires = ["setuptools", "cffi>=1.0.0"]
+build-backend = "setuptools.build_meta"
+
+# Commented out for present because support is beta in setuptools so we should just use setup.cfg
+#[project]
+#name = "python-qpid-proton"
+#description = "An AMQP based messaging library."
+#readme = "README.rst"
+#license = {text = "Apache Software License"}
+#classifiers = [
+#  "License :: OSI Approved :: Apache Software License",
+#  "Intended Audience :: Developers",
+#  "Programming Language :: Python",
+#  "Programming Language :: Python :: 3",
+#  "Programming Language :: Python :: 3 :: Only",
+#  "Programming Language :: Python :: 3.6",
+#  "Programming Language :: Python :: 3.7",
+#  "Programming Language :: Python :: 3.8",
+#  "Programming Language :: Python :: 3.9",
+#  "Programming Language :: Python :: 3.10",
+#  "Programming Language :: Python :: 3.11"
+#]
+#dependencies = [
+#    "cffi>=1.0.0"
+#]
+#authors = [
+#    {name = "Apache Qpid", email = "users@qpid.apache.org"}
+#]
+#dynamic = ["version"]
+#
+#[project.urls]
+#homepage = "http://qpid.apache.org/proton/"
+#
+#[project.optional-dependencies]
+#opentracing = ["opentracing", "jaeger_client"]
+#
+#[tool.setuptools]
+#packages = ["proton"]
+#py-modules = ["cproton"]
+## cffi-modules = "ext_build.py:ffibuilder"
+#
+#[tool.setuptools.dynamic]
+#version = {file = "VERSION.txt"}
diff --git a/python/setup.cfg b/python/setup.cfg
index 30ddffdcb..00791fc6a 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -1,4 +1,5 @@
 [metadata]
+name = python-qpid-proton
 version = file: VERSION.txt
 description = An AMQP based messaging library.
 long_description = file: README.rst
@@ -20,6 +21,8 @@ classifiers =
 [options]
 packages = proton
 py_modules = cproton
+setup_requires = cffi>=1.0.0
+install_requires = cffi>=1.0.0
 
 [options.extras_require]
 opentracing = opentracing; jaeger_client
diff --git a/python/setup.py b/python/setup.py
index c6f7c2b54..c78fe6e7e 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -17,178 +17,9 @@
 # specific language governing permissions and limitations
 # under the License.
 
-"""
-python-qpid-proton setup script
 
-DISCLAIMER: This script took lots of inspirations from PyZMQ, which is licensed
-under the 'MODIFIED BSD LICENSE'.
+from setuptools import setup
 
-Although inspired by the work in PyZMQ, this script and the modules it depends
-on were largely simplified to meet the requirements of the library.
-
-The behavior of this script is to build the registered `_cproton` extension
-using the installed Qpid Proton C library and header files. If the library and
-headers are not installed, or the installed version does not match the version
-of these python bindings, then the script will attempt to build the extension
-using the Proton C sources included in the python source distribution package.
-"""
-
-import os
-
-from setuptools import setup, Extension
-from setuptools.command.build_ext import build_ext
-
-from setuputils import log
-from setuputils import misc
-
-
-class BuildExtension(build_ext):
-    description = "Build Qpid Proton extension"
-
-    def use_bundled_proton(self):
-        """The proper version of libqpid-proton-core is not installed on the system,
-        so use the included proton-c sources to build the extension
-        """
-        log.info("Building the bundled proton-c sources into the extension")
-
-        setup_path = os.path.dirname(os.path.realpath(__file__))
-        base = self.get_finalized_command('build').build_base
-        build_include = os.path.join(base, 'include')
-        proton_base = os.path.relpath(setup_path)
-        proton_src = os.path.join(proton_base, 'src')
-        proton_core_src = os.path.join(proton_base, 'src', 'core')
-        proton_include = os.path.join(proton_base, 'include')
-
-        log.debug("Using Proton C sources: %s" % proton_base)
-
-        # Collect all the Proton C files packaged in the sdist and strip out
-        # anything windows and configuration-dependent
-
-        sources = []
-        for root, _, files in os.walk(proton_core_src):
-            for file_ in files:
-                if file_.endswith(('.c', '.cpp')):
-                    sources.append(os.path.join(root, file_))
-
-        # Look for any optional libraries that proton needs, and adjust the
-        # source list and compile flags as necessary.
-        library_dirs = []
-        libraries = []
-        includes = []
-        macros = []
-        extra = []
-
-        # -D flags (None means no value, just define)
-        macros += [('PROTON_DECLARE_STATIC', None)]
-
-        cc = self.compiler
-
-        if cc.compiler_type == 'msvc':
-            sources += [
-                os.path.join(proton_src, 'compiler', 'msvc', 'start.c')
-            ]
-        elif cc.compiler_type == 'unix':
-            sources += [
-                os.path.join(proton_src, 'compiler', 'gcc', 'start.c')
-            ]
-            extra += ['-std=c99']
-
-        # Check whether openssl is installed by poking
-        # pkg-config for a minimum version 0. If it's installed, it should
-        # return True and we'll use it. Otherwise, we'll use the stub.
-        if misc.pkg_config_version_installed('openssl', atleast='0'):
-            library_dirs += [misc.pkg_config_get_var('openssl', 'libdir')]
-            libraries += ['ssl', 'crypto']
-            includes += [misc.pkg_config_get_var('openssl', 'includedir')]
-            sources.append(os.path.join(proton_src, 'ssl', 'openssl.c'))
-        elif os.name == 'nt':
-            libraries += ['crypt32', 'secur32']
-            sources.append(os.path.join(proton_src, 'ssl', 'schannel.cpp'))
-        else:
-            sources.append(os.path.join(proton_src, 'ssl', 'ssl_stub.c'))
-            log.warn("OpenSSL not installed - disabling SSL support!")
-
-        sources.append(os.path.join(proton_src, 'sasl', 'sasl.c'))
-        sources.append(os.path.join(proton_src, 'sasl', 'default_sasl.c'))
-
-        # Check whether cyrus sasl is installed by asking pkg-config
-        # This works for all recent versions of cyrus sasl
-        if misc.pkg_config_version_installed('libsasl2', atleast='0'):
-            library_dirs += [misc.pkg_config_get_var('libsasl2', 'libdir')]
-            libraries.append('sasl2')
-            includes += [misc.pkg_config_get_var('libsasl2', 'includedir')]
-            sources.append(os.path.join(proton_src, 'sasl', 'cyrus_sasl.c'))
-        else:
-            sources.append(os.path.join(proton_src, 'sasl', 'cyrus_stub.c'))
-            log.warn("Cyrus SASL not installed - only the ANONYMOUS and PLAIN mechanisms will be supported!")
-
-        # compile all the proton sources.  We'll add the resulting list of
-        # objects to the _cproton extension as 'extra objects'.  We do this
-        # instead of just lumping all the sources into the extension to prevent
-        # any proton-specific compilation flags from affecting the compilation
-        # of the generated swig code
-        objects = cc.compile(sources,
-                             macros=macros,
-                             include_dirs=[build_include,
-                                           proton_include,
-                                           proton_src] + includes,
-                             # compiler command line options:
-                             extra_preargs=extra,
-                             output_dir=self.build_temp)
-
-        #
-        # Now update the _cproton extension instance passed to setup to include
-        # the objects and libraries
-        #
-        _cproton = self.distribution.ext_modules[-1]
-        _cproton.extra_objects = objects
-        _cproton.include_dirs.append(build_include)
-        _cproton.include_dirs.append(proton_include)
-
-        # lastly replace the libqpid-proton-core dependency with libraries required
-        # by the Proton objects:
-        _cproton.library_dirs = library_dirs
-        _cproton.libraries = libraries
-        _cproton.extra_compile_args = ['-DPROTON_DECLARE_STATIC']
-
-    def libqpid_proton_installed(self):
-        """Check to see if the proper version of the Proton development library
-        and headers are already installed
-        """
-        return misc.pkg_config_version_installed('libqpid-proton-core', atleast='0.38')
-
-    def use_installed_proton(self):
-        """The Proton development headers and library are installed, update the
-        _cproton extension to tell it where to find the library and headers.
-        """
-        # update the Extension instance passed to setup() to use the installed
-        # headers and link library
-        _cproton = self.distribution.ext_modules[-1]
-        incs = misc.pkg_config_get_var('libqpid-proton-core', 'includedir')
-        for i in incs.split():
-            _cproton.swig_opts.append('-I%s' % i)
-            _cproton.include_dirs.append(i)
-        ldirs = misc.pkg_config_get_var('libqpid-proton-core', 'libdir')
-        _cproton.library_dirs.extend(ldirs.split())
-
-    def build_extensions(self):
-        # check if the Proton library and headers are installed and are
-        # compatible with this version of the binding.
-        if self.libqpid_proton_installed():
-            self.use_installed_proton()
-        else:
-            # Proton not installed or compatible, use bundled proton-c sources
-            self.use_bundled_proton()
-        super().build_extensions()
-
-
-setup(name='python-qpid-proton',
-      cmdclass={
-          'build_ext': BuildExtension
-      },
-      # Note well: the following extension instance is modified during the
-      # installation!  If you make changes below, you may need to update the
-      # Configure class above
-      ext_modules=[Extension('_cproton',
-                             sources=['cprotonPYTHON_wrap.c'],
-                             libraries=['qpid-proton-core'])])
+setup(
+    cffi_modules='ext_build.py:ffibuilder'
+)
diff --git a/python/setuputils/PYZMQ_LICENSE.BSD b/python/setuputils/PYZMQ_LICENSE.BSD
deleted file mode 100644
index a0a3790ba..000000000
--- a/python/setuputils/PYZMQ_LICENSE.BSD
+++ /dev/null
@@ -1,32 +0,0 @@
-PyZMQ is licensed under the terms of the Modified BSD License (also known as
-New or Revised BSD), as follows:
-
-Copyright (c) 2009-2012, Brian Granger, Min Ragan-Kelley
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-
-Neither the name of PyZMQ nor the names of its contributors may be used to
-endorse or promote products derived from this software without specific prior
-written permission.
-
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/python/setuputils/__init__.py b/python/setuputils/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/python/setuputils/log.py b/python/setuputils/log.py
deleted file mode 100644
index 06b8e50d5..000000000
--- a/python/setuputils/log.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# -----------------------------------------------------------------------------
-#  Copyright (C) PyZMQ Developers
-#  Distributed under the terms of the Modified BSD License.
-#
-#  This bundling code is largely adapted from pyzmq-static's get.sh by
-#  Brandon Craig-Rhodes, which is itself BSD licensed.
-# -----------------------------------------------------------------------------
-
-# -----------------------------------------------------------------------------
-# Logging (adapted from h5py: http://h5py.googlecode.com)
-# -----------------------------------------------------------------------------
-
-# -----------------------------------------------------------------------------
-#  This log code is largely adapted from pyzmq's code
-#  PyZMQ Developers, which is itself Modified BSD licensed.
-# -----------------------------------------------------------------------------
-
-
-import os
-import sys
-import logging
-
-
-logger = logging.getLogger()
-if os.environ.get('DEBUG'):
-    logger.setLevel(logging.DEBUG)
-else:
-    logger.setLevel(logging.INFO)
-logger.addHandler(logging.StreamHandler(sys.stderr))
-
-
-def debug(msg):
-    logger.debug(msg)
-
-
-def info(msg):
-    logger.info(msg)
-
-
-def fatal(msg, code=1):
-    logger.error("Fatal: " + msg)
-    exit(code)
-
-
-def warn(msg):
-    logger.error("Warning: " + msg)
diff --git a/python/setuputils/misc.py b/python/setuputils/misc.py
deleted file mode 100644
index 009190d1e..000000000
--- a/python/setuputils/misc.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# -----------------------------------------------------------------------------
-#  Copyright (C) PyZMQ Developers
-#  Distributed under the terms of the Modified BSD License.
-#
-#  This bundling code is largely adapted from pyzmq-static's get.sh by
-#  Brandon Craig-Rhodes, which is itself BSD licensed.
-# -----------------------------------------------------------------------------
-
-# -----------------------------------------------------------------------------
-#  These functions were largely adapted from pyzmq's code
-#  PyZMQ Developers, which is itself Modified BSD licensed.
-# -----------------------------------------------------------------------------
-
-
-import errno
-import os
-import subprocess
-import sys
-
-from . import log
-
-
-def _call_pkg_config(args):
-    """Spawn a subprocess running pkg-config with the given args.
-
-    :param args: list of strings to pass to pkg-config's command line.
-    Refer to pkg-config's documentation for more detail.
-
-    Return the Popen object, or None if the command failed
-    """
-    try:
-        return subprocess.Popen(['pkg-config'] + args,
-                                stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                                universal_newlines=True)
-    except OSError as e:
-        if e.errno == errno.ENOENT:
-            log.warn("command not found: pkg-config")
-        else:
-            log.warn("Running pkg-config failed - %s." % e)
-    return None
-
-
-def pkg_config_version_installed(package, version=None, atleast=None):
-    """Check if version of a package is is installed
-
-    This function returns True/False depending on whether
-    the package is found and is the correct version.
-
-    :param version: The exact version of the package required
-    :param atleast: True if installed package is at least this version
-    """
-
-    if version is None and atleast is None:
-        log.fatal('package version string required')
-    elif version and atleast:
-        log.fatal('Specify either version or atleast, not both')
-
-    check = 'exact' if version else 'atleast'
-    p = _call_pkg_config(['--%s-version=%s' % (check,
-                                               version or atleast),
-                          package])
-    if p:
-        out, err = p.communicate()
-        if p.returncode:
-            log.info("Did not find %s via pkg-config: %s" % (package, err))
-            return False
-        log.info("Using %s version %s (found via pkg-config)" %
-                 (package,
-                  _call_pkg_config(['--modversion', package]).communicate()[0].splitlines()[0]))
-        return True
-    return False
-
-
-def pkg_config_get_var(package, name):
-    """Retrieve the value of the named package variable as a string
-    """
-    p = _call_pkg_config(['--variable=%s' % name, package])
-    if not p:
-        log.warn("pkg-config: var %s get failed, package %s", name, package)
-        return ""
-    out, err = p.communicate()
-    if p.returncode:
-        out = ""
-        log.warn(err)
-    return out.splitlines()[0]


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