Package AccessEngine :: Module AEAccAdapter
[hide private]
[frames] | no frames]

Source Code for Module AccessEngine.AEAccAdapter

  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  # adapter registries 
 31  _adapters = {} 
 32  _default_adapters = {} 
 33   
34 -class AdaptationError(RuntimeError):
35 '''Error raised when a suitable adapter could not be found.''' 36 pass
37
38 -def registerAdapters(adapters=[]):
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 # get the caller's locals 56 frame = sys._getframe(1) 57 adapters = frame.f_locals.values() 58 # iterate over all objects in the caller's namespace 59 for item in adapters: 60 try: 61 # get the item's base classes 62 is_adapter = issubclass(item, AEAccAdapter) 63 except: 64 # the item is not a class 65 continue 66 # if one of the base classes is AEAccAdapter 67 if is_adapter: 68 adapter = item 69 if adapter.when is None: 70 # no condition implies default 71 condition = None 72 elif callable(adapter.when): 73 # just store it for later invocation 74 condition = adapter.when 75 else: 76 # ignore unknown when statements 77 continue 78 if adapter.singleton: 79 # if marked as singleton, store one instance of adapter instead of class 80 adapter = adapter() 81 for interface in adapter.provides: 82 # register the caller under all of the interfaces it provides 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 # get all adapters to this interface 128 possible = _adapters[interface] 129 except KeyError: 130 pass 131 else: 132 # check if this subject satisfies any adapter condition 133 for condition, adapter in possible: 134 try: 135 if condition(subject): 136 #log.debug('using adapter %s for %s', adapter, subject) 137 return adapter(subject) 138 except Exception, e: 139 #log.debug('could not evaluate %s: %s', adapter.when, str(e)) 140 continue 141 try: 142 # try to use a default adapter 143 adapter = _default_adapters[interface] 144 #log.debug('using default adapter %s for %s', adapter, subject) 145 return adapter(subject) 146 except KeyError: 147 raise AdaptationError('Could not adapt %s to %s' % (subject, interface))
148
149 -class Interface(object):
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 #try: 180 #if interface in subject.provides: 181 #return subject 182 #except AttributeError: 183 #pass 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
195 -class AEAccAdapter(object):
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
230 - def __init__(self, subject=None):
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
239 - def __call__(self, subject):
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
251 -class PORAdapter(AEAccAdapter):
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 '''
267 - def __init__(self, subject=None):
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
284 - def __call__(self, subject):
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