1 '''
2 Defines machinery for stating interfaces and adapting objects to those
3 interfaces on demand.
4
5 @var _adapters: Lists of adapters and conditions under which they should be
6 applied keyed by the interface provided
7 @type _adapters: dictionary
8 @var _default_adapters: Adapters to be used when no better adapter is available
9 for a given subject or interface keyed by the interface provided
10 @type _default_adapters: dictionary
11
12 @author: Peter Parente
13 @organization: IBM Corporation
14 @copyright: Copyright (c) 2005, 2007 IBM Corporation
15
16 @author: Frank Zenker
17 @organization: IT Science Center Ruegen gGmbH, Germany
18 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
19
20 @license: I{The BSD License}
21 All rights reserved. This program and the accompanying materials are made
22 available under the terms of the BSD license which accompanies
23 this distribution, and is available at
24 U{http://www.opensource.org/licenses/bsd-license.php}
25 '''
26 import sys, logging, weakref
27
28 log = logging.getLogger('Adapt')
29
30
31 _adapters = {}
32 _default_adapters = {}
33
35 '''Error raised when a suitable adapter could not be found.'''
36 pass
37
39 '''
40 Registers all L{AEAccAdapter}s in the caller's local namespace.
41
42 An adapter must be a class deriving from L{AEAccAdapter} and should have a
43 class variable named I{provides} containing a list of L{Interface}s that the
44 adapter implements. An adapter can also expose a I{when} attribute containing
45 a condition under which the adapter can be used. If I{when} is not given, the
46 adapter is used as the default for the given interface. Only one default may
47 be registered per interface.
48
49 @note: This function uses a small hack to get the caller's locals. This hack
50 can be removed in favor of the caller passing in its locals dictionary
51 explicitly, but that places more responsibility on the caller who may
52 already forget to call this function to begin with.
53 '''
54 if not len(adapters):
55
56 frame = sys._getframe(1)
57 adapters = frame.f_locals.values()
58
59 for item in adapters:
60 try:
61
62 is_adapter = issubclass(item, AEAccAdapter)
63 except:
64
65 continue
66
67 if is_adapter:
68 adapter = item
69 if adapter.when is None:
70
71 condition = None
72 elif callable(adapter.when):
73
74 condition = adapter.when
75 else:
76
77 continue
78 if adapter.singleton:
79
80 adapter = adapter()
81 for interface in adapter.provides:
82
83 _add(adapter, interface, condition)
84
85 -def _add(adapter, interface, condition=None):
86 '''
87 Adds an adapter to the registry.
88
89 If condition is specified, the adapter is
90 added to the L{_adapters} dictionary. If no condition is given, the adapter
91 is set as the default for the given interface in the L{_default_adapters}
92 dictionary.
93
94 @param adapter: Instance to be registered as an L{AEAccAdapter}
95 @type adapter: L{AEAccAdapter}
96 @param interface: L{Interface} provided by the L{AEAccAdapter}
97 @type interface: L{Interface}
98 @param condition: Function expressing a condition under which the
99 adapter can be applied to a subject. The subject is available in the
100 variable I{subject} in the condition. A value of None implies the
101 L{AEAccAdapter} is a default to be used when no other L{AEAccAdapter} is
102 available for this interface.
103 @type condition: string
104 '''
105 if condition is None:
106 _default_adapters[interface] = adapter
107 else:
108 possible = _adapters.setdefault(interface, [])
109 possible.append((condition, adapter))
110
111 -def _get(interface, subject):
112 '''
113 Gets an appropriate adapter to the desired interface for the given subject.
114
115 First tries to find an L{AEAccAdapter} to the L{Interface} by satisfying one
116 of the conditions of the registered L{AEAccAdapter}s. If that fails, trys to
117 use a default L{AEAccAdapter} to the L{Interface}. If no default is
118 registered, raises an exception.
119
120 @param interface: L{Interface} to which the subject should be adapted
121 @type interface: L{Interface}
122 @param subject: Object to adapt to the interface
123 @type subject: object
124 @raise AdaptationError: When no suitable adapter is available for the subject
125 '''
126 try:
127
128 possible = _adapters[interface]
129 except KeyError:
130 pass
131 else:
132
133 for condition, adapter in possible:
134 try:
135 if condition(subject):
136
137 return adapter(subject)
138 except Exception, e:
139
140 continue
141 try:
142
143 adapter = _default_adapters[interface]
144
145 return adapter(subject)
146 except KeyError:
147 raise AdaptationError('Could not adapt %s to %s' % (subject, interface))
148
150 '''
151 Base class for all interfaces.
152
153 Acts as a factory for instantiating the proper
154 registered adapter for the given L{Interface} subclass and subject. For
155 example, assume INavigable is a subclass of this class. The code:
156
157 C{adapted_object = INavigable(some_object)}
158
159 will return an object adapting some_object to the INavigable interface if such
160 and adapter exists. If some_object has an attribute called providesInterfaces
161 which contains INavigable, the original object will be returned. If no
162 suitable adapter exists, an exception is raised.
163
164 The L{_get} function does most of the work in retrieving an appropriate
165 adapter.
166 '''
167 - def __new__(interface, subject=None):
168 '''
169 Creates an adapter instance providing the given interface for the given
170 subject.
171
172 @param interface: Desired interface for the subject
173 @type interface: L{Interface}
174 @param subject: Object to adapt to the interface
175 @type subject: object
176 @raise AdaptationError: When no suitable adapter exists to provide the
177 desired interface for the given subject
178 '''
179
180
181
182
183
184 try:
185 return subject.getAdapter(interface)(subject)
186 except (AttributeError, KeyError):
187 pass
188 adapter = _get(interface, subject)
189 try:
190 subject.cacheAdapter(interface, adapter)
191 except AttributeError:
192 pass
193 return adapter
194
196 '''
197 Base class for all adapter classes.
198
199 Has default class attributes indicating
200 the adapter provides no interfaces by default. Has a default constructor that
201 takes and stores the subject being adapted.
202
203 @cvar provides: L{Interface}s provided by this adapter
204 @type provides: list of L{Interface}
205 @cvar when: Condition under which this adapter is applicable to the given
206 subject.
207
208 The condition can either be a staticmethod callable that results
209 in a boolean value or a string that will be evaluated as a boolean Python
210 expression in the namespace in which it is defined. This implies any
211 variables in the global namespace of the L{AEAccAdapter} subclass can be used
212 in the when clause. String conditions are only evaluated once at startup
213 rather than on each call. If the condition is None, the L{AEAccAdapter} is
214 considered a default.
215 @type when: string or callable
216 @cvar singleton: If True, only one instance of this adapter will be registered
217 for use such that it is reused for all subjects.
218
219 If False, this class will be registered for use such that it will act as a
220 factory for producing adapter instances for all subjects.
221 @type singleton: boolean
222
223 @ivar subject:Object being adapted
224 @type subject: object
225 '''
226 provides = []
227 when = None
228 singleton = False
229
231 '''
232 Stores the subject of the adapter.
233
234 @param subject: Object being adapted
235 @type subject: object
236 '''
237 self.subject = subject
238
240 '''
241 Stores the subject of the adapter.
242
243 @param subject: Object being adapted
244 @type subject: object
245 @return: Reference to this instance
246 @rtype: object
247 '''
248 self.subject = subject
249 return self
250
252 '''
253 Convenience base class for L{AEAccAdapter}s for L{AEPor <AEPor.AEPor>}s.
254
255 Provides direct access
256 to L{AEPor <AEPor.AEPor>} data through instance variables.
257
258 @ivar subject: Point of regard adapted by this object
259 @type subject: L{AEPor <AEPor.AEPor>}
260 @ivar accessible: Reference to the accessible in the L{AEPor <AEPor.AEPor>}
261 @type accessible: C{pyatspi.Accessible}
262 @ivar item_offset: Reference to the item offset in the L{AEPor <AEPor.AEPor>}
263 @type item_offset: integer
264 @ivar char_offset: Reference to the character offset in the L{AEPor <AEPor.AEPor>}
265 @type char_offset: integer
266 '''
268 '''
269 Override storing of subject L{AEPor <AEPor.AEPor>} in adapter to also store
270 its accessible and item offset in instance variables for convenince.
271
272 @param subject: Point of regard adapted by this object
273 @type subject: L{AEPor <AEPor.AEPor>}
274 '''
275 try:
276 self.subject = weakref.proxy(subject)
277 except TypeError:
278 self.subject = subject
279 if self.subject is not None:
280 self.accessible = subject.accessible
281 self.item_offset = subject.item_offset
282 self.char_offset = subject.char_offset
283
285 '''
286 Override storing of subject L{AEPor <AEPor.AEPor>} in adapter to also store
287 its accessible and item offset in instance variables for convenince.
288
289 @param subject: Point of regard adapted by this object
290 @type subject: L{AEPor <AEPor.AEPor>}
291 '''
292 try:
293 self.subject = weakref.proxy(subject)
294 except TypeError:
295 self.subject = subject
296 self.accessible = subject.accessible
297 self.item_offset = subject.item_offset
298 self.char_offset = subject.char_offset
299 return self
300