Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Python3 support #22

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
@@ -1,4 +1,6 @@
language: python
python:
- "2.7"
- "3.2"
- "3.3"
script: python -m unittest discover
14 changes: 6 additions & 8 deletions redis_collections/base.py
Expand Up @@ -4,16 +4,16 @@
~~~~
"""


import uuid
import redis
import functools
import six
from abc import ABCMeta, abstractmethod

try:
import cPickle as pickle
from six.moves import pickle # cPikle
except ImportError:
import pickle as pickle # NOQA
import pickle # NOQA


def same_types(fn):
Expand Down Expand Up @@ -43,13 +43,11 @@ def wrapper(self, *args):
return wrapper


class RedisCollection:
class RedisCollection(six.with_metaclass(ABCMeta)):
"""Abstract class providing backend functionality for all the other
Redis collections.
"""

__metaclass__ = ABCMeta

_same_types = ()

not_impl_msg = ('Cannot be implemented efficiently or atomically '
Expand Down Expand Up @@ -210,7 +208,7 @@ def _pickle(self, data):
:type data: anything serializable
:rtype: string
"""
return str(self.pickler.dumps(data))
return self.pickler.dumps(data)

def _unpickle(self, string):
"""Converts given string serialization back to corresponding data.
Expand All @@ -222,7 +220,7 @@ def _unpickle(self, string):
"""
if string is None:
return None
if not isinstance(string, basestring):
if not isinstance(string, six.binary_type):
msg = 'Only strings can be unpickled (%r given).' % string
raise TypeError(msg)
return self.pickler.loads(string)
Expand Down
8 changes: 4 additions & 4 deletions redis_collections/dicts.py
Expand Up @@ -8,6 +8,7 @@


import collections
import six

from .base import RedisCollection, same_types

Expand Down Expand Up @@ -366,7 +367,7 @@ def __init__(self, *args, **kwargs):
super(Counter, self).__init__(*args, **kwargs)

def _pickle(self, data):
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what is purpose of this line. Test suite passes even with identity function (return data). Why you convert data to int and then to unicode? I'm asking because my "fix" isn't precise either.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is type check (int) and serialization (unicode). In this implementation of Counter, data has to be integer only and then they are serialized for Redis just by calling unicode. Maybe it involves some redundancy, but it is improving readability and consistency. I agree coercing to int is stupid "validation", but it did the job quite well so far.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not sure. If int is called as type check only, it would work. There's no unicode in Py3 and "it works" even without it. I'll try to fix it so it reflects original behavior.

Edit: Added d873e1a

return unicode(int(data))
return int(data)

def _unpickle(self, string):
if string is None:
Expand All @@ -378,7 +379,7 @@ def _obj_to_data(self, obj):
is_mapping = isinstance(obj, collections.Mapping)

data = obj._data() if is_redis else obj
return dict(data) if is_mapping else iter(data)
return dict(data) if is_mapping else map(six.b, data)

def getmany(self, *keys):
values = super(Counter, self).getmany(*keys)
Expand All @@ -401,7 +402,7 @@ def elements(self):
"""
for element, count in self._data():
if count:
for _ in xrange(0, count):
for _ in range(0, count):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But range in Python 2.X is not the same as range in Python 3.X, isn't it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I wasn't precise enough :) There's fix in a0d966b

yield element

def _update(self, data, pipe=None):
Expand Down Expand Up @@ -457,7 +458,6 @@ def op_trans(pipe):

c1 = collections.Counter(d1)
result = fn(c1, d2)

if update:
result = c1

Expand Down
5 changes: 2 additions & 3 deletions redis_collections/sets.py
Expand Up @@ -9,16 +9,15 @@

import itertools
import collections
import six
from abc import ABCMeta, abstractmethod

from .base import RedisCollection, same_types


class SetOperation(object):
class SetOperation(six.with_metaclass(ABCMeta)):
"""Helper class for implementing standard set operations."""

__metaclass__ = ABCMeta

def __init__(self, s, update=False, flipped=False, return_cls=None):
"""
:param s: :class:`collections.Set` instance.
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
@@ -1 +1,2 @@
redis
six
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -50,7 +50,7 @@
license=open('LICENSE').read(),
packages=find_packages(exclude=['tests']),
include_package_data=True,
install_requires=['redis>=2.7.2'],
install_requires=['redis>=2.7.2', 'six'],
zip_safe=False,
classifiers=(
'Development Status :: 4 - Beta',
Expand Down