first commit

This commit is contained in:
2009-07-20 15:34:38 -04:00
commit 95a1cc8c91
167 changed files with 118494 additions and 0 deletions

64
objc/_FoundationSignatures.py Executable file
View File

@@ -0,0 +1,64 @@
from _objc import setSignatureForSelector
setSignatureForSelector("NSScanner", "scanCharactersFromSet:intoString:", "c@:@o^@")
setSignatureForSelector("NSScanner", "scanDouble:", "c@:o^d")
setSignatureForSelector("NSScanner", "scanFloat:", "c@:o^f")
setSignatureForSelector("NSScanner", "scanHexInt:", "c@:o^I")
setSignatureForSelector("NSScanner", "scanInt:", "c@:o^i")
setSignatureForSelector("NSScanner", "scanLongLong:", "c@:o^q")
setSignatureForSelector("NSScanner", "scanString:intoString:", "c@:@o^@")
setSignatureForSelector("NSScanner", "scanUpToCharactersFromSet:intoString:", "c@:@o^@")
setSignatureForSelector("NSScanner", "scanUpToString:intoString:", "c@:@o^@")
setSignatureForSelector("NSStream", "getStreamsToHost:port:inputStream:outputStream:", "v@:@io^@o^@")
setSignatureForSelector("NSString", "completePathIntoString:caseSensitive:matchesIntoArray:filterTypes:", "I@:o^@co^@@")
setSignatureForSelector("NSString", "getLineStart:end:contentsEnd:forRange:", "v@:o^Io^Io^I{_NSRange=II}")
setSignatureForSelector("NSMutableAttributedString", "readFromURL:options:documentAttributes:", "c@:@@o^@")
setSignatureForSelector("NSAttributedString", "attribute:atIndex:effectiveRange:", "@0@4:8@12I16o^{_NSRange=II}20")
setSignatureForSelector("NSAttributedString", "attribute:atIndex:longestEffectiveRange:inRange:", "@0@4:8@12I16o^{_NSRange=II}20{_NSRange=II}24")
setSignatureForSelector("NSAttributedString", "attributesAtIndex:effectiveRange:", "@0@4:8I12o^{_NSRange=II}16")
setSignatureForSelector("NSAttributedString", "attributesAtIndex:longestEffectiveRange:inRange:", "@0@4:8I12o^{_NSRange=II}16{_NSRange=II}20")
setSignatureForSelector("NSAttributedString", "initWithHTML:baseURL:documentAttributes:", "@0@4:8@12@16o^@20")
setSignatureForSelector("NSAttributedString", "initWithHTML:documentAttributes:", "@@:@o^@")
setSignatureForSelector("NSAttributedString", "initWithPath:documentAttributes:", "@@:@o^@")
setSignatureForSelector("NSAttributedString", "initWithRTFDFileWrapper:documentAttributes:", "@@:@o^@")
setSignatureForSelector("NSAttributedString", "initWithRTFD:documentAttributes:", "@0@4:8@12o^@16")
setSignatureForSelector("NSAttributedString", "initWithRTF:documentAttributes:", "@0@4:8@12o^@16")
setSignatureForSelector("NSAttributedString", "initWithURL:documentAttributes:", "@0@4:8@12o^@16")
setSignatureForSelector("NSFileManager", "fileExistsAtPath:isDirectory:", "c0@4:8@12o^c16")
setSignatureForSelector("NSArray", "getObject:atIndex:", "c@:o^@I")
setSignatureForSelector("NSCalendarDate", "years:months:days:hours:minutes:seconds:sinceDate:", "v@:o^io^io^io^io^io^i@")
setSignatureForSelector("NSPropertyListSerialization", "dataFromPropertyList:format:errorDescription:", "@@:@io^@")
setSignatureForSelector("NSPropertyListSerialization", "propertyListFromData:mutabilityOption:format:errorDescription:", "@@:@io^io^@")
setSignatureForSelector("NSAppleScript", "_initWithContentsOfFile:error:", "@@:@o^@")
setSignatureForSelector("NSAppleScript", "_initWithData:error:", "@@:@o^@")
setSignatureForSelector("NSAppleScript", "compileAndReturnError:", "c@:o^@")
setSignatureForSelector("NSAppleScript", "executeAndReturnError:", "@@:o^@")
setSignatureForSelector("NSAppleScript", "executeAppleEvent:error:", "@@:@o^@")
setSignatureForSelector("NSAppleScript", "initWithContentsOfURL:error:", "@@:@o^@")
setSignatureForSelector("NSFaultHandler", "extraData", "i@:")
setSignatureForSelector("NSFaultHandler", "setTargetClass:extraData:", "v@:#i")
setSignatureForSelector("NSFormatter", "getObjectValue:forString:errorDescription:", "c@:o^@@o^@")
setSignatureForSelector("NSFormatter", "isPartialStringValid:newEditingString:errorDescription:", "c@:@o^@o^@")
setSignatureForSelector("NSFormatter", "isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:", "c0@4:8o^@12o^{_NSRange=II}16@20{_NSRange=II}24o^@32")
setSignatureForSelector("NSObject", "validateValue:forKey:error:", "c@:N^@@o^@")
setSignatureForSelector("NSObject", "validateValue:forKeyPath:error:", "c@:N^@@o^@")
setSignatureForSelector("NSObject", "addObserver:forKeyPath:options:context:",
"v@:@@Ii")
setSignatureForSelector("NSObject", "observeValueForKeyPath:ofObject:change:context:", "v@:@@@i")
setSignatureForSelector("NSArray", "addObserver:toObjectsAtIndexes:forKeyPath:options:context:", 'v@:@@@Ii')
setSignatureForSelector("NSURLConnection", "sendSynchronousRequest:returningResponse:error:", "@@:@o^@o^@")
setSignatureForSelector("NSString", "getParagraphStart:end:contentsEnd:forRange:", "v@:o^Io^Io^I{_NSRange=II}")
setSignatureForSelector("NSMutableAttributedString", "readFromData:options:documentAttributes:", "c@:@@o^@")
setSignatureForSelector("NSAttributedString", "initWithHTML:options:documentAttributes:", "@20@0:4@8@12o^@16")
setSignatureForSelector("NSAttributedString", "initWithDocFormat:documentAttributes:", "@@:@o^@")
setSignatureForSelector("NSKeyValueProperty", "keyPathIfAffectedByValueForKey:exactMatch:", "@@:@o^c")
setSignatureForSelector("NSKeyValueObservationForwarder", "initWithObserver:relationshipKey:keyPathFromRelatedObject:options:context:", "@@:@@@Ii")
setSignatureForSelector("NSDeserializer", "deserializePropertyListFromData:atCursor:mutableContainers:", "@@:@O^Ic")
setSignatureForSelector("NSDeserializer", "deserializePropertyListLazilyFromData:atCursor:length:mutableContainers:", "@@:@O^IIc")
setSignatureForSelector("OC_PythonObject", "addObserver:forKeyPath:options:context:", "v@:@@Ii")
# Technically incorrect, but not used anyway
setSignatureForSelector("NSObject", "setObservationInfo:", "v@:i")
setSignatureForSelector("NSObject", "observationInfo", "i@:")

40
objc/__init__.py Executable file
View File

@@ -0,0 +1,40 @@
"""
Python <-> Objective-C bridge (PyObjC)
This module defines the core interfaces of the Python<->Objective-C bridge.
"""
# Aliases for some common Objective-C constants
nil = None
YES = True
NO = False
# Import the namespace from the _objc extension
def _update(g=globals()):
import _objc
for k,v in _objc.__dict__.iteritems():
g.setdefault(k,v)
_update()
del _update
from _convenience import *
import _FoundationSignatures
# Add useful utility functions below
if platform == 'MACOSX':
from _dyld import *
else:
from _gnustep import *
from _protocols import *
from _descriptors import *
from _category import *
from _bridges import *
from _compat import *
from _pythonify import *
from _functions import *
###
# This can be usefull to hunt down memory problems in the testsuite.
#import atexit
#atexit.register(recycleAutoreleasePool)

30
objc/_bridges.py Executable file
View File

@@ -0,0 +1,30 @@
from _objc import *
__all__ = []
BRIDGED_STRUCTURES = {}
BRIDGED_TYPES = []
try:
from array import array
from Carbon.File import FSRef
except ImportError:
pass
else:
def struct_to_FSRef(s):
return FSRef(rawdata=array('B', s[0]).tostring())
BRIDGED_STRUCTURES['{FSRef=[80C]}'] = struct_to_FSRef
def FSRef_to_struct(fsRef):
return (tuple(array('B').fromstring(fsRef.data)),)
BRIDGED_TYPES.append((FSRef, FSRef_to_struct))
def _bridgePythonTypes():
# python TO Obj-C
OC_PythonObject = lookUpClass('OC_PythonObject')
if BRIDGED_TYPES:
OC_PythonObject.depythonifyTable().extend(BRIDGED_TYPES)
if BRIDGED_STRUCTURES:
OC_PythonObject.pythonifyStructTable().update(BRIDGED_STRUCTURES)
_bridgePythonTypes()

63
objc/_category.py Executable file
View File

@@ -0,0 +1,63 @@
__all__ = ['classAddMethod', 'Category']
from _objc import selector, classAddMethods, objc_class
def classAddMethod(cls, name, method):
"""
Add a single method to a class. 'name' is the ObjC selector
"""
if isinstance(method, selector):
sel = selector(method.callable,
selector=name,
signature=method.signature,
isClassMethod=method.isClassMethod)
else:
sel = selector(method, selector=name)
return classAddMethods(cls, [sel])
#
# Syntactic support for categories
#
class _CategoryMeta(type):
"""
Meta class for categories.
"""
__slots__ = ()
_IGNORENAMES = ('__module__', '__name__')
def _newSubclass(cls, name, bases, methods):
return type.__new__(cls, name, bases, methods)
_newSubclass = classmethod(_newSubclass)
def __new__(cls, name, bases, methods):
if len(bases) != 1:
raise TypeError, "Cannot have multiple inheritance with Categories"
c = bases[0].real_class
if c.__name__ != name:
raise TypeError, "Category name must be same as class name"
m = [ x[1] for x in methods.iteritems() if x[0] not in cls._IGNORENAMES ]
classAddMethods(c, m)
return c
def Category(cls):
"""
Create a category on ``cls``.
Usage:
class SomeClass (Category(SomeClass)):
def method(self):
pass
``SomeClass`` is an existing class that will be rebound to the same
value. The side-effect of this class definition is that the methods
in the class definition will be added to the existing class.
"""
if not isinstance(cls, objc_class):
raise TypeError, "Category can only be used on Objective-C classes"
retval = _CategoryMeta._newSubclass('Category', (), dict(real_class=cls))
return retval

58
objc/_compat.py Executable file
View File

@@ -0,0 +1,58 @@
__all__ = ['runtime', 'pluginBundle', 'registerPlugin']
class Runtime:
"""
Backward compatibility interface.
This class provides (partial) support for the interface of
older versions of PyObjC.
"""
def __getattr__(self, name):
import warnings
warnings.warn("Deprecated: use objc.lookUpClass",
DeprecationWarning)
import objc
if name == '__objc_classes__':
return objc.getClassList()
elif name == '__kind__':
return 'python'
try:
return objc.lookUpClass(name)
except objc.nosuchclass_error:
raise AttributeError, name
def __eq__(self, other):
return self is other
def __repr__(self):
return "objc.runtime"
runtime = Runtime()
_PLUGINS = {}
def registerPlugin(pluginName):
"""
Deprecated: use currentBundle()
Register the current py2app plugin by name and return its bundle
"""
import os
import sys
path = os.path.dirname(os.path.dirname(os.environ['RESOURCEPATH']))
if not isinstance(path, unicode):
path = unicode(path, sys.getfilesystemencoding())
_PLUGINS[pluginName] = path
return pluginBundle(pluginName)
def pluginBundle(pluginName):
"""
Deprecated: use currentBundle()
Return the main bundle for the named plugin. This should be used
only after it has been registered with registerPlugin
"""
import warnings
warnings.warn("Deprecated: use currentBundle()", DeprecationWarning)
from Foundation import NSBundle
return NSBundle.bundleWithPath_(_PLUGINS[pluginName])

602
objc/_convenience.py Executable file
View File

@@ -0,0 +1,602 @@
"""
This module implements a callback function that is used by the C code to
add Python special methods to Objective-C classes with a suitable interface.
This module contains no user callable code.
TODO:
- Add external interface: Framework specific modules may want to add to this.
- These are candidates for implementation:
>>> from Foundation import *
>>> set(dir(list)) - set(dir(NSMutableArray))
set(['__delslice__', '__imul__', '__getslice__', '__setslice__',
'__iadd__', '__mul__', '__add__', '__rmul__'])
>>> set(dir(dict)) - set(dir(NSMutableDictionary))
set(['__cmp__'])
"""
from _objc import setClassExtender, selector, lookUpClass, currentBundle, repythonify, splitSignature
from itertools import imap
__all__ = ['CONVENIENCE_METHODS', 'CLASS_METHODS']
CONVENIENCE_METHODS = {}
CLASS_METHODS = {}
NSObject = lookUpClass('NSObject')
HAS_KVO = NSObject.instancesRespondToSelector_('willChangeValueForKey:')
def isNative(sel):
return not hasattr(sel, 'callable')
def add_convenience_methods(super_class, name, type_dict):
try:
return _add_convenience_methods(super_class, name, type_dict)
except:
import traceback
traceback.print_exc()
raise
def _add_convenience_methods(super_class, name, type_dict):
"""
Add additional methods to the type-dict of subclass 'name' of
'super_class'.
CONVENIENCE_METHODS is a global variable containing a mapping from
an Objective-C selector to a Python method name and implementation.
CLASS_METHODS is a global variable containing a mapping from
class name to a list of Python method names and implementation.
Matching entries from both mappings are added to the 'type_dict'.
"""
if type_dict.get('__objc_python_subclass__'):
if 'bundleForClass' not in type_dict:
cb = currentBundle()
def bundleForClass(cls):
return cb
type_dict['bundleForClass'] = selector(bundleForClass, isClassMethod=True)
if (HAS_KVO and
'__useKVO__' not in type_dict and
isNative(type_dict.get('willChangeValueForKey_')) and
isNative(type_dict.get('didChangeValueForKey_'))):
useKVO = issubclass(super_class, NSObject)
type_dict['__useKVO__'] = useKVO
if '__bundle_hack__' in type_dict:
import warnings
warnings.warn(
"__bundle_hack__ is not necessary in PyObjC 1.3+ / py2app 0.1.8+",
DeprecationWarning)
for k, sel in type_dict.items():
if not isinstance(sel, selector):
continue
if sel.selector == "alloc" or sel.selector == "allocWithZone:":
sel.isAlloc = 1
if sel.selector.endswith(':error:'):
sigParts = splitSignature(sel.signature)
if sigParts[-1] == '^@':
sigParts = sigParts[:-1] + ('o^@',)
sel.signature = ''.join(sigParts)
if sel.selector in ( 'copy', 'copyWithZone:',
'mutableCopy', 'mutableCopyWithZone:'):
# These methods transfer ownership to the caller, the runtime uses
# this information to adjust the reference count.
sel.doesDonateReference = 1
sel = sel.selector
if sel in CONVENIENCE_METHODS:
v = CONVENIENCE_METHODS[sel]
for nm, value in v:
if nm in type_dict and isinstance(type_dict[nm], selector):
# Clone attributes of already existing version
t = type_dict[nm]
v = selector(value, selector=t.selector,
signature=t.signature, isClassMethod=t.isClassMethod)
v.isAlloc = t.isAlloc
type_dict[nm] = v
else:
type_dict[nm] = value
if name in CLASS_METHODS:
for nm, value in CLASS_METHODS[name]:
type_dict[nm] = value
setClassExtender(add_convenience_methods)
def __getitem__objectForKey_(self, key):
res = self.objectForKey_(container_wrap(key))
return container_unwrap(res, KeyError, key)
def has_key_objectForKey_(self, key):
res = self.objectForKey_(container_wrap(key))
return res is not None
def get_objectForKey_(self, key, dflt=None):
res = self.objectForKey_(container_wrap(key))
if res is None:
res = dflt
return res
CONVENIENCE_METHODS['objectForKey:'] = (
('__getitem__', __getitem__objectForKey_),
('has_key', has_key_objectForKey_),
('get', get_objectForKey_),
('__contains__', has_key_objectForKey_),
)
def __delitem__removeObjectForKey_(self, key):
self.removeObjectForKey_(container_wrap(key))
CONVENIENCE_METHODS['removeObjectForKey:'] = (
('__delitem__', __delitem__removeObjectForKey_),
)
def update_setObject_forKey_(self, other):
# XXX - should this be more flexible?
for key, value in other.items():
self[key] = value
def setdefault_setObject_forKey_(self, key, dflt=None):
try:
return self[key]
except KeyError:
self[key] = dflt
return dflt
def __setitem__setObject_forKey_(self, key, value):
self.setObject_forKey_(container_wrap(value), container_wrap(key))
def pop_setObject_forKey_(self, key, dflt=None):
try:
res = self[key]
except KeyError:
res = dflt
else:
del self[key]
return res
def popitem_setObject_forKey_(self):
try:
k = self[iter(self).next()]
except StopIteration:
raise KeyError, "popitem on an empty %s" % (type(self).__name__,)
else:
return (k, self[k])
CONVENIENCE_METHODS['setObject:forKey:'] = (
('__setitem__', __setitem__setObject_forKey_),
('update', update_setObject_forKey_),
('setdefault', setdefault_setObject_forKey_),
('pop', pop_setObject_forKey_),
('popitem', popitem_setObject_forKey_),
)
CONVENIENCE_METHODS['count'] = (
('__len__', lambda self: self.count()),
)
CONVENIENCE_METHODS['containsObject:'] = (
('__contains__', lambda self, elem: bool(self.containsObject_(container_wrap(elem)))),
)
CONVENIENCE_METHODS['hash'] = (
('__hash__', lambda self: self.hash()),
)
CONVENIENCE_METHODS['isEqualTo:'] = (
('__eq__', lambda self, other: bool(self.isEqualTo_(other))),
)
CONVENIENCE_METHODS['isEqual:'] = (
('__eq__', lambda self, other: bool(self.isEqual_(other))),
)
CONVENIENCE_METHODS['isGreaterThan:'] = (
('__gt__', lambda self, other: bool(self.isGreaterThan_(other))),
)
CONVENIENCE_METHODS['isGreaterThanOrEqualTo:'] = (
('__ge__', lambda self, other: bool(self.isGreaterThanOrEqualTo_(other))),
)
CONVENIENCE_METHODS['isLessThan:'] = (
('__lt__', lambda self, other: bool(self.isLessThan_(other))),
)
CONVENIENCE_METHODS['isLessThanOrEqualTo:'] = (
('__le__', lambda self, other: bool(self.isLessThanOrEqualTo_(other))),
)
CONVENIENCE_METHODS['isNotEqualTo:'] = (
('__ne__', lambda self, other: bool(self.isNotEqualTo_(other))),
)
CONVENIENCE_METHODS['length'] = (
('__len__', lambda self: self.length()),
)
CONVENIENCE_METHODS['addObject:'] = (
('append', lambda self, item: self.addObject_(container_wrap(item))),
)
def reverse_exchangeObjectAtIndex_withObjectAtIndex_(self):
begin = 0
end = len(self) - 1
while begin < end:
self.exchangeObjectAtIndex_withObjectAtIndex_(begin, end)
begin += 1
end -= 1
CONVENIENCE_METHODS['exchangeObjectAtIndex:withObjectAtIndex:'] = (
('reverse', reverse_exchangeObjectAtIndex_withObjectAtIndex_),
)
def ensureArray(anArray):
if not isinstance(anArray, (NSArray, list, tuple)):
anArray = list(anArray)
return anArray
def extend_addObjectsFromArray_(self, anArray):
self.addObjectsFromArray_(ensureArray(anArray))
CONVENIENCE_METHODS['addObjectsFromArray:'] = (
('extend', extend_addObjectsFromArray_),
)
def index_indexOfObject_(self, item):
from Foundation import NSNotFound
res = self.indexOfObject_(container_wrap(item))
if res == NSNotFound:
raise ValueError, "%s.index(x): x not in list" % (type(self).__name__,)
return res
CONVENIENCE_METHODS['indexOfObject:'] = (
('index', index_indexOfObject_),
)
def insert_insertObject_atIndex_(self, idx, item):
idx = slice(idx, None, None).indices(len(self)).start
self.insertObject_atIndex_(container_wrap(item), idx)
CONVENIENCE_METHODS['insertObject:atIndex:'] = (
( 'insert', insert_insertObject_atIndex_),
)
def __getitem__objectAtIndex_(self, idx):
length = len(self)
if isinstance(idx, slice):
start, stop, step = idx.indices(length)
#if step == 1:
# m = getattr(self, 'subarrayWithRange_', None)
# if m is not None:
# return m((start, stop - start))
return [self[i] for i in xrange(start, stop, step)]
if idx < 0:
idx += length
if idx < 0:
raise IndexError, "index out of range"
elif idx >= length:
raise IndexError, "index out of range"
return container_unwrap(self.objectAtIndex_(idx), RuntimeError)
CONVENIENCE_METHODS['objectAtIndex:'] = (
('__getitem__', __getitem__objectAtIndex_),
)
def __delitem__removeObjectAtIndex_(self, idx):
length = len(self)
if isinstance(idx, slice):
start, stop, step = idx.indices(length)
if step == 1:
if start > stop:
start, stop = stop, start
m = getattr(self, 'removeObjectsInRange_', None)
if m is not None:
m((start, stop - start))
return
r = range(start, stop, step)
r.sort()
r.reverse()
for i in r:
self.removeObjectAtIndex_(i)
return
if idx < 0:
idx += length
if idx < 0:
raise IndexError, "index out of range"
elif idx >= length:
raise IndexError, "index out of range"
self.removeObjectAtIndex_(idx)
def pop_removeObjectAtIndex_(self, idx=-1):
length = len(self)
if length <= 0:
raise IndexError("pop from empty list")
elif idx >= length or (idx + length) < 0:
raise IndexError("pop index out of range")
elif idx < 0:
idx += length
rval = self[idx]
self.removeObjectAtIndex_(idx)
return rval
def remove_removeObjectAtIndex_(self, obj):
idx = self.index(obj)
self.removeObjectAtIndex_(idx)
CONVENIENCE_METHODS['removeObjectAtIndex:'] = (
('remove', remove_removeObjectAtIndex_),
('pop', pop_removeObjectAtIndex_),
('__delitem__', __delitem__removeObjectAtIndex_),
)
def __setitem__replaceObjectAtIndex_withObject_(self, idx, anObject):
length = len(self)
if isinstance(idx, slice):
start, stop, step = idx.indices(length)
if step == 1:
m = getattr(self, 'replaceObjectsInRange_withObjectsFromArray_', None)
if m is not None:
m((start, stop - start), ensureArray(anObject))
return
# XXX - implement this..
raise NotImplementedError
if idx < 0:
idx += length
if idx < 0:
raise IndexError, "index out of range"
elif idx >= length:
raise IndexError, "index out of range"
self.replaceObjectAtIndex_withObject_(idx, anObject)
CONVENIENCE_METHODS['replaceObjectAtIndex:withObject:'] = (
('__setitem__', __setitem__replaceObjectAtIndex_withObject_),
)
def enumeratorGenerator(anEnumerator):
while True:
yield container_unwrap(anEnumerator.nextObject(), StopIteration)
def dictItems(aDict):
"""
NSDictionary.items()
"""
keys = aDict.allKeys()
return zip(keys, imap(aDict.__getitem__, keys))
CONVENIENCE_METHODS['allKeys'] = (
('keys', lambda self: self.allKeys()),
('items', lambda self: dictItems(self)),
)
CONVENIENCE_METHODS['allValues'] = (
('values', lambda self: self.allValues()),
)
def itemsGenerator(aDict):
for key in aDict:
yield (key, aDict[key])
def __iter__objectEnumerator_keyEnumerator(self):
meth = getattr(self, 'keyEnumerator', None)
if meth is None:
meth = self.objectEnumerator
return iter(meth())
CONVENIENCE_METHODS['keyEnumerator'] = (
('__iter__', __iter__objectEnumerator_keyEnumerator),
('iterkeys', lambda self: iter(self.keyEnumerator())),
('iteritems', lambda self: itemsGenerator(self)),
)
CONVENIENCE_METHODS['objectEnumerator'] = (
('__iter__', __iter__objectEnumerator_keyEnumerator),
('itervalues', lambda self: iter(self.objectEnumerator())),
)
CONVENIENCE_METHODS['reverseObjectEnumerator'] = (
('__reversed__', lambda self: iter(self.reverseObjectEnumerator())),
)
CONVENIENCE_METHODS['removeAllObjects'] = (
('clear', lambda self: self.removeAllObjects()),
)
CONVENIENCE_METHODS['dictionaryWithDictionary:'] = (
('copy', lambda self: type(self).dictionaryWithDictionary_(self)),
)
CONVENIENCE_METHODS['nextObject'] = (
('__iter__', enumeratorGenerator),
)
#
# NSNumber seems to be and abstract base-class that is implemented using
# NSCFNumber, a CoreFoundation 'proxy'.
#
NSNull = lookUpClass('NSNull')
NSArray = lookUpClass('NSArray')
null = NSNull.null()
number_wrap = repythonify
def container_wrap(v):
if v is None:
return null
return v
def container_unwrap(v, exc_type, *exc_args):
if v is None:
raise exc_type(*exc_args)
elif v is null:
return None
return v
#
# Special wrappers for a number of varargs functions (constructors)
#
def initWithObjects_(self, *args):
if args[-1] is not None:
raise ValueError, "Need None as the last argument"
return self.initWithArray_(args[:-1])
CONVENIENCE_METHODS['initWithObjects:'] = (
('initWithObjects_', initWithObjects_),
)
def arrayWithObjects_(self, *args):
if args[-1] is not None:
raise ValueError, "Need None as the last argument"
return self.arrayWithArray_(args[:-1])
CONVENIENCE_METHODS['arrayWithObjects:'] = (
('arrayWithObjects_', selector(arrayWithObjects_, signature='@@:@', isClassMethod=1)),
)
def setWithObjects_(self, *args):
if args[-1] is not None:
raise ValueError, "Need None as the last argument"
return self.setWithArray_(args[:-1])
CONVENIENCE_METHODS['setWithObjects:'] = (
('setWithObjects_', selector(setWithObjects_, signature='@@:@', isClassMethod=1)),
)
def setWithObjects_count_(self, args, count):
return self.setWithArray_(args[:count])
CONVENIENCE_METHODS['setWithObjects:count:'] = (
('setWithObjects_count_', selector(setWithObjects_count_, signature='@@:^@i', isClassMethod=1)),
)
def splitObjectsAndKeys(values):
if values[-1] is not None:
raise ValueError, "Need None as the last argument"
if len(values) % 2 != 1:
raise ValueError, "Odd number of arguments"
objects = []
keys = []
for i in range(0, len(values)-1, 2):
objects.append(container_wrap(values[i]))
keys.append(container_wrap(values[i+1]))
return objects, keys
def dictionaryWithObjectsAndKeys_(self, *values):
objects, keys = splitObjectsAndKeys(values)
return self.dictionaryWithObjects_forKeys_(objects, keys)
CONVENIENCE_METHODS['dictionaryWithObjectsAndKeys:'] = (
('dictionaryWithObjectsAndKeys_',
selector(dictionaryWithObjectsAndKeys_, signature='@@:@',isClassMethod=1)),
)
def fromkeys_dictionaryWithObjects_forKeys_(cls, keys, values=None):
if not isinstance(keys, (list, tuple)):
keys = list(keys)
if values is None:
values = (None,) * len(keys)
elif not isinstance(values, (list, tuple)):
values = list(values)
return cls.dictionaryWithObjects_forKeys_(values, keys)
CONVENIENCE_METHODS['dictionaryWithObjects:forKeys:'] = (
('fromkeys',
classmethod(fromkeys_dictionaryWithObjects_forKeys_)),
)
def initWithObjectsAndKeys_(self, *values):
objects, keys = splitObjectsAndKeys(values)
return self.initWithObjects_forKeys_(objects, keys)
CONVENIENCE_METHODS['initWithObjectsAndKeys:'] = (
( 'initWithObjectsAndKeys_', initWithObjectsAndKeys_ ),
)
def sort(self, cmpfunc=cmp):
def doCmp(a, b, cmpfunc):
return cmpfunc(a, b)
self.sortUsingFunction_context_(doCmp, cmpfunc)
CONVENIENCE_METHODS['sortUsingFunction:context:'] = (
('sort', sort),
)
CONVENIENCE_METHODS['hasPrefix:'] = (
('startswith', lambda self, pfx: self.hasPrefix_(pfx)),
)
CONVENIENCE_METHODS['hasSuffix:'] = (
('endswith', lambda self, pfx: self.hasSuffix_(pfx)),
)
CLASS_METHODS['NSNull'] = (
('__nonzero__', lambda self: False ),
)
NSDecimalNumber = lookUpClass('NSDecimalNumber')
def _makeD(v):
if isinstance(v, NSDecimalNumber):
return v
return NSDecimalNumber.decimalNumberWithDecimal_(v)
CLASS_METHODS['NSDecimalNumber'] = (
('__add__', lambda self, other: _makeD(self.decimalValue() + other)),
('__radd__', lambda self, other: _makeD(other + self.decimalValue())),
('__sub__', lambda self, other: _makeD(self.decimalValue() - other)),
('__rsub__', lambda self, other: _makeD(other - self.decimalValue())),
('__mul__', lambda self, other: _makeD(self.decimalValue() * other)),
('__rmul__', lambda self, other: _makeD(other * self.decimalValue())),
('__div__', lambda self, other: _makeD(self.decimalValue() / other)),
('__rdiv__', lambda self, other: _makeD(other / self.decimalValue())),
('__mod__', lambda self, other: _makeD(self.decimalValue() % other)),
('__rmod__', lambda self, other: _makeD(other % self.decimalValue())),
('__neg__', lambda self: _makeD(-(self.decimalValue()))),
('__pos__', lambda self: _makeD(+(self.decimalValue()))),
('__abs__', lambda self: _makeD(abs(self.decimalValue()))),
)
def NSData__getslice__(self, i, j):
return self.bytes()[i:j]
def NSData__getitem__(self, item):
buff = self.bytes()
try:
return buff[item]
except TypeError:
return buff[:][item]
CLASS_METHODS['NSData'] = (
('__str__', lambda self: self.bytes()[:]),
('__getitem__', NSData__getitem__),
('__getslice__', NSData__getslice__),
)
def NSMutableData__setslice__(self, i, j, sequence):
# XXX - could use replaceBytes:inRange:, etc.
self.mutableBytes()[i:j] = sequence
def NSMutableData__setitem__(self, item, value):
self.mutableBytes()[item] = value
CLASS_METHODS['NSMutableData'] = (
('__setslice__', NSMutableData__setslice__),
('__setitem__', NSMutableData__setitem__),
)

98
objc/_descriptors.py Executable file
View File

@@ -0,0 +1,98 @@
"""
Python <-> Objective-C bridge (PyObjC)
This module defines the core interfaces of the Python<->Objective-C bridge.
"""
__all__ = ['IBOutlet', 'IBAction', 'accessor', 'Accessor', 'typedAccessor']
from _objc import ivar, selector
#
# Interface builder support.
#
def IBOutlet(name):
"""
Create an instance variable that can be used as an outlet in
Interface Builder.
"""
return ivar(name, isOutlet=1)
def IBAction(func):
"""
Return an Objective-C method object that can be used as an action
in Interface Builder.
"""
return selector(func, signature="v@:@")
def accessor(func, typeSignature='@'):
"""
Return an Objective-C method object that is conformant with key-value coding
and key-value observing.
"""
from inspect import getargspec
args, varargs, varkw, defaults = getargspec(func)
funcName = func.__name__
maxArgs = len(args)
minArgs = maxArgs - len(defaults or ())
# implicit self
selArgs = 1 + funcName.count('_')
if varargs is not None or varkw is not None:
raise TypeError('%s can not be an accessor because it accepts varargs or varkw' % (funcName,))
if not (minArgs <= selArgs <= maxArgs):
if selArgs == 3 and (minArgs <= 2 <= maxArgs) and funcName.startswith('validate') and funcName.endswith('_error_'):
return selector(func, signature='c@:N^@o^@')
elif minArgs == maxArgs:
raise TypeError('%s expected to take %d args, but must accept %d from Objective-C (implicit self plus count of underscores)' % (funcName, maxArgs, selArgs))
else:
raise TypeError('%s expected to take between %d and %d args, but must accept %d from Objective-C (implicit self plus count of underscores)' % (funcName, minArgs, maxArgs, selArgs))
if selArgs == 3:
if funcName.startswith('insertObject_in') and funcName.endswith('AtIndex_'):
return selector(func, signature='v@:@i')
elif funcName.startswith('replaceObjectIn') and funcName.endswith('AtIndex_withObject_'):
return selector(func, signature='v@:i@')
# pass through to "too many arguments"
elif selArgs == 2:
if funcName.startswith('objectIn') and funcName.endswith('AtIndex_'):
return selector(func, signature='@@:i')
elif funcName.startswith('removeObjectFrom') and funcName.endswith('AtIndex_'):
return selector(func, signature='v@:i')
elif funcName.startswith('get') and funcName.endswith('_range_'):
return selector(func, signature='@@:{_NSRange=ii}')
return selector(func, signature="v@:" + typeSignature)
elif selArgs == 1:
if typeSignature == '@' and func.func_name.startswith('countOf'):
typeSignature = 'i'
return selector(func, signature=typeSignature + "@:")
raise TypeError("%s takes too many arguments to be an accessor" % (funcName,))
def typedAccessor(typeSignature):
"""
Python 2.4 decorator for creating a typed accessor, usage:
@typedAccessor('i')
def someIntegerAccessor(self):
return self.someInteger
@typedAccessor('i')
def setSomeIntegerAccessor_(self, anInteger):
self.someInteger = anInteger
"""
def _typedAccessor(func):
return accessor(func, typeSignature)
return _typedAccessor
def Accessor(func):
import warnings
warnings.warn(
"Use objc.accessor instead of objc.Accessor", DeprecationWarning)
return accessor(func)

129
objc/_dyld.py Executable file
View File

@@ -0,0 +1,129 @@
"""
dyld emulation
"""
__all__ = [
'dyld_framework', 'dyld_library', 'dyld_find', 'pathForFramework',
'infoForFramework',
]
import os
from _framework import infoForFramework
# These are the defaults as per man dyld(1)
#
DEFAULT_FRAMEWORK_FALLBACK = u':'.join([
os.path.expanduser(u"~/Library/Frameworks"),
u"/Library/Frameworks",
u"/Network/Library/Frameworks",
u"/System/Library/Frameworks",
])
DEFAULT_LIBRARY_FALLBACK = u':'.join([
os.path.expanduser(u"~/lib"),
u"/usr/local/lib",
u"/lib",
u"/usr/lib",
])
def ensure_unicode(s):
"""Not all of PyObjC understands unicode paths very well yet"""
if isinstance(s, str):
return unicode(s, 'utf8')
return s
def injectSuffixes(iterator):
suffix = ensure_unicode(os.environ.get('DYLD_IMAGE_SUFFIX', None))
if suffix is None:
return iterator
def _inject(iterator=iterator,suffix=suffix):
for path in iterator:
if path.endswith(u'.dylib'):
yield path[:-6] + suffix + u'.dylib'
else:
yield path + suffix
yield path
return _inject()
def dyld_framework(filename, framework_name, version=None):
"""Find a framework using dyld semantics"""
filename = ensure_unicode(filename)
framework_name = ensure_unicode(framework_name)
version = ensure_unicode(version)
def _search():
spath = ensure_unicode(os.environ.get('DYLD_FRAMEWORK_PATH', None))
if spath is not None:
for path in spath.split(u':'):
if version:
yield os.path.join(
path, framework_name + u'.framework',
u'Versions', version, framework_name
)
else:
yield os.path.join(
path, framework_name + u'.framework', framework_name
)
yield filename
spath = ensure_unicode(os.environ.get(
'DYLD_FALLBACK_FRAMEWORK_PATH', DEFAULT_FRAMEWORK_FALLBACK
))
for path in spath.split(u':'):
if version:
yield os.path.join(
path, framework_name + u'.framework', u'Versions',
version, framework_name
)
else:
yield os.path.join(
path, framework_name + u'.framework', framework_name
)
for f in injectSuffixes(_search()):
if os.path.exists(f):
return f
# raise ..
raise ValueError, "Framework %s could not be found" % (framework_name,)
def dyld_library(filename, libname):
"""Find a dylib using dyld semantics"""
filename = ensure_unicode(filename)
libname = ensure_unicode(libname)
def _search():
spath = ensure_unicode(os.environ.get('DYLD_LIBRARY_PATH', None))
if spath is not None:
for path in spath.split(u':'):
yield os.path.join(path, libname)
yield filename
spath = ensure_unicode(os.environ.get(
'DYLD_FALLBACK_LIBRARY_PATH', DEFAULT_LIBRARY_FALLBACK
))
for path in spath.split(u':'):
yield os.path.join(path, libname)
for f in injectSuffixes(_search()):
if os.path.exists(f):
return f
raise ValueError, "dylib %s could not be found" % (filename,)
def dyld_find(filename):
"""Generic way to locate a dyld framework or dyld"""
# if they passed in a framework directory, not a mach-o file
# then go ahead and look where one would expect to find the mach-o
filename = ensure_unicode(filename)
if os.path.isdir(filename):
filename = os.path.join(
filename,
os.path.basename(filename)[:-len(os.path.splitext(filename)[-1])]
)
filename = os.path.realpath(filename)
res = infoForFramework(filename)
if res:
framework_loc, framework_name, version = res
return dyld_framework(filename, framework_name, version)
else:
return dyld_library(filename, os.path.basename(filename))
def pathForFramework(path):
fpath, name, version = infoForFramework(dyld_find(path))
return os.path.join(fpath, name + u'.framework')

24
objc/_framework.py Executable file
View File

@@ -0,0 +1,24 @@
"""
Generic framework path manipulation
"""
__all__ = ['infoForFramework']
# This regexp should find:
# \1 - framework location
# \2 - framework name
# \3 - framework version (optional)
#
FRAMEWORK_RE_STR = ur"""(^.*)(?:^|/)(\w+).framework(?:/(?:Versions/([^/]+)/)?\2)?$"""
FRAMEWORK_RE = None
def infoForFramework(filename):
"""returns (location, name, version) or None"""
global FRAMEWORK_RE
if FRAMEWORK_RE is None:
import re
FRAMEWORK_RE = re.compile(FRAMEWORK_RE_STR)
is_framework = FRAMEWORK_RE.findall(filename)
if not is_framework:
return None
return is_framework[-1]

44
objc/_functions.py Executable file
View File

@@ -0,0 +1,44 @@
__all__ = ['inject', 'signature']
import os
import sys
def _ensure_path(p):
p = os.path.realpath(p)
if isinstance(p, unicode):
p = p.encode(sys.getfilesystemencoding())
return p
def inject(pid, bundle, useMainThread=True):
"""Loads the given MH_BUNDLE in the target process identified by pid"""
try:
from _objc import _inject
from _dyld import dyld_find
except ImportError:
raise NotImplementedError("objc.inject is only supported on Mac OS X 10.3 and later")
bundlePath = bundle
systemPath = dyld_find('/usr/lib/libSystem.dylib')
carbonPath = dyld_find('/System/Library/Frameworks/Carbon.framework/Carbon')
paths = map(_ensure_path, (bundlePath, systemPath, carbonPath))
return _inject(
pid,
useMainThread,
*paths
)
def signature(signature, **kw):
"""
A Python method decorator that allows easy specification
of Objective-C selectors.
Usage::
@objc.signature('i@:if')
def methodWithX_andY_(self, x, y):
return 0
"""
from _objc import selector
kw['signature'] = signature
def makeSignature(func):
return selector(func, **kw)
return makeSignature

13
objc/_gnustep.py Executable file
View File

@@ -0,0 +1,13 @@
__all__ = ['pathForFramework', 'infoForFramework']
#
# TODO - I have no idea what the semantics are for GNUStep ..
#
from _framework import infoForFramework
def ensure_unicode(s):
if isinstance(s, str):
return unicode(s)
return s
def pathForFramework(path):
return ensure_unicode(path)

BIN
objc/_objc.so Executable file

Binary file not shown.

31
objc/_protocols.py Executable file
View File

@@ -0,0 +1,31 @@
import _objc
__all__ = ['protocolNamed', 'ProtocolError']
class ProtocolError(_objc.error):
__module__ = 'objc'
PROTOCOL_CACHE = {}
def protocolNamed(name):
"""
Returns a Protocol object for the named protocol. This is the
equivalent of @protocol(name) in Objective-C.
Raises objc.ProtocolError when the protocol does not exist.
"""
name = unicode(name)
try:
return PROTOCOL_CACHE[name]
except KeyError:
pass
for p in _objc.protocolsForProcess():
pname = p.__name__
PROTOCOL_CACHE.setdefault(pname, p)
if pname == name:
return p
for cls in _objc.getClassList():
for p in _objc.protocolsForClass(cls):
pname = p.__name__
PROTOCOL_CACHE.setdefault(pname, p)
if pname == name:
return p
raise ProtocolError("protocol %r does not exist" % (name,), name)

81
objc/_pythonify.py Executable file
View File

@@ -0,0 +1,81 @@
import _objc
__all__ = []
class OC_PythonFloat(float):
__slots__=('__pyobjc_object__',)
def __new__(cls, obj, value):
self = float.__new__(cls, value)
self.__pyobjc_object__ = obj
return self
__class__ = property(lambda self: self.__pyobjc_object__.__class__)
def __getattr__(self, attr):
return getattr(self.__pyobjc_object__, attr)
class OC_PythonLong(long):
def __new__(cls, obj, value):
self = long.__new__(cls, value)
self.__pyobjc_object__ = obj
return self
__class__ = property(lambda self: self.__pyobjc_object__.__class__)
def __getattr__(self, attr):
return getattr(self.__pyobjc_object__, attr)
# The long type doesn't support __slots__ on subclasses, fake
# one part of the effect of __slots__: don't allow setting of attributes.
def __setattr__(self, attr, value):
if attr != '__pyobjc_object__':
raise AttributeError, "'%s' object has no attribute '%s')"%(self.__class__.__name__, attr)
self.__dict__['__pyobjc_object__'] = value
class OC_PythonInt(int):
__slots__=('__pyobjc_object__',)
def __new__(cls, obj, value):
self = int.__new__(cls, value)
self.__pyobjc_object__ = obj
return self
__class__ = property(lambda self: self.__pyobjc_object__.__class__)
def __getattr__(self, attr):
return getattr(self.__pyobjc_object__, attr)
NSNumber = _objc.lookUpClass('NSNumber')
NSDecimalNumber = _objc.lookUpClass('NSDecimalNumber')
Foundation = None
def numberWrapper(obj):
if isinstance(obj, NSDecimalNumber):
return obj
# ensure that NSDecimal is around
global Foundation
if Foundation is None:
import Foundation
# return NSDecimal
return Foundation.NSDecimal(obj)
try:
tp = obj.objCType()
except AttributeError:
import warnings
warnings.warn(RuntimeWarning, "NSNumber instance doesn't implement objCType? %r" % (obj,))
return obj
if tp in 'qQLfd':
if tp == 'q':
return OC_PythonLong(obj, obj.longLongValue())
elif tp in 'QL':
return OC_PythonLong(obj, obj.unsignedLongLongValue())
else:
return OC_PythonFloat(obj, obj.doubleValue())
else:
return OC_PythonInt(obj, obj.longValue())
_objc.setNSNumberWrapper(numberWrapper)