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