├── LICENSE ├── README.md ├── app ├── README └── apptest │ └── __main__.py ├── app_packages ├── README └── rubicon │ ├── __init__.py │ └── objc │ ├── __init__.py │ ├── core_foundation.py │ ├── objc.py │ └── types.py ├── apptest.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── fujun.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── fujun.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── apptest.xcscheme │ └── xcschememanagement.plist └── apptest ├── Images.xcassets ├── AppIcon.appiconset │ ├── Contents.json │ ├── icon-120.png │ ├── icon-152.png │ ├── icon-167.png │ ├── icon-180.png │ ├── icon-29.png │ ├── icon-40.png │ ├── icon-58.png │ ├── icon-76.png │ ├── icon-80.png │ └── icon-87.png └── LaunchImage.launchimage │ ├── Contents.json │ ├── launch-1024x768.png │ ├── launch-1536x2048.png │ ├── launch-2048x1536.png │ ├── launch-640x1136.png │ ├── launch-640x960.png │ └── launch-768x1024.png ├── apptest-Info.plist ├── apptest-Prefix.pch ├── en.lproj └── InfoPlist.strings └── main.m /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Fujun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Python-iOS 2 | ---- 3 | ### What is this? 4 | ---- 5 | 6 | 这是一个使用Python开发iOS程序的demo。详细介绍可以参考这篇博客: 7 | 8 | **[http://ifujun.com/shi-yong-pythonkai-fa-ioscheng-xu/](http://ifujun.com/shi-yong-pythonkai-fa-ioscheng-xu/)** 9 | 10 | 主要使用了: 11 | - [pybee](http://pybee.org/)开源的编译脚本[Python-iOS-support](https://github.com/pybee/Python-iOS-support)。 12 | - [rubicon-objc](https://github.com/pybee/rubicon-objc),一个连接Python和objc的桥梁。 13 | - [cookiecutter](http://cookiecutter.rtfd.org/),一个可以在模板中快速创建工程的一个工具。 14 | - [Python-iOS-template](https://github.com/pybee/Python-iOS-template),[pybee](http://pybee.org/)开源的使用Python开发iOS工程的模板。 15 | 16 | ### How to install it? 17 | ---- 18 | 19 | 1. clone下工程。 20 | 2. [下载](https://github.com/pybee/Python-iOS-support/releases)一个可用版本的`Python.framework`和`OpenSSL.framework`。 21 | 3. 将下载好的`Python.framework`和`OpenSSL.framework`放置于工程根目录上,也就是和`xcodeproj`文件同级目录。 22 | 4. 编译运行。 23 | 24 | 运行结果: 25 | 26 | ![](http://7i7i81.com1.z0.glb.clouddn.com/blogimage_python_ios_6.png) 27 | 28 | demo中只修改了rootViewController的背景,然后在上面添加了一个label。 29 | 30 | 当然,它还能做很多事情。 31 | 32 | ### License 33 | ---- 34 | MIT. -------------------------------------------------------------------------------- /app/README: -------------------------------------------------------------------------------- 1 | Your application code should be placed in this directory. 2 | 3 | The native code will be looking for a apptest/__main__.py file as the entry point. -------------------------------------------------------------------------------- /app/apptest/__main__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function, with_statement 2 | 3 | import sys 4 | 5 | from ctypes import cdll, c_int, c_void_p, util 6 | from rubicon.objc import ObjCClass, objc_method 7 | from rubicon.objc.core_foundation import from_value, to_value 8 | 9 | __all__ = ('UIKit', 'ObjCClass', 'objc_method', 'NSObject', 10 | 'UIScreen', 'UIView', 'UIViewController','UIResponder', 11 | 'UIWindow', 'UIColor', 'UILabel') 12 | 13 | UIKit = cdll.LoadLibrary(util.find_library('UIKit')) 14 | UIKit.UIApplicationMain.restypes = (c_int, c_void_p, c_void_p, 15 | c_void_p) 16 | UIKit.UIApplicationMain.restype = c_int 17 | 18 | for item in __all__: 19 | if item not in globals(): 20 | globals()[item] = ObjCClass(item) 21 | 22 | 23 | class ObjCAppDelegate(NSObject): 24 | @objc_method('@B') 25 | def application_didFinishLaunchingWithOptions_(self, launchOptions): 26 | self.__dict__['delegate'] = PyAppDelegate() 27 | return 0 28 | 29 | 30 | class PyAppDelegate(object): 31 | def __init__(self): 32 | self.window = UIWindow.alloc().initWithFrame_(UIScreen.mainScreen().bounds) 33 | 34 | view_controller = UIViewController.alloc().init() 35 | view_controller.view.setBackgroundColor_(UIColor.colorWithWhite_alpha_(0.5, 1)) 36 | 37 | label = UILabel.alloc().initWithFrame_(view_controller.view.bounds) 38 | 39 | label.setText_("test, this is a label") 40 | label.setTextAlignment_(1) 41 | label.setTextColor_(UIColor.colorWithRed_green_blue_alpha_(30/255., 70/255., 137/255., 1)) 42 | view_controller.view.addSubview_(label) 43 | 44 | self.window.rootViewController = view_controller 45 | self.window.makeKeyAndVisible() 46 | 47 | 48 | if __name__ == '__main__': 49 | sys.exit(UIKit.UIApplicationMain(0, None, None, from_value('ObjCAppDelegate'))) 50 | -------------------------------------------------------------------------------- /app_packages/README: -------------------------------------------------------------------------------- 1 | This directory exists so that 3rd party packages can be installed here. 2 | -------------------------------------------------------------------------------- /app_packages/rubicon/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | try: 3 | # If we're on iOS, we won't have pkg-resources; but then, 4 | # we won't need to register the namespace package, either. 5 | # Ignore the error if it occurs. 6 | __import__("pkg_resources").declare_namespace(__name__) 7 | except ImportError: 8 | print('Rubicon namespace package not registered!') 9 | -------------------------------------------------------------------------------- /app_packages/rubicon/objc/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division, unicode_literals 2 | 3 | __version__ = '0.1.3' 4 | 5 | from .objc import ( 6 | objc, send_message, send_super, 7 | get_selector, 8 | ObjCClass, ObjCInstance, NSObject, 9 | objc_ivar, objc_rawmethod, objc_method, objc_classmethod 10 | ) 11 | 12 | from .core_foundation import at, to_str, to_number, to_value, to_set, to_list 13 | 14 | from .types import ( 15 | text, 16 | NSInteger, NSUInteger, 17 | CGFloat, 18 | NSPointEncoding, NSSizeEncoding, NSRectEncoding, NSRangeEncoding, 19 | CGPoint, NSPoint, 20 | CGSize, NSSize, 21 | CGRect, NSRect, 22 | CGSizeMake, NSMakeSize, 23 | CGRectMake, NSMakeRect, 24 | CGPointMake, NSMakePoint, 25 | NSTimeInterval, 26 | CFIndex, UniChar, unichar, CGGlyph, 27 | CFRange, NSRange, 28 | NSZeroPoint 29 | ) 30 | -------------------------------------------------------------------------------- /app_packages/rubicon/objc/core_foundation.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | 3 | from ctypes import * 4 | from ctypes import util 5 | from decimal import Decimal 6 | 7 | from .objc import ObjCInstance, send_message, get_class, get_selector, objc 8 | from .types import * 9 | 10 | ###################################################################### 11 | 12 | # CORE FOUNDATION 13 | 14 | cf = cdll.LoadLibrary(util.find_library('CoreFoundation')) 15 | 16 | kCFStringEncodingUTF8 = 0x08000100 17 | 18 | CFAllocatorRef = c_void_p 19 | CFStringEncoding = c_uint32 20 | 21 | cf.CFStringCreateWithCString.restype = c_void_p 22 | cf.CFStringCreateWithCString.argtypes = [CFAllocatorRef, c_char_p, CFStringEncoding] 23 | 24 | cf.CFRelease.restype = c_void_p 25 | cf.CFRelease.argtypes = [c_void_p] 26 | 27 | cf.CFStringGetLength.restype = CFIndex 28 | cf.CFStringGetLength.argtypes = [c_void_p] 29 | 30 | cf.CFStringGetMaximumSizeForEncoding.restype = CFIndex 31 | cf.CFStringGetMaximumSizeForEncoding.argtypes = [CFIndex, CFStringEncoding] 32 | 33 | cf.CFStringGetCString.restype = c_bool 34 | cf.CFStringGetCString.argtypes = [c_void_p, c_char_p, CFIndex, CFStringEncoding] 35 | 36 | cf.CFStringGetTypeID.restype = CFTypeID 37 | cf.CFStringGetTypeID.argtypes = [] 38 | 39 | cf.CFAttributedStringCreate.restype = c_void_p 40 | cf.CFAttributedStringCreate.argtypes = [CFAllocatorRef, c_void_p, c_void_p] 41 | 42 | # Core Foundation type to Python type conversion functions 43 | 44 | def CFSTR(string): 45 | return ObjCInstance(c_void_p(cf.CFStringCreateWithCString( 46 | None, string.encode('utf-8'), kCFStringEncodingUTF8))) 47 | 48 | # Other possible names for this method: 49 | # ampersat, arobe, apenstaartje (little monkey tail), strudel, 50 | # klammeraffe (spider monkey), little_mouse, arroba, sobachka (doggie) 51 | # malpa (monkey), snabel (trunk), papaki (small duck), afna (monkey), 52 | # kukac (caterpillar). 53 | def at(string): 54 | """Autoreleased version of CFSTR""" 55 | return ObjCInstance(send_message(CFSTR(string), 'autorelease')) 56 | 57 | def to_str(cfstring): 58 | length = cf.CFStringGetLength(cfstring) 59 | size = cf.CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) 60 | buffer = c_buffer(size + 1) 61 | result = cf.CFStringGetCString(cfstring, buffer, len(buffer), kCFStringEncodingUTF8) 62 | if result: 63 | return text(buffer.value, 'utf-8') 64 | 65 | cf.CFDataCreate.restype = c_void_p 66 | cf.CFDataCreate.argtypes = [c_void_p, c_void_p, CFIndex] 67 | 68 | cf.CFDataGetBytes.restype = None 69 | cf.CFDataGetBytes.argtypes = [c_void_p, CFRange, c_void_p] 70 | 71 | cf.CFDataGetLength.restype = CFIndex 72 | cf.CFDataGetLength.argtypes = [c_void_p] 73 | 74 | cf.CFDictionaryGetValue.restype = c_void_p 75 | cf.CFDictionaryGetValue.argtypes = [c_void_p, c_void_p] 76 | 77 | cf.CFDictionaryAddValue.restype = None 78 | cf.CFDictionaryAddValue.argtypes = [c_void_p, c_void_p, c_void_p] 79 | 80 | cf.CFDictionaryCreateMutable.restype = c_void_p 81 | cf.CFDictionaryCreateMutable.argtypes = [CFAllocatorRef, CFIndex, c_void_p, c_void_p] 82 | 83 | cf.CFNumberCreate.restype = c_void_p 84 | cf.CFNumberCreate.argtypes = [CFAllocatorRef, CFNumberType, c_void_p] 85 | 86 | cf.CFNumberGetType.restype = CFNumberType 87 | cf.CFNumberGetType.argtypes = [c_void_p] 88 | 89 | cf.CFNumberGetValue.restype = c_ubyte 90 | cf.CFNumberGetValue.argtypes = [c_void_p, CFNumberType, c_void_p] 91 | 92 | cf.CFNumberGetTypeID.restype = CFTypeID 93 | cf.CFNumberGetTypeID.argtypes = [] 94 | 95 | cf.CFGetTypeID.restype = CFTypeID 96 | cf.CFGetTypeID.argtypes = [c_void_p] 97 | 98 | # CFNumber.h 99 | kCFNumberSInt8Type = 1 100 | kCFNumberSInt16Type = 2 101 | kCFNumberSInt32Type = 3 102 | kCFNumberSInt64Type = 4 103 | kCFNumberFloat32Type = 5 104 | kCFNumberFloat64Type = 6 105 | kCFNumberCharType = 7 106 | kCFNumberShortType = 8 107 | kCFNumberIntType = 9 108 | kCFNumberLongType = 10 109 | kCFNumberLongLongType = 11 110 | kCFNumberFloatType = 12 111 | kCFNumberDoubleType = 13 112 | kCFNumberCFIndexType = 14 113 | kCFNumberNSIntegerType = 15 114 | kCFNumberCGFloatType = 16 115 | kCFNumberMaxType = 16 116 | 117 | 118 | def to_number(cfnumber): 119 | """Convert CFNumber to python int or float.""" 120 | numeric_type = cf.CFNumberGetType(cfnumber) 121 | cfnum_to_ctype = { 122 | kCFNumberSInt8Type: c_int8, 123 | kCFNumberSInt16Type: c_int16, 124 | kCFNumberSInt32Type: c_int32, 125 | kCFNumberSInt64Type: c_int64, 126 | kCFNumberFloat32Type: c_float, 127 | kCFNumberFloat64Type: c_double, 128 | kCFNumberCharType: c_byte, 129 | kCFNumberShortType: c_short, 130 | kCFNumberIntType: c_int, 131 | kCFNumberLongType: c_long, 132 | kCFNumberLongLongType: c_longlong, 133 | kCFNumberFloatType: c_float, 134 | kCFNumberDoubleType: c_double, 135 | kCFNumberCFIndexType: CFIndex, 136 | kCFNumberCGFloatType: CGFloat 137 | } 138 | 139 | # NSDecimalNumber reports as a double. So does an NSNumber of type double. 140 | # In the case of NSDecimalNumber, convert to a Python decimal. 141 | if numeric_type == kCFNumberDoubleType \ 142 | and isinstance(cfnumber, ObjCInstance) \ 143 | and cfnumber.__dict__['objc_class'].name == 'NSDecimalNumber': 144 | return Decimal(cfnumber.stringValue) 145 | 146 | # Otherwise, just do the conversion. 147 | try: 148 | t = cfnum_to_ctype[numeric_type] 149 | result = t() 150 | if cf.CFNumberGetValue(cfnumber, numeric_type, byref(result)): 151 | return result.value 152 | except KeyError: 153 | raise Exception('to_number: unhandled CFNumber type %d' % numeric_type) 154 | 155 | 156 | # We need to be able to create raw NSDecimalNumber objects; if we use an 157 | # normal ObjCClass() wrapper, the return values of constructors will be 158 | # auto-converted back into Python Decimals. However, we want to cache 159 | # class/selector/method lookups so that we don't have the overhead 160 | # every time we use a decimal. 161 | class NSDecimalNumber(object): 162 | objc_class = None 163 | 164 | @classmethod 165 | def from_decimal(cls, value): 166 | if cls.objc_class is None: 167 | cls.objc_class = get_class('NSDecimalNumber') 168 | cls.selector = get_selector('decimalNumberWithString:') 169 | method = c_void_p(objc.class_getClassMethod(cls.objc_class, cls.selector)) 170 | impl = c_void_p(objc.method_getImplementation(method)) 171 | cls.constructor = cast(impl, CFUNCTYPE(c_void_p, c_void_p)) 172 | 173 | return ObjCInstance(cls.constructor(cls.objc_class, cls.selector, at(value.to_eng_string()))) 174 | 175 | 176 | def from_value(value): 177 | """Convert a Python type into an equivalent CFType type. 178 | """ 179 | if isinstance(value, text): 180 | return at(value) 181 | elif isinstance(value, bytes): 182 | return at(value.decode('utf-8')) 183 | elif isinstance(value, Decimal): 184 | return NSDecimalNumber.from_decimal(value) 185 | else: 186 | return value 187 | 188 | 189 | # Dictionary of cftypes matched to the method converting them to python values. 190 | known_cftypes = { 191 | cf.CFStringGetTypeID(): to_str, 192 | cf.CFNumberGetTypeID(): to_number 193 | } 194 | 195 | 196 | def to_value(cftype): 197 | """Convert a CFType into an equivalent python type. 198 | The convertible CFTypes are taken from the known_cftypes 199 | dictionary, which may be added to if another library implements 200 | its own conversion methods.""" 201 | if not cftype: 202 | return None 203 | typeID = cf.CFGetTypeID(cftype) 204 | try: 205 | convert_function = known_cftypes[typeID] 206 | return convert_function(cftype) 207 | except KeyError: 208 | return cftype 209 | 210 | cf.CFSetGetCount.restype = CFIndex 211 | cf.CFSetGetCount.argtypes = [c_void_p] 212 | 213 | cf.CFSetGetValues.restype = None 214 | # PyPy 1.7 is fine with 2nd arg as POINTER(c_void_p), 215 | # but CPython ctypes 1.1.0 complains, so just use c_void_p. 216 | cf.CFSetGetValues.argtypes = [c_void_p, c_void_p] 217 | 218 | def to_set(cfset): 219 | """Convert CFSet to python set.""" 220 | count = cf.CFSetGetCount(cfset) 221 | buffer = (c_void_p * count)() 222 | cf.CFSetGetValues(cfset, byref(buffer)) 223 | return set(to_value(c_void_p(buffer[i])) for i in range(count)) 224 | 225 | cf.CFArrayGetCount.restype = CFIndex 226 | cf.CFArrayGetCount.argtypes = [c_void_p] 227 | 228 | cf.CFArrayGetValueAtIndex.restype = c_void_p 229 | cf.CFArrayGetValueAtIndex.argtypes = [c_void_p, CFIndex] 230 | 231 | def to_list(cfarray): 232 | """Convert CFArray to python list.""" 233 | count = cf.CFArrayGetCount(cfarray) 234 | return [ 235 | to_value(c_void_p(cf.CFArrayGetValueAtIndex(cfarray, i))) 236 | for i in range(count) 237 | ] 238 | 239 | 240 | kCFRunLoopDefaultMode = c_void_p.in_dll(cf, 'kCFRunLoopDefaultMode') 241 | 242 | cf.CFRunLoopGetCurrent.restype = c_void_p 243 | cf.CFRunLoopGetCurrent.argtypes = [] 244 | 245 | cf.CFRunLoopGetMain.restype = c_void_p 246 | cf.CFRunLoopGetMain.argtypes = [] 247 | -------------------------------------------------------------------------------- /app_packages/rubicon/objc/objc.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division, unicode_literals 2 | 3 | import platform 4 | import struct 5 | 6 | from ctypes import * 7 | from ctypes import util 8 | 9 | from .types import * 10 | 11 | __LP64__ = (8*struct.calcsize("P") == 64) 12 | __i386__ = (platform.machine() == 'i386') 13 | __x86_64__ = (platform.machine() == 'x86_64') 14 | 15 | if sizeof(c_void_p) == 4: 16 | c_ptrdiff_t = c_int32 17 | elif sizeof(c_void_p) == 8: 18 | c_ptrdiff_t = c_int64 19 | 20 | ###################################################################### 21 | 22 | objc = cdll.LoadLibrary(util.find_library(b'objc')) 23 | 24 | ###################################################################### 25 | 26 | # BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types) 27 | objc.class_addIvar.restype = c_bool 28 | objc.class_addIvar.argtypes = [c_void_p, c_char_p, c_size_t, c_uint8, c_char_p] 29 | 30 | # BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) 31 | objc.class_addMethod.restype = c_bool 32 | 33 | # BOOL class_addProtocol(Class cls, Protocol *protocol) 34 | objc.class_addProtocol.restype = c_bool 35 | objc.class_addProtocol.argtypes = [c_void_p, c_void_p] 36 | 37 | # BOOL class_conformsToProtocol(Class cls, Protocol *protocol) 38 | objc.class_conformsToProtocol.restype = c_bool 39 | objc.class_conformsToProtocol.argtypes = [c_void_p, c_void_p] 40 | 41 | # Ivar * class_copyIvarList(Class cls, unsigned int *outCount) 42 | # Returns an array of pointers of type Ivar describing instance variables. 43 | # The array has *outCount pointers followed by a NULL terminator. 44 | # You must free() the returned array. 45 | objc.class_copyIvarList.restype = POINTER(c_void_p) 46 | objc.class_copyIvarList.argtypes = [c_void_p, POINTER(c_uint)] 47 | 48 | # Method * class_copyMethodList(Class cls, unsigned int *outCount) 49 | # Returns an array of pointers of type Method describing instance methods. 50 | # The array has *outCount pointers followed by a NULL terminator. 51 | # You must free() the returned array. 52 | objc.class_copyMethodList.restype = POINTER(c_void_p) 53 | objc.class_copyMethodList.argtypes = [c_void_p, POINTER(c_uint)] 54 | 55 | # objc_property_t * class_copyPropertyList(Class cls, unsigned int *outCount) 56 | # Returns an array of pointers of type objc_property_t describing properties. 57 | # The array has *outCount pointers followed by a NULL terminator. 58 | # You must free() the returned array. 59 | objc.class_copyPropertyList.restype = POINTER(c_void_p) 60 | objc.class_copyPropertyList.argtypes = [c_void_p, POINTER(c_uint)] 61 | 62 | # Protocol ** class_copyProtocolList(Class cls, unsigned int *outCount) 63 | # Returns an array of pointers of type Protocol* describing protocols. 64 | # The array has *outCount pointers followed by a NULL terminator. 65 | # You must free() the returned array. 66 | objc.class_copyProtocolList.restype = POINTER(c_void_p) 67 | objc.class_copyProtocolList.argtypes = [c_void_p, POINTER(c_uint)] 68 | 69 | # id class_createInstance(Class cls, size_t extraBytes) 70 | objc.class_createInstance.restype = c_void_p 71 | objc.class_createInstance.argtypes = [c_void_p, c_size_t] 72 | 73 | # Method class_getClassMethod(Class aClass, SEL aSelector) 74 | # Will also search superclass for implementations. 75 | objc.class_getClassMethod.restype = c_void_p 76 | objc.class_getClassMethod.argtypes = [c_void_p, c_void_p] 77 | 78 | # Ivar class_getClassVariable(Class cls, const char* name) 79 | objc.class_getClassVariable.restype = c_void_p 80 | objc.class_getClassVariable.argtypes = [c_void_p, c_char_p] 81 | 82 | # Method class_getInstanceMethod(Class aClass, SEL aSelector) 83 | # Will also search superclass for implementations. 84 | objc.class_getInstanceMethod.restype = c_void_p 85 | objc.class_getInstanceMethod.argtypes = [c_void_p, c_void_p] 86 | 87 | # size_t class_getInstanceSize(Class cls) 88 | objc.class_getInstanceSize.restype = c_size_t 89 | objc.class_getInstanceSize.argtypes = [c_void_p] 90 | 91 | # Ivar class_getInstanceVariable(Class cls, const char* name) 92 | objc.class_getInstanceVariable.restype = c_void_p 93 | objc.class_getInstanceVariable.argtypes = [c_void_p, c_char_p] 94 | 95 | # const char *class_getIvarLayout(Class cls) 96 | objc.class_getIvarLayout.restype = c_char_p 97 | objc.class_getIvarLayout.argtypes = [c_void_p] 98 | 99 | # IMP class_getMethodImplementation(Class cls, SEL name) 100 | objc.class_getMethodImplementation.restype = c_void_p 101 | objc.class_getMethodImplementation.argtypes = [c_void_p, c_void_p] 102 | 103 | # IMP class_getMethodImplementation_stret(Class cls, SEL name) 104 | #objc.class_getMethodImplementation_stret.restype = c_void_p 105 | #objc.class_getMethodImplementation_stret.argtypes = [c_void_p, c_void_p] 106 | 107 | # const char * class_getName(Class cls) 108 | objc.class_getName.restype = c_char_p 109 | objc.class_getName.argtypes = [c_void_p] 110 | 111 | # objc_property_t class_getProperty(Class cls, const char *name) 112 | objc.class_getProperty.restype = c_void_p 113 | objc.class_getProperty.argtypes = [c_void_p, c_char_p] 114 | 115 | # Class class_getSuperclass(Class cls) 116 | objc.class_getSuperclass.restype = c_void_p 117 | objc.class_getSuperclass.argtypes = [c_void_p] 118 | 119 | # int class_getVersion(Class theClass) 120 | objc.class_getVersion.restype = c_int 121 | objc.class_getVersion.argtypes = [c_void_p] 122 | 123 | # const char *class_getWeakIvarLayout(Class cls) 124 | objc.class_getWeakIvarLayout.restype = c_char_p 125 | objc.class_getWeakIvarLayout.argtypes = [c_void_p] 126 | 127 | # BOOL class_isMetaClass(Class cls) 128 | objc.class_isMetaClass.restype = c_bool 129 | objc.class_isMetaClass.argtypes = [c_void_p] 130 | 131 | # IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) 132 | objc.class_replaceMethod.restype = c_void_p 133 | objc.class_replaceMethod.argtypes = [c_void_p, c_void_p, c_void_p, c_char_p] 134 | 135 | # BOOL class_respondsToSelector(Class cls, SEL sel) 136 | objc.class_respondsToSelector.restype = c_bool 137 | objc.class_respondsToSelector.argtypes = [c_void_p, c_void_p] 138 | 139 | # void class_setIvarLayout(Class cls, const char *layout) 140 | objc.class_setIvarLayout.restype = None 141 | objc.class_setIvarLayout.argtypes = [c_void_p, c_char_p] 142 | 143 | # Class class_setSuperclass(Class cls, Class newSuper) 144 | objc.class_setSuperclass.restype = c_void_p 145 | objc.class_setSuperclass.argtypes = [c_void_p, c_void_p] 146 | 147 | # void class_setVersion(Class theClass, int version) 148 | objc.class_setVersion.restype = None 149 | objc.class_setVersion.argtypes = [c_void_p, c_int] 150 | 151 | # void class_setWeakIvarLayout(Class cls, const char *layout) 152 | objc.class_setWeakIvarLayout.restype = None 153 | objc.class_setWeakIvarLayout.argtypes = [c_void_p, c_char_p] 154 | 155 | ###################################################################### 156 | 157 | # const char * ivar_getName(Ivar ivar) 158 | objc.ivar_getName.restype = c_char_p 159 | objc.ivar_getName.argtypes = [c_void_p] 160 | 161 | # ptrdiff_t ivar_getOffset(Ivar ivar) 162 | objc.ivar_getOffset.restype = c_ptrdiff_t 163 | objc.ivar_getOffset.argtypes = [c_void_p] 164 | 165 | # const char * ivar_getTypeEncoding(Ivar ivar) 166 | objc.ivar_getTypeEncoding.restype = c_char_p 167 | objc.ivar_getTypeEncoding.argtypes = [c_void_p] 168 | 169 | ###################################################################### 170 | 171 | # char * method_copyArgumentType(Method method, unsigned int index) 172 | # You must free() the returned string. 173 | objc.method_copyArgumentType.restype = c_char_p 174 | objc.method_copyArgumentType.argtypes = [c_void_p, c_uint] 175 | 176 | # char * method_copyReturnType(Method method) 177 | # You must free() the returned string. 178 | objc.method_copyReturnType.restype = c_char_p 179 | objc.method_copyReturnType.argtypes = [c_void_p] 180 | 181 | # void method_exchangeImplementations(Method m1, Method m2) 182 | objc.method_exchangeImplementations.restype = None 183 | objc.method_exchangeImplementations.argtypes = [c_void_p, c_void_p] 184 | 185 | # void method_getArgumentType(Method method, unsigned int index, char *dst, size_t dst_len) 186 | # Functionally similar to strncpy(dst, parameter_type, dst_len). 187 | objc.method_getArgumentType.restype = None 188 | objc.method_getArgumentType.argtypes = [c_void_p, c_uint, c_char_p, c_size_t] 189 | 190 | # IMP method_getImplementation(Method method) 191 | objc.method_getImplementation.restype = c_void_p 192 | objc.method_getImplementation.argtypes = [c_void_p] 193 | 194 | # SEL method_getName(Method method) 195 | objc.method_getName.restype = c_void_p 196 | objc.method_getName.argtypes = [c_void_p] 197 | 198 | # unsigned method_getNumberOfArguments(Method method) 199 | objc.method_getNumberOfArguments.restype = c_uint 200 | objc.method_getNumberOfArguments.argtypes = [c_void_p] 201 | 202 | # void method_getReturnType(Method method, char *dst, size_t dst_len) 203 | # Functionally similar to strncpy(dst, return_type, dst_len) 204 | objc.method_getReturnType.restype = None 205 | objc.method_getReturnType.argtypes = [c_void_p, c_char_p, c_size_t] 206 | 207 | # const char * method_getTypeEncoding(Method method) 208 | objc.method_getTypeEncoding.restype = c_char_p 209 | objc.method_getTypeEncoding.argtypes = [c_void_p] 210 | 211 | # IMP method_setImplementation(Method method, IMP imp) 212 | objc.method_setImplementation.restype = c_void_p 213 | objc.method_setImplementation.argtypes = [c_void_p, c_void_p] 214 | 215 | ###################################################################### 216 | 217 | # Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) 218 | objc.objc_allocateClassPair.restype = c_void_p 219 | objc.objc_allocateClassPair.argtypes = [c_void_p, c_char_p, c_size_t] 220 | 221 | # Protocol **objc_copyProtocolList(unsigned int *outCount) 222 | # Returns an array of *outcount pointers followed by NULL terminator. 223 | # You must free() the array. 224 | objc.objc_copyProtocolList.restype = POINTER(c_void_p) 225 | objc.objc_copyProtocolList.argtypes = [POINTER(c_int)] 226 | 227 | # id objc_getAssociatedObject(id object, void *key) 228 | objc.objc_getAssociatedObject.restype = c_void_p 229 | objc.objc_getAssociatedObject.argtypes = [c_void_p, c_void_p] 230 | 231 | # id objc_getClass(const char *name) 232 | objc.objc_getClass.restype = c_void_p 233 | objc.objc_getClass.argtypes = [c_char_p] 234 | 235 | # int objc_getClassList(Class *buffer, int bufferLen) 236 | # Pass None for buffer to obtain just the total number of classes. 237 | objc.objc_getClassList.restype = c_int 238 | objc.objc_getClassList.argtypes = [c_void_p, c_int] 239 | 240 | # id objc_getMetaClass(const char *name) 241 | objc.objc_getMetaClass.restype = c_void_p 242 | objc.objc_getMetaClass.argtypes = [c_char_p] 243 | 244 | # Protocol *objc_getProtocol(const char *name) 245 | objc.objc_getProtocol.restype = c_void_p 246 | objc.objc_getProtocol.argtypes = [c_char_p] 247 | 248 | # You should set return and argument types depending on context. 249 | # id objc_msgSend(id theReceiver, SEL theSelector, ...) 250 | # id objc_msgSendSuper(struct objc_super *super, SEL op, ...) 251 | 252 | # The _stret and _fpret variants only exist on x86-based architectures. 253 | if __i386__ or __x86_64__: 254 | # void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...) 255 | objc.objc_msgSendSuper_stret.restype = None 256 | 257 | # double objc_msgSend_fpret(id self, SEL op, ...) 258 | objc.objc_msgSend_fpret.restype = c_double 259 | 260 | # void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...) 261 | objc.objc_msgSend_stret.restype = None 262 | 263 | # void objc_registerClassPair(Class cls) 264 | objc.objc_registerClassPair.restype = None 265 | objc.objc_registerClassPair.argtypes = [c_void_p] 266 | 267 | # void objc_removeAssociatedObjects(id object) 268 | objc.objc_removeAssociatedObjects.restype = None 269 | objc.objc_removeAssociatedObjects.argtypes = [c_void_p] 270 | 271 | # void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy) 272 | objc.objc_setAssociatedObject.restype = None 273 | objc.objc_setAssociatedObject.argtypes = [c_void_p, c_void_p, c_void_p, c_int] 274 | 275 | ###################################################################### 276 | 277 | # id object_copy(id obj, size_t size) 278 | objc.object_copy.restype = c_void_p 279 | objc.object_copy.argtypes = [c_void_p, c_size_t] 280 | 281 | # id object_dispose(id obj) 282 | objc.object_dispose.restype = c_void_p 283 | objc.object_dispose.argtypes = [c_void_p] 284 | 285 | # Class object_getClass(id object) 286 | objc.object_getClass.restype = c_void_p 287 | objc.object_getClass.argtypes = [c_void_p] 288 | 289 | # const char *object_getClassName(id obj) 290 | objc.object_getClassName.restype = c_char_p 291 | objc.object_getClassName.argtypes = [c_void_p] 292 | 293 | # Ivar object_getInstanceVariable(id obj, const char *name, void **outValue) 294 | objc.object_getInstanceVariable.restype = c_void_p 295 | objc.object_getInstanceVariable.argtypes=[c_void_p, c_char_p, c_void_p] 296 | 297 | # id object_getIvar(id object, Ivar ivar) 298 | objc.object_getIvar.restype = c_void_p 299 | objc.object_getIvar.argtypes = [c_void_p, c_void_p] 300 | 301 | # Class object_setClass(id object, Class cls) 302 | objc.object_setClass.restype = c_void_p 303 | objc.object_setClass.argtypes = [c_void_p, c_void_p] 304 | 305 | # Ivar object_setInstanceVariable(id obj, const char *name, void *value) 306 | # Set argtypes based on the data type of the instance variable. 307 | objc.object_setInstanceVariable.restype = c_void_p 308 | 309 | # void object_setIvar(id object, Ivar ivar, id value) 310 | objc.object_setIvar.restype = None 311 | objc.object_setIvar.argtypes = [c_void_p, c_void_p, c_void_p] 312 | 313 | ###################################################################### 314 | 315 | # const char *property_getAttributes(objc_property_t property) 316 | objc.property_getAttributes.restype = c_char_p 317 | objc.property_getAttributes.argtypes = [c_void_p] 318 | 319 | # const char *property_getName(objc_property_t property) 320 | objc.property_getName.restype = c_char_p 321 | objc.property_getName.argtypes = [c_void_p] 322 | 323 | ###################################################################### 324 | 325 | # BOOL protocol_conformsToProtocol(Protocol *proto, Protocol *other) 326 | objc.protocol_conformsToProtocol.restype = c_bool 327 | objc.protocol_conformsToProtocol.argtypes = [c_void_p, c_void_p] 328 | 329 | class OBJC_METHOD_DESCRIPTION(Structure): 330 | _fields_ = [ ("name", c_void_p), ("types", c_char_p) ] 331 | 332 | # struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount) 333 | # You must free() the returned array. 334 | objc.protocol_copyMethodDescriptionList.restype = POINTER(OBJC_METHOD_DESCRIPTION) 335 | objc.protocol_copyMethodDescriptionList.argtypes = [c_void_p, c_bool, c_bool, POINTER(c_uint)] 336 | 337 | # objc_property_t * protocol_copyPropertyList(Protocol *protocol, unsigned int *outCount) 338 | objc.protocol_copyPropertyList.restype = c_void_p 339 | objc.protocol_copyPropertyList.argtypes = [c_void_p, POINTER(c_uint)] 340 | 341 | # Protocol **protocol_copyProtocolList(Protocol *proto, unsigned int *outCount) 342 | objc.protocol_copyProtocolList = POINTER(c_void_p) 343 | objc.protocol_copyProtocolList.argtypes = [c_void_p, POINTER(c_uint)] 344 | 345 | # struct objc_method_description protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod) 346 | objc.protocol_getMethodDescription.restype = OBJC_METHOD_DESCRIPTION 347 | objc.protocol_getMethodDescription.argtypes = [c_void_p, c_void_p, c_bool, c_bool] 348 | 349 | # const char *protocol_getName(Protocol *p) 350 | objc.protocol_getName.restype = c_char_p 351 | objc.protocol_getName.argtypes = [c_void_p] 352 | 353 | ###################################################################### 354 | 355 | # const char* sel_getName(SEL aSelector) 356 | objc.sel_getName.restype = c_char_p 357 | objc.sel_getName.argtypes = [c_void_p] 358 | 359 | # SEL sel_getUid(const char *str) 360 | # Use sel_registerName instead. 361 | 362 | # BOOL sel_isEqual(SEL lhs, SEL rhs) 363 | objc.sel_isEqual.restype = c_bool 364 | objc.sel_isEqual.argtypes = [c_void_p, c_void_p] 365 | 366 | # SEL sel_registerName(const char *str) 367 | objc.sel_registerName.restype = c_void_p 368 | objc.sel_registerName.argtypes = [c_char_p] 369 | 370 | ###################################################################### 371 | 372 | def ensure_bytes(x): 373 | if isinstance(x, bytes): 374 | return x 375 | return x.encode('ascii') 376 | 377 | ###################################################################### 378 | 379 | def get_selector(name): 380 | "Return a reference to the selector with the given name." 381 | return c_void_p(objc.sel_registerName(ensure_bytes(name))) 382 | 383 | def get_class(name): 384 | "Return a reference to the class with the given name." 385 | return c_void_p(objc.objc_getClass(ensure_bytes(name))) 386 | 387 | def get_metaclass(name): 388 | "Return a reference to the metaclass for the given name." 389 | return c_void_p(objc.objc_getMetaClass(ensure_bytes(name))) 390 | 391 | def get_superclass_of_object(obj): 392 | "Return a reference to the superclass of the given object." 393 | cls = c_void_p(objc.object_getClass(obj)) 394 | return c_void_p(objc.class_getSuperclass(cls)) 395 | 396 | 397 | # http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html 398 | # http://www.x86-64.org/documentation/abi-0.99.pdf (pp.17-23) 399 | # executive summary: on x86-64, who knows? 400 | def should_use_stret(restype): 401 | """Try to figure out when a return type will be passed on stack.""" 402 | if type(restype) != type(Structure): 403 | return False 404 | if not __LP64__ and sizeof(restype) <= 8: 405 | return False 406 | if __LP64__ and sizeof(restype) <= 16: # maybe? I don't know? 407 | return False 408 | return True 409 | 410 | # http://www.sealiesoftware.com/blog/archive/2008/11/16/objc_explain_objc_msgSend_fpret.html 411 | def should_use_fpret(restype): 412 | """Determine if objc_msgSend_fpret is required to return a floating point type.""" 413 | if not (__i386__ or __x86_64__): 414 | # Unneeded on non-intel processors 415 | return False 416 | if __LP64__ and restype == c_longdouble: 417 | # Use only for long double on x86_64 418 | return True 419 | if not __LP64__ and restype in (c_float, c_double, c_longdouble): 420 | return True 421 | return False 422 | 423 | def send_message(receiver, selName, *args, **kwargs): 424 | """Send a mesage named selName to the receiver with the provided arguments. 425 | 426 | This is the equivalen of [receiver selname:args] 427 | 428 | By default, assumes that return type for the message is c_void_p, 429 | and that all arguments are wrapped inside c_void_p. Use the restype 430 | and argtypes keyword arguments to change these values. restype should 431 | be a ctypes type and argtypes should be a list of ctypes types for 432 | the arguments of the message only. 433 | """ 434 | if isinstance(receiver, text): 435 | receiver = get_class(receiver) 436 | selector = get_selector(selName) 437 | restype = kwargs.get('restype', c_void_p) 438 | argtypes = kwargs.get('argtypes', []) 439 | # Choose the correct version of objc_msgSend based on return type. 440 | if should_use_fpret(restype): 441 | objc.objc_msgSend_fpret.restype = restype 442 | objc.objc_msgSend_fpret.argtypes = [c_void_p, c_void_p] + argtypes 443 | result = objc.objc_msgSend_fpret(receiver, selector, *args) 444 | elif should_use_stret(restype): 445 | objc.objc_msgSend_stret.argtypes = [POINTER(restype), c_void_p, c_void_p] + argtypes 446 | result = restype() 447 | objc.objc_msgSend_stret(byref(result), receiver, selector, *args) 448 | else: 449 | objc.objc_msgSend.restype = restype 450 | objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + argtypes 451 | result = objc.objc_msgSend(receiver, selector, *args) 452 | if restype == c_void_p: 453 | result = c_void_p(result) 454 | return result 455 | 456 | 457 | class OBJC_SUPER(Structure): 458 | _fields_ = [ ('receiver', c_void_p), ('class', c_void_p) ] 459 | 460 | OBJC_SUPER_PTR = POINTER(OBJC_SUPER) 461 | 462 | 463 | #http://stackoverflow.com/questions/3095360/what-exactly-is-super-in-objective-c 464 | def send_super(receiver, selName, *args, **kwargs): 465 | """Send a message named selName to the super of the receiver. 466 | 467 | This is the equivalent of [super selname:args]. 468 | """ 469 | if hasattr(receiver, '_as_parameter_'): 470 | receiver = receiver._as_parameter_ 471 | superclass = get_superclass_of_object(receiver) 472 | super_struct = OBJC_SUPER(receiver, superclass) 473 | selector = get_selector(selName) 474 | restype = kwargs.get('restype', c_void_p) 475 | argtypes = kwargs.get('argtypes', None) 476 | objc.objc_msgSendSuper.restype = restype 477 | if argtypes: 478 | objc.objc_msgSendSuper.argtypes = [OBJC_SUPER_PTR, c_void_p] + argtypes 479 | else: 480 | objc.objc_msgSendSuper.argtypes = None 481 | result = objc.objc_msgSendSuper(byref(super_struct), selector, *args) 482 | if restype == c_void_p: 483 | result = c_void_p(result) 484 | return result 485 | 486 | ###################################################################### 487 | 488 | cfunctype_table = {} 489 | 490 | def parse_type_encoding(encoding): 491 | """Takes a type encoding string and outputs a list of the separated type codes. 492 | Currently does not handle unions or bitfields and strips out any field width 493 | specifiers or type specifiers from the encoding. For Python 3.2+, encoding is 494 | assumed to be a bytes object and not unicode. 495 | 496 | Examples: 497 | parse_type_encoding('^v16@0:8') --> ['^v', '@', ':'] 498 | parse_type_encoding('{CGSize=dd}40@0:8{CGSize=dd}16Q32') --> ['{CGSize=dd}', '@', ':', '{CGSize=dd}', 'Q'] 499 | """ 500 | type_encodings = [] 501 | brace_count = 0 # number of unclosed curly braces 502 | bracket_count = 0 # number of unclosed square brackets 503 | typecode = b'' 504 | for c in encoding: 505 | # In Python 3, c comes out as an integer in the range 0-255. In Python 2, c is a single character string. 506 | # To fix the disparity, we convert c to a bytes object if necessary. 507 | if isinstance(c, int): 508 | c = bytes([c]) 509 | 510 | if c == b'{': 511 | # Check if this marked the end of previous type code. 512 | if typecode and typecode[-1:] != b'^' and brace_count == 0 and bracket_count == 0: 513 | type_encodings.append(typecode) 514 | typecode = b'' 515 | typecode += c 516 | brace_count += 1 517 | elif c == b'}': 518 | typecode += c 519 | brace_count -= 1 520 | assert(brace_count >= 0) 521 | elif c == b'[': 522 | # Check if this marked the end of previous type code. 523 | if typecode and typecode[-1:] != b'^' and brace_count == 0 and bracket_count == 0: 524 | type_encodings.append(typecode) 525 | typecode = b'' 526 | typecode += c 527 | bracket_count += 1 528 | elif c == b']': 529 | typecode += c 530 | bracket_count -= 1 531 | assert(bracket_count >= 0) 532 | elif brace_count or bracket_count: 533 | # Anything encountered while inside braces or brackets gets stuck on. 534 | typecode += c 535 | elif c in b'0123456789': 536 | # Ignore field width specifiers for now. 537 | pass 538 | elif c in b'rnNoORV': 539 | # Also ignore type specifiers. 540 | pass 541 | elif c in b'^cislqCISLQfdBv*@#:b?': 542 | if typecode and typecode[-1:] == b'^': 543 | # Previous char was pointer specifier, so keep going. 544 | typecode += c 545 | else: 546 | # Add previous type code to the list. 547 | if typecode: 548 | type_encodings.append(typecode) 549 | # Start a new type code. 550 | typecode = c 551 | 552 | # Add the last type code to the list 553 | if typecode: 554 | type_encodings.append(typecode) 555 | 556 | return type_encodings 557 | 558 | 559 | # Limited to basic types and pointers to basic types. 560 | # Does not try to handle arrays, arbitrary structs, unions, or bitfields. 561 | # Assume that encoding is a bytes object and not unicode. 562 | def cfunctype_for_encoding(encoding): 563 | # Check if we've already created a CFUNCTYPE for this encoding. 564 | # If so, then return the cached CFUNCTYPE. 565 | if encoding in cfunctype_table: 566 | return cfunctype_table[encoding] 567 | 568 | # Otherwise, create a new CFUNCTYPE for the encoding. 569 | typecodes = { 570 | b'c': c_char, 571 | b'i': c_int, 572 | b's': c_short, 573 | b'l': c_long, 574 | b'q': c_longlong, 575 | b'C': c_ubyte, 576 | b'I': c_uint, 577 | b'S': c_ushort, 578 | b'L': c_ulong, 579 | b'Q': c_ulonglong, 580 | b'f': c_float, 581 | b'd': c_double, 582 | b'B': c_bool, 583 | b'v': None, 584 | b'*': c_char_p, 585 | b'@': c_void_p, 586 | b'#': c_void_p, 587 | b':': c_void_p, 588 | NSPointEncoding: NSPoint, 589 | NSSizeEncoding: NSSize, 590 | NSRectEncoding: NSRect, 591 | NSRangeEncoding: NSRange, 592 | PyObjectEncoding: py_object 593 | } 594 | argtypes = [] 595 | for code in parse_type_encoding(encoding): 596 | if code in typecodes: 597 | argtypes.append(typecodes[code]) 598 | elif code[0:1] == b'^' and code[1:] in typecodes: 599 | argtypes.append(POINTER(typecodes[code[1:]])) 600 | else: 601 | raise Exception('unknown type encoding: ' + code) 602 | 603 | cfunctype = CFUNCTYPE(*argtypes) 604 | 605 | # Cache the new CFUNCTYPE in the cfunctype_table. 606 | # We do this mainly because it prevents the CFUNCTYPE 607 | # from being garbage-collected while we need it. 608 | cfunctype_table[encoding] = cfunctype 609 | return cfunctype 610 | 611 | ###################################################################### 612 | 613 | def add_method(cls, selName, method, types): 614 | """Add a new instance method named selName to cls. 615 | 616 | method should be a Python method that does all necessary type conversions. 617 | 618 | types is a string encoding the argument types of the method. 619 | The first type code of types is the return type (e.g. 'v' if void) 620 | The second type code must be '@' for id self. 621 | The third type code must be ':' for SEL cmd. 622 | Additional type codes are for types of other arguments if any. 623 | """ 624 | type_encodings = parse_type_encoding(types) 625 | assert(type_encodings[1] == b'@') # ensure id self typecode 626 | assert(type_encodings[2] == b':') # ensure SEL cmd typecode 627 | selector = get_selector(selName) 628 | cfunctype = cfunctype_for_encoding(types) 629 | imp = cfunctype(method) 630 | objc.class_addMethod.argtypes = [c_void_p, c_void_p, cfunctype, c_char_p] 631 | objc.class_addMethod(cls, selector, imp, types) 632 | return imp 633 | 634 | 635 | def add_ivar(cls, name, vartype): 636 | "Add a new instance variable of type vartype to cls." 637 | return objc.class_addIvar(cls, ensure_bytes(name), sizeof(vartype), alignment(vartype), encoding_for_ctype(vartype)) 638 | 639 | 640 | def set_instance_variable(obj, varname, value, vartype): 641 | "Do the equivalent of `obj.varname = value`, where value is of type vartype." 642 | objc.object_setInstanceVariable.argtypes = [c_void_p, c_char_p, vartype] 643 | objc.object_setInstanceVariable(obj, ensure_bytes(varname), value) 644 | 645 | 646 | def get_instance_variable(obj, varname, vartype): 647 | "Return the value of `obj.varname`, where the value is of type vartype." 648 | variable = vartype() 649 | objc.object_getInstanceVariable(obj, ensure_bytes(varname), byref(variable)) 650 | return variable.value 651 | 652 | ###################################################################### 653 | 654 | class ObjCMethod(object): 655 | """This represents an unbound Objective-C method (really an IMP).""" 656 | 657 | # Note, need to map 'c' to c_byte rather than c_char, because otherwise 658 | # ctypes converts the value into a one-character string which is generally 659 | # not what we want at all, especially when the 'c' represents a bool var. 660 | typecodes = { 661 | b'c': c_byte, 662 | b'i': c_int, 663 | b's': c_short, 664 | b'l': c_long, 665 | b'q': c_longlong, 666 | b'C': c_ubyte, 667 | b'I': c_uint, 668 | b'S': c_ushort, 669 | b'L': c_ulong, 670 | b'Q': c_ulonglong, 671 | b'f': c_float, 672 | b'd': c_double, 673 | b'B': c_bool, 674 | b'v': None, 675 | b'Vv': None, 676 | b'*': c_char_p, 677 | b'@': c_void_p, 678 | b'#': c_void_p, 679 | b':': c_void_p, 680 | b'^v': c_void_p, 681 | b'?': c_void_p, 682 | NSPointEncoding: NSPoint, 683 | NSSizeEncoding: NSSize, 684 | NSRectEncoding: NSRect, 685 | NSRangeEncoding: NSRange, 686 | PyObjectEncoding: py_object 687 | } 688 | 689 | cfunctype_table = {} 690 | 691 | def __init__(self, method): 692 | """Initialize with an Objective-C Method pointer. We then determine 693 | the return type and argument type information of the method.""" 694 | self.selector = c_void_p(objc.method_getName(method)) 695 | self.name = objc.sel_getName(self.selector) 696 | self.pyname = self.name.replace(b':', b'_') 697 | self.encoding = objc.method_getTypeEncoding(method) 698 | self.return_type = objc.method_copyReturnType(method) 699 | self.nargs = objc.method_getNumberOfArguments(method) 700 | self.imp = c_void_p(objc.method_getImplementation(method)) 701 | self.argument_types = [] 702 | 703 | for i in range(self.nargs): 704 | buffer = c_buffer(512) 705 | objc.method_getArgumentType(method, i, buffer, len(buffer)) 706 | self.argument_types.append(buffer.value) 707 | # Get types for all the arguments. 708 | try: 709 | self.argtypes = [self.ctype_for_encoding(t) for t in self.argument_types] 710 | except: 711 | print('No argtypes encoding for %s (%s)' % (self.name, self.argument_types)) 712 | self.argtypes = None 713 | # Get types for the return type. 714 | try: 715 | if self.return_type == b'@': 716 | self.restype = ObjCInstance 717 | elif self.return_type == b'#': 718 | self.restype = ObjCClass 719 | else: 720 | self.restype = self.ctype_for_encoding(self.return_type) 721 | except: 722 | print('No restype encoding for %s (%s)' % (self.name, self.return_type)) 723 | self.restype = None 724 | self.func = None 725 | 726 | def ctype_for_encoding(self, encoding): 727 | """Return ctypes type for an encoded Objective-C type.""" 728 | if encoding in self.typecodes: 729 | return self.typecodes[encoding] 730 | elif encoding[0:1] == b'^' and encoding[1:] in self.typecodes: 731 | return POINTER(self.typecodes[encoding[1:]]) 732 | elif encoding[0:1] == b'^' and encoding[1:] in [CGImageEncoding, NSZoneEncoding]: 733 | # special cases 734 | return c_void_p 735 | elif encoding[0:1] == b'r' and encoding[1:] in self.typecodes: 736 | # const decorator, don't care 737 | return self.typecodes[encoding[1:]] 738 | elif encoding[0:2] == b'r^' and encoding[2:] in self.typecodes: 739 | # const pointer, also don't care 740 | return POINTER(self.typecodes[encoding[2:]]) 741 | else: 742 | raise Exception('unknown encoding for %s: %s' % (self.name, encoding)) 743 | 744 | def get_prototype(self): 745 | """Returns a ctypes CFUNCTYPE for the method.""" 746 | if self.restype == ObjCInstance or self.restype == ObjCClass: 747 | # Some hacky stuff to get around ctypes issues on 64-bit. Can't let 748 | # ctypes convert the return value itself, because it truncates the pointer 749 | # along the way. So instead, we must do set the return type to c_void_p to 750 | # ensure we get 64-bit addresses and then convert the return value manually. 751 | self.prototype = CFUNCTYPE(c_void_p, *self.argtypes) 752 | else: 753 | self.prototype = CFUNCTYPE(self.restype, *self.argtypes) 754 | return self.prototype 755 | 756 | def __repr__(self): 757 | return "" % (self.name, self.encoding) 758 | 759 | def get_callable(self): 760 | """Returns a python-callable version of the method's IMP.""" 761 | if not self.func: 762 | prototype = self.get_prototype() 763 | self.func = cast(self.imp, prototype) 764 | if self.restype == ObjCInstance or self.restype == ObjCClass: 765 | self.func.restype = c_void_p 766 | else: 767 | self.func.restype = self.restype 768 | self.func.argtypes = self.argtypes 769 | return self.func 770 | 771 | def __call__(self, objc_id, *args): 772 | """Call the method with the given id and arguments. You do not need 773 | to pass in the selector as an argument since it will be automatically 774 | provided.""" 775 | f = self.get_callable() 776 | try: 777 | # Automatically convert Python strings into ObjC strings 778 | from .core_foundation import from_value, to_value 779 | result = f(objc_id, self.selector, *(from_value(arg) for arg in args)) 780 | # Convert result to python type if it is a instance or class pointer. 781 | if self.restype == ObjCInstance: 782 | result = to_value(ObjCInstance(result)) 783 | elif self.restype == ObjCClass: 784 | result = ObjCClass(result) 785 | return result 786 | except ArgumentError as error: 787 | # Add more useful info to argument error exceptions, then reraise. 788 | error.args += ('selector = ' + self.name.decode('utf-8'), 789 | 'argtypes =' + text(self.argtypes), 790 | 'encoding = ' + self.encoding.decode('utf-8')) 791 | raise 792 | 793 | 794 | ###################################################################### 795 | 796 | class ObjCBoundMethod(object): 797 | """This represents an Objective-C method (an IMP) which has been bound 798 | to some id which will be passed as the first parameter to the method.""" 799 | 800 | def __init__(self, method, objc_id): 801 | """Initialize with a method and ObjCInstance or ObjCClass object.""" 802 | self.method = method 803 | self.objc_id = objc_id 804 | 805 | def __repr__(self): 806 | return '' % (self.method.name, self.objc_id) 807 | 808 | def __call__(self, *args): 809 | """Call the method with the given arguments.""" 810 | return self.method(self.objc_id, *args) 811 | 812 | ###################################################################### 813 | 814 | 815 | def cache_instance_method(self, name): 816 | """Returns a python representation of the named instance method, 817 | either by looking it up in the cached list of methods or by searching 818 | for and creating a new method object.""" 819 | try: 820 | return self.__dict__['instance_methods'][name] 821 | except KeyError: 822 | selector = get_selector(name.replace('_', ':')) 823 | method = c_void_p(objc.class_getInstanceMethod(self.__dict__['ptr'], selector)) 824 | if method.value: 825 | objc_method = ObjCMethod(method) 826 | self.__dict__['instance_methods'][name] = objc_method 827 | return objc_method 828 | return None 829 | 830 | 831 | def cache_class_method(self, name): 832 | """Returns a python representation of the named class method, 833 | either by looking it up in the cached list of methods or by searching 834 | for and creating a new method object.""" 835 | try: 836 | return self.__dict__['class_methods'][name] 837 | except KeyError: 838 | selector = get_selector(name.replace('_', ':')) 839 | method = c_void_p(objc.class_getClassMethod(self.__dict__['ptr'], selector)) 840 | 841 | if method.value: 842 | objc_method = ObjCMethod(method) 843 | self.__dict__['class_methods'][name] = objc_method 844 | return objc_method 845 | return None 846 | 847 | 848 | def cache_instance_property_methods(self, name): 849 | """Return the accessor and mutator for the named property. 850 | """ 851 | if name.endswith('_'): 852 | # If the requested name ends with _, that's a marker that we're 853 | # dealing with a method call, not a property, so we can shortcut 854 | # the process. 855 | methods = None 856 | else: 857 | # Check 1: Does the class respond to the property? 858 | responds = objc.class_getProperty(self.__dict__['ptr'], name.encode('utf-8')) 859 | 860 | # Check 2: Does the class have an instance method to retrieve the given name 861 | accessor_selector = get_selector(name) 862 | accessor = objc.class_getInstanceMethod(self.__dict__['ptr'], accessor_selector) 863 | 864 | # Check 3: Is there a setName: method to set the property with the given name 865 | mutator_selector = get_selector('set' + name[0].title() + name[1:] + ':') 866 | mutator = objc.class_getInstanceMethod(self.__dict__['ptr'], mutator_selector) 867 | 868 | # If the class responds as a property, or it has both an accessor *and* 869 | # and mutator, then treat it as a property in Python. 870 | if responds or (accessor and mutator): 871 | if accessor: 872 | objc_accessor = ObjCMethod(c_void_p(accessor)) 873 | else: 874 | objc_accessor = None 875 | 876 | if mutator: 877 | objc_mutator = ObjCMethod(c_void_p(mutator)) 878 | else: 879 | objc_mutator = None 880 | 881 | methods = (objc_accessor, objc_mutator) 882 | else: 883 | methods = None 884 | return methods 885 | 886 | 887 | def cache_instance_property_accessor(self, name): 888 | """Returns a python representation of an accessor for the named 889 | property. Existence of a property is done by looking for the write 890 | selector (set:). 891 | """ 892 | try: 893 | methods = self.__dict__['instance_properties'][name] 894 | except KeyError: 895 | methods = cache_instance_property_methods(self, name) 896 | self.__dict__['instance_properties'][name] = methods 897 | if methods: 898 | return methods[0] 899 | return None 900 | 901 | 902 | def cache_instance_property_mutator(self, name): 903 | """Returns a python representation of an accessor for the named 904 | property. Existence of a property is done by looking for the write 905 | selector (set:). 906 | """ 907 | try: 908 | methods = self.__dict__['instance_properties'][name] 909 | except KeyError: 910 | methods = cache_instance_property_methods(self, name) 911 | self.__dict__['instance_properties'][name] = methods 912 | if methods: 913 | return methods[1] 914 | return None 915 | 916 | 917 | def cache_class_property_methods(self, name): 918 | """Return the accessor and mutator for the named property. Existence 919 | of a property is done by looking for the pair of selectors "name" and 920 | "set:". If both exist, we assume this is a static property. 921 | """ 922 | if name.endswith('_'): 923 | # If the requested name ends with _, that's a marker that we're 924 | # dealing with a method call, not a property, so we can shortcut 925 | # the process. 926 | methods = None 927 | else: 928 | accessor_selector = get_selector(name) 929 | accessor = c_void_p(objc.class_getClassMethod(self.__dict__['ptr'], accessor_selector)) 930 | if accessor.value: 931 | objc_accessor = ObjCMethod(accessor) 932 | else: 933 | objc_accessor = None 934 | 935 | mutator_selector = get_selector('set' + name[0].title() + name[1:] + ':') 936 | mutator = c_void_p(objc.class_getClassMethod(self.__dict__['ptr'], mutator_selector)) 937 | if mutator.value: 938 | objc_mutator = ObjCMethod(mutator) 939 | else: 940 | objc_mutator = None 941 | 942 | if objc_accessor and objc_mutator: 943 | methods = (objc_accessor, objc_mutator) 944 | else: 945 | methods = None 946 | return methods 947 | 948 | def cache_class_property_accessor(self, name): 949 | """Returns a python representation of an accessor for the named 950 | property. Existence of a property is done by looking for the write 951 | selector (set:). 952 | """ 953 | try: 954 | methods = self.__dict__['class_properties'][name] 955 | except KeyError: 956 | methods = cache_class_property_methods(self, name) 957 | self.__dict__['class_properties'][name] = methods 958 | if methods: 959 | return methods[0] 960 | return None 961 | 962 | 963 | def cache_class_property_mutator(self, name): 964 | """Returns a python representation of an accessor for the named 965 | property. Existence of a property is done by looking for the write 966 | selector (set:). 967 | """ 968 | try: 969 | methods = self.__dict__['class_properties'][name] 970 | except KeyError: 971 | methods = cache_class_property_methods(self, name) 972 | self.__dict__['class_properties'][name] = methods 973 | if methods: 974 | return methods[1] 975 | return None 976 | 977 | ###################################################################### 978 | 979 | def convert_method_arguments(encoding, args): 980 | """Used to convert Objective-C method arguments to Python values 981 | before passing them on to the Python-defined method. 982 | """ 983 | from .core_foundation import to_value 984 | new_args = [] 985 | arg_encodings = parse_type_encoding(encoding)[3:] 986 | for e, a in zip(arg_encodings, args): 987 | if e == b'@': 988 | new_args.append(to_value(ObjCInstance(a))) 989 | elif e == b'#': 990 | new_args.append(ObjCClass(a)) 991 | else: 992 | new_args.append(a) 993 | return new_args 994 | 995 | 996 | def objc_method(encoding): 997 | """Function decorator for instance methods.""" 998 | # Add encodings for hidden self and cmd arguments. 999 | encoding = ensure_bytes(encoding) 1000 | typecodes = parse_type_encoding(encoding) 1001 | typecodes.insert(1, b'@:') 1002 | encoding = b''.join(typecodes) 1003 | def decorator(f): 1004 | def _objc_method(objc_self, objc_cmd, *args): 1005 | from .core_foundation import at 1006 | py_self = ObjCInstance(objc_self) 1007 | args = convert_method_arguments(encoding, args) 1008 | result = f(py_self, *args) 1009 | if isinstance(result, ObjCClass): 1010 | result = result.ptr.value 1011 | elif isinstance(result, ObjCInstance): 1012 | result = result.ptr.value 1013 | elif isinstance(result, text): 1014 | result = at(result).ptr.value 1015 | return result 1016 | 1017 | def register(cls): 1018 | return add_method(cls.__dict__['ptr'], f.__name__.replace('_', ':'), _objc_method, encoding) 1019 | 1020 | _objc_method.register = register 1021 | 1022 | return _objc_method 1023 | return decorator 1024 | 1025 | 1026 | def objc_classmethod(encoding): 1027 | """Function decorator for class methods.""" 1028 | # Add encodings for hidden self and cmd arguments. 1029 | encoding = ensure_bytes(encoding) 1030 | typecodes = parse_type_encoding(encoding) 1031 | typecodes.insert(1, b'@:') 1032 | encoding = b''.join(typecodes) 1033 | def decorator(f): 1034 | def _objc_classmethod(objc_cls, objc_cmd, *args): 1035 | from .core_foundation import at 1036 | py_cls = ObjCClass(objc_cls) 1037 | args = convert_method_arguments(encoding, args) 1038 | result = f(py_cls, *args) 1039 | if isinstance(result, ObjCClass): 1040 | result = result.ptr.value 1041 | elif isinstance(result, ObjCInstance): 1042 | result = result.ptr.value 1043 | elif isinstance(result, text): 1044 | result = at(result).ptr.value 1045 | return result 1046 | 1047 | def register(cls): 1048 | return add_method(cls.__dict__['metaclass'], f.__name__.replace('_', ':'), _objc_classmethod, encoding) 1049 | 1050 | _objc_classmethod.register = register 1051 | 1052 | return _objc_classmethod 1053 | return decorator 1054 | 1055 | 1056 | class objc_ivar(object): 1057 | """Add instance variable named varname to the subclass. 1058 | varname should be a string. 1059 | vartype is a ctypes type. 1060 | The class must be registered AFTER adding instance variables. 1061 | """ 1062 | def __init__(self, vartype): 1063 | self.vartype = vartype 1064 | 1065 | def pre_register(self, ptr, name): 1066 | return add_ivar(ptr, name, self.vartype) 1067 | 1068 | 1069 | def objc_rawmethod(encoding): 1070 | """Decorator for instance methods without any fancy shenanigans. 1071 | The function must have the signature f(self, cmd, *args) 1072 | where both self and cmd are just pointers to objc objects. 1073 | """ 1074 | # Add encodings for hidden self and cmd arguments. 1075 | encoding = ensure_bytes(encoding) 1076 | typecodes = parse_type_encoding(encoding) 1077 | typecodes.insert(1, b'@:') 1078 | encoding = b''.join(typecodes) 1079 | def decorator(f): 1080 | name = f.__name__.replace('_', ':') 1081 | 1082 | def register(cls): 1083 | return add_method(cls, name, f, encoding) 1084 | f.register = register 1085 | return f 1086 | return decorator 1087 | 1088 | ###################################################################### 1089 | 1090 | 1091 | class ObjCClass(type): 1092 | """Python wrapper for an Objective-C class.""" 1093 | 1094 | # We only create one Python object for each Objective-C class. 1095 | # Any future calls with the same class will return the previously 1096 | # created Python object. Note that these aren't weak references. 1097 | # After you create an ObjCClass, it will exist until the end of the 1098 | # program. 1099 | _registered_classes = {} 1100 | 1101 | def __new__(cls, *args): 1102 | """Create a new ObjCClass instance or return a previously created 1103 | instance for the given Objective-C class. The argument may be either 1104 | the name of the class to retrieve, a pointer to the class, or the 1105 | usual (name, bases, attrs) triple that is provided when called as 1106 | a subclass.""" 1107 | 1108 | if len(args) == 1: 1109 | # A single argument provided. If it's a string, treat it as 1110 | # a class name. Anything else treat as a class pointer. 1111 | 1112 | # Determine name and ptr values from passed in argument. 1113 | class_name_or_ptr = args[0] 1114 | attrs = {} 1115 | 1116 | if isinstance(class_name_or_ptr, (bytes, text)): 1117 | name = ensure_bytes(class_name_or_ptr) 1118 | ptr = get_class(name) 1119 | if ptr.value is None: 1120 | raise NameError("ObjC Class '%s' couldn't be found." % class_name_or_ptr) 1121 | else: 1122 | ptr = class_name_or_ptr 1123 | # Make sure that ptr value is wrapped in c_void_p object 1124 | # for safety when passing as ctypes argument. 1125 | if not isinstance(ptr, c_void_p): 1126 | ptr = c_void_p(ptr) 1127 | name = objc.class_getName(ptr) 1128 | # "nil" is an ObjC answer confirming the ptr didn't work. 1129 | if name == b'nil': 1130 | raise RuntimeError("Couldn't create ObjC class for pointer '%s'." % class_name_or_ptr) 1131 | 1132 | else: 1133 | name, bases, attrs = args 1134 | name = ensure_bytes(name) 1135 | if not isinstance(bases[0], ObjCClass): 1136 | raise RuntimeError("Base class isn't an ObjCClass.") 1137 | 1138 | ptr = get_class(name) 1139 | if ptr.value is None: 1140 | # Create the ObjC class description 1141 | ptr = c_void_p(objc.objc_allocateClassPair(bases[0].__dict__['ptr'], name, 0)) 1142 | 1143 | # Pre-Register all the instance variables 1144 | for attr, obj in attrs.items(): 1145 | try: 1146 | obj.pre_register(ptr, attr) 1147 | except AttributeError: 1148 | # The class attribute doesn't have a pre_register method. 1149 | pass 1150 | 1151 | # Register the ObjC class 1152 | objc.objc_registerClassPair(ptr) 1153 | else: 1154 | raise RuntimeError("ObjC runtime already contains a registered class named '%s'." % name.decode('utf-8')) 1155 | 1156 | # Check if we've already created a Python object for this class 1157 | # and if so, return it rather than making a new one. 1158 | try: 1159 | objc_class = cls._registered_classes[name] 1160 | except KeyError: 1161 | 1162 | # We can get the metaclass only after the class is registered. 1163 | metaclass = get_metaclass(name) 1164 | 1165 | # Py2/3 compatibility; the class name must be "str". 1166 | # If the unicode class exists, we're in Python 2. 1167 | try: 1168 | unicode 1169 | objc_class_name = name 1170 | except NameError: 1171 | objc_class_name = name.decode('utf-8') 1172 | 1173 | # Otherwise create a new Python object and then initialize it. 1174 | objc_class = super(ObjCClass, cls).__new__(cls, objc_class_name, (ObjCInstance,), { 1175 | 'ptr': ptr, 1176 | 'metaclass': metaclass, 1177 | 'name': objc_class_name, 1178 | 'instance_methods': {}, # mapping of name -> instance method 1179 | 'class_methods': {}, # mapping of name -> class method 1180 | 'instance_properties': {}, # mapping of name -> (accessor method, mutator method) 1181 | 'class_properties': {}, # mapping of name -> (accessor method, mutator method) 1182 | 'imp_table': {}, # Mapping of name -> Native method references 1183 | '_as_parameter_': ptr, # for ctypes argument passing 1184 | }) 1185 | 1186 | # Store the new class in dictionary of registered classes. 1187 | cls._registered_classes[name] = objc_class 1188 | 1189 | # Register all the methods, class methods, etc 1190 | for attr, obj in attrs.items(): 1191 | try: 1192 | objc_class.__dict__['imp_table'][attr] = obj.register(objc_class) 1193 | except AttributeError: 1194 | # The class attribute doesn't have a register method. 1195 | pass 1196 | 1197 | return objc_class 1198 | 1199 | def __repr__(self): 1200 | return "" % (self.__dict__['name'], text(self.__dict__['ptr'].value)) 1201 | 1202 | def __getattr__(self, name): 1203 | """Returns a callable method object with the given name.""" 1204 | # If name refers to a class method, then return a callable object 1205 | # for the class method with self.__dict__['ptr'] as hidden first parameter. 1206 | if not name.endswith('_'): 1207 | method = cache_class_property_accessor(self, name) 1208 | if method: 1209 | return ObjCBoundMethod(method, self.__dict__['ptr'])() 1210 | 1211 | method = cache_class_method(self, name) 1212 | if method: 1213 | return ObjCBoundMethod(method, self.__dict__['ptr']) 1214 | 1215 | # Otherwise, raise an exception. 1216 | raise AttributeError('ObjCClass %s has no attribute %s' % (self.name, name)) 1217 | 1218 | def __setattr__(self, name, value): 1219 | # Set the value of an attribute. 1220 | method = cache_class_property_mutator(self, name) 1221 | if method: 1222 | ObjCBoundMethod(method, self.__dict__['ptr'])(value) 1223 | return 1224 | 1225 | raise AttributeError('ObjCClass %s cannot set attribute %s' % (self.__dict__['name'], name)) 1226 | 1227 | ###################################################################### 1228 | 1229 | class ObjCInstance(object): 1230 | """Python wrapper for an Objective-C instance.""" 1231 | 1232 | _cached_objects = {} 1233 | 1234 | def __new__(cls, object_ptr): 1235 | """Create a new ObjCInstance or return a previously created one 1236 | for the given object_ptr which should be an Objective-C id.""" 1237 | # Make sure that object_ptr is wrapped in a c_void_p. 1238 | if not isinstance(object_ptr, c_void_p): 1239 | object_ptr = c_void_p(object_ptr) 1240 | 1241 | # If given a nil pointer, return None. 1242 | if not object_ptr.value: 1243 | return None 1244 | 1245 | # Check if we've already created an python ObjCInstance for this 1246 | # object_ptr id and if so, then return it. A single ObjCInstance will 1247 | # be created for any object pointer when it is first encountered. 1248 | # This same ObjCInstance will then persist until the object is 1249 | # deallocated. 1250 | if object_ptr.value in cls._cached_objects: 1251 | return cls._cached_objects[object_ptr.value] 1252 | 1253 | # Otherwise, create a new ObjCInstance. 1254 | objc_instance = super(ObjCInstance, cls).__new__(cls) 1255 | objc_instance.__dict__['ptr'] = object_ptr 1256 | objc_instance.__dict__['_as_parameter_'] = object_ptr 1257 | # Determine class of this object. 1258 | class_ptr = c_void_p(objc.object_getClass(object_ptr)) 1259 | objc_instance.__dict__['objc_class'] = ObjCClass(class_ptr) 1260 | 1261 | # Store new object in the dictionary of cached objects, keyed 1262 | # by the (integer) memory address pointed to by the object_ptr. 1263 | cls._cached_objects[object_ptr.value] = objc_instance 1264 | 1265 | # Create a DeallocationObserver and associate it with this object. 1266 | # When the Objective-C object is deallocated, the observer will remove 1267 | # the ObjCInstance corresponding to the object from the cached objects 1268 | # dictionary, effectively destroying the ObjCInstance. 1269 | observer = send_message(send_message('DeallocationObserver', 'alloc'), 'initWithObject:', objc_instance) 1270 | objc.objc_setAssociatedObject(objc_instance, observer, observer, 0x301) 1271 | 1272 | # The observer is retained by the object we associate it to. We release 1273 | # the observer now so that it will be deallocated when the associated 1274 | # object is deallocated. 1275 | send_message(observer, 'release') 1276 | 1277 | return objc_instance 1278 | 1279 | def __repr__(self): 1280 | if self.__dict__['objc_class'].__dict__['name'] == '__NSCFString': 1281 | # Display contents of NSString objects 1282 | from .core_foundation import to_str 1283 | string = to_str(self) 1284 | return "" % (id(self), self.__dict__['objc_class'].name, string, text(self.__dict__['ptr'].value)) 1285 | 1286 | return "" % (id(self), self.__dict__['objc_class'].name, text(self.__dict__['ptr'].value)) 1287 | 1288 | def __getattr__(self, name): 1289 | """Returns a callable method object with the given name.""" 1290 | # Search for named instance method in the class object and if it 1291 | # exists, return callable object with self as hidden argument. 1292 | # Note: you should give self and not self.__dict__['ptr'] as a parameter to 1293 | # ObjCBoundMethod, so that it will be able to keep the ObjCInstance 1294 | # alive for chained calls like MyClass.alloc().init() where the 1295 | # object created by alloc() is not assigned to a variable. 1296 | 1297 | # If there's a property with this name; return the value directly. 1298 | # If the name ends with _, we can shortcut this step, because it's 1299 | # clear that we're dealing with a method call. 1300 | if not name.endswith('_'): 1301 | method = cache_instance_property_accessor(self.__dict__['objc_class'], name) 1302 | if method: 1303 | return ObjCBoundMethod(method, self)() 1304 | 1305 | method = cache_instance_method(self.__dict__['objc_class'], name) 1306 | if method: 1307 | return ObjCBoundMethod(method, self) 1308 | 1309 | # Otherwise raise an exception. 1310 | raise AttributeError('ObjCInstance %s has no attribute %s' % (self.__dict__['objc_class'].name, name)) 1311 | 1312 | def __setattr__(self, name, value): 1313 | # Set the value of an attribute. 1314 | method = cache_instance_property_mutator(self.__dict__['objc_class'], name) 1315 | if method: 1316 | ObjCBoundMethod(method, self)(value) 1317 | return 1318 | 1319 | raise AttributeError('ObjCInstance %s cannot set attribute %s' % (self.__dict__['objc_class'].name, name)) 1320 | 1321 | ###################################################################### 1322 | 1323 | # Instances of DeallocationObserver are associated with every 1324 | # Objective-C object that gets wrapped inside an ObjCInstance. 1325 | # Their sole purpose is to watch for when the Objective-C object 1326 | # is deallocated, and then remove the object from the dictionary 1327 | # of cached ObjCInstance objects kept by the ObjCInstance class. 1328 | # 1329 | # The methods of the class defined below are decorated with 1330 | # rawmethod() instead of method() because DeallocationObservers 1331 | # are created inside of ObjCInstance's __new__ method and we have 1332 | # to be careful to not create another ObjCInstance here (which 1333 | # happens when the usual method decorator turns the self argument 1334 | # into an ObjCInstance), or else get trapped in an infinite recursion. 1335 | 1336 | NSObject = ObjCClass('NSObject') 1337 | 1338 | class DeallocationObserver(NSObject): 1339 | 1340 | observed_object = objc_ivar(c_void_p) 1341 | 1342 | @objc_rawmethod('@@') 1343 | def initWithObject_(self, cmd, anObject): 1344 | self = send_super(self, 'init') 1345 | self = self.value 1346 | set_instance_variable(self, 'observed_object', anObject, c_void_p) 1347 | return self 1348 | 1349 | @objc_rawmethod('v') 1350 | def dealloc(self, cmd): 1351 | anObject = get_instance_variable(self, 'observed_object', c_void_p) 1352 | ObjCInstance._cached_objects.pop(anObject, None) 1353 | send_super(self, 'dealloc') 1354 | 1355 | @objc_rawmethod('v') 1356 | def finalize(self, cmd): 1357 | # Called instead of dealloc if using garbage collection. 1358 | # (which would have to be explicitly started with 1359 | # objc_startCollectorThread(), so probably not too much reason 1360 | # to have this here, but I guess it can't hurt.) 1361 | anObject = get_instance_variable(self, 'observed_object', c_void_p) 1362 | ObjCInstance._cached_objects.pop(anObject, None) 1363 | send_super(self, 'finalize') 1364 | -------------------------------------------------------------------------------- /app_packages/rubicon/objc/types.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | 3 | from ctypes import * 4 | 5 | import platform, struct 6 | 7 | __LP64__ = (8*struct.calcsize("P") == 64) 8 | __i386__ = (platform.machine() == 'i386') 9 | __x86_64__ = (platform.machine() == 'x86_64') 10 | 11 | PyObjectEncoding = b'{PyObject=@}' 12 | 13 | def encoding_for_ctype(vartype): 14 | typecodes = {c_char:b'c', c_int:b'i', c_short:b's', c_long:b'l', c_longlong:b'q', 15 | c_ubyte:b'C', c_uint:b'I', c_ushort:b'S', c_ulong:b'L', c_ulonglong:b'Q', 16 | c_float:b'f', c_double:b'd', c_bool:b'B', c_char_p:b'*', c_void_p:b'@', 17 | py_object:PyObjectEncoding} 18 | return typecodes.get(vartype, b'?') 19 | 20 | try: 21 | text = unicode 22 | except NameError: 23 | text = str 24 | 25 | 26 | # Note CGBase.h located at 27 | # /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Headers/CGBase.h 28 | # defines CGFloat as double if __LP64__, otherwise it's a float. 29 | if __LP64__: 30 | NSInteger = c_long 31 | NSUInteger = c_ulong 32 | CGFloat = c_double 33 | NSPointEncoding = b'{CGPoint=dd}' 34 | NSSizeEncoding = b'{CGSize=dd}' 35 | NSRectEncoding = b'{CGRect={CGPoint=dd}{CGSize=dd}}' 36 | NSRangeEncoding = b'{_NSRange=QQ}' 37 | else: 38 | NSInteger = c_int 39 | NSUInteger = c_uint 40 | CGFloat = c_float 41 | NSPointEncoding = b'{CGPoint=ff}' 42 | NSSizeEncoding = b'{CGSize=ff}' 43 | NSRectEncoding = b'{CGRect={CGPoint=ff}{CGSize=ff}}' 44 | NSRangeEncoding = b'{NSRange=II}' 45 | 46 | NSIntegerEncoding = encoding_for_ctype(NSInteger) 47 | NSUIntegerEncoding = encoding_for_ctype(NSUInteger) 48 | CGFloatEncoding = encoding_for_ctype(CGFloat) 49 | 50 | # Special case so that NSImage.initWithCGImage_size_() will work. 51 | CGImageEncoding = b'{CGImage=}' 52 | 53 | NSZoneEncoding = b'{_NSZone=}' 54 | 55 | # from /System/Library/Frameworks/Foundation.framework/Headers/NSGeometry.h 56 | class NSPoint(Structure): 57 | _fields_ = [ 58 | ("x", CGFloat), 59 | ("y", CGFloat) 60 | ] 61 | CGPoint = NSPoint 62 | 63 | class NSSize(Structure): 64 | _fields_ = [ 65 | ("width", CGFloat), 66 | ("height", CGFloat) 67 | ] 68 | CGSize = NSSize 69 | 70 | class NSRect(Structure): 71 | _fields_ = [ 72 | ("origin", NSPoint), 73 | ("size", NSSize) 74 | ] 75 | CGRect = NSRect 76 | 77 | def NSMakeSize(w, h): 78 | return NSSize(w, h) 79 | 80 | CGSizeMake = NSMakeSize 81 | 82 | def NSMakeRect(x, y, w, h): 83 | return NSRect(NSPoint(x, y), NSSize(w, h)) 84 | 85 | CGRectMake = NSMakeRect 86 | 87 | 88 | def NSMakePoint(x, y): 89 | return NSPoint(x, y) 90 | 91 | CGPointMake = NSMakePoint 92 | 93 | # NSDate.h 94 | NSTimeInterval = c_double 95 | 96 | CFIndex = c_long 97 | UniChar = c_ushort 98 | unichar = c_wchar # (actually defined as c_ushort in NSString.h, but need ctypes to convert properly) 99 | CGGlyph = c_ushort 100 | 101 | # CFRange struct defined in CFBase.h 102 | # This replaces the CFRangeMake(LOC, LEN) macro. 103 | class CFRange(Structure): 104 | _fields_ = [ 105 | ("location", CFIndex), 106 | ("length", CFIndex) 107 | ] 108 | 109 | # NSRange.h (Note, not defined the same as CFRange) 110 | class NSRange(Structure): 111 | _fields_ = [ 112 | ("location", NSUInteger), 113 | ("length", NSUInteger) 114 | ] 115 | 116 | NSZeroPoint = NSPoint(0,0) 117 | 118 | CFTypeID = c_ulong 119 | CFNumberType = c_uint32 120 | -------------------------------------------------------------------------------- /apptest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6023B23D1C27D20F006F2562 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B23C1C27D20F006F2562 /* libsqlite3.tbd */; }; 11 | 6023B23F1C27D218006F2562 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B23E1C27D218006F2562 /* libz.tbd */; }; 12 | 608C6CC91B3FF01D00BD512E /* OpenSSL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 608C6CC81B3FF01D00BD512E /* OpenSSL.framework */; }; 13 | 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; 14 | 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; 15 | 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; 16 | 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; 17 | 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; 18 | 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; 19 | 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; 20 | 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 6023B23C1C27D20F006F2562 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; 25 | 6023B23E1C27D218006F2562 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 26 | 608C6CC81B3FF01D00BD512E /* OpenSSL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OpenSSL.framework; sourceTree = ""; }; 27 | 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; 28 | 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 29 | 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 30 | 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 31 | 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 32 | 608C6CCC1B3FF4DF00BD512E /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; 33 | 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; 34 | 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 60796EE219190F4100A9926B /* apptest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "apptest.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 60796EED19190F4100A9926B /* apptest-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "apptest-Info.plist"; sourceTree = ""; }; 37 | 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 38 | 60796EF319190F4100A9926B /* apptest-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "apptest-Prefix.pch"; sourceTree = ""; }; 39 | 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 60796EDF19190F4100A9926B /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | 6023B23F1C27D218006F2562 /* libz.tbd in Frameworks */, 48 | 6023B23D1C27D20F006F2562 /* libsqlite3.tbd in Frameworks */, 49 | 608C6CC91B3FF01D00BD512E /* OpenSSL.framework in Frameworks */, 50 | 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, 51 | 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, 52 | 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, 53 | 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, 54 | 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 60796ED919190F4100A9926B = { 62 | isa = PBXGroup; 63 | children = ( 64 | 60796EEB19190F4100A9926B /* apptest */, 65 | 60796EE419190F4100A9926B /* Frameworks */, 66 | 60796EE319190F4100A9926B /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | 60796EE319190F4100A9926B /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 60796EE219190F4100A9926B /* apptest.app */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | 60796EE419190F4100A9926B /* Frameworks */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 6023B23C1C27D20F006F2562 /* libsqlite3.tbd */, 82 | 6023B23E1C27D218006F2562 /* libz.tbd */, 83 | 608C6CC81B3FF01D00BD512E /* OpenSSL.framework */, 84 | 60796F2B1919C70800A9926B /* Python.framework */, 85 | 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, 86 | 60796EE719190F4100A9926B /* CoreGraphics.framework */, 87 | 60796EE519190F4100A9926B /* Foundation.framework */, 88 | 60796EE919190F4100A9926B /* UIKit.framework */, 89 | ); 90 | name = Frameworks; 91 | sourceTree = ""; 92 | }; 93 | 60796EEB19190F4100A9926B /* apptest */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 608C6CCC1B3FF4DF00BD512E /* app */, 97 | 60F0BABF191FC868006EC268 /* app_packages */, 98 | 60796EF719190F4100A9926B /* Images.xcassets */, 99 | 60796EEC19190F4100A9926B /* Supporting Files */, 100 | ); 101 | path = apptest; 102 | sourceTree = ""; 103 | }; 104 | 60796EEC19190F4100A9926B /* Supporting Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 60796EED19190F4100A9926B /* apptest-Info.plist */, 108 | 60796EEE19190F4100A9926B /* InfoPlist.strings */, 109 | 60796EF119190F4100A9926B /* main.m */, 110 | 60796EF319190F4100A9926B /* apptest-Prefix.pch */, 111 | ); 112 | name = "Supporting Files"; 113 | sourceTree = ""; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | 60796EE119190F4100A9926B /* apptest */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "apptest" */; 121 | buildPhases = ( 122 | 60796F2F1919C7E700A9926B /* Refresh Python source */, 123 | 60796EDE19190F4100A9926B /* Sources */, 124 | 60796EDF19190F4100A9926B /* Frameworks */, 125 | 60796EE019190F4100A9926B /* Resources */, 126 | ); 127 | buildRules = ( 128 | ); 129 | dependencies = ( 130 | ); 131 | name = "apptest"; 132 | productName = apptest; 133 | productReference = 60796EE219190F4100A9926B /* apptest.app */; 134 | productType = "com.apple.product-type.application"; 135 | }; 136 | /* End PBXNativeTarget section */ 137 | 138 | /* Begin PBXProject section */ 139 | 60796EDA19190F4100A9926B /* Project object */ = { 140 | isa = PBXProject; 141 | attributes = { 142 | LastUpgradeCheck = 0720; 143 | ORGANIZATIONNAME = "apptest"; 144 | }; 145 | buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "apptest" */; 146 | compatibilityVersion = "Xcode 3.2"; 147 | developmentRegion = English; 148 | hasScannedForEncodings = 0; 149 | knownRegions = ( 150 | en, 151 | ); 152 | mainGroup = 60796ED919190F4100A9926B; 153 | productRefGroup = 60796EE319190F4100A9926B /* Products */; 154 | projectDirPath = ""; 155 | projectRoot = ""; 156 | targets = ( 157 | 60796EE119190F4100A9926B /* */, 158 | ); 159 | }; 160 | /* End PBXProject section */ 161 | 162 | /* Begin PBXResourcesBuildPhase section */ 163 | 60796EE019190F4100A9926B /* Resources */ = { 164 | isa = PBXResourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, 168 | 60796EF819190F4100A9926B /* Images.xcassets in Resources */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | /* End PBXResourcesBuildPhase section */ 173 | 174 | /* Begin PBXShellScriptBuildPhase section */ 175 | 60796F2F1919C7E700A9926B /* Refresh Python source */ = { 176 | isa = PBXShellScriptBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | ); 180 | inputPaths = ( 181 | ); 182 | name = "Refresh Python source"; 183 | outputPaths = ( 184 | ); 185 | runOnlyForDeploymentPostprocessing = 0; 186 | shellPath = /bin/sh; 187 | shellScript = "mkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/com.ifujun.apptest.apptest\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/lib\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/include\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/com.ifujun.apptest.apptest\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app_packages/\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\n"; 188 | }; 189 | /* End PBXShellScriptBuildPhase section */ 190 | 191 | /* Begin PBXSourcesBuildPhase section */ 192 | 60796EDE19190F4100A9926B /* Sources */ = { 193 | isa = PBXSourcesBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | 60796EF219190F4100A9926B /* main.m in Sources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXSourcesBuildPhase section */ 201 | 202 | /* Begin PBXVariantGroup section */ 203 | 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { 204 | isa = PBXVariantGroup; 205 | children = ( 206 | 60796EEF19190F4100A9926B /* en */, 207 | ); 208 | name = InfoPlist.strings; 209 | sourceTree = ""; 210 | }; 211 | /* End PBXVariantGroup section */ 212 | 213 | /* Begin XCBuildConfiguration section */ 214 | 60796F0C19190F4100A9926B /* Debug */ = { 215 | isa = XCBuildConfiguration; 216 | buildSettings = { 217 | ALWAYS_SEARCH_USER_PATHS = NO; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 219 | CLANG_CXX_LIBRARY = "libc++"; 220 | CLANG_ENABLE_MODULES = YES; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_CONSTANT_CONVERSION = YES; 224 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 225 | CLANG_WARN_EMPTY_BODY = YES; 226 | CLANG_WARN_ENUM_CONVERSION = YES; 227 | CLANG_WARN_INT_CONVERSION = YES; 228 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 229 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 230 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 231 | COPY_PHASE_STRIP = NO; 232 | ENABLE_TESTABILITY = YES; 233 | GCC_C_LANGUAGE_STANDARD = gnu99; 234 | GCC_DYNAMIC_NO_PIC = NO; 235 | GCC_OPTIMIZATION_LEVEL = 0; 236 | GCC_PREPROCESSOR_DEFINITIONS = ( 237 | "DEBUG=1", 238 | "$(inherited)", 239 | ); 240 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 241 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 242 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 243 | GCC_WARN_UNDECLARED_SELECTOR = YES; 244 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 245 | GCC_WARN_UNUSED_FUNCTION = YES; 246 | GCC_WARN_UNUSED_VARIABLE = YES; 247 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 248 | ONLY_ACTIVE_ARCH = YES; 249 | SDKROOT = iphoneos; 250 | TARGETED_DEVICE_FAMILY = "1,2"; 251 | }; 252 | name = Debug; 253 | }; 254 | 60796F0D19190F4100A9926B /* Release */ = { 255 | isa = XCBuildConfiguration; 256 | buildSettings = { 257 | ALWAYS_SEARCH_USER_PATHS = NO; 258 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 259 | CLANG_CXX_LIBRARY = "libc++"; 260 | CLANG_ENABLE_MODULES = YES; 261 | CLANG_ENABLE_OBJC_ARC = YES; 262 | CLANG_WARN_BOOL_CONVERSION = YES; 263 | CLANG_WARN_CONSTANT_CONVERSION = YES; 264 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INT_CONVERSION = YES; 268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 271 | COPY_PHASE_STRIP = YES; 272 | ENABLE_NS_ASSERTIONS = NO; 273 | GCC_C_LANGUAGE_STANDARD = gnu99; 274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 276 | GCC_WARN_UNDECLARED_SELECTOR = YES; 277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 278 | GCC_WARN_UNUSED_FUNCTION = YES; 279 | GCC_WARN_UNUSED_VARIABLE = YES; 280 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 281 | SDKROOT = iphoneos; 282 | TARGETED_DEVICE_FAMILY = "1,2"; 283 | VALIDATE_PRODUCT = YES; 284 | }; 285 | name = Release; 286 | }; 287 | 60796F0F19190F4100A9926B /* Debug */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 291 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 292 | FRAMEWORK_SEARCH_PATHS = ( 293 | "$(inherited)", 294 | "$(PROJECT_DIR)", 295 | ); 296 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 297 | GCC_PREFIX_HEADER = "apptest/apptest-Prefix.pch"; 298 | HEADER_SEARCH_PATHS = ( 299 | "$(inherited)", 300 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 301 | "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", 302 | ); 303 | INFOPLIST_FILE = "apptest/apptest-Info.plist"; 304 | PRODUCT_BUNDLE_IDENTIFIER = "com.ifujun.apptest.${PRODUCT_NAME:rfc1034identifier}"; 305 | PRODUCT_NAME = "$(TARGET_NAME)"; 306 | USER_HEADER_SEARCH_PATHS = include/python2.7; 307 | WRAPPER_EXTENSION = app; 308 | }; 309 | name = Debug; 310 | }; 311 | 60796F1019190F4100A9926B /* Release */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 315 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 316 | FRAMEWORK_SEARCH_PATHS = ( 317 | "$(inherited)", 318 | "$(PROJECT_DIR)", 319 | ); 320 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 321 | GCC_PREFIX_HEADER = "apptest/apptest-Prefix.pch"; 322 | HEADER_SEARCH_PATHS = ( 323 | "$(inherited)", 324 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 325 | "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", 326 | ); 327 | INFOPLIST_FILE = "apptest/apptest-Info.plist"; 328 | PRODUCT_BUNDLE_IDENTIFIER = "com.ifujun.apptest.${PRODUCT_NAME:rfc1034identifier}"; 329 | PRODUCT_NAME = "$(TARGET_NAME)"; 330 | WRAPPER_EXTENSION = app; 331 | }; 332 | name = Release; 333 | }; 334 | /* End XCBuildConfiguration section */ 335 | 336 | /* Begin XCConfigurationList section */ 337 | 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "apptest" */ = { 338 | isa = XCConfigurationList; 339 | buildConfigurations = ( 340 | 60796F0C19190F4100A9926B /* Debug */, 341 | 60796F0D19190F4100A9926B /* Release */, 342 | ); 343 | defaultConfigurationIsVisible = 0; 344 | defaultConfigurationName = Release; 345 | }; 346 | 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "apptest" */ = { 347 | isa = XCConfigurationList; 348 | buildConfigurations = ( 349 | 60796F0F19190F4100A9926B /* Debug */, 350 | 60796F1019190F4100A9926B /* Release */, 351 | ); 352 | defaultConfigurationIsVisible = 0; 353 | defaultConfigurationName = Release; 354 | }; 355 | /* End XCConfigurationList section */ 356 | }; 357 | rootObject = 60796EDA19190F4100A9926B /* Project object */; 358 | } 359 | -------------------------------------------------------------------------------- /apptest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /apptest.xcodeproj/project.xcworkspace/xcuserdata/fujun.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest.xcodeproj/project.xcworkspace/xcuserdata/fujun.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /apptest.xcodeproj/xcuserdata/fujun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /apptest.xcodeproj/xcuserdata/fujun.xcuserdatad/xcschemes/apptest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /apptest.xcodeproj/xcuserdata/fujun.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | apptest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 60796EE119190F4100A9926B 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "icon-58.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "icon-87.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "icon-80.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "icon-120.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "icon-120.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon-180.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "29x29", 41 | "idiom" : "ipad", 42 | "filename" : "icon-29.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "icon-58.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "40x40", 53 | "idiom" : "ipad", 54 | "filename" : "icon-40.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "icon-80.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "76x76", 65 | "idiom" : "ipad", 66 | "filename" : "icon-76.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "icon-152.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "83.5x83.5", 77 | "idiom" : "ipad", 78 | "filename" : "icon-167.png", 79 | "scale" : "2x" 80 | } 81 | ], 82 | "info" : { 83 | "version" : 1, 84 | "author" : "xcode" 85 | } 86 | } -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-120.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-152.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-167.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-180.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-58.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-80.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/AppIcon.appiconset/icon-87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/AppIcon.appiconset/icon-87.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "launch-640x960.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "launch-640x1136.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "ipad", 23 | "extent" : "full-screen", 24 | "minimum-system-version" : "7.0", 25 | "filename" : "launch-768x1024.png", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "orientation" : "landscape", 30 | "idiom" : "ipad", 31 | "extent" : "full-screen", 32 | "minimum-system-version" : "7.0", 33 | "filename" : "launch-1024x768.png", 34 | "scale" : "1x" 35 | }, 36 | { 37 | "orientation" : "portrait", 38 | "idiom" : "ipad", 39 | "extent" : "full-screen", 40 | "minimum-system-version" : "7.0", 41 | "filename" : "launch-1536x2048.png", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "landscape", 46 | "idiom" : "ipad", 47 | "extent" : "full-screen", 48 | "minimum-system-version" : "7.0", 49 | "filename" : "launch-2048x1536.png", 50 | "scale" : "2x" 51 | } 52 | ], 53 | "info" : { 54 | "version" : 1, 55 | "author" : "xcode" 56 | } 57 | } -------------------------------------------------------------------------------- /apptest/Images.xcassets/LaunchImage.launchimage/launch-1024x768.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/LaunchImage.launchimage/launch-1024x768.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/LaunchImage.launchimage/launch-1536x2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/LaunchImage.launchimage/launch-1536x2048.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/LaunchImage.launchimage/launch-2048x1536.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/LaunchImage.launchimage/launch-2048x1536.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/LaunchImage.launchimage/launch-640x1136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/LaunchImage.launchimage/launch-640x1136.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/LaunchImage.launchimage/launch-640x960.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/LaunchImage.launchimage/launch-640x960.png -------------------------------------------------------------------------------- /apptest/Images.xcassets/LaunchImage.launchimage/launch-768x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Forkong/Python-iOS/6667e633810a809e9f2ee13390a8741155614733/apptest/Images.xcassets/LaunchImage.launchimage/launch-768x1024.png -------------------------------------------------------------------------------- /apptest/apptest-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.ifujun.apptest.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /apptest/apptest-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /apptest/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /apptest/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // A main module for starting Python projects under iOS. 4 | // 5 | 6 | #import 7 | #import 8 | #include 9 | #include 10 | 11 | int main(int argc, char *argv[]) { 12 | int ret = 0; 13 | unsigned int i; 14 | NSString *tmp_path; 15 | NSString *python_home; 16 | wchar_t *wpython_home; 17 | const char* main_script; 18 | wchar_t** python_argv; 19 | 20 | @autoreleasepool { 21 | 22 | NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; 23 | 24 | // Special environment to prefer .pyo; also, don't write bytecode 25 | // because the process will not have write permissions on the device. 26 | putenv("PYTHONOPTIMIZE=1"); 27 | putenv("PYTHONDONTWRITEBYTECODE=1"); 28 | 29 | python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; 30 | NSLog(@"PythonHome is: %@", python_home); 31 | wpython_home = _Py_char2wchar([python_home UTF8String], NULL); 32 | Py_SetPythonHome(wpython_home); 33 | 34 | // iOS provides a specific directory for temp files. 35 | tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; 36 | putenv((char *)[tmp_path UTF8String]); 37 | 38 | NSLog(@"Initializing Python runtime"); 39 | Py_Initialize(); 40 | 41 | // Set the name of the main script 42 | main_script = [[[NSBundle mainBundle] pathForResource:@"Library/Application Support/com.ifujun.apptest.apptest/app/apptest/__main__" 43 | ofType:@"py"] 44 | cStringUsingEncoding:NSUTF8StringEncoding]; 45 | 46 | if (main_script == NULL) { 47 | NSLog(@"Unable to locate apptest main module file"); 48 | exit(-1); 49 | } 50 | 51 | // Construct argv for the interpreter 52 | python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); 53 | 54 | python_argv[0] = _Py_char2wchar(main_script, NULL); 55 | for (i = 1; i < argc; i++) { 56 | python_argv[i] = _Py_char2wchar(argv[i], NULL); 57 | } 58 | 59 | PySys_SetArgv(argc, python_argv); 60 | 61 | // If other modules are using threads, we need to initialize them. 62 | PyEval_InitThreads(); 63 | 64 | // Start the main.py script 65 | NSLog(@"Running %s", main_script); 66 | 67 | @try { 68 | FILE* fd = fopen(main_script, "r"); 69 | if (fd == NULL) { 70 | ret = 1; 71 | NSLog(@"Unable to open main.py, abort."); 72 | } else { 73 | ret = PyRun_SimpleFileEx(fd, main_script, 1); 74 | if (ret != 0) { 75 | NSLog(@"Application quit abnormally!"); 76 | } else { 77 | // In a normal iOS application, the following line is what 78 | // actually runs the application. It requires that the 79 | // Objective-C runtime environment has a class named 80 | // "PythonAppDelegate". This project doesn't define 81 | // one, because Objective-C bridging isn't something 82 | // Python does out of the box. You'll need to use 83 | // a library like Rubicon-ObjC [1], Pyobjus [2] or 84 | // PyObjC [3] if you want to run an *actual* iOS app. 85 | // [1] http://pybee.org/rubicon 86 | // [2] http://pyobjus.readthedocs.org/ 87 | // [3] https://pythonhosted.org/pyobjc/ 88 | 89 | UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); 90 | } 91 | } 92 | } 93 | @catch (NSException *exception) { 94 | NSLog(@"Python runtime error: %@", [exception reason]); 95 | } 96 | @finally { 97 | Py_Finalize(); 98 | } 99 | 100 | PyMem_RawFree(wpython_home); 101 | if (python_argv) { 102 | for (i = 0; i < argc; i++) { 103 | PyMem_RawFree(python_argv[i]); 104 | } 105 | PyMem_RawFree(python_argv); 106 | } 107 | NSLog(@"Leaving"); 108 | } 109 | 110 | exit(ret); 111 | return ret; 112 | } 113 | --------------------------------------------------------------------------------