1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18  """Caching proxy for Jabber/XMPP objects. 
 19   
 20  This package provides facilities to retrieve and transparently cache 
 21  cachable objects like Service Discovery responses or e.g. client version 
 22  informations.""" 
 23   
 24  __revision__ = "$Id: cache.py 647 2006-08-26 18:27:39Z jajcus $" 
 25  __docformat__ = "restructuredtext en" 
 26   
 27  import threading 
 28  from datetime import datetime, timedelta 
 29   
 30  _state_values = { 
 31          'new': 0, 
 32          'fresh': 1, 
 33          'old': 2, 
 34          'stale': 3, 
 35          'purged': 4 
 36      }; 
 37   
 38   
 39   
 40   
 42      """An item in a cache. 
 43   
 44      :Ivariables: 
 45          - `value`: item value (cached object). 
 46          - `address`: item address. 
 47          - `state`: current state. 
 48          - `state_value`: numerical value of the current state (lower number means 
 49            fresher item). 
 50          - `timestamp`: time when the object was created. 
 51          - `freshness_time`: time when the object stops being fresh. 
 52          - `expire_time`: time when the object expires. 
 53          - `purge_time`: time when the object should be purged. When 0 then 
 54            item will never be automaticaly purged. 
 55          - `_lock`: lock for thread safety. 
 56      :Types: 
 57          - `value`: `instance` 
 58          - `address`: any hashable 
 59          - `state`: `str` 
 60          - `state_value`: `int` 
 61          - `timestamp`: `datetime` 
 62          - `freshness_time`: `datetime` 
 63          - `expire_time`: `datetime` 
 64          - `purge_time`: `datetime` 
 65          - `_lock`: `threading.RLock`""" 
 66      __slots__ = ['value', 'address', 'state', 'timestamp', 'freshness_time', 
 67              'expire_time', 'purge_time', 'state_value', '_lock'] 
 68 -    def __init__(self, address, value, freshness_period, expiration_period, 
 69              purge_period, state = "new"): 
  70          """Initialize an CacheItem object. 
 71   
 72          :Parameters: 
 73              - `address`: item address. 
 74              - `value`: item value (cached object). 
 75              - `freshness_period`: time interval after which the object stops being fresh. 
 76              - `expiration_period`: time interval after which the object expires. 
 77              - `purge_period`: time interval after which the object should be purged. When 0 then 
 78                item will never be automaticaly purged. 
 79              - `state`: initial state. 
 80          :Types: 
 81              - `address`: any hashable 
 82              - `value`: `instance` 
 83              - `freshness_period`: `timedelta` 
 84              - `expiration_period`: `timedelta` 
 85              - `purge_period`: `timedelta` 
 86              - `state`: `str`""" 
 87          if freshness_period>expiration_period: 
 88              raise ValueError, "freshness_period greater then expiration_period" 
 89          if expiration_period>purge_period: 
 90              raise ValueError, "expiration_period greater then purge_period" 
 91          self.address = address 
 92          self.value = value 
 93          now = datetime.utcnow() 
 94          self.timestamp = now 
 95          self.freshness_time = now+freshness_period 
 96          self.expire_time = now+expiration_period 
 97          if purge_period: 
 98              self.purge_time = now+purge_period 
 99          else: 
100              self.purge_time = datetime.max 
101          self.state = state 
102          self.state_value = _state_values[state] 
103          self._lock = threading.RLock() 
 104   
106          """Update current status of the item and compute time of the next 
107          state change. 
108   
109          :return: the new state. 
110          :returntype: `datetime`""" 
111          self._lock.acquire() 
112          try: 
113              now = datetime.utcnow() 
114              if self.state == 'new': 
115                  self.state = 'fresh' 
116              if self.state == 'fresh': 
117                  if now > self.freshness_time: 
118                      self.state = 'old' 
119              if self.state == 'old': 
120                  if now > self.expire_time: 
121                      self.state = 'stale' 
122              if self.state == 'stale': 
123                  if now > self.purge_time: 
124                      self.state = 'purged' 
125              self.state_value = _state_values[self.state] 
126              return self.state 
127          finally: 
128              self._lock.release() 
 129   
 138   
139  _hour = timedelta(hours = 1) 
140   
142      """Base class for cache object fetchers -- classes responsible for 
143      retrieving objects from network. 
144   
145      An instance of a fetcher class is created for each object requested and 
146      not found in the cache, then `fetch` method is called to initialize 
147      the asynchronous retrieval process. Fetcher object's `got_it` method 
148      should be called on a successfull retrieval and `error` otherwise. 
149      `timeout` will be called when the request timeouts. 
150   
151      :Ivariables: 
152          - `cache`: cache object which created this fetcher. 
153          - `address`: requested item address. 
154          - `timeout_time`: timeout time. 
155          - `active`: `True` as long as the fetcher is active and requestor 
156            expects one of the handlers to be called. 
157      :Types: 
158          - `cache`: `Cache` 
159          - `address`: any hashable 
160          - `timeout_time`: `datetime` 
161          - `active`: `bool` 
162      """ 
163 -    def __init__(self, cache, address, 
164              item_freshness_period, item_expiration_period, item_purge_period, 
165              object_handler, error_handler, timeout_handler, timeout_period, 
166              backup_state = None): 
 167          """Initialize an `CacheFetcher` object. 
168   
169          :Parameters: 
170              - `cache`: cache object which created this fetcher. 
171              - `address`: requested item address. 
172              - `item_freshness_period`: freshness period for the requested item. 
173              - `item_expiration_period`: expiration period for the requested item. 
174              - `item_purge_period`: purge period for the requested item. 
175              - `object_handler`: function to be called after the item is fetched. 
176              - `error_handler`: function to be called on error. 
177              - `timeout_handler`: function to be called on timeout 
178              - `timeout_period`: timeout interval. 
179              - `backup_state`: when not `None` and the fetch fails than an 
180                object from cache of at least this state will be passed to the 
181                `object_handler`. If such object is not available, then 
182                `error_handler` is called. 
183          :Types: 
184              - `cache`: `Cache` 
185              - `address`: any hashable 
186              - `item_freshness_period`: `timedelta` 
187              - `item_expiration_period`: `timedelta` 
188              - `item_purge_period`: `timedelta` 
189              - `object_handler`: callable(address, value, state) 
190              - `error_handler`: callable(address, error_data) 
191              - `timeout_handler`: callable(address) 
192              - `timeout_period`: `timedelta` 
193              - `backup_state`: `bool`""" 
194          self.cache = cache 
195          self.address = address 
196          self._item_freshness_period = item_freshness_period 
197          self._item_expiration_period = item_expiration_period 
198          self._item_purge_period = item_purge_period 
199          self._object_handler = object_handler 
200          self._error_handler = error_handler 
201          self._timeout_handler = timeout_handler 
202          if timeout_period: 
203              self.timeout_time = datetime.utcnow()+timeout_period 
204          else: 
205              self.timeout_time = datetime.max 
206          self._backup_state = backup_state 
207          self.active = True 
 208   
214   
216          """Mark the fetcher inactive after it is removed from the cache.""" 
217          self.active = False 
 218   
220          """Start the retrieval process. 
221   
222          This method must be implemented in any fetcher class.""" 
223          raise RuntimeError, "Pure virtual method called" 
 224   
225 -    def got_it(self, value, state = "new"): 
 226          """Handle a successfull retrieval and call apriopriate handler. 
227   
228          Should be called when retrieval succeeds. 
229   
230          Do nothing when the fetcher is not active any more (after 
231          one of handlers was already called). 
232   
233          :Parameters: 
234              - `value`: fetched object. 
235              - `state`: initial state of the object. 
236          :Types: 
237              - `value`: any 
238              - `state`: `str`""" 
239          if not self.active: 
240              return 
241          item = CacheItem(self.address, value, self._item_freshness_period, 
242                  self._item_expiration_period, self._item_purge_period, state) 
243          self._object_handler(item.address, item.value, item.state) 
244          self.cache.add_item(item) 
245          self._deactivate() 
 246   
247 -    def error(self, error_data): 
 248          """Handle a retrieval error and call apriopriate handler. 
249   
250          Should be called when retrieval fails. 
251   
252          Do nothing when the fetcher is not active any more (after 
253          one of handlers was already called). 
254   
255          :Parameters: 
256              - `error_data`: additional information about the error (e.g. `StanzaError` instance). 
257          :Types: 
258              - `error_data`: fetcher dependant 
259          """ 
260          if not self.active: 
261              return 
262          if not self._try_backup_item(): 
263              self._error_handler(self.address, error_data) 
264          self.cache.invalidate_object(self.address) 
265          self._deactivate() 
 266   
268          """Handle fetcher timeout and call apriopriate handler. 
269   
270          Is called by the cache object and should _not_ be called by fetcher or 
271          application. 
272   
273          Do nothing when the fetcher is not active any more (after 
274          one of handlers was already called).""" 
275          if not self.active: 
276              return 
277          if not self._try_backup_item(): 
278              if self._timeout_handler: 
279                  self._timeout_handler(self.address) 
280              else: 
281                  self._error_handler(self.address, None) 
282          self.cache.invalidate_object(self.address) 
283          self._deactivate() 
 284   
286          """Check if a backup item is available in cache and call 
287          the item handler if it is. 
288   
289          :return: `True` if backup item was found. 
290          :returntype: `bool`""" 
291          if not self._backup_state: 
292              return False 
293          item = self.cache.get_item(self.address, self._backup_state) 
294          if item: 
295              self._object_handler(item.address, item.value, item.state) 
296              return True 
297          else: 
298              False 
  299   
301      """Caching proxy for object retrieval and caching. 
302   
303      Object factories ("fetchers") are registered in the `Cache` object and used 
304      to e.g. retrieve requested objects from network.  They are called only when 
305      the requested object is not in the cache or is not fresh enough. 
306   
307      A state (freshness level) name may be provided when requesting an object. 
308      When the cached item state is "less fresh" then requested, then new object 
309      will be retrieved. 
310   
311      Following states are defined: 
312   
313        - 'new': always a new object should be retrieved. 
314        - 'fresh': a fresh object (not older than freshness time) 
315        - 'old': object not fresh, but most probably still valid. 
316        - 'stale': object known to be expired. 
317   
318      :Ivariables: 
319          - `default_freshness_period`: default freshness period (in seconds). 
320          - `default_expiration_period`: default expiration period (in seconds). 
321          - `default_purge_period`: default purge period (in seconds). When 
322            0 then items are never purged because of their age. 
323          - `max_items`: maximum number of items to store. 
324          - `_items`: dictionary of stored items. 
325          - `_items_list`: list of stored items with the most suitable for 
326            purging first. 
327          - `_fetcher`: fetcher class for this cache. 
328          - `_active_fetchers`: list of active fetchers sorted by the time of 
329            its expiration time. 
330          - `_lock`: lock for thread safety. 
331      :Types: 
332          - `default_freshness_period`: timedelta 
333          - `default_expiration_period`: timedelta 
334          - `default_purge_period`: timedelta 
335          - `max_items`: `int` 
336          - `_items`: `dict` of (`classobj`, addr) -> `CacheItem` 
337          - `_items_list`: `list` of (`int`, `datetime`, `CacheItem`) 
338          - `_fetcher`: `CacheFetcher` based class 
339          - `_active_fetchers`: `list` of (`int`, `CacheFetcher`) 
340          - `_lock`: `threading.RLock` 
341      """ 
342 -    def __init__(self, max_items, default_freshness_period = _hour, 
343              default_expiration_period = 12*_hour, default_purge_period = 24*_hour): 
 344          """Initialize a `Cache` object. 
345   
346              :Parameters: 
347                  - `default_freshness_period`: default freshness period (in seconds). 
348                  - `default_expiration_period`: default expiration period (in seconds). 
349                  - `default_purge_period`: default purge period (in seconds). When 
350                    0 then items are never purged because of their age. 
351                  - `max_items`: maximum number of items to store. 
352              :Types: 
353                  - `default_freshness_period`: number 
354                  - `default_expiration_period`: number 
355                  - `default_purge_period`: number 
356                  - `max_items`: number 
357          """ 
358          self.default_freshness_period = default_freshness_period 
359          self.default_expiration_period = default_expiration_period 
360          self.default_purge_period = default_purge_period 
361          self.max_items = max_items 
362          self._items = {} 
363          self._items_list = [] 
364          self._fetcher = None 
365          self._active_fetchers = [] 
366          self._purged = 0 
367          self._lock = threading.RLock() 
 368   
369 -    def request_object(self, address, state, object_handler, 
370              error_handler = None, timeout_handler = None, 
371              backup_state = None, timeout = timedelta(minutes=60), 
372              freshness_period = None, expiration_period = None, 
373              purge_period = None): 
 374          """Request an object with given address and state not worse than 
375          `state`. The object will be taken from cache if available, and 
376          created/fetched otherwise. The request is asynchronous -- this metod 
377          doesn't return the object directly, but the `object_handler` is called 
378          as soon as the object is available (this may be before `request_object` 
379          returns and may happen in other thread). On error the `error_handler` 
380          will be called, and on timeout -- the `timeout_handler`. 
381   
382          :Parameters: 
383              - `address`: address of the object requested. 
384              - `state`: the worst acceptable object state. When 'new' then always 
385                a new object will be created/fetched. 'stale' will select any 
386                item available in cache. 
387              - `object_handler`: function to be called when object is available. 
388                It will be called with the following arguments: address, object 
389                and its state. 
390              - `error_handler`: function to be called on object retrieval error. 
391                It will be called with two arguments: requested address and 
392                additional error information (fetcher-specific, may be 
393                StanzaError for XMPP objects).  If not given, then the object 
394                handler will be called with object set to `None` and state 
395                "error". 
396              - `timeout_handler`: function to be called on object retrieval 
397                timeout.  It will be called with only one argument: the requested 
398                address. If not given, then the `error_handler` will be called 
399                instead, with error details set to `None`. 
400              - `backup_state`: when set and object in state `state` is not 
401                available in the cache and object retrieval failed then object 
402                with this state will also be looked-up in the cache and provided 
403                if available. 
404              - `timeout`: time interval after which retrieval of the object 
405                should be given up. 
406              - `freshness_period`: time interval after which the item created 
407                should become 'old'. 
408              - `expiration_period`: time interval after which the item created 
409                should become 'stale'. 
410              - `purge_period`: time interval after which the item created 
411                shuld be removed from the cache. 
412          :Types: 
413              - `address`: any hashable 
414              - `state`: "new", "fresh", "old" or "stale" 
415              - `object_handler`: callable(address, value, state) 
416              - `error_handler`: callable(address, error_data) 
417              - `timeout_handler`: callable(address) 
418              - `backup_state`: "new", "fresh", "old" or "stale" 
419              - `timeout`: `timedelta` 
420              - `freshness_period`: `timedelta` 
421              - `expiration_period`: `timedelta` 
422              - `purge_period`: `timedelta` 
423          """ 
424          self._lock.acquire() 
425          try: 
426              if state == 'stale': 
427                  state = 'purged' 
428              item = self.get_item(address, state) 
429              if item: 
430                  object_handler(item.address, item.value, item.state) 
431                  return 
432              if not self._fetcher: 
433                  raise TypeError, "No cache fetcher defined" 
434              if not error_handler: 
435                  def default_error_handler(address, _unused): 
436                      "Default error handler." 
437                      return object_handler(address, None, 'error') 
 438                  error_handler = default_error_handler 
439              if not timeout_handler: 
440                  def default_timeout_handler(address): 
441                      "Default timeout handler." 
442                      return error_handler(address, None) 
 443                  timeout_handler = default_timeout_handler 
444              if freshness_period is None: 
445                  freshness_period = self.default_freshness_period 
446              if expiration_period is None: 
447                  expiration_period = self.default_expiration_period 
448              if purge_period is None: 
449                  purge_period = self.default_purge_period 
450   
451              fetcher = self._fetcher(self, address, freshness_period, 
452                      expiration_period, purge_period, object_handler, error_handler, 
453                      timeout_handler, timeout, backup_state) 
454              fetcher.fetch() 
455              self._active_fetchers.append((fetcher.timeout_time,fetcher)) 
456              self._active_fetchers.sort() 
457          finally: 
458              self._lock.release() 
459   
461          """Force cache item state change (to 'worse' state only). 
462   
463          :Parameters: 
464              - `state`: the new state requested. 
465          :Types: 
466              - `state`: `str`""" 
467          self._lock.acquire() 
468          try: 
469              item = self.get_item(address) 
470              if item and item.state_value<_state_values[state]: 
471                  item.state=state 
472                  item.update_state() 
473                  self._items_list.sort() 
474          finally: 
475              self._lock.release() 
 476   
478          """Add an item to the cache. 
479   
480          Item state is updated before adding it (it will not be 'new' any more). 
481   
482          :Parameters: 
483              - `item`: the item to add. 
484          :Types: 
485              - `item`: `CacheItem` 
486   
487          :return: state of the item after addition. 
488          :returntype: `str` 
489          """ 
490          self._lock.acquire() 
491          try: 
492              state = item.update_state() 
493              if state != 'purged': 
494                  if len(self._items_list) >= self.max_items: 
495                      self.purge_items() 
496                  self._items[item.address] = item 
497                  self._items_list.append(item) 
498                  self._items_list.sort() 
499              return item.state 
500          finally: 
501              self._lock.release() 
 502   
503 -    def get_item(self, address, state = 'fresh'): 
 504          """Get an item from the cache. 
505   
506          :Parameters: 
507              - `address`: its address. 
508              - `state`: the worst state that is acceptable. 
509          :Types: 
510              - `address`: any hashable 
511              - `state`: `str` 
512   
513          :return: the item or `None` if it was not found. 
514          :returntype: `CacheItem`""" 
515          self._lock.acquire() 
516          try: 
517              item = self._items.get(address) 
518              if not item: 
519                  return None 
520              self.update_item(item) 
521              if _state_values[state] >= item.state_value: 
522                  return item 
523              return None 
524          finally: 
525              self._lock.release() 
 526   
528          """Update state of an item in the cache. 
529   
530          Update item's state and remove the item from the cache 
531          if its new state is 'purged' 
532   
533          :Parameters: 
534              - `item`: item to update. 
535          :Types: 
536              - `item`: `CacheItem` 
537   
538          :return: new state of the item. 
539          :returntype: `str`""" 
540   
541          self._lock.acquire() 
542          try: 
543              state = item.update_state() 
544              self._items_list.sort() 
545              if item.state == 'purged': 
546                  self._purged += 1 
547                  if self._purged > 0.25*self.max_items: 
548                      self.purge_items() 
549              return state 
550          finally: 
551              self._lock.release() 
 552   
554          """Get the number of items in the cache. 
555   
556          :return: number of items. 
557          :returntype: `int`""" 
558          return len(self._items_list) 
 559   
561          """Remove purged and overlimit items from the cache. 
562   
563          TODO: optimize somehow. 
564   
565          Leave no more than 75% of `self.max_items` items in the cache.""" 
566          self._lock.acquire() 
567          try: 
568              il=self._items_list 
569              num_items = len(il) 
570              need_remove = num_items - int(0.75 * self.max_items) 
571   
572              for _unused in range(need_remove): 
573                  item=il.pop(0) 
574                  try: 
575                      del self._items[item.address] 
576                  except KeyError: 
577                      pass 
578   
579              while il and il[0].update_state()=="purged": 
580                  item=il.pop(0) 
581                  try: 
582                      del self._items[item.address] 
583                  except KeyError: 
584                      pass 
585          finally: 
586              self._lock.release() 
 587   
589          """Do the regular cache maintenance. 
590   
591          Must be called from time to time for timeouts and cache old items 
592          purging to work.""" 
593          self._lock.acquire() 
594          try: 
595              now = datetime.utcnow() 
596              for t,f in list(self._active_fetchers): 
597                  if t > now: 
598                      break 
599                  f.timeout() 
600              self.purge_items() 
601          finally: 
602              self._lock.release() 
 603   
605          """Remove a running fetcher from the list of active fetchers. 
606   
607          :Parameters: 
608              - `fetcher`: fetcher instance. 
609          :Types: 
610              - `fetcher`: `CacheFetcher`""" 
611          self._lock.acquire() 
612          try: 
613              for t, f in list(self._active_fetchers): 
614                  if f is fetcher: 
615                      self._active_fetchers.remove((t, f)) 
616                      f._deactivated() 
617                      return 
618          finally: 
619              self._lock.release() 
 620   
622          """Set the fetcher class. 
623   
624          :Parameters: 
625              - `fetcher_class`: the fetcher class. 
626          :Types: 
627              - `fetcher_class`: `CacheFetcher` based class 
628          """ 
629          self._lock.acquire() 
630          try: 
631              self._fetcher = fetcher_class 
632          finally: 
633              self._lock.release() 
 634   
636      """Caching proxy for object retrieval and caching. 
637   
638      Object factories for other classes are registered in the 
639      `Cache` object and used to e.g. retrieve requested objects from network. 
640      They are called only when the requested object is not in the cache 
641      or is not fresh enough. 
642   
643      Objects are addressed using their class and a class dependant address. 
644      Eg. `pyxmpp.jabber.disco.DiscoInfo` objects are addressed using 
645      (`pyxmpp.jabber.disco.DiscoInfo`,(jid, node)) tuple. 
646   
647      Additionaly a state (freshness level) name may be provided when requesting 
648      an object. When the cached item state is "less fresh" then requested, then 
649      new object will be retrieved. 
650   
651      Following states are defined: 
652   
653        - 'new': always a new object should be retrieved. 
654        - 'fresh': a fresh object (not older than freshness time) 
655        - 'old': object not fresh, but most probably still valid. 
656        - 'stale': object known to be expired. 
657   
658      :Ivariables: 
659          - `default_freshness_period`: default freshness period (in seconds). 
660          - `default_expiration_period`: default expiration period (in seconds). 
661          - `default_purge_period`: default purge period (in seconds). When 
662            0 then items are never purged because of their age. 
663          - `max_items`: maximum number of obejects of one class to store. 
664          - `_caches`: dictionary of per-class caches. 
665          - `_lock`: lock for thread safety. 
666      :Types: 
667          - `default_freshness_period`: timedelta 
668          - `default_expiration_period`: timedelta 
669          - `default_purge_period`: timedelta 
670          - `max_items`: `int` 
671          - `_caches`: `dict` of (`classobj`, addr) -> `Cache` 
672          - `_lock`: `threading.RLock` 
673      """ 
674 -    def __init__(self, max_items, default_freshness_period = _hour, 
675              default_expiration_period = 12*_hour, default_purge_period = 24*_hour): 
 676          """Initialize a `Cache` object. 
677   
678              :Parameters: 
679                  - `default_freshness_period`: default freshness period (in seconds). 
680                  - `default_expiration_period`: default expiration period (in seconds). 
681                  - `default_purge_period`: default purge period (in seconds). When 
682                    0 then items are never purged because of their age. 
683                  - `max_items`: maximum number of items to store. 
684              :Types: 
685                  - `default_freshness_period`: number 
686                  - `default_expiration_period`: number 
687                  - `default_purge_period`: number 
688                  - `max_items`: number 
689          """ 
690          self.default_freshness_period = default_freshness_period 
691          self.default_expiration_period = default_expiration_period 
692          self.default_purge_period = default_purge_period 
693          self.max_items = max_items 
694          self._caches = {} 
695          self._lock = threading.RLock() 
 696   
697 -    def request_object(self, object_class, address, state, object_handler, 
698              error_handler = None, timeout_handler = None, 
699              backup_state = None, timeout = None, 
700              freshness_period = None, expiration_period = None, purge_period = None): 
 701          """Request an object of given class, with given address and state not 
702          worse than `state`. The object will be taken from cache if available, 
703          and created/fetched otherwise. The request is asynchronous -- this 
704          metod doesn't return the object directly, but the `object_handler` is 
705          called as soon as the object is available (this may be before 
706          `request_object` returns and may happen in other thread). On error the 
707          `error_handler` will be called, and on timeout -- the 
708          `timeout_handler`. 
709   
710          :Parameters: 
711              - `object_class`: class (type) of the object requested. 
712              - `address`: address of the object requested. 
713              - `state`: the worst acceptable object state. When 'new' then always 
714                a new object will be created/fetched. 'stale' will select any 
715                item available in cache. 
716              - `object_handler`: function to be called when object is available. 
717                It will be called with the following arguments: address, object 
718                and its state. 
719              - `error_handler`: function to be called on object retrieval error. 
720                It will be called with two arguments: requested address and 
721                additional error information (fetcher-specific, may be 
722                StanzaError for XMPP objects).  If not given, then the object 
723                handler will be called with object set to `None` and state 
724                "error". 
725              - `timeout_handler`: function to be called on object retrieval 
726                timeout.  It will be called with only one argument: the requested 
727                address. If not given, then the `error_handler` will be called 
728                instead, with error details set to `None`. 
729              - `backup_state`: when set and object in state `state` is not 
730                available in the cache and object retrieval failed then object 
731                with this state will also be looked-up in the cache and provided 
732                if available. 
733              - `timeout`: time interval after which retrieval of the object 
734                should be given up. 
735              - `freshness_period`: time interval after which the item created 
736                should become 'old'. 
737              - `expiration_period`: time interval after which the item created 
738                should become 'stale'. 
739              - `purge_period`: time interval after which the item created 
740                shuld be removed from the cache. 
741          :Types: 
742              - `object_class`: `classobj` 
743              - `address`: any hashable 
744              - `state`: "new", "fresh", "old" or "stale" 
745              - `object_handler`: callable(address, value, state) 
746              - `error_handler`: callable(address, error_data) 
747              - `timeout_handler`: callable(address) 
748              - `backup_state`: "new", "fresh", "old" or "stale" 
749              - `timeout`: `timedelta` 
750              - `freshness_period`: `timedelta` 
751              - `expiration_period`: `timedelta` 
752              - `purge_period`: `timedelta` 
753          """ 
754   
755          self._lock.acquire() 
756          try: 
757              if object_class not in self._caches: 
758                  raise TypeError, "No cache for %r" % (object_class,) 
759   
760              self._caches[object_class].request_object(address, state, object_handler, 
761                      error_handler, timeout_handler, backup_state, timeout, 
762                      freshness_period, expiration_period, purge_period) 
763          finally: 
764              self._lock.release() 
 765   
767          """Do the regular cache maintenance. 
768   
769          Must be called from time to time for timeouts and cache old items 
770          purging to work.""" 
771          self._lock.acquire() 
772          try: 
773              for cache in self._caches.values(): 
774                  cache.tick() 
775          finally: 
776              self._lock.release() 
 777   
779          """Register a fetcher class for an object class. 
780   
781          :Parameters: 
782              - `object_class`: class to be retrieved by the fetcher. 
783              - `fetcher_class`: the fetcher class. 
784          :Types: 
785              - `object_class`: `classobj` 
786              - `fetcher_class`: `CacheFetcher` based class 
787          """ 
788          self._lock.acquire() 
789          try: 
790              cache = self._caches.get(object_class) 
791              if not cache: 
792                  cache = Cache(self.max_items, self.default_freshness_period, 
793                          self.default_expiration_period, self.default_purge_period) 
794                  self._caches[object_class] = cache 
795              cache.set_fetcher(fetcher_class) 
796          finally: 
797              self._lock.release() 
 798   
800          """Unregister a fetcher class for an object class. 
801   
802          :Parameters: 
803              - `object_class`: class retrieved by the fetcher. 
804          :Types: 
805              - `object_class`: `classobj` 
806          """ 
807          self._lock.acquire() 
808          try: 
809              cache = self._caches.get(object_class) 
810              if not cache: 
811                  return 
812              cache.set_fetcher(None) 
813          finally: 
814              self._lock.release() 
  815   
816   
817