Skip to content

Commit

Permalink
fix user-declared deleguates
Browse files Browse the repository at this point in the history
  • Loading branch information
tito committed Nov 9, 2018
1 parent 1e0b6cc commit ecd059e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 29 deletions.
39 changes: 17 additions & 22 deletions pyobjus/pyobjus.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -772,35 +772,25 @@ cdef id protocol_methodSignatureForSelector(id self, SEL _cmd, SEL selector) wit
# returns a method signature for a specific selector, needed for the
# fallback forwardInvocation:
cdef ObjcClassInstance sig
sig_name = "_sig_{}".format(sel_getName(selector))
sel_name = sel_getName(selector)
py_sel_name = (<bytes>sel_name).decode("utf8")
sig_name = "_sig_{}".format(py_sel_name)
delegate = get_python_delegate_from_id(self)
if not delegate:
return NULL

dprint("protocol_methodSignatureForSelector", delegate, sig_name)
if not hasattr(delegate, sig_name):
dprint("signatureWithObjCTypes_")
# we didn't find a cached method signature, so create a new one.
sel_name = sel_getName(selector)
py_method_name = sel_name.replace(b':', b'_').decode("utf8")

dprint("getattr", py_method_name)
protocol_name = getattr(delegate, py_method_name).__protocol__
dprint("protocol_name", protocol_name)
d = objc_protocol_get_delegates(protocol_name)
dprint("d", d)
sigs = d.get(sel_name)
dprint("sigs", sigs)
sigs = d.get(py_sel_name)

dprint("new NSMethodSignature")
NSMethodSignature = autoclass("NSMethodSignature")
dprint("signatureWithObjCTypes_")
dprint("signatureWithObjCTypes_")
sig = NSMethodSignature.signatureWithObjCTypes_(sigs[-1])
dprint("set")
setattr(delegate, sig_name, sig)
else:
dprint("found it, getattr!")
sig = getattr(delegate, sig_name)

return sig.o_instance
Expand Down Expand Up @@ -890,7 +880,10 @@ def objc_protocol_get_delegates(py_protocol_name):
desc = descs[i]
selector = desc.name
selector_name = sel_getName(selector)
delegates_types[selector_name] = [desc.types, desc.types]
if selector_name == NULL:
continue
py_selector_name = (<bytes>selector_name).decode("utf8")
delegates_types[py_selector_name] = [desc.types, desc.types]
free(descs)
# get required methods
descs = protocol_copyMethodDescriptionList(
Expand All @@ -899,14 +892,16 @@ def objc_protocol_get_delegates(py_protocol_name):
desc = descs[i]
selector = desc.name
selector_name = sel_getName(selector)
delegates_types[selector_name] = [desc.types, desc.types]
if selector_name == NULL:
continue
py_selector_name = (<bytes>selector_name).decode("utf8")
delegates_types[py_selector_name] = [desc.types, desc.types]
free(descs)
return delegates_types

# not found, try to search in the user-build protocols
from .protocols import protocols
if protocol_name in protocols:
return protocols.get(protocol_name)
return protocols.get(py_protocol_name)


cdef ObjcClassInstance objc_create_delegate(py_obj):
Expand Down Expand Up @@ -964,7 +959,7 @@ cdef ObjcClassInstance objc_create_delegate(py_obj):
if d is None:
raise ObjcException('Undeclared protocol {}'.format(protocol_name))

selector_name = funcname.replace('_', ':').encode("utf8")
selector_name = funcname.replace('_', ':')
dprint(' search the selector {}'.format(selector_name))
sigs = d.get(selector_name)
if not sigs:
Expand All @@ -983,15 +978,15 @@ cdef ObjcClassInstance objc_create_delegate(py_obj):

dprint(' register methodSignatureForSelector:')
class_addMethod(
objc_cls, sel_registerName("methodSignatureForSelector:"),
objc_cls, sel_registerName(b"methodSignatureForSelector:"),
<IMP>&protocol_methodSignatureForSelector, "@@::")
dprint(' register forwardInvocation:')
class_addMethod(
objc_cls, sel_registerName("forwardInvocation:"),
objc_cls, sel_registerName(b"forwardInvocation:"),
<IMP>&protocol_forwardInvocation, "v@:@")
dprint(' register respondsToSelector:')
class_addMethod(
objc_cls, sel_registerName("respondsToSelector:"),
objc_cls, sel_registerName(b"respondsToSelector:"),
<IMP>&protocol_respondsToSelector, "v@::")

objc_registerClassPair(objc_cls)
Expand Down
6 changes: 2 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,11 @@
extra_link_args = []
include_dirs = []
depends = [join('pyobjus', x) for x in (
'config.pxi',
'common.pxi',
'ffi.pxi',
'objc_cy_types.pxi',
'config.pxi',
'debug.pxi',
'pyobjus_conversions.pxi',
'pyobjus_types.pxi',
'runtime.pxi',
'type_enc.pxi',
'pyobjus.pyx')]

Expand Down
45 changes: 42 additions & 3 deletions tests/test_delegate.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import unittest
import ctypes
from pyobjus import autoclass, protocol, objc_str
from pyobjus import autoclass, protocol, objc_str, selector
from pyobjus.dylib_manager import load_dylib, load_framework, INCLUDE
from pyobjus.protocols import protocols

NSURL = NSURLConnection = NSURLRequest = None

Expand Down Expand Up @@ -29,6 +30,7 @@ class _CFString(ctypes.Structure):


class DelegateExample(object):
# example of a delegate required by NSURLConnection
def request_connection(self):
self.delegate_called = False
# This method request connection to an invalid URL so the
Expand All @@ -47,7 +49,35 @@ def connection_didFailWithError_(self, connection, error):
cf.CFRunLoopStop(current)


class DeleguateTest(unittest.TestCase):
class IOSKeyboard(object):
# example of Keyboard user-defined delegate (aka doesn't exists
# in pyobjus) actually used for Kivy

def __init__(self, **kwargs):
super(IOSKeyboard, self).__init__()
self.kheight = 0
NSNotificationCenter = autoclass("NSNotificationCenter")
center = NSNotificationCenter.defaultCenter()
center.addObserver_selector_name_object_(
self, selector("keyboardWillShow"),
"UIKeyboardWillShowNotification",
None)
center.addObserver_selector_name_object_(
self, selector("keyboardDidHide"),
"UIKeyboardDidHideNotification",
None)

@protocol('KeyboardDelegates')
def keyboardWillShow(self, notification):
self.kheight = get_scale() * notification.userInfo().objectForKey_(
'UIKeyboardFrameEndUserInfoKey').CGRectValue().size.height

@protocol('KeyboardDelegates')
def keyboardDidHide(self, notification):
self.kheight = 0


class DelegateTest(unittest.TestCase):
def setUp(self):
global NSURL, NSURLConnection, NSURLRequest
load_framework(INCLUDE.AppKit)
Expand All @@ -56,8 +86,17 @@ def setUp(self):
NSURLConnection = autoclass('NSURLConnection')
NSURLRequest = autoclass('NSURLRequest')

def test_delegate(self):
def test_existing_delegate(self):
instance = DelegateExample()
instance.request_connection()
cf.CFRunLoopRunInMode(K_CF_RUNLOOP_DEFAULT_MODE, 1, False)
self.assertTrue(instance.delegate_called)

def test_user_registration_delegate(self):
protocols["KeyboardDelegates"] = {
'keyboardWillShow': ('v16@0:4@8', "v32@0:8@16"),
'keyboardDidHide': ('v16@0:4@8', "v32@0:8@16")}

# if everything is find, the keyboard should instanciate
# without issue
iOSKeyboard = IOSKeyboard()

0 comments on commit ecd059e

Please sign in to comment.