1__all__ = ['classAddMethod', 'Category']
2
3from objc._objc import selector, classAddMethods, objc_class, ivar
4
5from types import FunctionType, MethodType
6
7def classAddMethod(cls, name, method):
8    """
9    Add a single method to a class. 'name' is the ObjC selector
10    """
11    if isinstance(method, selector):
12        sel = selector(method.callable,
13                    selector=name,
14                    signature=method.signature,
15                    isClassMethod=method.isClassMethod)
16    else:
17        sel = selector(method, selector=name)
18
19    return classAddMethods(cls, [sel])
20
21
22#
23# Syntactic support for categories
24#
25
26class _CategoryMeta(type):
27    """
28    Meta class for categories.
29    """
30    __slots__ = ()
31    _IGNORENAMES = ('__module__', '__name__', '__doc__')
32    def _newSubclass(cls, name, bases, methods):
33        return type.__new__(cls, name, bases, methods)
34    _newSubclass = classmethod(_newSubclass)
35
36    def __new__(cls, name, bases, methods):
37        if len(bases) != 1:
38            raise TypeError("Cannot have multiple inheritance with Categories")
39
40        c = bases[0].real_class
41
42        if c.__name__ != name:
43            raise TypeError("Category name must be same as class name")
44
45
46        m = [ x[1] for x in methods.iteritems() if x[0] not in cls._IGNORENAMES  and isinstance(x[1], (FunctionType, MethodType, selector, classmethod))]
47        vars = [ x for x in methods.iteritems() if x[0] not in cls._IGNORENAMES  and not isinstance(x[1], (FunctionType, MethodType, selector, classmethod))]
48        for k, v in vars:
49            if isinstance(v, ivar):
50                raise TypeError("Cannot add instance variables in a Category")
51
52        classAddMethods(c, m)
53        for k, v in vars:
54            setattr(c, k, v)
55        return c
56
57def Category(cls):
58    """
59    Create a category on ``cls``.
60
61    Usage:
62        class SomeClass (Category(SomeClass)):
63            def method(self):
64                pass
65
66    ``SomeClass`` is an existing class that will be rebound to the same
67    value. The side-effect of this class definition is that the methods
68    in the class definition will be added to the existing class.
69    """
70    if not isinstance(cls, objc_class):
71        raise TypeError, "Category can only be used on Objective-C classes"
72    retval = _CategoryMeta._newSubclass('Category', (), dict(real_class=cls))
73    return retval
74