Module AccAdapt
[hide private]
[frames] | no frames]

Source Code for Module AccAdapt

  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  # adapter registries 
 27  _adapters = {} 
 28  _default_adapters = {} 
 29   
30 -class AdaptationError(RuntimeError):
31 '''Error raised when a suitable adapter could not be found.''' 32 pass
33
34 -def registerAdapters(adapters=[]):
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 # get the caller's locals 51 frame = sys._getframe(1) 52 adapters = frame.f_locals.values() 53 # iterate over all objects in the caller's namespace 54 for item in adapters: 55 try: 56 # get the item's base classes 57 is_adapter = issubclass(item, Adapter) 58 except: 59 # the item is not a class 60 continue 61 # if one of the base classes is Adapter 62 if is_adapter: 63 adapter = item 64 if adapter.when is None: 65 # no condition implies default 66 condition = None 67 elif callable(adapter.when): 68 # just store it for later invocation 69 condition = adapter.when 70 else: 71 # ignore unknown when statements 72 continue 73 if adapter.singleton: 74 # if marked as singleton, store one instance of adapter instead of class 75 adapter = adapter() 76 for interface in adapter.provides: 77 # register the caller under all of the interfaces it provides 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 # get all adapters to this interface 120 possible = _adapters[interface] 121 except KeyError: 122 pass 123 else: 124 # check if this subject satisfies any adapter condition 125 for condition, adapter in possible: 126 try: 127 if condition(subject): 128 #log.debug('using adapter %s for %s', adapter, subject) 129 return adapter(subject) 130 except Exception, e: 131 #log.debug('could not evaluate %s: %s', adapter.when, str(e)) 132 continue 133 try: 134 # try to use a default adapter 135 adapter = _default_adapters[interface] 136 #log.debug('using default adapter %s for %s', adapter, subject) 137 return adapter(subject) 138 except KeyError: 139 raise AdaptationError('Could not adapt %s to %s' % (subject, interface))
140
141 -class Interface(object):
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 #try: 170 #if interface in subject.provides: 171 #return subject 172 #except AttributeError: 173 #pass 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
185 -class Adapter(object):
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
212 - def __init__(self, subject=None):
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
221 - def __call__(self, subject):
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
233 -class PORAdapter(Adapter):
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 '''
245 - def __init__(self, subject=None):
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
262 - def __call__(self, subject):
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