Package pyxmpp :: Module cache
[hide private]

Source Code for Module pyxmpp.cache

  1  # 
  2  # (C) Copyright 2005-2010 Jacek Konieczny <jajcus@jajcus.net> 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU Lesser General Public License Version 
  6  # 2.1 as published by the Free Software Foundation. 
  7  # 
  8  # This program is distributed in the hope that it will be useful, 
  9  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 11  # GNU Lesser General Public License for more details. 
 12  # 
 13  # You should have received a copy of the GNU Lesser General Public 
 14  # License along with this program; if not, write to the Free Software 
 15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 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 714 2010-04-05 10:20:10Z 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  # locking order (anti-deadlock): 
 39  # CacheSuite, Cache, CacheHandler, CacheItem 
 40   
41 -class CacheItem(object):
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
105 - def update_state(self):
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
130 - def __cmp__(self,other):
131 try: 132 return cmp( 133 (-self.state_value, self.timestamp, id(self)), 134 (-other.state_value, other.timestamp, id(other)) 135 ) 136 except AttributeError: 137 return cmp(id(self),id(other))
138 139 _hour = timedelta(hours = 1) 140
141 -class CacheFetcher:
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
209 - def _deactivate(self):
210 """Remove the fetcher from cache and mark it not active.""" 211 self.cache.remove_fetcher(self) 212 if self.active: 213 self._deactivated()
214
215 - def _deactivated(self):
216 """Mark the fetcher inactive after it is removed from the cache.""" 217 self.active = False
218
219 - def fetch(self):
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
267 - def timeout(self):
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
285 - def _try_backup_item(self):
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
300 -class Cache:
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
460 - def invalidate_object(self, address, state = 'stale'):
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
477 - def add_item(self, item):
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
527 - def update_item(self, item):
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
553 - def num_items(self):
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
560 - def purge_items(self):
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
588 - def tick(self):
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
604 - def remove_fetcher(self, fetcher):
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
621 - def set_fetcher(self, fetcher_class):
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
635 -class CacheSuite:
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
766 - def tick(self):
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
778 - def register_fetcher(self, object_class, fetcher_class):
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
799 - def unregister_fetcher(self, object_class):
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 # vi: sts=4 et sw=4 817