Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
django-pylibmc is a cache backend using pylibmc
- Loading branch information
0 parents
commit 5cec8b9
Showing
10 changed files
with
470 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Copyright (c) 2010, Jeff Balogh. | ||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without modification, | ||
are permitted provided that the following conditions are met: | ||
|
||
1. Redistributions of source code must retain the above copyright notice, | ||
this list of conditions and the following disclaimer. | ||
|
||
2. Redistributions in binary form must reproduce the above copyright | ||
notice, this list of conditions and the following disclaimer in the | ||
documentation and/or other materials provided with the distribution. | ||
|
||
3. Neither the name of django-pylibmc nor the names of its contributors may | ||
be used to endorse or promote products derived from this software | ||
without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include LICENSE | ||
include README.rst |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
================================ | ||
pylibmc cache backend for Django | ||
================================ | ||
|
||
This package provides a memcached cache backend for Django using | ||
`pylibmc http://github.com/lericson/pylibmc`_. You want to use pylibmc because | ||
it's fast. | ||
|
||
|
||
Requirements | ||
------------ | ||
|
||
django-pylibmc requires Django 1.2. It was written and tested on Python 2.6. | ||
|
||
|
||
Installation | ||
------------ | ||
|
||
|
||
Get it from `pypi <http://pypi.python.org/pypi/django-pylibmc>`_:: | ||
|
||
pip install django-pylibmc | ||
|
||
or `github <http://github.com/jbalogh/django-pylibmc>`_:: | ||
|
||
pip install -e git://github.com/jbalogh/django-pylibmc.git#egg=django-pylibmc | ||
|
||
|
||
Caveats | ||
------- | ||
|
||
This package breaks away from the current handling of ``timeout=0`` in Django. | ||
Django converts 0 into the default timeout, while django-pylibmc leaves it as | ||
0. memcached takes 0 to mean "infinite timeout." You can still pass ``None`` | ||
to get the default timeout. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
VERSION = (0, 1) | ||
__version__ = '.'.join(map(str, VERSION)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
""" | ||
Memcached cache backend for Django using pylibmc. | ||
If you want to use the binary protocol, specify &binary=1 in your | ||
CACHE_BACKEND. The default is 0, using the text protocol. | ||
pylibmc behaviors can be declared as a dict in settings.py under the name | ||
PYLIBMC_BEHAVIORS. | ||
Unlike the default Django caching backends, this backend lets you pass 0 as a | ||
timeout, which translates to an infinite timeout in memcached. | ||
""" | ||
import time | ||
|
||
from django.conf import settings | ||
from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError | ||
from django.utils.encoding import smart_unicode, smart_str | ||
|
||
try: | ||
import pylibmc | ||
except ImportError: | ||
raise InvalidCacheBackendError('Could not import pylibmc.') | ||
|
||
|
||
# It would be nice to inherit from Django's memcached backend, but that | ||
# requires import python-memcache or cmemcache. Those probably aren't | ||
# available since we're using pylibmc, hence the copy/paste. | ||
|
||
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
class CacheClass(BaseCache): | ||
|
||
def __init__(self, server, params): | ||
BaseCache.__init__(self, params) | ||
binary = int(params.get('binary', False)) | ||
self._cache = pylibmc.Client(server.split(';'), binary=binary) | ||
self._cache.behaviors = getattr(settings, 'PYLIBMC_BEHAVIORS', {}) | ||
|
||
def _get_memcache_timeout(self, timeout): | ||
""" | ||
Memcached deals with long (> 30 days) timeouts in a special | ||
way. Call this function to obtain a safe value for your timeout. | ||
""" | ||
timeout = self.default_timeout if timeout is None else timeout | ||
if timeout > 2592000: # 60*60*24*30, 30 days | ||
# See http://code.google.com/p/memcached/wiki/FAQ | ||
# "You can set expire times up to 30 days in the future. After that | ||
# memcached interprets it as a date, and will expire the item after | ||
# said date. This is a simple (but obscure) mechanic." | ||
# | ||
# This means that we have to switch to absolute timestamps. | ||
timeout += int(time.time()) | ||
return timeout | ||
|
||
def add(self, key, value, timeout=None): | ||
if isinstance(value, unicode): | ||
value = value.encode('utf-8') | ||
return self._cache.add(smart_str(key), value, self._get_memcache_timeout(timeout)) | ||
|
||
def get(self, key, default=None): | ||
val = self._cache.get(smart_str(key)) | ||
if val is None: | ||
return default | ||
return val | ||
|
||
def set(self, key, value, timeout=None): | ||
self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout)) | ||
|
||
def delete(self, key): | ||
self._cache.delete(smart_str(key)) | ||
|
||
def get_many(self, keys): | ||
return self._cache.get_multi(map(smart_str,keys)) | ||
|
||
def close(self, **kwargs): | ||
self._cache.disconnect_all() | ||
|
||
def incr(self, key, delta=1): | ||
try: | ||
return self._cache.incr(key, delta) | ||
except pylibmc.NotFound: | ||
raise ValueError("Key '%s' not found" % key) | ||
|
||
def decr(self, key, delta=1): | ||
try: | ||
return self._cache.decr(key, delta) | ||
except pylibmc.NotFound: | ||
raise ValueError("Key '%s' not found" % key) | ||
|
||
def set_many(self, data, timeout=None): | ||
safe_data = {} | ||
for key, value in data.items(): | ||
if isinstance(value, unicode): | ||
value = value.encode('utf-8') | ||
safe_data[smart_str(key)] = value | ||
self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout)) | ||
|
||
def delete_many(self, keys): | ||
self._cache.delete_multi(map(smart_str, keys)) | ||
|
||
def clear(self): | ||
self._cache.flush_all() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from setuptools import setup | ||
|
||
import django_pylibmc | ||
|
||
|
||
setup( | ||
name='django-pylibmc', | ||
version=django_pylibmc.__version__, | ||
description='Django cache backend using pylibmc', | ||
long_description=open('README.rst').read(), | ||
author='Jeff Balogh', | ||
author_email='jbalogh@mozilla.com', | ||
url='http://github.com/jbalogh/django-pylibmc', | ||
license='BSD', | ||
packages=['django_pylibmc'], | ||
include_package_data=True, | ||
zip_safe=False, | ||
install_requires=['pylibmc', 'Django>=1.2'], | ||
classifiers=[ | ||
'Development Status :: 4 - Beta', | ||
'Environment :: Web Environment', | ||
# I don't know what exactly this means, but why not? | ||
'Environment :: Web Environment :: Mozilla', | ||
'Framework :: Django', | ||
'Intended Audience :: Developers', | ||
'License :: OSI Approved :: BSD License', | ||
'Operating System :: OS Independent', | ||
'Programming Language :: Python', | ||
'Topic :: Software Development :: Libraries :: Python Modules', | ||
] | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from django.db import models | ||
from datetime import datetime | ||
|
||
def expensive_calculation(): | ||
expensive_calculation.num_runs += 1 | ||
return datetime.now() | ||
|
||
class Poll(models.Model): | ||
question = models.CharField(max_length=200) | ||
answer = models.CharField(max_length=200) | ||
pub_date = models.DateTimeField('date published', default=expensive_calculation) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
DATABASES = { | ||
'default': { | ||
'ENGINE': 'django.db.backends.sqlite3', | ||
'NAME': 'testing', | ||
'SUPPORTS_TRANSACTIONS': False, | ||
}, | ||
} | ||
|
||
INSTALLED_APPS = ( | ||
'app', | ||
) |
Oops, something went wrong.
FYI: It doesn't look like this is the case anymore...
https://github.com/django/django/blob/master/django/core/cache/backends/memcached.py
Also, a pylibmc backend was added. But it looks like this has some extra guards and also the preferred timeout=0 behavior.
It looks like you could import and inherit from the
BaseMemcachedCache
class and avoid some extra copy/paste.