diff options
Diffstat (limited to 'cython')
| -rw-r--r-- | cython/Makefile.am | 96 | ||||
| -rw-r--r-- | cython/afc.pxi | 28 | ||||
| -rw-r--r-- | cython/debugserver.pxi | 3 | ||||
| -rw-r--r-- | cython/imobiledevice.pxd | 11 | ||||
| -rw-r--r-- | cython/imobiledevice.pyx | 9 | ||||
| -rw-r--r-- | cython/installation_proxy.pxi | 2 | ||||
| -rw-r--r-- | cython/lockdown.pxi | 26 | ||||
| -rw-r--r-- | cython/mobile_image_mounter.pxi | 43 | ||||
| -rw-r--r-- | cython/mobilebackup2.pxi | 31 | ||||
| -rw-r--r-- | cython/mobilesync.pxi | 2 | ||||
| -rw-r--r-- | cython/notification_proxy.pxi | 2 | 
11 files changed, 176 insertions, 77 deletions
| diff --git a/cython/Makefile.am b/cython/Makefile.am index 6aeaf08..93ea6ed 100644 --- a/cython/Makefile.am +++ b/cython/Makefile.am @@ -1,52 +1,84 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include +AM_CPPFLAGS = \ +	-I$(top_srcdir)/include -AM_CFLAGS = $(GLOBAL_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(openssl_CFLAGS) $(libplist_CFLAGS) $(LFS_CFLAGS) $(PTHREAD_CFLAGS) -AM_LIBS = $(libgnutls_LIBS) $(libtasn1_LIBS) $(openssl_LIBS) $(libplist_LIBS) $(PTHREAD_LIBS) +AM_CFLAGS = \ +	$(GLOBAL_CFLAGS) \ +	$(ssl_lib_CFLAGS) \ +	$(LFS_CFLAGS) \ +	$(PTHREAD_CFLAGS) \ +	$(libplist_CFLAGS) + +AM_LIBS = \ +	$(ssl_lib_LIBS) \ +	$(PTHREAD_LIBS) \ +	$(libplist_LIBS)  if HAVE_CYTHON  BUILT_SOURCES = imobiledevice.c -PXDINCLUDES = imobiledevice.pxd $(CYTHON_PLIST_INCLUDE_DIR)/plist.pxd -PXIINCLUDES =					\ -	lockdown.pxi				\ -	mobilesync.pxi				\ -	notification_proxy.pxi		\ -	sbservices.pxi				\ -	mobilebackup.pxi			\ -	mobilebackup2.pxi			\ -	afc.pxi						\ -	file_relay.pxi				\ -	screenshotr.pxi				\ -	installation_proxy.pxi		\ -	webinspector.pxi			\ -	heartbeat.pxi				\ -	diagnostics_relay.pxi		\ -	misagent.pxi				\ -	house_arrest.pxi			\ -	restore.pxi					\ -	mobile_image_mounter.pxi    \ +PXDINCLUDES = \ +	imobiledevice.pxd \ +	$(CYTHON_PLIST_INCLUDE_DIR)/plist.pxd + +PXIINCLUDES = \ +	lockdown.pxi \ +	mobilesync.pxi \ +	notification_proxy.pxi \ +	sbservices.pxi \ +	mobilebackup.pxi \ +	mobilebackup2.pxi \ +	afc.pxi \ +	file_relay.pxi \ +	screenshotr.pxi \ +	installation_proxy.pxi \ +	webinspector.pxi \ +	heartbeat.pxi \ +	diagnostics_relay.pxi \ +	misagent.pxi \ +	house_arrest.pxi \ +	restore.pxi \ +	mobile_image_mounter.pxi \  	debugserver.pxi -CLEANFILES =		\ -	*.pyc			\ -	*.pyo			\ +CLEANFILES = \ +	*.pyc \ +	*.pyo \  	imobiledevice.c -EXTRA_DIST = imobiledevice.pyx imobiledevice.pxd $(PXIINCLUDES) +EXTRA_DIST = \ +	imobiledevice.pyx \ +	imobiledevice.pxd \ +	$(PXIINCLUDES)  imobiledevicedir = $(pyexecdir)  imobiledevice_LTLIBRARIES = imobiledevice.la  imobiledevice_la_SOURCES = imobiledevice.pyx -imobiledevice_la_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(PYTHON_CPPFLAGS) $(AM_CFLAGS) -Wno-shadow -Wno-redundant-decls -Wno-switch-default -Wno-strict-aliasing -Wno-implicit-function-declaration -fvisibility=default -imobiledevice_la_LDFLAGS = -module -avoid-version -L$(libdir) $(PYTHON_LIBS) $(AM_LIBS) -no-undefined -imobiledevice_la_LIBADD = $(top_builddir)/src/libimobiledevice.la +imobiledevice_la_CFLAGS = \ +	-I$(top_srcdir)/include \ +	-I$(top_srcdir)/src \ +	$(PYTHON_CPPFLAGS) \ +	$(AM_CFLAGS) \ +	-Wno-shadow \ +	-Wno-redundant-decls \ +	-Wno-switch-default \ +	-Wno-strict-aliasing \ +	-Wno-implicit-function-declaration \ +	-fvisibility=default \ +	$(CYTHON_CFLAGS) + +imobiledevice_la_LDFLAGS = \ +	-module \ +	-avoid-version \ +	-L$(libdir) \ +	$(PYTHON_LIBS) \ +	$(AM_LIBS) \ +	-no-undefined + +imobiledevice_la_LIBADD = $(top_builddir)/src/libimobiledevice-1.0.la  imobiledevice.c: imobiledevice.pyx $(PXDINCLUDES) $(PXIINCLUDES)  .pyx.c:  	$(CYTHON) -I$(CYTHON_PLIST_INCLUDE_DIR) -I$(top_srcdir)/src -o $@ $< -# imobiledevice_private.c: $(IMOBILEDEVICE_PRIVATE_SOURCES) $(IMOBILEDEVICE_INCLUDES) $(PLIST_INCLUDES) -#	$(CYTHON) $(IMOBILEDEVICE_CPPFLAGS) -I$(top_srcdir)/src -o $@ $< -  endif diff --git a/cython/afc.pxi b/cython/afc.pxi index e34588f..6bd8182 100644 --- a/cython/afc.pxi +++ b/cython/afc.pxi @@ -52,6 +52,7 @@ cdef extern from "libimobiledevice/afc.h":      afc_error_t afc_read_directory(afc_client_t client, char *dir, char ***list)      afc_error_t afc_get_file_info(afc_client_t client, char *filename, char ***infolist)      afc_error_t afc_remove_path(afc_client_t client, char *path) +    afc_error_t afc_remove_path_and_contents(afc_client_t client, char *path)      afc_error_t afc_rename_path(afc_client_t client, char *f, char *to)      afc_error_t afc_make_directory(afc_client_t client, char *dir)      afc_error_t afc_truncate(afc_client_t client, char *path, uint64_t newsize) @@ -235,17 +236,17 @@ cdef class AfcClient(BaseService):              afc_file_mode_t c_mode              uint64_t handle              AfcFile f -        if mode == <bytes>'r': +        if mode == b'r':              c_mode = AFC_FOPEN_RDONLY -        elif mode == <bytes>'r+': +        elif mode == b'r+':              c_mode = AFC_FOPEN_RW -        elif mode == <bytes>'w': +        elif mode == b'w':              c_mode = AFC_FOPEN_WRONLY -        elif mode == <bytes>'w+': +        elif mode == b'w+':              c_mode = AFC_FOPEN_WR -        elif mode == <bytes>'a': +        elif mode == b'a':              c_mode = AFC_FOPEN_APPEND -        elif mode == <bytes>'a+': +        elif mode == b'a+':              c_mode = AFC_FOPEN_RDAPPEND          else:              raise ValueError("mode string must be 'r', 'r+', 'w', 'w+', 'a', or 'a+'") @@ -282,6 +283,9 @@ cdef class AfcClient(BaseService):      cpdef remove_path(self, bytes path):          self.handle_error(afc_remove_path(self._c_client, path)) +    cpdef remove_path_and_contents(self, bytes path): +        self.handle_error(afc_remove_path_and_contents(self._c_client, path)) +      cpdef rename_path(self, bytes f, bytes t):          self.handle_error(afc_rename_path(self._c_client, f, t)) @@ -308,17 +312,17 @@ cdef class Afc2Client(AfcClient):              afc_file_mode_t c_mode              uint64_t handle              AfcFile f -        if mode == <bytes>'r': +        if mode == b'r':              c_mode = AFC_FOPEN_RDONLY -        elif mode == <bytes>'r+': +        elif mode == b'r+':              c_mode = AFC_FOPEN_RW -        elif mode == <bytes>'w': +        elif mode == b'w':              c_mode = AFC_FOPEN_WRONLY -        elif mode == <bytes>'w+': +        elif mode == b'w+':              c_mode = AFC_FOPEN_WR -        elif mode == <bytes>'a': +        elif mode == b'a':              c_mode = AFC_FOPEN_APPEND -        elif mode == <bytes>'a+': +        elif mode == b'a+':              c_mode = AFC_FOPEN_RDAPPEND          else:              raise ValueError("mode string must be 'r', 'r+', 'w', 'w+', 'a', or 'a+'") diff --git a/cython/debugserver.pxi b/cython/debugserver.pxi index 4ecb9e1..fb96320 100644 --- a/cython/debugserver.pxi +++ b/cython/debugserver.pxi @@ -43,8 +43,7 @@ cdef class DebugServerError(BaseError):          BaseError.__init__(self, *args, **kwargs) -# from http://stackoverflow.com/a/17511714 -from cpython.string cimport PyString_AsString +from cpython.bytes cimport PyBytes_AsString as PyString_AsString  cdef char ** to_cstring_array(list_str):      if not list_str:          return NULL diff --git a/cython/imobiledevice.pxd b/cython/imobiledevice.pxd index 8523c94..238df68 100644 --- a/cython/imobiledevice.pxd +++ b/cython/imobiledevice.pxd @@ -1,3 +1,6 @@ +#!python +#cython: language_level=3str +  cimport plist  from libc.stdint cimport * @@ -23,13 +26,17 @@ cdef extern from "libimobiledevice/libimobiledevice.h":      cdef struct idevice_connection_private:          pass      ctypedef idevice_connection_private* idevice_connection_t +    cdef enum idevice_connection_type: +        CONNECTION_USBMUXD = 1 +        CONNECTION_NETWORK      cdef enum idevice_event_type: -        IDEVICE_DEVICE_ADD = 1, +        IDEVICE_DEVICE_ADD = 1          IDEVICE_DEVICE_REMOVE +        IDEVICE_DEVICE_PAIRED      ctypedef struct idevice_event_t:          idevice_event_type event          char *udid -        int conn_type +        idevice_connection_type conn_type      ctypedef idevice_event_t* const_idevice_event_t "const idevice_event_t*"  cdef class iDeviceEvent: diff --git a/cython/imobiledevice.pyx b/cython/imobiledevice.pyx index 141f67c..8da2296 100644 --- a/cython/imobiledevice.pyx +++ b/cython/imobiledevice.pyx @@ -40,6 +40,10 @@ cdef extern from "libimobiledevice/libimobiledevice.h":          IDEVICE_E_NOT_ENOUGH_DATA = -4          IDEVICE_E_SSL_ERROR = -6          IDEVICE_E_TIMEOUT = -7 +    cdef enum idevice_options: +        IDEVICE_LOOKUP_USBMUX = 1 << 1 +        IDEVICE_LOOKUP_NETWORK = 1 << 2 +        IDEVICE_LOOKUP_PREFER_NETWORK = 1 << 3      ctypedef void (*idevice_event_cb_t) (const_idevice_event_t event, void *user_data)      cdef extern idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data)      cdef extern idevice_error_t idevice_event_unsubscribe() @@ -47,6 +51,7 @@ cdef extern from "libimobiledevice/libimobiledevice.h":      idevice_error_t idevice_device_list_free(char **devices)      void idevice_set_debug_level(int level)      idevice_error_t idevice_new(idevice_t *device, char *udid) +    idevice_error_t idevice_new_with_options(idevice_t *device, const char *udid, idevice_options options);      idevice_error_t idevice_free(idevice_t device)      idevice_error_t idevice_get_udid(idevice_t device, char** udid)      idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) @@ -89,7 +94,7 @@ cdef class iDeviceEvent:          def __get__(self):              return self._c_event.conn_type -cdef void idevice_event_cb(const_idevice_event_t c_event, void *user_data) with gil: +cdef void idevice_event_cb(const_idevice_event_t c_event, void *user_data) noexcept:      cdef iDeviceEvent event = iDeviceEvent.__new__(iDeviceEvent)      event._c_event = c_event      (<object>user_data)(event) @@ -171,7 +176,7 @@ from libc.stdlib cimport *  cdef class iDevice(Base):      def __cinit__(self, object udid=None, *args, **kwargs):          cdef char* c_udid = NULL -        if isinstance(udid, basestring): +        if isinstance(udid, (str, bytes)):              c_udid = <bytes>udid          elif udid is not None:              raise TypeError("iDevice's constructor takes a string or None as the udid argument") diff --git a/cython/installation_proxy.pxi b/cython/installation_proxy.pxi index bf2c1da..1d3e323 100644 --- a/cython/installation_proxy.pxi +++ b/cython/installation_proxy.pxi @@ -27,7 +27,7 @@ cdef extern from "libimobiledevice/installation_proxy.h":      instproxy_error_t instproxy_restore(instproxy_client_t client, char *appid, plist.plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)      instproxy_error_t instproxy_remove_archive(instproxy_client_t client, char *appid, plist.plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) -cdef void instproxy_notify_cb(plist.plist_t command, plist.plist_t status, void *py_callback) with gil: +cdef void instproxy_notify_cb(plist.plist_t command, plist.plist_t status, void *py_callback) noexcept:      (<object>py_callback)(plist.plist_t_to_node(command, False), plist.plist_t_to_node(status, False))  cdef class InstallationProxyError(BaseError): diff --git a/cython/lockdown.pxi b/cython/lockdown.pxi index f249049..25edb4c 100644 --- a/cython/lockdown.pxi +++ b/cython/lockdown.pxi @@ -210,14 +210,23 @@ cdef class LockdownClient(PropertyListService):              raise      cpdef set_value(self, bytes domain, bytes key, object value): -        cdef plist.plist_t c_node = plist.native_to_plist_t(value) +        cdef:  +            plist.plist_t c_node = NULL +            char* c_domain = NULL +            char* c_key = NULL + +        c_node = plist.native_to_plist_t(value) +        if domain is not None: +            c_domain = domain +        if key is not None: +            c_key = key          try: -            self.handle_error(lockdownd_set_value(self._c_client, domain, key, c_node)) +            self.handle_error(lockdownd_set_value(self._c_client, c_domain, c_key, c_node))          except BaseError, e:              raise          finally:              if c_node != NULL: -                plist.plist_free(c_node) +                c_node = NULL      cpdef remove_value(self, bytes domain, bytes key):          self.handle_error(lockdownd_remove_value(self._c_client, domain, key)) @@ -230,12 +239,13 @@ cdef class LockdownClient(PropertyListService):          if issubclass(service, BaseService) and \              service.__service_name__ is not None \ -            and isinstance(service.__service_name__, basestring): -            c_service_name = <bytes>service.__service_name__ -        elif isinstance(service, basestring): -            c_service_name = <bytes>service +            and isinstance(service.__service_name__, (str, bytes)): +            c_service_name_str = service.__service_name__.encode('utf-8') +        elif isinstance(service, (str, bytes)): +            c_service_name_str = service.encode('utf-8')          else:              raise TypeError("LockdownClient.start_service() takes a BaseService or string as its first argument") +        c_service_name = c_service_name_str          try:              self.handle_error(lockdownd_start_service(self._c_client, c_service_name, &c_descriptor)) @@ -253,7 +263,7 @@ cdef class LockdownClient(PropertyListService):          if not hasattr(service_class, '__service_name__') and \              not service_class.__service_name__ is not None \ -            and not isinstance(service_class.__service_name__, basestring): +            and not isinstance(service_class.__service_name__, (str, bytes)):              raise TypeError("LockdownClient.get_service_client() takes a BaseService as its first argument")          descriptor = self.start_service(service_class) diff --git a/cython/mobile_image_mounter.pxi b/cython/mobile_image_mounter.pxi index a23a59b..d9d40d5 100644 --- a/cython/mobile_image_mounter.pxi +++ b/cython/mobile_image_mounter.pxi @@ -13,7 +13,9 @@ cdef extern from "libimobiledevice/mobile_image_mounter.h":      mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, lockdownd_service_descriptor_t descriptor, mobile_image_mounter_client_t *client)      mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client)      mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, char *image_type, plist.plist_t *result) -    mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, char *image_path, char *image_signature, uint16_t signature_length, char *image_type, plist.plist_t *result) +    mobile_image_mounter_error_t mobile_image_mounter_mount_image_with_options(mobile_image_mounter_client_t client, char *image_path, const unsigned char *signature, unsigned int signature_length, char *image_type, plist.plist_t options, plist.plist_t *result) +    mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, char *image_path, const unsigned char *signature, unsigned int signature_length, char *image_type, plist.plist_t *result) +    mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path);      mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client)  cdef class MobileImageMounterError(BaseError): @@ -57,11 +59,39 @@ cdef class MobileImageMounterClient(PropertyListService):              if c_node != NULL:                  plist.plist_free(c_node) -    cpdef plist.Node mount_image(self, bytes image_path, bytes image_signature, bytes image_type): +    cpdef plist.Node mount_image_with_options(self, bytes image_path, bytes signature, bytes image_type, object options):          cdef: +            plist.Node n_options +            plist.plist_t c_options +            plist.plist_t c_result = NULL +            bint free_options = False              plist.plist_t c_node = NULL              mobile_image_mounter_error_t err -        err = mobile_image_mounter_mount_image(self._c_client, image_path, image_signature, len(image_signature), +        if isinstance(options, plist.Dict): +            n_options = options +            c_options = n_options._c_node +        elif isinstance(options, dict): +            c_options = plist.native_to_plist_t(options) +            free_options = True +        else: +            raise InstallationProxyError(INSTPROXY_E_INVALID_ARG) +        err = mobile_image_mounter_mount_image_with_options(self._c_client, image_path, signature, len(signature), +                                               image_type, c_options, &c_node) +        if free_options: +            plist.plist_free(c_options) +        try: +            self.handle_error(err) + +            return plist.plist_t_to_node(c_node) +        except Exception, e: +            if c_node != NULL: +                plist.plist_free(c_node) + +    cpdef plist.Node mount_image(self, bytes image_path, bytes signature, bytes image_type): +        cdef: +            plist.plist_t c_node = NULL +            mobile_image_mounter_error_t err +        err = mobile_image_mounter_mount_image(self._c_client, image_path, signature, len(signature),                                                 image_type, &c_node)          try: @@ -72,6 +102,13 @@ cdef class MobileImageMounterClient(PropertyListService):              if c_node != NULL:                  plist.plist_free(c_node) +    cpdef unmount_image(self, bytes mount_path): +        cdef: +            mobile_image_mounter_error_t err +        err = mobile_image_mounter_unmount_image(self._c_client, mount_path) + +        self.handle_error(err) +      cpdef hangup(self):          cdef mobile_image_mounter_error_t err          err = mobile_image_mounter_hangup(self._c_client) diff --git a/cython/mobilebackup2.pxi b/cython/mobilebackup2.pxi index 4eccae6..4b47e5b 100644 --- a/cython/mobilebackup2.pxi +++ b/cython/mobilebackup2.pxi @@ -58,10 +58,10 @@ cdef class MobileBackup2Client(PropertyListService):      cdef inline BaseError _error(self, int16_t ret):          return MobileBackup2Error(ret) -    cdef send_message(self, bytes message, plist.Node options): +    cpdef send_message(self, bytes message, plist.Node options):          self.handle_error(mobilebackup2_send_message(self._c_client, message, options._c_node)) -    cdef tuple receive_message(self): +    cpdef tuple receive_message(self):          cdef:              char* dlmessage = NULL              plist.plist_t c_node = NULL @@ -77,29 +77,34 @@ cdef class MobileBackup2Client(PropertyListService):                  free(dlmessage)              raise -    cdef int send_raw(self, bytes data, int length): +    cpdef int send_raw(self, bytes data, int length):          cdef: -            uint32_t bytes = 0 +            uint32_t bytes_recvd = 0              mobilebackup2_error_t err -        err = mobilebackup2_send_raw(self._c_client, data, length, &bytes) +        err = mobilebackup2_send_raw(self._c_client, data, length, &bytes_recvd)          try:              self.handle_error(err) -            return <bint>bytes +            return <bint>bytes_recvd          except BaseError, e:              raise -    cdef int receive_raw(self, bytes data, int length): +    cpdef int receive_raw(self, bytearray data, int length):          cdef: -            uint32_t bytes = 0 +            uint32_t bytes_recvd = 0              mobilebackup2_error_t err -        err = mobilebackup2_receive_raw(self._c_client, data, length, &bytes) +        err = mobilebackup2_receive_raw(self._c_client, data, length, &bytes_recvd) + +        # Throwing an exception when we test if theres more data to read is excessive +        if err == -1 and bytes_recvd == 0: +            return 0 +          try:              self.handle_error(err) -            return <bint>bytes +            return <bint>bytes_recvd          except BaseError, e:              raise -    cdef float version_exchange(self, double[::1] local_versions): +    cpdef float version_exchange(self, double[::1] local_versions):          cdef:              double[::1] temp = None              double remote_version = 0.0 @@ -111,8 +116,8 @@ cdef class MobileBackup2Client(PropertyListService):          except BaseError, e:              raise -    cdef send_request(self, bytes request, bytes target_identifier, bytes source_identifier, plist.Node options): +    cpdef send_request(self, bytes request, bytes target_identifier, bytes source_identifier, plist.Node options):          self.handle_error(mobilebackup2_send_request(self._c_client, request, target_identifier, source_identifier, options._c_node)) -    cdef send_status_response(self, int status_code, bytes status1, plist.Node status2): +    cpdef send_status_response(self, int status_code, bytes status1, plist.Node status2):          self.handle_error(mobilebackup2_send_status_response(self._c_client, status_code, status1, status2._c_node)) diff --git a/cython/mobilesync.pxi b/cython/mobilesync.pxi index 8dfe674..23f0005 100644 --- a/cython/mobilesync.pxi +++ b/cython/mobilesync.pxi @@ -62,7 +62,7 @@ cdef class MobileSyncError(BaseError):              MOBILESYNC_E_INVALID_ARG: "Invalid argument",              MOBILESYNC_E_PLIST_ERROR: "Property list error",              MOBILESYNC_E_MUX_ERROR: "MUX error", -            MOBILESYNC_E_SSL_ERROR: "SSL eror", +            MOBILESYNC_E_SSL_ERROR: "SSL error",              MOBILESYNC_E_RECEIVE_TIMEOUT: "Receive timeout",              MOBILESYNC_E_BAD_VERSION: "Bad version",              MOBILESYNC_E_SYNC_REFUSED: "Sync refused", diff --git a/cython/notification_proxy.pxi b/cython/notification_proxy.pxi index 4ffbf07..261200e 100644 --- a/cython/notification_proxy.pxi +++ b/cython/notification_proxy.pxi @@ -70,7 +70,7 @@ NP_ITDBPREP_DID_END = C_NP_ITDBPREP_DID_END  NP_LANGUAGE_CHANGED = C_NP_LANGUAGE_CHANGED  NP_ADDRESS_BOOK_PREF_CHANGED = C_NP_ADDRESS_BOOK_PREF_CHANGED -cdef void np_notify_cb(const_char_ptr notification, void *py_callback): +cdef void np_notify_cb(const_char_ptr notification, void *py_callback) noexcept:      (<object>py_callback)(notification)  cdef class NotificationProxyError(BaseError): | 
