Skip to content

Commit

Permalink
Merge pull request #335 from SmileyChris/pymemcache
Browse files Browse the repository at this point in the history
Add `pymemcache` cache backend for Django 3.2+
  • Loading branch information
sergeyklay committed Oct 13, 2021
2 parents f5ab1f1 + 1c62ff5 commit 64c83fe
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 10 deletions.
6 changes: 4 additions & 2 deletions docs/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ Supported types
* Dummy: ``dummycache://``
* File: ``filecache://``
* Memory: ``locmemcache://``
* Memcached: ``memcache://``
* Python memory: ``pymemcache://``
* Memcached:
* ``memcache://`` (uses ``python-memcached`` backend, deprecated in Django 3.2)
* ``pymemcache://`` (uses ``pymemcache`` backend if Django >=3.2 and package is installed, otherwise will use ``pylibmc`` backend to keep config backwards compatibility)
* ``pylibmc://``
* Redis: ``rediscache://``, ``redis://``, or ``rediss://``

* ``search_url``
Expand Down
21 changes: 17 additions & 4 deletions environ/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

"""This module handles import compatibility issues."""

import pkgutil
from pkgutil import find_loader


if pkgutil.find_loader('simplejson'):
if find_loader('simplejson'):
import simplejson as json
else:
import json

if pkgutil.find_loader('django'):
if find_loader('django'):
from django import VERSION as DJANGO_VERSION
from django.core.exceptions import ImproperlyConfigured
else:
Expand All @@ -33,7 +33,20 @@ class ImproperlyConfigured(Exception):
DJANGO_POSTGRES = 'django.db.backends.postgresql'

# back compatibility with redis_cache package
if pkgutil.find_loader('redis_cache'):
if find_loader('redis_cache'):
REDIS_DRIVER = 'redis_cache.RedisCache'
else:
REDIS_DRIVER = 'django_redis.cache.RedisCache'


# back compatibility for pymemcache
def choose_pymemcache_driver():
old_django = DJANGO_VERSION is not None and DJANGO_VERSION < (3, 2)
if old_django or not find_loader('pymemcache'):
# The original backend choice for the 'pymemcache' scheme is
# unfortunately 'pylibmc'.
return 'django.core.cache.backends.memcached.PyLibMCCache'
return 'django.core.cache.backends.memcached.PyMemcacheCache'


PYMEMCACHE_DRIVER = choose_pymemcache_driver()
11 changes: 9 additions & 2 deletions environ/environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@
urlunparse,
)

from .compat import DJANGO_POSTGRES, ImproperlyConfigured, json, REDIS_DRIVER
from .compat import (
DJANGO_POSTGRES,
ImproperlyConfigured,
json,
PYMEMCACHE_DRIVER,
REDIS_DRIVER,
)
from .fileaware_mapping import FileAwareMapping

try:
Expand Down Expand Up @@ -116,7 +122,8 @@ class Env:
'filecache': 'django.core.cache.backends.filebased.FileBasedCache',
'locmemcache': 'django.core.cache.backends.locmem.LocMemCache',
'memcache': 'django.core.cache.backends.memcached.MemcachedCache',
'pymemcache': 'django.core.cache.backends.memcached.PyLibMCCache',
'pymemcache': PYMEMCACHE_DRIVER,
'pylibmc': 'django.core.cache.backends.memcached.PyLibMCCache',
'rediscache': REDIS_DRIVER,
'redis': REDIS_DRIVER,
'rediss': REDIS_DRIVER,
Expand Down
22 changes: 20 additions & 2 deletions tests/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
# For the full copyright and license information, please view
# the LICENSE.txt file that was distributed with this source code.

from unittest import mock

import pytest

import environ.compat
from environ import Env
from environ.compat import REDIS_DRIVER, ImproperlyConfigured
from environ.compat import PYMEMCACHE_DRIVER, REDIS_DRIVER, ImproperlyConfigured


def test_base_options_parsing():
Expand Down Expand Up @@ -63,7 +66,7 @@ def test_base_options_parsing():
'django.core.cache.backends.memcached.MemcachedCache',
'127.0.0.1:11211'),
('pymemcache://127.0.0.1:11211',
'django.core.cache.backends.memcached.PyLibMCCache',
PYMEMCACHE_DRIVER,
'127.0.0.1:11211'),
],
ids=[
Expand All @@ -90,6 +93,21 @@ def test_cache_parsing(url, backend, location):
assert url['LOCATION'] == location


@pytest.mark.parametrize('django_version', ((3, 2), (3, 1), None))
@pytest.mark.parametrize('pymemcache_installed', (True, False))
def test_pymemcache_compat(django_version, pymemcache_installed):
old = 'django.core.cache.backends.memcached.PyLibMCCache'
new = 'django.core.cache.backends.memcached.PyMemcacheCache'
with mock.patch.object(environ.compat, 'DJANGO_VERSION', django_version):
with mock.patch('environ.compat.find_loader') as mock_find_loader:
mock_find_loader.return_value = pymemcache_installed
driver = environ.compat.choose_pymemcache_driver()
if django_version and django_version < (3, 2):
assert driver == old
else:
assert driver == new if pymemcache_installed else old


def test_redis_parsing():
url = ('rediscache://127.0.0.1:6379/1?client_class='
'django_redis.client.DefaultClient&password=secret')
Expand Down

0 comments on commit 64c83fe

Please sign in to comment.