1""" 2Python <-> Objective-C bridge (PyObjC) 3 4This module defines the core interfaces of the Python<->Objective-C bridge. 5""" 6 7__all__ = ['IBOutlet', 'IBAction', 'accessor', 'Accessor', 'typedAccessor', 'callbackFor', 'selectorFor', 'synthesize', 'namedselector', 'typedSelector', 'namedSelector' ] 8 9from objc._objc import ivar, selector, _makeClosure, selector, _C_SEL, _C_ID 10import sys, textwrap 11 12# 13# Interface builder support. 14# 15def IBOutlet(name=None): 16 """ 17 Create an instance variable that can be used as an outlet in 18 Interface Builder. 19 """ 20 if name is None: 21 return ivar(isOutlet=1) 22 else: 23 return ivar(name, isOutlet=1) 24 25def IBAction(func): 26 """ 27 Return an Objective-C method object that can be used as an action 28 in Interface Builder. 29 """ 30 return selector(func, signature=b"v@:@") 31 32def instancemethod(func): 33 return selector(func, isClassMethod=False) 34 35def accessor(func, typeSignature=b'@'): 36 """ 37 Return an Objective-C method object that is conformant with key-value coding 38 and key-value observing. 39 """ 40 41 from inspect import getargspec 42 args, varargs, varkw, defaults = getargspec(func) 43 funcName = func.__name__ 44 maxArgs = len(args) 45 minArgs = maxArgs - len(defaults or ()) 46 # implicit self 47 selArgs = 1 + funcName.count('_') 48 if varargs is not None or varkw is not None: 49 raise TypeError('%s can not be an accessor because it accepts varargs or varkw' % (funcName,)) 50 51 if not (minArgs <= selArgs <= maxArgs): 52 if selArgs == 3 and (minArgs <= 2 <= maxArgs) and funcName.startswith('validate') and funcName.endswith('_error_'): 53 return selector(func, signature=b'Z@:N^@o^@') 54 elif minArgs == maxArgs: 55 raise TypeError('%s expected to take %d args, but must accept %d from Objective-C (implicit self plus count of underscores)' % (funcName, maxArgs, selArgs)) 56 else: 57 raise TypeError('%s expected to take between %d and %d args, but must accept %d from Objective-C (implicit self plus count of underscores)' % (funcName, minArgs, maxArgs, selArgs)) 58 59 if selArgs == 3: 60 if funcName.startswith('validate') and funcName.endswith('_error_'): 61 return selector(func, signature=b'Z@:N^@o^@') 62 63 if funcName.startswith('insertObject_in') and funcName.endswith('AtIndex_'): 64 return selector(func, signature=b'v@:@i') 65 elif funcName.startswith('replaceObjectIn') and funcName.endswith('AtIndex_withObject_'): 66 return selector(func, signature=b'v@:i@') 67 68 # pass through to "too many arguments" 69 70 elif selArgs == 2: 71 if funcName.startswith('objectIn') and funcName.endswith('AtIndex_'): 72 return selector(func, signature=b'@@:i') 73 elif funcName.startswith('removeObjectFrom') and funcName.endswith('AtIndex_'): 74 return selector(func, signature=b'v@:i') 75 elif funcName.startswith('get') and funcName.endswith('_range_'): 76 return selector(func, signature=b'@@:{_NSRange=ii}') 77 78 return selector(func, signature=b"v@:" + typeSignature) 79 80 elif selArgs == 1: 81 if typeSignature == b'@' and func.func_name.startswith('countOf'): 82 typeSignature = 'i' 83 84 return selector(func, signature=typeSignature + b"@:") 85 86 raise TypeError("%s takes too many arguments to be an accessor" % (funcName,)) 87 88 89def typedSelector(signature): 90 def _typedSelector(func): 91 return selector(func, signature=signature) 92 return _typedSelector 93 94def namedSelector(name, signature=None): 95 """ 96 Python 2.4 decorator for overriding the Objective-C SEL for a method, usage: 97 98 @namedSelector("foo:bar:") 99 def foobar(self, foo, bar): 100 return foo + bar 101 """ 102 if signature is not None: 103 def _namedselector(func): 104 return selector(func, selector=name, signature=signature) 105 else: 106 def _namedselector(func): 107 return selector(func, selector=name) 108 109 return _namedselector 110 111def namedselector(name, signature=None): 112 import warnings 113 warnings.warn("use objc.namedSelector instead of objc.namedselector", stacklevel=2) 114 return namedSelector(name, signature) 115 116def typedAccessor(typeSignature): 117 """ 118 Python 2.4 decorator for creating a typed accessor, usage: 119 120 @typedAccessor('i') 121 def someIntegerAccessor(self): 122 return self.someInteger 123 124 @typedAccessor('i') 125 def setSomeIntegerAccessor_(self, anInteger): 126 self.someInteger = anInteger 127 """ 128 def _typedAccessor(func): 129 return accessor(func, typeSignature) 130 return _typedAccessor 131 132def Accessor(func): 133 import warnings 134 warnings.warn( 135 "Use objc.accessor instead of objc.Accessor", DeprecationWarning) 136 return accessor(func) 137 138# 139# Callback support 140# 141def callbackFor(callable, argIndex=-1): 142 """ 143 Decorator for converting a function into an object that can be used 144 as a callback function for (Objective-)C API's that take such a beast 145 as one of their arguments. 146 147 Note that using this decorator for methods is unsupported and that this 148 decorator is optional when the callback isn't stored by the called function 149 150 Usage:: 151 152 @objc.callbackFor(NSArray.sortedArrayUsingFunction_context_) 153 def compare(left, right, context): 154 return 1 155 """ 156 def addClosure(function): 157 closure = _makeClosure(function, callable, argIndex) 158 function.pyobjc_closure = closure 159 return function 160 161 return addClosure 162 163def selectorFor(callable, argIndex=-1): 164 """ 165 Decorator that makes sure that the method has the right signature to be 166 used as the selector argument to the specified method. 167 168 Usage:: 169 170 @objc.selectorFor(NSApplication.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_) 171 def sheetDidEnd_returnCode_contextInfo_(self, sheet, returnCode, info): 172 pass 173 """ 174 if argIndex == -1: 175 for arg in callable.__metadata__()['arguments']: 176 if arg['type'] == _C_SEL and 'sel_of_type' in arg: 177 signature = arg['sel_of_type'] 178 break 179 else: 180 raise ValueError("No selector argument with type information") 181 182 else: 183 signature = callable.__metadata__().arguments[idx]['type_of_sel'] 184 185 def addSignature(function): 186 return selector(function, signature=signature) 187 188 return addSignature 189 190 191def synthesize(name, copy=False, readwrite=True, type=_C_ID, ivarName=None): 192 """ 193 Use this in a class dictionary to syntheze simple setting/setter methods. 194 195 Note: this is only necessary to get propper behaviour when Key-Value coding 196 is used and special features (like copying) are needed 197 198 usage:: 199 200 class MyClass (NSObject): 201 objc.synthesize('someTitle', copy=True) 202 203 """ 204 if not name: 205 raise ValueError("Empty property name") 206 207 if ivarName is None: 208 ivarName = '_' + name 209 210 classDict = sys._getframe(1).f_locals 211 212 setterName = 'set%s%s_'%(name[0].upper(), name[1:]) 213 214 if copy: 215 setter = textwrap.dedent(''' 216 def %(name)s(self, value): 217 self.%(ivar)s = value.copy() 218 ''' % dict(name=setterName, ivar=ivarName)) 219 220 else: 221 setter = textwrap.dedent(''' 222 def %(name)s(self, value): 223 self.%(ivar)s = value 224 ''' % dict(name=setterName, ivar=ivarName)) 225 226 getter = textwrap.dedent(''' 227 def %(name)s(self): 228 return self.%(ivar)s 229 ''' % dict(name=name, ivar=ivarName)) 230 231 if readwrite: 232 exec setter in classDict 233 234 exec getter in classDict 235 236 classDict[ivarName] = ivar(type=type) 237