1""" 2Process the (optional) BrigeSupport.xml for a framework. 3 4XXX: This file needs to be rewritten in C for optimal speed. 5""" 6__all__ = ('initFrameworkWrapper', ) 7 8import objc 9import pkg_resources 10import sys, os, struct 11import textwrap 12 13from objc import function, registerMetaDataForSelector 14 15 16# Are we in a 64-bit build: 17is64Bit = (sys.maxint > 2147483647) 18# Are we in a little-endian build: 19isLittleEndian = (sys.byteorder == 'little') 20 21# Cannot import Foundation, we're in the framework loading code. 22NSAutoreleasePool = objc.lookUpClass('NSAutoreleasePool') 23_gBridgeSupportDirectories = ( 24 '/System/Library/BridgeSupport', 25 26# Don't use the rest of the default search path to avoid relying on data that 27# might be on a single system. That would make it harder to create standalone 28# apps in some cases. 29# '/Library/BridgeSupport', 30# os.path.expanduser('~/Library/BridgeSupport'), 31 ) 32 33for method in (b'alloc', b'copy', b'copyWithZone:', b'mutableCopy', b'mutableCopyWithZone:'): 34 objc.registerMetaDataForSelector(b'NSObject', method, 35 dict( 36 retval=dict(already_retained=True), 37 )) 38 39def _parseBridgeSupport(data, globals, frameworkName, *args, **kwds): 40 try: 41 try: 42 objc.parseBridgeSupport(data, globals, frameworkName, *args, **kwds) 43 except objc.internal_error, e: 44 import warnings 45 warnings.warn("Error parsing BridgeSupport data for %s: %s" % (frameworkName, e), RuntimeWarning) 46 finally: 47 # Add formal protocols to the protocols submodule, for backward 48 # compatibility with earlier versions of PyObjC 49 if 'protocols' in globals: 50 for p in objc.protocolsForProcess(): 51 setattr(globals['protocols'], p.__name__, p) 52 53def initFrameworkWrapper(frameworkName, 54 frameworkPath, frameworkIdentifier, globals, inlineTab=None, 55 scan_classes=None, frameworkResourceName=None): 56 """ 57 Load the named framework, using the identifier if that has result otherwise 58 using the path. Also loads the information in the bridgesupport file ( 59 either one embedded in the framework or one in a BrigeSupport library 60 directory). 61 """ 62 if frameworkResourceName is None: 63 frameworkResourceName = frameworkName 64 65 if frameworkIdentifier is None: 66 if scan_classes is None: 67 bundle = objc.loadBundle( 68 frameworkName, 69 globals, 70 bundle_path=frameworkPath) 71 else: 72 bundle = objc.loadBundle( 73 frameworkName, 74 globals, 75 bundle_path=frameworkPath, 76 scan_classes=scan_classes) 77 78 else: 79 try: 80 if scan_classes is None: 81 bundle = objc.loadBundle( 82 frameworkName, 83 globals, 84 bundle_identifier=frameworkIdentifier) 85 86 else: 87 bundle = objc.loadBundle( 88 frameworkName, 89 globals, 90 bundle_identifier=frameworkIdentifier, 91 scan_classes=scan_classes) 92 93 except ImportError: 94 if scan_classes is None: 95 bundle = objc.loadBundle( 96 frameworkName, 97 globals, 98 bundle_path=frameworkPath) 99 else: 100 bundle = objc.loadBundle( 101 frameworkName, 102 globals, 103 bundle_path=frameworkPath, 104 scan_classes=scan_classes) 105 106 107 # Make the objc module available, because it contains a lot of useful 108 # functionality. 109 globals['objc'] = objc 110 111 # Explicitly push objc.super into the globals dict, that way super 112 # calls will behave as expected in all cases. 113 globals['super'] = objc.super 114 115 if 1: 116 # Look for metadata in the Python wrapper and prefer that over the 117 # data in the framework or in system locations. 118 # Needed because the system bridgesupport files are buggy. 119 try: 120 exists = pkg_resources.resource_exists( 121 frameworkResourceName, "PyObjC.bridgesupport") 122 123 except ImportError: 124 pass 125 126 else: 127 if exists: 128 data = pkg_resources.resource_string(frameworkResourceName, 129 "PyObjC.bridgesupport") 130 if data: 131 _parseBridgeSupport(data, globals, frameworkName, inlineTab=inlineTab) 132 return bundle 133 134 # Look for metadata in the framework bundle 135 path = bundle.pathForResource_ofType_inDirectory_(frameworkName, 'bridgesupport', 'BridgeSupport') 136 if path is not None: 137 dylib_path = bundle.pathForResource_ofType_inDirectory_(frameworkName, 'dylib', 'BridgeSupport') 138 data = open(path, 'rb').read() 139 if dylib_path is not None: 140 _parseBridgeSupport(data, globals, frameworkName, dylib_path) 141 else: 142 _parseBridgeSupport(data, globals, frameworkName) 143 144 # Check if we have additional metadata bundled with PyObjC 145 try: 146 exists = pkg_resources.resource_exists( 147 frameworkResourceName, "PyObjCOverrides.bridgesupport") 148 149 except ImportError: 150 pass 151 152 else: 153 if exists: 154 data = pkg_resources.resource_string(frameworkResourceName, 155 "PyObjCOverrides.bridgesupport") 156 if data: 157 _parseBridgeSupport(data, globals, frameworkName, inlineTab=inlineTab) 158 159 return bundle 160 161 # If there is no metadata there look for metadata in the standard Library 162 # locations 163 fn = frameworkName + '.bridgesupport' 164 for dn in _gBridgeSupportDirectories: 165 path = os.path.join(dn, fn) 166 if os.path.exists(path): 167 data = open(path, 'rb').read() 168 169 dylib_path = os.path.join(dn, frameworkName + '.dylib') 170 if os.path.exists(dylib_path): 171 _parseBridgeSupport(data, globals, frameworkName, dylib_path) 172 else: 173 _parseBridgeSupport(data, globals, frameworkName) 174 175 # Check if we have additional metadata bundled with PyObjC 176 try: 177 exists = pkg_resources.resource_exists( 178 frameworkResourceName, "PyObjCOverrides.bridgesupport") 179 180 except ImportError: 181 pass 182 183 else: 184 if exists: 185 data = pkg_resources.resource_string(frameworkResourceName, 186 "PyObjCOverrides.bridgesupport") 187 if data: 188 _parseBridgeSupport(data, globals, frameworkName, inlineTab=inlineTab) 189 return bundle 190 191 # And if that fails look for the metadata in the framework wrapper 192 if pkg_resources.resource_exists( 193 frameworkName, "PyObjC.bridgesupport"): 194 data = pkg_resources.resource_string(frameworkResourceName, 195 "PyObjC.bridgesupport") 196 if data: 197 _parseBridgeSupport(data, globals, frameworkName, inlineTab=inlineTab) 198 return bundle 199 200 return bundle 201 202def _setupCFClasses(globalDict, cftypes): 203 """ 204 Foundation types have a fully procedural C-interface, but that can 205 easily be transformed into an OO interface. This function performs that 206 transformation. 207 208 Function are transformed into methods by looking for functions whose name 209 starts with the type and and whose first argument is of the type. As a 210 special case 'Create' functions are transformed into class methods. 211 212 Note that this function *adds* an OO-interface, the fully procedural API 213 won't be removed. 214 215 Example: 216 217 url = CFURL.createWithFileSystemPath("/tmp") 218 print url.copyHostName() 219 220 In the procedural API: 221 url = CFURLCreateWithFileSystemPath(None, "/tmp") 222 print CFURLCopyHostName(url) 223 224 XXX: need to add information about this feature to the documentation 225 """ 226 for name, encoding in cftypes: 227 if name.endswith('Ref'): 228 name = name[:-3] 229 tp = globalDict[name + 'Ref'] 230 231 for funcname in globalDict: 232 if not funcname.startswith(name): 233 continue 234 f = globalDict[funcname] 235 if not isinstance(f, function): 236 continue 237 metadata = f.__metadata__() 238 239 rest = funcname[len(name):] 240 if not rest[0].isupper(): 241 continue 242 243 rest = rest[0].lower() + rest[1:] 244 245 if rest.startswith('create') and metadata['retval']['type'] == encoding: 246 if len(metadata['arguments']) >= 1 and metadata['arguments'][0]['type'] == '^{__CFAllocator=}': 247 argcount = len(metadata['arguments']) - 1 248 argPrefix= 'None, ' 249 decorator = classmethod 250 251 else: 252 argcount = len(metadata['arguments']) 253 argPrefix = '' 254 decorator = classmethod 255 256 else: 257 argcount = len(metadata['arguments']) - 1 258 argPrefix= 'self, ' 259 decorator = lambda x: x 260 261 if 'arguments' not in metadata or len(metadata['arguments']) == 0: 262 if metadata['retval']['type'] == encoding: 263 argPrefix = '' 264 decorator = classmethod 265 argcount = 0 266 267 else: 268 continue 269 270 elif metadata['arguments'][0]['type'] != encoding: 271 continue 272 273 # We don't have argument names, therefore just count the 274 # arguments... 275 argList = ', '.join([ 'x%d'%(i) for i in range(argcount)]) 276 funcdef = textwrap.dedent('''\ 277 def %(rest)s(self, %(argList)s): 278 return %(funcname)s(%(argPrefix)s%(argList)s) 279 ''') 280 funcdef = funcdef % locals() 281 g = {} 282 g.update(globalDict) 283 exec funcdef in g 284 func = g[rest] 285 286 setattr(tp, rest, decorator(func)) 287 288 # XXX: for compatibility with MacPython we migth want to add 289 # `funcname` as a method as well (although preferably a version 290 # that gives a DeprecationWarning) 291 292 293_ivar_dict = objc._ivar_dict() 294del objc._ivar_dict 295def _structConvenience(structname, structencoding): 296 def makevar(self, name=None): 297 if name is None: 298 return objc.ivar(type=structencoding) 299 else: 300 return objc.ivar(name=name, type=structencoding) 301 _ivar_dict[structname] = classmethod(makevar) 302 303 304# Fake it for basic C types 305_structConvenience("bool", objc._C_BOOL) 306_structConvenience("char", objc._C_CHR) 307_structConvenience("int", objc._C_INT) 308_structConvenience("short", objc._C_SHT) 309_structConvenience("long", objc._C_LNG) 310_structConvenience("long_long", objc._C_LNG_LNG) 311_structConvenience("unsigned_char", objc._C_UCHR) 312_structConvenience("unsigned_int", objc._C_UINT) 313_structConvenience("unsigned_short", objc._C_USHT) 314_structConvenience("unsigned_long", objc._C_ULNG) 315_structConvenience("unsigned_long_long", objc._C_ULNG_LNG) 316_structConvenience("float", objc._C_FLT) 317_structConvenience("double", objc._C_DBL) 318_structConvenience("BOOL", objc._C_NSBOOL) 319_structConvenience("UniChar", objc._C_UNICHAR) 320_structConvenience("char_text", objc._C_CHAR_AS_TEXT) 321_structConvenience("char_int", objc._C_CHAR_AS_INT) 322 323objc._setStructConvenience(_structConvenience) 324del objc._setStructConvenience 325 326 327# XXX: this is a nice-to-have, but adds a full second to the 328# load time of importing Quartz. 329#objc._setSetupCFClasses(_setupCFClasses) 330#del objc._setSetupCFClasses 331