Package wtf :: Package app :: Package services :: Module memcache
[hide private]
[frames] | no frames]

Source Code for Module wtf.app.services.memcache

   1  # -*- coding: ascii -*- 
   2  # 
   3  # Copyright 2007-2012 
   4  # Andr\xe9 Malo or his licensors, as applicable 
   5  # 
   6  # Licensed under the Apache License, Version 2.0 (the "License"); 
   7  # you may not use this file except in compliance with the License. 
   8  # You may obtain a copy of the License at 
   9  # 
  10  #     http://www.apache.org/licenses/LICENSE-2.0 
  11  # 
  12  # Unless required by applicable law or agreed to in writing, software 
  13  # distributed under the License is distributed on an "AS IS" BASIS, 
  14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  15  # See the License for the specific language governing permissions and 
  16  # limitations under the License. 
  17  """ 
  18  Memcache service 
  19  ================ 
  20   
  21  The service provides configuration and construction of memcache connectors. 
  22   
  23  Configuration 
  24  ~~~~~~~~~~~~~ 
  25   
  26  You need to configure the memcache service to be loaded 
  27  (``wtf.app.services.memcache.MemcacheService``) in the services section). This 
  28  requires the following additional configuration:: 
  29   
  30    [memcache] 
  31    servers = host[:port] ... 
  32   
  33    # pool failover/maintenance options 
  34    # --------------------------------- 
  35    #grace_time = [int] Grace time on dead pools until they're backuped 
  36    #             (Default: 30) 
  37    #retry_time = [int] Retry interval after they're backuped (Default: 60) 
  38   
  39    # storage options 
  40    #compress_threshold = [int] Min size for value compression (Default: 128) 
  41    #padded = [bool] Use padding for small values (< 16 bytes)? (Default: yes) 
  42    #prefix = [str] Prefix keys with some arbitrary string (intended for 
  43    #         developers using the same memcache) (Default: ``''``) 
  44    #split = [bool] Allow splitting of values if they are bigger than the 
  45    #        largest slab? See largest_slab option below. (Default: yes) 
  46    #largest_slab = [int] Size of the largest slab in bytes. The value is 
  47    #               directly connected to the memcache implementation. 
  48    #               (Default: 1MB) 
  49   
  50    # global defaults 
  51    #max_age = [int] expire time (max age) per item 
  52    #          (Default: no default max_age) 
  53   
  54    # default values *per server* 
  55    #maxconn = [int] hard connection maximum (Default: 0 == unlimited) 
  56    #maxcached = [int] max cached connections (Default: 0) 
  57    #weight = [int] relative weight, compared to the other servers. The higher 
  58    #         the more requests it gets. (Default: 1) 
  59    #timeout = [float] communication timeout in seconds. (Default: 2.6) 
  60   
  61    # You can refine various settings per server with optional sections: 
  62    #[memcache host[:port]] 
  63    # set specific settings per server here 
  64    # (maxconn, maxcached, weight, timeout) 
  65   
  66   
  67  Usage 
  68  ~~~~~ 
  69   
  70  Now you can import ``__svc__.wtf.memcache`` and take the ``connection`` 
  71  decorator from there. It will inject you an ``mc`` keyword into the argument 
  72  list:: 
  73   
  74    from __svc__.wtf import memcache 
  75   
  76    @memcache.connection 
  77    def foo(..., mc=None): 
  78        mc.set(...) 
  79   
  80  The decorator takes optional arguments, ``max_age``, ``nocache``, 
  81  ``prepare`` and ``exceptions``. ``max_age`` defines the default max age for 
  82  this connector (overriding the configured one). 
  83   
  84  ``nocache`` determines whether the ``nocache`` call argument should be 
  85  evaluated. The value can be ``False`` (``0``), ``True`` (``1``), ``2`` or 
  86  ``-1``. If it evaluates to ``False``, no special handling will be applied. 
  87  Otherwise the  function (keyword) argument ``nocache`` will be checked as 
  88  boolean. If the caller supplies a true value, the memcache connector will 
  89  behave like the memcache was not available. If the decorator ``nocache`` 
  90  argument is ``2`` (``> 1``), the ``nocache`` function call argument will be 
  91  passed through, otherwise it's been swallowed by the decorator. If it's 
  92  ``-1``, the nocache parameter is swallowed but not evaluated. Default is 
  93  ``False``. Now this all sounds confusing, I guess. Here's an example:: 
  94   
  95    @memcache.connection(nocache=True) 
  96    def foo(..., mc=None): 
  97      ... 
  98   
  99    foo(nocache=True) 
 100   
 101  The call to ``foo`` causes every memcache operation inside ``foo`` like 
 102  the memcache was not running, without any change on ``foo``. For a more 
 103  complex handling, you can define:: 
 104   
 105    @memcache.connection(nocache=2) 
 106    def foo(..., nocache=None, mc=None): 
 107      ... 
 108   
 109    foo(nocache=True) 
 110   
 111  This call to ``foo`` causes the same "oblivion" of the memcache connector, but 
 112  passes the nocache value to the ``foo`` function for further evaluation. 
 113   
 114  One further note: If the connector is passed from outside, like:: 
 115   
 116    @memcache.connection(nocache=True) 
 117    def foo(..., mc=None): 
 118      ... 
 119   
 120    @memcache.connection(nocache=False) 
 121    def bar(..., mc=None): 
 122      foo(..., mc=mc) 
 123   
 124  The "inside" settings (here: ``foo``'s decorator's parameters) are ignored. 
 125   
 126  ``prepare`` defines a key preparation callable (overriding the default one, 
 127  which MD5s the keys). This callables takes a key and returns a key (at least 
 128  the returned value must be a ``str``):: 
 129   
 130    # identify preparation. Note that a configured prefix is still applied to 
 131    # the result. 
 132    prepare = lambda x: x 
 133   
 134    @memcache.connection(prepare=prepare) 
 135    def foo(..., mc=None): 
 136        mc.set(...) 
 137   
 138  ``exceptions`` determines whether the memcache user wants to see memcache 
 139  exceptions or not. If ``True`` the exceptions are passed through. If 
 140  ``False``, they're swallowed and treated as failed memcache response. 
 141  """ 
 142  __author__ = u"Andr\xe9 Malo" 
 143  __docformat__ = "restructuredtext en" 
 144   
 145  import copy as _copy 
 146  try: 
 147      import hashlib as _md5 
 148  except ImportError: 
 149      import md5 as _md5 
 150  import time as _time 
 151   
 152  from wtf import services as _services 
 153  from wtf import util as _util 
 154  from wtf.ext import memcache as _memcache 
155 156 -def memcached(keygen, **kwargs):
157 """ Failsafe memcached decorator (against missing service) """ 158 try: 159 # pylint: disable = E0611 160 from __svc__.wtf import memcache 161 except ImportError: 162 kwargs['disabled'] = True 163 def inner(func): 164 """ Decorator """ 165 return TransparentCacheDecorator( 166 func, keygen, NoMemcacheWrapper(), 0, **kwargs 167 )
168 else: 169 def inner(func): 170 """ Decorator """ 171 return memcache.memcached(keygen, **kwargs)(func) 172 return inner 173
174 175 -def connection(*args, **kwargs):
176 """ Failsafe connection decorator (against missing service) """ 177 try: 178 # pylint: disable = E0611 179 from __svc__.wtf import memcache 180 except ImportError: 181 if len(args) == 1 and not kwargs and callable(args[0]): 182 return args[0] 183 def inner(func): 184 """ Decorator """ 185 return func
186 return inner 187 188 return memcache.connection(*args, **kwargs) 189
190 191 -class TransparentCacheDecorator(_util.BaseDecorator):
192 """ Decorator which transparently memoizes a function call """ 193
194 - def __new__(cls, func, keygen, mcc, max_age, nocache=False, 195 disabled=False, pass_=False, local=False, nolocal=False, 196 recache=False):
197 """ Construction """ 198 # pylint: disable = R0913 199 self = super(TransparentCacheDecorator, cls).__new__(cls) 200 extra = {} 201 if nocache == 1: 202 extra['nocache'] = False 203 if nolocal == 1: 204 extra['nolocal'] = False 205 if recache: 206 extra['recache'] = False 207 self.__init__(func, keygen, mcc, max_age, 208 nocache=nocache, 209 disabled=disabled, 210 pass_=pass_, 211 local=local, 212 nolocal=nolocal, 213 recache=recache, 214 ) 215 return _util.decorating(func, extra=extra or None)(self)
216
217 - def __init__(self, func, keygen, mcc, max_age, nocache=False, 218 disabled=False, pass_=False, local=False, nolocal=False, 219 recache=False):
220 """ 221 Initialization 222 223 :Parameters: 224 `func` : ``callable`` 225 The function to decorate 226 227 `keygen` : ``callable`` 228 Key generator callable 229 230 `mcc` : `Memcache` 231 Memcache connector 232 233 `nocache` : ``int`` 234 Evaluate nocache argument? 235 236 `disabled` : ``bool`` 237 Is this decorator disabled? 238 239 `pass_` : ``bool`` 240 Pass memcache to the function? 241 242 `local` : ``bool`` or ``float`` 243 Cache locally as well? (It will be deepcopied for usage) 244 The local cachetime will be `local` * max_age of the memcache age. 245 (if False, it's 0, if True, it's 1) 246 247 `nolocal` : ``int`` 248 Evaluate nolocal argument? 249 250 `recache` : ``bool`` 251 Evaluate recache argument? Useful for backfilling. 252 The memcache won't be asked, but set unconditionally. 253 """ 254 # pylint: disable = R0913 255 super(TransparentCacheDecorator, self).__init__(func) 256 self._keygen = keygen 257 self._mc = mcc 258 self._nocache = nocache 259 self._disabled = disabled 260 self._pass = pass_ 261 self._recache = recache 262 self._nolocal = nolocal 263 if local and max_age > 0: 264 max_age = int(max_age * local) 265 if max_age > 0: 266 self._local = max_age, {} 267 else: 268 self._local = None 269 else: 270 self._local = None
271
272 - def __call__(self, *args, **kwargs):
273 """ 274 Compute the key, check for presence and return the cached result 275 276 If the key is not cached yet, just call the function and store the 277 result in the cache. Except nocache is requested by the caller and 278 activated in this decorator instance. 279 280 :Parameters: 281 `args` : ``tuple`` 282 Function's positional arguments 283 284 `kwargs` : ``dict`` 285 Function's keyword arguments 286 287 :Return: Whatever the decorated function returns 288 :Rtype: any 289 290 :Exceptions: 291 - `Exception` : Whatever the decorated function raises 292 """ 293 # pylint: disable = R0912 294 if self._recache: 295 recache = kwargs.pop('recache', False) 296 else: 297 recache = False 298 if self._nocache: 299 nocache = kwargs.pop('nocache', False) 300 else: 301 nocache = False 302 if self._nolocal: 303 nolocal = kwargs.pop('nolocal', False) 304 else: 305 nolocal = False 306 if self._disabled or self._nocache: 307 if self._disabled: 308 nocache = True 309 if self._nocache > 1: 310 kwargs['nocache'] = nocache 311 if self._nolocal > 1: 312 kwargs['nolocal'] = nolocal 313 if self._pass: 314 kwargs['mc'] = NoMemcacheWrapper() 315 if nocache and self._nocache > 0: 316 return self._func(*args, **kwargs) 317 318 mcc = self._mc 319 if self._pass: 320 kwargs['mc'] = mcc 321 key = self._keygen(*args, **kwargs) 322 if not nolocal: 323 local = self._local 324 if local is not None and not recache: 325 found = local[1].get(key) 326 if found is not None: 327 stamp, found = found 328 if stamp >= _time.time(): 329 return _copy.deepcopy(found) 330 try: 331 del local[1][key] 332 except KeyError: 333 pass 334 if not recache: 335 cached = mcc.get(key) 336 if cached: 337 if not nolocal and local is not None: 338 local[1][key] = ( 339 _time.time() + local[0], 340 _copy.deepcopy(cached[key]) 341 ) 342 return cached[key] 343 result = self._func(*args, **kwargs) 344 mcc.set(key, result) 345 if not nolocal and local is not None: 346 local[1][key] = (_time.time() + local[0], _copy.deepcopy(result)) 347 return result
348
349 350 -class MemcacheDecorator(_util.BaseDecorator):
351 """ 352 Memcache decorator 353 354 :IVariables: 355 - `_mc`: Memcache connector 356 357 :Types: 358 - `_mc`: `MemcacheWrapper` or `Memcache` 359 """ 360
361 - def __init__(self, func, mcc, nocache=False, disabled=False):
362 """ 363 Initialization 364 365 :Parameters: 366 - `func`: The function to decorate 367 - `mcc`: The memcache connector 368 - `nocache`: Nocache behavior 369 - `disabled`: Is this decorator disabled? 370 371 :Types: 372 - `func`: ``callable`` 373 - `mcc`: `MemcacheWrapper` or `Memcache` 374 - `nocache`: ``int`` 375 - `disabled`: ``bool`` 376 """ 377 super(MemcacheDecorator, self).__init__(func) 378 self._mc = mcc 379 self._nocache = int(nocache) 380 self._disabled = disabled
381
382 - def __call__(self, *args, **kwargs):
383 """ 384 Create a memcache connector or reuse a supplied one 385 386 The resulting connector is passed as a keyword argument into the 387 decorated function. 388 389 :Parameters: 390 - `args`: Function's positional arguments 391 - `kwargs`: Function's keyword arguments 392 393 :Types: 394 - `args`: ``tuple`` 395 - `kwargs`: ``dict`` 396 397 :return: Whatever the decorated function returns 398 :rtype: any 399 400 :Exceptions: 401 - `Exception`: Whatever the decorated function raises 402 """ 403 if self._disabled: 404 kwargs['mc'] = NoMemcacheWrapper() 405 else: 406 mcc = kwargs.get('mc') 407 if not mcc: 408 mcc = self._mc 409 if self._nocache: 410 nocache = kwargs.pop('nocache', False) 411 if self._nocache > 1: 412 kwargs['nocache'] = nocache 413 if self._nocache > 0 and nocache: 414 mcc = NoMemcacheWrapper() 415 kwargs['mc'] = mcc 416 return self._func(*args, **kwargs)
417
418 419 -class NoMemcacheWrapper(object):
420 """ Dummy connector, which does nothing actually, but provide the API """ 421
422 - def set(self, key, value, max_age=None):
423 """ 424 Set a key/value pair unconditionally 425 426 :Parameters: 427 - `key`: The key to store under 428 - `value`: The value to store (should be picklable) 429 - `max_age`: Maximum age in seconds. If omitted or ``None`` the 430 default is applied. 431 432 :Types: 433 - `key`: ``str`` 434 - `value`: any 435 - `max_age`: ``int`` 436 437 :return: Stored successfully? 438 :rtype: ``bool`` 439 """ 440 # pylint: disable = W0613 441 442 return False
443
444 - def add(self, key, value, max_age=None):
445 """ 446 Set a key/value pair if the key does not exist yet 447 448 :Parameters: 449 - `key`: The key to store under 450 - `value`: The value to store (should be picklable) 451 - `max_age`: Maximum age in seconds. If omitted or ``None`` the 452 default is applied. 453 454 :Types: 455 - `key`: ``str`` 456 - `value`: any 457 - `max_age`: ``int`` 458 459 :return: Stored successfully? 460 :rtype: ``bool`` 461 """ 462 # pylint: disable = W0613 463 464 return False
465
466 - def replace(self, key, value, max_age=None):
467 """ 468 Set a key/value pair only if the key does exist already 469 470 :Parameters: 471 - `key`: The key to store under 472 - `value`: The value to store (should be picklable) 473 - `max_age`: Maximum age in seconds. If omitted or ``None`` the 474 default is applied. 475 476 :Types: 477 - `key`: ``str`` 478 - `value`: any 479 - `max_age`: ``int`` 480 481 :return: Stored successfully? 482 :rtype: ``bool`` 483 """ 484 # pylint: disable = W0613 485 486 return False
487
488 - def delete(self, key, block_time=None, all_pools=False):
489 """ 490 Delete a key/value pair from the cache 491 492 :Parameters: 493 - `key`: The key to identify the item to delete 494 - `block_time`: Time to block add and replace requests for this key 495 in seconds. If omitted or ``None``, the blocking time is ``0``. 496 - `all_pools`: Issue delete to each pool? This may be useful to 497 enforce the deletion on backup pools, too. However, it won't 498 delete the key from currently dead pools. So, it might be not 499 that useful after all, but it's the best we can do from this 500 side of the ocean. 501 502 :Types: 503 - `key`: ``str`` 504 - `block_time`: ``int`` 505 - `all_pools`: ``bool`` 506 507 :return: Whether it was really deleted from the main pool (or the 508 current backup pool) of this key (i.e. whether it existed 509 before) 510 :rtype: ``bool`` 511 """ 512 # pylint: disable = W0613 513 514 return False
515
516 - def get(self, *keys):
517 """ 518 Get a list of key/value pairs from the cache (if applicable) 519 520 The returned dict contains all pairs it could get. But keys maybe 521 missing or the dict might be completely empty (of course). 522 523 :Parameters: 524 - `keys`: The keys to fetch 525 526 :Types: 527 - `keys`: ``tuple`` 528 529 :return: The dict of key/value pairs 530 :rtype: ``dict`` 531 """ 532 # pylint: disable = W0613 533 534 return {}
535
536 537 -class ExceptionWrapper(object):
538 """ 539 Exception catching wrapper 540 541 :IVariables: 542 - `_mc`: Memcache connector 543 544 :Types: 545 - `_mc`: `Memcache` 546 """ 547
548 - def __init__(self, mcc):
549 """ 550 Initialization 551 552 :Parameters: 553 - `mcc`: Memcache connector 554 555 :Types: 556 - `mcc`: `Memcache` 557 """ 558 self._mc = mcc
559
560 - def __getattr__(self, name):
561 """ 562 Create proxy around callables, catching memcache errors. 563 564 The proxy functions are cached. 565 566 :Parameters: 567 - `name`: The attribute to wrap up 568 569 :Types: 570 - `name`: ``str`` 571 572 :return: The original attribute or the proxied placeholder 573 :rtype: any 574 575 :Exceptions: 576 - `AttributeError`: The attribute was not found 577 """ 578 attr = getattr(self._mc, name) 579 if callable(attr): 580 def proxy(*args, **kwargs): 581 """ Catching proxy """ 582 try: 583 return attr(*args, **kwargs) 584 except _memcache.Error: 585 return False
586 try: 587 proxy.__name__ = attr.__name__ # pylint: disable = W0622 588 except AttributeError: 589 pass 590 proxy.__doc__ = attr.__doc__ # pylint: disable = W0622 591 setattr(self, name, proxy) 592 return proxy 593 return attr
594
595 - def get(self, *keys):
596 """ 597 Get a list of key/value pairs from the cache (if applicable) 598 599 The returned dict contains all pairs it could get. But keys maybe 600 missing or the dict might be completely empty (of course). 601 602 :Parameters: 603 - `keys`: The keys to fetch 604 605 :Types: 606 - `keys`: ``tuple`` 607 608 :return: The dict of key/value pairs 609 :rtype: ``dict`` 610 """ 611 try: 612 return self._mc.get(*keys) 613 except _memcache.Error: 614 return {}
615
616 617 -class MemcacheWrapper(object):
618 """ 619 `Memcache` wrapper, applying default max age 620 621 This is, what the decorator injects if a default max age is configured 622 or the exceptions are caught. 623 624 :IVariables: 625 - `_mc`: Memcache connector 626 - `_max_age`: Default max age 627 628 :Types: 629 - `_mc`: `Memcache` 630 - `_max_age`: ``int`` 631 """ 632
633 - def __init__(self, mcc, max_age, exceptions):
634 """ 635 Initialization 636 637 :Parameters: 638 - `mcc`: Memcache connector 639 - `max_age`: Default expire time 640 - `exceptions`: pass exceptions to the caller? 641 642 :Types: 643 - `mcc`: `Memcache` 644 - `max_age`: ``int`` 645 - `exceptions`: ``bool`` 646 """ 647 if not exceptions: 648 mcc = ExceptionWrapper(mcc) 649 self._mc = mcc 650 self._max_age = max_age 651 652 self.delete = mcc.delete 653 self.get = mcc.get 654 if max_age is None: 655 self.set = mcc.set 656 self.add = mcc.add 657 self.replace = mcc.replace
658
659 - def set(self, key, value, max_age=None):
660 """ 661 Set a key/value pair unconditionally 662 663 :Parameters: 664 - `key`: The key to store under 665 - `value`: The value to store (should be picklable) 666 - `max_age`: Maximum age in seconds. If omitted or ``None`` the 667 default is applied. 668 669 :Types: 670 - `key`: ``str`` 671 - `value`: any 672 - `max_age`: ``int`` 673 674 :return: Stored successfully? 675 :rtype: ``bool`` 676 """ 677 # pylint: disable = E0202 678 679 if max_age is None: 680 max_age = self._max_age 681 return self._mc.store("set", key, value, max_age)
682
683 - def add(self, key, value, max_age=None):
684 """ 685 Set a key/value pair if the key does not exist yet 686 687 :Parameters: 688 - `key`: The key to store under 689 - `value`: The value to store (should be picklable) 690 - `max_age`: Maximum age in seconds. If omitted or ``None`` the 691 default is applied. 692 693 :Types: 694 - `key`: ``str`` 695 - `value`: any 696 - `max_age`: ``int`` 697 698 :return: Stored successfully? 699 :rtype: ``bool`` 700 """ 701 # pylint: disable = E0202 702 703 if max_age is None: 704 max_age = self._max_age 705 return self._mc.store("add", key, value, max_age)
706
707 - def replace(self, key, value, max_age=None):
708 """ 709 Set a key/value pair only if the key does exist already 710 711 :Parameters: 712 - `key`: The key to store under 713 - `value`: The value to store (should be picklable) 714 - `max_age`: Maximum age in seconds. If omitted or ``None`` the 715 default is applied. 716 717 :Types: 718 - `key`: ``str`` 719 - `value`: any 720 - `max_age`: ``int`` 721 722 :return: Stored successfully? 723 :rtype: ``bool`` 724 """ 725 # pylint: disable = E0202 726 727 if max_age is None: 728 max_age = self._max_age 729 return self._mc.store("replace", key, value, max_age)
730
731 732 -class GlobalMemcache(object):
733 """ 734 Actual global memcache service object 735 736 :IVariables: 737 - `_pools`: Pool list 738 - `_create`: Memcache wrapper creator 739 - `_max_age`: Globally configured max age 740 - `_mc`: Default memcache wrapper 741 742 :Types: 743 - `_pools`: ``tuple`` 744 - `_create`: ``callable`` 745 - `_max_age`: ``int`` 746 - `_mc`: `MemcacheWrapper` or `Memcache` 747 """ 748
749 - def __init__(self, pools, max_age, grace_time, retry_time, 750 compress_threshold, padded, split, prefix, largest_slab):
751 """ 752 Initialization 753 754 :Parameters: 755 - `pools`: Pool list 756 - `max_age`: Default expire time (``None`` for no such default) 757 - `grace_time`: Grace time, see `ext.memcache.Memcache.__init__` 758 for details 759 - `retry_time`: Retry time, see `ext.memcache.Memcache.__init__` 760 for details 761 - `compress_threshold`: Compression threshold 762 - `padded`: Padded, yes/no? (``None`` for the default) 763 - `split`: Split yes/no? (``None`` for the default) 764 - `prefix`: global key prefix 765 - `largest_slab`: Largest slab size (``None`` for the default) 766 767 :Types: 768 - `pools`: ``iterable`` 769 - `max_age`: ``int`` 770 - `grace_time`: ``int`` 771 - `retry_time`: ``int`` 772 - `compress_threshold`: ``int`` 773 - `padded`: ``bool`` 774 - `split`: ``bool`` 775 - `prefix`: ``str`` 776 - `largest_slab`: ``int`` 777 """ 778 self._pools = tuple(pools) 779 780 def create(prepare, max_age, exceptions): 781 """ 782 Memcache connector creator 783 784 :Parameters: 785 - `prepare`: Key preparation function 786 - `max_age`: Default expire time (or ``None``) 787 - `exceptions`: Raise exceptions? 788 789 :Types: 790 - `prepare`: ``callable`` 791 - `max_age`: ``int`` 792 - `exceptions`: ``bool`` 793 794 :return: The memcache connector 795 :rtype: `Memcache` or `MemcacheWrapper` 796 """ 797 mcc = _memcache.Memcache(self._pools, 798 prepare=prepare, 799 grace_time=grace_time, 800 retry_time=retry_time, 801 compress_threshold=compress_threshold, 802 padded=padded, 803 split=split, 804 prefix=prefix, 805 largest_slab=largest_slab, 806 ) 807 if max_age is not None or not exceptions: 808 mcc = MemcacheWrapper(mcc, max_age, exceptions) 809 return mcc
810 self._max_age = max_age 811 self._create = create 812 self._mc = create(self._prepare, max_age, False)
813
814 - def status(self):
815 """ 816 Determine pool status 817 818 Each status is a dict 819 ``{'spec': 'spec', 'alive': bool, 'weight': int}``. 820 821 :return: The status of the pools (``[status, ...]``) 822 :rtype: ``list`` 823 """ 824 return [dict( 825 spec=pool.spec, 826 weight=pool.weight, 827 alive=not pool.dead, 828 ) for pool in self._pools]
829
830 - def shutdown(self):
831 """ 832 Shutdown the memcache pools 833 834 The pools are no longer usable after that. This is for final 835 application shutdown. Don't use it in the application itself! 836 """ 837 pools, self._pools = self._pools, () 838 for pool in pools: 839 try: 840 pool.shutdown() 841 except (SystemExit, KeyboardInterrupt): 842 raise 843 except: 844 pass
845
846 - def connect(self, max_age=None, prepare=None, exceptions=None):
847 """ 848 Create a memcache connector 849 850 Although the method name suggests it, the method doesn't actually 851 connect. The connection is selected by key later when using the 852 connector to actually do something. 853 854 :Parameters: 855 - `max_age`: Default max age for store commands in seconds (overriding 856 the configured default) 857 - `prepare`: Key preparation function (overriding the default) 858 - `exceptions`: Pass exceptions to the caller? (Default: ``False``) 859 860 :Types: 861 - `max_age`: ``int`` 862 - `prepare`: ``callable`` 863 - `exceptions`: ``bool`` 864 865 :return: The memcache connector (may be wrapped for requested 866 functionality, so it's not necessarily a real `Memcache`) 867 :rtype: `Memcache` 868 """ 869 if max_age is None and prepare is None and exceptions is None: 870 return self._mc 871 872 if max_age is None: 873 max_age = self._max_age 874 if prepare is None: 875 prepare = self._prepare 876 if exceptions is None: 877 exceptions = False 878 return self._create(prepare, max_age, exceptions)
879
880 - def memcached(self, keygen, **kwargs):
881 """ 882 Cache the function transparently 883 884 Recognized keyword parameters: 885 886 ``max_age`` 887 [int] Default expire time for storing operations 888 ``prepare`` 889 [callable] Key preparation function 890 ``nocache`` 891 [int] Nocache behavior. See `services.memcache` for details. 892 ``recache`` 893 [bool] Recache behavioue. See `services.memcache` for details. 894 ``disabled`` 895 [bool] Disable this memcache decorator (useful for debugging)? 896 ``pass_`` 897 [bool] If true, the memcache connector will be passed to the 898 function 899 ``exceptions`` 900 [bool] Memcache exception behavior (pass through = True) 901 902 :Parameters: 903 `keygen` : ``callable`` 904 Key generator function 905 906 `kwargs` : ``dict`` 907 Keyword arguments 908 909 :Return: The memcached decorator 910 :Rtype: `TransparentCacheDecorator` 911 """ 912 if self._max_age is None and kwargs.get('max_age') is None: 913 raise RuntimeError("@memcached needs a max age set") 914 915 nocache = kwargs.pop('nocache', None) 916 disabled = kwargs.pop('disabled', None) 917 pass_ = kwargs.pop('pass_', None) 918 local = kwargs.pop('local', None) 919 nolocal = kwargs.pop('nolocal', None) 920 recache = kwargs.pop('recache', None) 921 if disabled is None: 922 disabled = False 923 if nocache is None: 924 nocache = False 925 if nolocal is None: 926 nolocal = False 927 if recache is None: 928 recache = False 929 if pass_ is None: 930 pass_ = False 931 mcc = self.connect(**kwargs) 932 def factory(func): 933 """ 934 Decorator factory 935 936 :Parameters: 937 - `func`: The function to decorate 938 939 :Types: 940 - `func`: ``callable`` 941 942 :return: The decorated function 943 :rtype: ``callable`` 944 """ 945 return TransparentCacheDecorator(func, keygen, mcc, self._max_age, 946 nocache=nocache, 947 disabled=disabled, 948 pass_=pass_, 949 local=local, 950 nolocal=nolocal, 951 recache=recache, 952 )
953 return factory 954
955 - def connection(self, *args, **kwargs):
956 """ 957 Inject a new connector into function's arguments 958 959 The method takes either one positional argument (the function to 960 decorate) *or* keyword arguments which override default options: 961 962 ``max_age`` 963 [int] Default expire time for storing operations 964 ``prepare`` 965 [callable] Key preparation function 966 ``nocache`` 967 [int] Nocache behavior. See `services.memcache` for details. 968 ``disabled`` 969 [bool] Disable the memcache connection (useful for debugging) 970 ``exceptions`` 971 [bool] Memcache exception behaviour (pass through = True) 972 973 :Parameters: 974 - `args`: Positional arguments 975 - `kwargs`: keyword arguments 976 977 :Types: 978 - `args`: ``tuple`` 979 - `kwargs`: ``tuple`` 980 981 :return: Decorator or decorator factory 982 :rtype: ``callable`` 983 984 :Exceptions: 985 - `TypeError`: The arguments are formally invalid 986 """ 987 if len(args) == 1 and not kwargs and callable(args[0]): 988 return MemcacheDecorator(args[0], self._mc) 989 elif not args: 990 nocache = kwargs.pop('nocache', None) 991 disabled = kwargs.pop('disabled', None) 992 if nocache is None: 993 nocache = False 994 if disabled is None: 995 disabled = False 996 mcc = self.connect(**kwargs) 997 def factory(func): 998 """ 999 Decorator factory 1000 1001 :Parameters: 1002 - `func`: The function to decorate 1003 1004 :Types: 1005 - `func`: ``callable`` 1006 1007 :return: The decorated function 1008 :rtype: ``callable`` 1009 """ 1010 return MemcacheDecorator(func, mcc, nocache=nocache)
1011 return factory 1012 raise TypeError( 1013 "Arguments have to be either one callable positional argument " 1014 "or keyword arguments" 1015 ) 1016
1017 - def _prepare(self, key):
1018 """ 1019 Default key preparator 1020 1021 The input key is assumed to be a string and is just MD5 hashed. 1022 The hexdigest is the resulting key then. 1023 1024 :Parameters: 1025 - `key`: The key to prepare 1026 1027 :Types: 1028 - `key`: ``str`` 1029 1030 :return: The prepared key 1031 :rtype: ``str`` 1032 """ 1033 return _md5.md5(key).hexdigest()
1034
1035 1036 -class MemcacheService(object):
1037 """ 1038 Memcache service 1039 1040 This service provides a global memcache access. 1041 1042 :IVariables: 1043 - `_mc`: Global memcache service 1044 1045 :Types: 1046 - `_mc`: `GlobalMemcache` 1047 """ 1048 __implements__ = [_services.ServiceInterface] 1049
1050 - def __init__(self, config, opts, args):
1051 """ :See: `wtf.services.ServiceInterface.__init__` """ 1052 section = config.memcache 1053 servertokens = tuple(section.servers) 1054 pools = [] 1055 for server in servertokens: 1056 key = u'memcache %s' % server 1057 if key in config: 1058 subsection = config[key] 1059 else: 1060 subsection = section 1061 server = _util.parse_socket_spec(server, _memcache.DEFAULT_PORT) 1062 pools.append(_memcache.MemcacheConnectionPool( 1063 subsection('maxconn', section('maxconn', 0)), 1064 subsection('maxcached', section('maxcached', 0)), 1065 server, 1066 weight=subsection('weight', section('weight', None)), 1067 timeout=subsection('timeout', section('timeout', 2.6)), 1068 )) 1069 max_age = unicode(section('max_age', u'')) or None 1070 if max_age is not None: 1071 max_age = int(max_age) 1072 self._mc = GlobalMemcache(pools, 1073 max_age, 1074 section('grace_time', None), 1075 section('retry_time', None), 1076 section('compress_threshold', None), 1077 section('padded', None), 1078 section('split', None), 1079 unicode(section('prefix', u'')).encode('utf-8'), 1080 section('largest_slab', None), 1081 )
1082 1083 @classmethod
1084 - def simple(cls, *spec, **kwargs):
1085 """ 1086 Create simple on-the-fly configured service 1087 1088 Recognized keyword args: 1089 1090 ``max_age`` 1091 [int] Default max age 1092 ``prefix`` 1093 [unicode] Global key prefix 1094 ``padded`` 1095 [bool] Padded small values? 1096 ``timeout`` 1097 [float] Server timeout 1098 1099 :Parameters: 1100 - `spec`: Memcache servers (``('spec', ...)``) 1101 - `kwargs`: Keyword parameters for memcache config 1102 1103 :Types: 1104 - `spec`: ``tuple`` 1105 - `kwargs`: ``dict`` 1106 1107 :return: The memcache service 1108 :rtype: `GlobalMemcache` 1109 1110 :Exceptions: 1111 - `TypeError`: Unrecognized keyword args 1112 """ 1113 from wtf import config as _config 1114 config = _config.Config(None) 1115 config['memcache'] = _config.Section() 1116 config['memcache']['servers'] = list(spec) 1117 1118 max_age = kwargs.pop('max_age', None) 1119 if max_age is not None: 1120 config['memcache']['max_age'] = int(max_age) 1121 1122 prefix = kwargs.pop('prefix', None) 1123 if prefix is not None: 1124 config['memcache']['prefix'] = unicode(prefix) 1125 1126 padded = kwargs.pop('padded', None) 1127 if padded is not None: 1128 config['memcache']['padded'] = bool(padded) 1129 1130 timeout = kwargs.pop('timeout', None) 1131 if timeout is not None: 1132 config['memcache']['timeout'] = float(timeout) 1133 1134 if kwargs: 1135 raise TypeError("Unrecognized keyword args: %s" % ", ".join( 1136 kwargs.iterkeys() 1137 )) 1138 return cls(config, None, []).global_service()[1]
1139
1140 - def shutdown(self):
1141 """ :See: `wtf.services.ServiceInterface.shutdown` """ 1142 self._mc.shutdown()
1143
1144 - def global_service(self):
1145 """ :See: `wtf.services.ServiceInterface.global_service` """ 1146 return 'wtf.memcache', self._mc
1147
1148 - def middleware(self, func):
1149 """ :See: `wtf.services.ServiceInterface.middleware` """ 1150 return func
1151