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