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