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