1"""
2Define a category on NSObject with some useful methods.
3
4FIXME:
5- add signature information (namedSelector) to all methods
6  (not strictly needed)
7- add docstrings everywhere
8- create unittests
9"""
10import objc
11import sys
12
13NSObject = objc.lookUpClass('NSObject')
14
15class NSObject (objc.Category(NSObject)):
16
17    @objc.namedSelector(b"_pyobjc_performOnThread:")
18    def _pyobjc_performOnThread_(self, callinfo):
19        try:
20            sel, arg = callinfo
21            m = self.methodForSelector_(sel)
22            m(arg)
23        except:
24            import traceback
25            traceback.print_exc(fp=sys.stderr)
26
27    @objc.namedSelector(b"_pyobjc_performOnThreadWithResult:")
28    def _pyobjc_performOnThreadWithResult_(self, callinfo):
29        try:
30            sel, arg, result = callinfo
31            m = self.methodForSelector_(sel)
32            r = m(arg)
33            result.append((True, r))
34        except:
35            result.append((False, sys.exc_info()))
36
37
38    if hasattr(NSObject, "performSelector_onThread_withObject_waitUntilDone_"):
39        @objc.namedSelector(b"pyobjc_performSelector:onThread:withObject:waitUntilDone:")
40        def pyobjc_performSelector_onThread_withObject_waitUntilDone_(
41                self, aSelector, thread, arg, wait):
42            """
43            A version of performSelector:onThread:withObject:waitUntilDone: that
44            will log exceptions in the called method (instead of aborting the
45            NSRunLoop on the other thread).
46            """
47            self.performSelector_onThread_withObject_waitUntilDone_(
48                    'pyobjc_performOnThread:', thread, (aSelector, arg), wait)
49
50        @objc.namedSelector(b"pyobjc_performSelector:onThread:withObject:waitUntilDone:modes:")
51        def pyobjc_performSelector_onThread_withObject_waitUntilDone_modes_(
52                self, aSelector, thread, arg, wait, modes):
53            """
54            A version of performSelector:onThread:withObject:waitUntilDone:modes:
55            that will log exceptions in the called method (instead of aborting the
56            NSRunLoop on the other thread).
57            """
58            self.performSelector_onThread_withObject_waitUntilDone_modes_(
59                'pyobjc_performOnThread:', thread, (aSelector, arg), wait, modes)
60
61    @objc.namedSelector(b"pyobjc_performSelector:withObject:afterDelay:")
62    def pyobjc_performSelector_withObject_afterDelay_(
63            self, aSelector, arg, delay):
64        """
65        A version of performSelector:withObject:afterDelay:
66        that will log exceptions in the called method (instead of aborting the
67        NSRunLoop).
68        """
69        self.performSelector_withObject_afterDelay_(
70            'pyobjc_performOnThread:', (aSelector, arg), delay)
71
72    @objc.namedSelector(b"pyobjc_performSelector:withObject:afterDelay:inModes:")
73    def pyobjc_performSelector_withObject_afterDelay_inModes_(
74            self, aSelector, arg, delay, modes):
75        """
76        A version of performSelector:withObject:afterDelay:inModes:
77        that will log exceptions in the called method (instead of aborting the
78        NSRunLoop).
79        """
80        self.performSelector_withObject_afterDelay_inModes_(
81            'pyobjc_performOnThread:', (aSelector, arg), delay, modes)
82
83    if hasattr(NSObject, "performSelectorInBackground_withObject_waitUntilDone_"):
84        @objc.namedSelector(b"pyobjc_performSelectorInBackground:withObject:")
85        def pyobjc_performSelectorInBackground_withObject_(
86                self, aSelector, arg):
87            """
88            A version of performSelectorInBackground:withObject:
89            that will log exceptions in the called method (instead of aborting the
90            NSRunLoop).
91            """
92            self.performSelectorInBackground_withObject_(
93                'pyobjc_performOnThread:', (aSelector, arg))
94
95
96    @objc.namedSelector(b"pyobjc_performSelectorInBackground:withObject:waitUntilDone:")
97    def pyobjc_performSelectorOnMainThread_withObject_waitUntilDone_(
98            self, aSelector, arg, wait):
99        """
100        A version of performSelectorOnMainThread:withObject:waitUntilDone:
101        that will log exceptions in the called method (instead of aborting the
102        NSRunLoop in the main thread).
103        """
104        self.performSelectorOnMainThread_withObject_waitUntilDone_(
105            'pyobjc_performOnThread:', (aSelector, arg), wait)
106
107    @objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:waitUntilDone:modes:")
108    def pyobjc_performSelectorOnMainThread_withObject_waitUntilDone_modes_(
109            self, aSelector, arg, wait, modes):
110        """
111        A version of performSelectorOnMainThread:withObject:waitUntilDone:modes:
112        that will log exceptions in the called method (instead of aborting the
113        NSRunLoop in the main thread).
114        """
115        self.performSelectorOnMainThread_withObject_waitUntilDone_modes_(
116            'pyobjc_performOnThread:', (aSelector, arg), wait, modes)
117
118
119    # And some a some versions that return results
120
121    @objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:modes:")
122    def pyobjc_performSelectorOnMainThread_withObject_modes_(
123            self, aSelector, arg, modes):
124        """
125        Simular to performSelectorOnMainThread:withObject:waitUntilDone:modes:,
126        but:
127
128        - always waits until done
129        - returns the return value of the called method
130        - if the called method raises an exception, this will raise the same
131           exception
132        """
133        result = []
134        self.performSelectorOnMainThread_withObject_waitUntilDone_modes_(
135            'pyobjc_performOnThreadWithResult:',
136            (aSelector, arg, result), True, modes)
137        isOK, result = result[0]
138
139        if isOK:
140            return result
141        else:
142            exc_type, exc_value, exc_trace = result
143            raise exc_type, exc_value, exc_trace
144
145    @objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:")
146    def pyobjc_performSelectorOnMainThread_withObject_(
147            self, aSelector, arg):
148        result = []
149        self.performSelectorOnMainThread_withObject_waitUntilDone_(
150            'pyobjc_performOnThreadWithResult:',
151            (aSelector, arg, result), True)
152        isOK, result = result[0]
153
154        if isOK:
155            return result
156        else:
157            exc_type, exc_value, exc_trace = result
158            raise exc_type, exc_value, exc_trace
159
160    if hasattr(NSObject, "performSelector_onThread_withObject_waitUntilDone_"):
161        # These methods require Leopard, don't define them if the
162        # platform functionality isn't present.
163
164        @objc.namedSelector(b"pyobjc_performSelector:onThread:withObject:modes:")
165        def pyobjc_performSelector_onThread_withObject_modes_(
166                self, aSelector, thread, arg, modes):
167            result = []
168            self.performSelector_onThread_withObject_waitUntilDone_modes_(
169                'pyobjc_performOnThreadWithResult:', thread,
170                (aSelector, arg, result), True, modes)
171            isOK, result = result[0]
172
173            if isOK:
174                return result
175            else:
176                exc_type, exc_value, exc_trace = result
177                raise exc_type, exc_value, exc_trace
178
179        @objc.namedSelector(b"pyobjc_performSelector:onThread:withObject:")
180        def pyobjc_performSelector_onThread_withObject_(
181                self, aSelector, thread, arg):
182            result = []
183            self.performSelector_onThread_withObject_waitUntilDone_(
184                'pyobjc_performOnThreadWithResult:', thread,
185                (aSelector, arg, result), True)
186            isOK, result = result[0]
187
188            if isOK:
189                return result
190            else:
191                exc_type, exc_value, exc_trace = result
192                raise exc_type, exc_value, exc_trace
193
194
195del NSObject
196