Skip to content

Fix pickle import in backend.py #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.. image:: http://unmaintained.tech/badge.svg
:target: http://unmaintained.tech/
:alt: No Maintenance Intended

==================
Django Redisession
==================
Expand Down
8 changes: 7 additions & 1 deletion redisession/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
version = '0.3.1'
from __future__ import unicode_literals

from distutils import version


__version__ = '0.5.0'
version_info = version.StrictVersion(__version__).version
104 changes: 60 additions & 44 deletions redisession/backend.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,72 @@
"""
A redis backend for django session, support string and hash mode.
"""
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import logging
import struct
import time
from importlib import import_module

import redis
from django.conf import settings
from django.contrib.sessions.backends.base import pickle, CreateError, SessionBase
from django.contrib.sessions.backends.base import CreateError, SessionBase

try:
from django.utils.six.moves import cPickle as pickle
except ImportError:
import pickle


logger = logging.getLogger('redisession')


conf = {
'SERVER': {},
'USE_HASH': True,
'KEY_GENERATOR': lambda x: x.decode('hex'),
'HASH_KEY_GENERATOR': lambda x: x[:4].decode('hex'),
'HASH_KEYS_CHECK_FOR_EXPIRY': lambda r: (reduce(lambda p,y :p.randomkey(),
xrange(100), r.pipeline()).execute()),
'HASH_KEYS_CHECK_FOR_EXPIRY':
lambda r: (reduce(
lambda p, y: p.randomkey(), xrange(100), r.pipeline()).execute()),
'COMPRESS_LIB': 'snappy',
'COMPRESS_MIN_LENGTH': 400,
'LOG_KEY_ERROR': False
}
# For session key contains '0-9a-z' in incoming Django 1.5
# conf['KEY_GENERATOR'] = lambda x: x.decode('base64')
# conf['HASH_KEY_GENERATOR'] = lambda x: x.decode('base64')[:2]
conf.update(getattr(settings, 'REDIS_SESSION_CONFIG', {}))

if conf['LOG_KEY_ERROR']:
import logging
logger = logging.getLogger('redisession')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.WARNING)

if isinstance(conf['SERVER'], dict):

class GetRedis(object):

def __call__(self, conf):
if not hasattr(self, '_redis'):
import redis
self._redis = redis.Redis(**conf)
return self._redis

get_redis = GetRedis()

else:
from redisession.helper import get_redis


if conf['COMPRESS_LIB']:
from django.utils.importlib import import_module
compress_lib = import_module(conf['COMPRESS_LIB'])

# TODO: flag for security verification?

FLAG_COMPRESSED = 1


class SessionStore(SessionBase):

def __init__(self, session_key=None):
self._redis = get_redis(conf['SERVER'])
super(SessionStore, self).__init__(session_key)

# XXX Try to partially comply w/ session API of newer Django (>= 1.4) for Django 1.3
# Instead of checking Django version, test the existence directly.
if not hasattr(SessionBase, '_get_or_create_session_key'):
session_key = property(SessionBase._get_session_key)

def _get_or_create_session_key(self):
if self._session_key is None:
self._session_key = self._get_new_session_key()
return self._session_key
if not hasattr(self, 'serializer'):
self.serializer = lambda: pickle

def encode(self, session_dict):
data = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
data = self.serializer().dumps(session_dict)
flag = 0
if conf['COMPRESS_LIB'] and len(data) >= conf['COMPRESS_MIN_LENGTH']:
compressed = compress_lib.compress(data)
Expand All @@ -76,9 +79,10 @@ def decode(self, session_data):
flag, data = ord(session_data[:1]), session_data[1:]
if flag & FLAG_COMPRESSED:
if conf['COMPRESS_LIB']:
return pickle.loads(compress_lib.decompress(data))
raise ValueError('redisession: found compressed data without COMPRESS_LIB specified.')
return pickle.loads(data)
return self.serializer().loads(compress_lib.decompress(data))
raise ValueError('redisession: found compressed data without '
'COMPRESS_LIB specified.')
return self.serializer().loads(data)

def create(self):
for i in xrange(10000):
Expand All @@ -94,24 +98,30 @@ def create(self):
if conf['USE_HASH']:
def _make_key(self, session_key):
try:
return (conf['HASH_KEY_GENERATOR'](session_key), conf['KEY_GENERATOR'](session_key))
except:
return (
conf['HASH_KEY_GENERATOR'](session_key),
conf['KEY_GENERATOR'](session_key)
)
except Exception:
if conf['LOG_KEY_ERROR']:
logger.warning('misconfigured key-generator or bad key "%s"' % session_key)
logger.warning(
'misconfigured key-generator or bad key "{}"'.format(
session_key
)
)

def save(self, must_create=False):
if must_create:
func = self._redis.hsetnx
else:
func = self._redis.hset
session_data = self.encode(self._get_session(no_load=must_create))
expire_date = struct.pack('>I', int(time.time()+self.get_expiry_age()))
expire_date = struct.pack(
'>I', int(time.time() + self.get_expiry_age()))
key = self._make_key(self._get_or_create_session_key())
if key is None:
# XXX must_create = True w/ bad key or misconfigured KEY_GENERATOR,
# which has already been logged in _make_key.
raise CreateError
result = func(*key, value=expire_date+session_data)
result = func(*key, value=expire_date + session_data)
if must_create and not result:
raise CreateError

Expand Down Expand Up @@ -141,13 +151,17 @@ def delete(self, session_key=None):
if key is not None:
self._redis.hdel(*key)

else: # not conf['USE_HASH']
else: # not conf['USE_HASH']
def _make_key(self, session_key):
try:
return conf['KEY_GENERATOR'](session_key)
except:
except Exception:
if conf['LOG_KEY_ERROR']:
logger.warning('misconfigured key-generator or bad key "%s"' % session_key)
logger.warning(
'misconfigured key-generator or bad key "{}"'.format(
session_key
)
)

def save(self, must_create=False):
pipe = self._redis.pipeline()
Expand All @@ -158,11 +172,13 @@ def save(self, must_create=False):
session_data = self.encode(self._get_session(no_load=must_create))
key = self._make_key(self._get_or_create_session_key())
if key is None:
# XXX must_create = True w/ bad key or misconfigured KEY_GENERATOR,
# which has already been logged in _make_key.
raise CreateError
result = pipe(key, session_data).expire(key, self.get_expiry_age()).execute()
if must_create and not (result[0] and result[1]): # for Python 2.4 (Django 1.3)
result = pipe(key, session_data).expire(
key, self.get_expiry_age()
).execute()

# for Python 2.4 (Django 1.3)
if must_create and not (result[0] and result[1]):
raise CreateError

def load(self):
Expand Down
12 changes: 9 additions & 3 deletions redisession/helper.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
from django.conf import settings
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import redis
from django.conf import settings


_connections = {}


def get_redis(conf_name='default'):
"""simple helper for getting global Redis connection instances"""
if conf_name not in _connections:
# refs redis.Redis for **v
_connections[conf_name] = redis.Redis(**getattr(
settings, 'REDIS_CONFIG', {'default': {}})[conf_name])
settings, 'REDIS_CONFIG', {'default': {}}
)[conf_name])
return _connections[conf_name]
9 changes: 8 additions & 1 deletion redisession/management/commands/cleanuprs.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import struct
import time

from django.core.management.base import NoArgsCommand


class Command(NoArgsCommand):
help = """Clean out expired sessions from redisession data."""

help = '''Clean out expired sessions from redisession data.'''

def handle_noargs(self, **options):
from redisession.backend import conf, get_redis
# don't bother to check if hash mode is disabled
Expand Down
9 changes: 6 additions & 3 deletions redisession/tests.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.contrib.sessions.tests import SessionTestsMixin
from django.utils import unittest


conf = getattr(settings, 'REDIS_SESSION_CONFIG', {})


class RedisSessionTests(SessionTestsMixin, unittest.TestCase):

override_config = {
'USE_HASH': False
}

def setUp(self):
test_conf = conf.copy()
test_conf.update(self.override_config)
Expand All @@ -16,9 +21,6 @@ def setUp(self):
self.backend = SessionStore
self.session = self.backend()

def test_decode_django12(self):
"We don't support Django 1.2 ever."

def test_delete(self):
self.session.save()
self.session.delete(self.session.session_key)
Expand All @@ -32,6 +34,7 @@ def set_session_key(session):


class RedisHashSessionTests(RedisSessionTests):

override_config = {
'USE_HASH': True
}
12 changes: 12 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[coverage:run]
omit =
*tests.py
debreach/models.py
include = debreach/*

[coverage:report]
exclude_lines =
pragma: no cover
def __repr__
raise NotImplementedError
if __name__ == .__main__.:
72 changes: 61 additions & 11 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,74 @@
from distutils.core import setup
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import re
import sys
from setuptools import setup, find_packages


def get_version(package):
'''
Return package version as listed in `__version__` in `init.py`.
'''
init_py = open(os.path.join(package, '__init__.py')).read()
return re.search(
'^__version__ = [\'"]([^\'"]+)[\'"]', init_py, re.MULTILINE
).group(1)


version = get_version('redisession')


if sys.argv[-1] == 'publish':
os.system('python setup.py sdist upload')
os.system('python setup.py bdist_wheel upload')
args = {'version': version}
print('You probably want to also tag the version now:')
print(' git tag -a {version} -m \'version {version}\''.format(
**args))
print(' git push --tags')
sys.exit()


setup(
name='django-redisession',
version='0.3.1',
name='django-redisession-ng',
version=version,
license='MIT',
author='Li Meng',
author_email='[email protected]',
packages=['redisession', 'redisession.management', 'redisession.management.commands'],
description='A Redis-based Django session engine for django.contrib.sessions.',
maintainer='Luke Pomfrey',
maintainer_email='[email protected]',
packages=find_packages(exclude=('test_project', 'docs')),
description=(
'A Redis-based Django session engine for django.contrib.sessions.'
),
long_description=open('README.rst').read(),
url='https://github.com/liokm/django-redisession',
download_url='https://github.com/liokm/django-redisession',
url='https://github.com/lpomfrey/django-redisession',
download_url='https://github.com/lpomfrey/django-redisession',
install_requires=[
'redis',
],
tests_require=[
'django',
],
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Framework :: Django',
'Framework :: Django :: 1.8',
'Framework :: Django :: 1.9',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Internet :: WWW/HTTP'
]
)