Repository URL to install this package:
|
Version:
15.1.0-1 ▾
|
# (c) Copyright 2009-2010. CodeWeavers, Inc.
"""Helpers to simplify calling Python functions from Objective-C code using
PyObjC.
The obstacle to overcome is that PyObjC provides no support for calling regular
functions. The workaround is to declare proxy class methods with Objective-C
imposed names and this module aims to simplify that process.
"""
import distversion
#####
#
# Thunking functions
#
#####
# dictionary of code objects for a function that takes N args
_FUNC_CODES = {}
def thunk(func, wrapper_name):
"""Returns a class method encapsulating the func function.
This makes it easy to declare class methods that act as a wrapper for
regular functions as part of a class declaration. For instance:
def some_function(arg):
pass
class MyClass(cxobj.Proxy):
someFunction_ = cxobj.thunk(some_function)
# Then the following call is valid:
# MyClass.someFunction_(my_arg)
"""
nargs = wrapper_name.count('_')
try:
code_object = _FUNC_CODES[nargs]
except KeyError:
arglist = ",".join('arg%s' % x for x in range(nargs))
# pylint: disable=W0123
func_object = eval('lambda _cls, %s: func(%s)' % (arglist, arglist))
try:
code_object = func_object.func_code
except AttributeError:
code_object = func_object.__code__
_FUNC_CODES[nargs] = code_object
try:
func_name = func.func_name
except AttributeError:
func_name = func.__name__
clsmethod = _FUNCTION_TYPE(code_object, {'func': func}, 'clsmethod_%s' % func_name)
return classmethod(clsmethod)
_FUNCTION_TYPE = type(thunk)
class Proxy(distversion.CXMacObject):
"""Serves as a base class for defining proxies so one can call Python
functions from Objective-C code using PyObjC.
"""
@classmethod
def add_thunk(cls, meth, func):
"""Dynamically adds a class method wrapper for the specified function.
This makes it possible to declare class methods that act as a wrapper
for regular functions right next to the function declaration, that is
outside of the class declaration. For instance:
class MyClass(cxobj.Proxy):
pass
def function_one(arg):
pass
MyClass.add_thunk('functionOne_', function_one)
def function_two():
pass
MyClass.add_thunk('functionTwo', function_two)
# Then the following calls are valid:
# MyClass.functionOne_(my_arg)
# MyClass.functionTwo()
"""
setattr(cls, meth, thunk(func, meth))
def nsobject_init(self):
"""Calls the init() method of NSObject. Classes that need an Objective C
init method should implement it like so:
def init(self): # with arguments if applicable
self = cxobjc.Proxy.nsobject_init(self)
if self is not None:
self.__init__() # with arguments if applicable
return self"""
# pylint: disable=E1101
# This won't be called on Linux (and should fail if it is) so we
# disable the pylint warning.
return super(Proxy, self).init()
def valueForUndefinedKey_(self, key):
"""Override of -[NSObject valueForUndefinedKey:] which directly
accesses the named attribute. NSObject's implementation throws an
exception, which provokes PyObjC to do this, but all the exceptions
makes it nearly impossible to debug CrossOver. Plus, they slow things
down quite a bit. I also suspect they cause leaks (e.g. KVO change
dictionaries)."""
return getattr(self, key)
def setValue_forUndefinedKey_(self, value, key):
"""Override of -[NSObject setValue:forUndefinedKey:] which directly
accesses the named attribute. See valueForUndefinedKey_."""
setattr(self, key, value)
#####
#
# @method decorator
#
#####
class _ObjCDecorator(object):
"""Decorators receive either their parameters or the function to decorate.
So we use this callable class as a trampoline to get both at the same time.
"""
def __init__(self, cls, meth):
"""Store the name of the method to create and the class to create it
into.
"""
self._cls = cls
self._method = meth
def __call__(self, function):
"""Create the class method and return the original function."""
self._cls.add_thunk(self._method, function)
return function
def method(cls, meth):
"""This decorator specifies that the function it applies to should be
callable from Objective-C code as the 'cls.method' class method.
This simplifies equivalent to Proxy.add_thunk() but simplifies the syntax.
Here is an example:
class MyClass(cxobj.Proxy):
pass
@cxobjc.method(MyClass, 'functionOne_')
def function_one(arg):
pass
@cxobjc.method(MyClass, 'functionTwo')
def function_two():
pass
# Then the following calls are valid:
# MyClass.functionOne_(my_arg)
# MyClass.functionTwo()
"""
return _ObjCDecorator(cls, meth)
#####
#
# @delegate decorator
#
#####
def delegate(func):
"""This decorator is a NOP.
Its sole purpose is to identify methods that are part of a delegate design
pattern, where the delegate may be implemented on the Objective-C side and
thus have to follow its naming conventions.
"""
return func