diff --git a/.gitignore b/.gitignore index 2fe0fc51d..691e8e338 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ dump.rdb /.emacs.desktop /redisco/.ropeproject/config.py .ropeproject +venv +venv2 +.idea diff --git a/.travis.yml b/.travis.yml index 77968275d..eaf6738dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,14 @@ language: python python: - "2.6" - "2.7" + - "3.4" + - "3.5" + - "3.6" services: - redis-server # command to install redisco with dependencies install: - - pip install . --use-mirrors + - pip install . + - pip install -r requirements.txt # command to run tests script: nosetests --with-doctest diff --git a/benchmarks/common.py b/benchmarks/common.py index 8eb5e8267..961dad1f6 100644 --- a/benchmarks/common.py +++ b/benchmarks/common.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from redisco import models diff --git a/benchmarks/create.py b/benchmarks/create.py index 633226d47..91e1b0483 100644 --- a/benchmarks/create.py +++ b/benchmarks/create.py @@ -1,6 +1,10 @@ -from redisco import get_client +from __future__ import absolute_import +from __future__ import print_function + import timeit -from common import Event + +from redisco import get_client +from .common import Event def create_events(): @@ -16,7 +20,7 @@ def find_events(): def display_results(results, name): - print "%s: 5000 Loops, best of 3: %.02f sec" % (name, min(results)) + print("%s: 5000 Loops, best of 3: %.02f sec" % (name, min(results))) def profile(): import cProfile diff --git a/redisco/__init__.py b/redisco/__init__.py index d7b507f16..26b110deb 100644 --- a/redisco/__init__.py +++ b/redisco/__init__.py @@ -7,7 +7,8 @@ default_connection_settings = { 'host': 'localhost', 'port': 6379, - 'db': 0 + 'db': 0, + 'decode_responses': True, } diff --git a/redisco/containers.py b/redisco/containers.py index 700bac5fe..37efd9333 100644 --- a/redisco/containers.py +++ b/redisco/containers.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- # doctest: +ELLIPSIS +from __future__ import absolute_import import collections -from functools import partial from . import default_expire_time +import six +from six.moves import range def _parse_values(values): @@ -35,7 +37,7 @@ def clear(self): 1 >>> s.clear() >>> s.members - set([]) + set() """ @@ -102,8 +104,8 @@ def sadd(self, *values): 3 >>> s.add(["4"]) 1 - >>> print s - + >>> s.members == {'4', '2', '1', '3'} + True >>> s.clear() """ @@ -138,7 +140,7 @@ def spop(self): >>> s.spop() '1' >>> s.members - set([]) + set() """ return self.db.spop(self.key) @@ -256,17 +258,17 @@ def union(self, key, *other_sets): 2 >>> s3 = s1.union('key3', s2) >>> s3.key - u'key3' - >>> s3.members - set(['a', 'c', 'b', 'e', 'd']) + 'key3' + >>> s3.members == {'e', 'd', 'b', 'a', 'c'} + True >>> s1.clear() >>> s2.clear() >>> s3.clear() """ - if not isinstance(key, str) and not isinstance(key, unicode): + if not isinstance(key, str) and not isinstance(key, six.text_type): raise ValueError("Expect a (unicode) string as key") - key = unicode(key) + key = six.text_type(key) self.db.sunionstore(key, [self.key] + [o.key for o in other_sets]) return Set(key) @@ -287,18 +289,18 @@ def intersection(self, key, *other_sets): 2 >>> s3 = s1.intersection('key3', s2) >>> s3.key - u'key3' + 'key3' >>> s3.members - set(['c']) + {'c'} >>> s1.clear() >>> s2.clear() >>> s3.clear() """ - if not isinstance(key, str) and not isinstance(key, unicode): + if not isinstance(key, str) and not isinstance(key, six.text_type): raise ValueError("Expect a (unicode) string as key") - key = unicode(key) + key = six.text_type(key) self.db.sinterstore(key, [self.key] + [o.key for o in other_sets]) return Set(key) @@ -319,17 +321,17 @@ def difference(self, key, *other_sets): 2 >>> s3 = s1.difference('key3', s2) >>> s3.key - u'key3' - >>> s3.members - set(['a', 'b']) + 'key3' + >>> s3.members == {'a', 'b'} + True >>> s1.clear() >>> s2.clear() >>> s3.clear() """ - if not isinstance(key, str) and not isinstance(key, unicode): + if not isinstance(key, str) and not isinstance(key, six.text_type): raise ValueError("Expect a (unicode) string as key") - key = unicode(key) + key = six.text_type(key) self.db.sdiffstore(key, [self.key] + [o.key for o in other_sets]) return Set(key) @@ -505,7 +507,7 @@ def lrange(self, start, stop): >>> l = List("test") >>> l.push(['a', 'b', 'c', 'd']) - 4L + 4 >>> l.lrange(1, 2) ['b', 'c'] >>> l.clear() @@ -522,7 +524,7 @@ def lpush(self, *values): >>> l = List("test") >>> l.lpush(['a', 'b']) - 2L + 2 >>> l.clear() """ return self.db.lpush(self.key, *_parse_values(values)) @@ -536,9 +538,9 @@ def rpush(self, *values): >>> l = List("test") >>> l.lpush(['a', 'b']) - 2L + 2 >>> l.rpush(['c', 'd']) - 4L + 4 >>> l.members ['b', 'a', 'c', 'd'] >>> l.clear() @@ -589,7 +591,7 @@ def rpoplpush(self, key): >>> l = List('list1') >>> l.push(['a', 'b', 'c']) - 3L + 3 >>> l.rpoplpush('list2') 'c' >>> l2 = List('list2') @@ -656,7 +658,7 @@ def lset(self, idx, value=0): >>> l = List('test') >>> l.push(['a', 'b', 'c']) - 3L + 3 >>> l.lset(0, 'e') True >>> l.members @@ -709,13 +711,13 @@ def __init__(self, key, target_type, type_args=[], type_kwargs={}, **kwargs): self.klass = self.value_type(target_type) self._klass_args = type_args self._klass_kwargs = type_kwargs - from models.base import Model + from .models.base import Model self._redisco_model = issubclass(self.klass, Model) def value_type(self, target_type): - if isinstance(target_type, basestring): + if isinstance(target_type, six.string_types): t = target_type - from models.base import get_model_from_key + from .models.base import get_model_from_key target_type = get_model_from_key(target_type) if target_type is None: raise ValueError("Unknown Redisco class %s" % t) @@ -729,7 +731,7 @@ def typecast_item(self, value): def typecast_iter(self, values): if self._redisco_model: - return filter(lambda o: o is not None, [self.klass.objects.get_by_id(v) for v in values]) + return [o for o in [self.klass.objects.get_by_id(v) for v in values] if o is not None] else: return [self.klass(v, *self._klass_args, **self._klass_kwargs) for v in values] @@ -757,13 +759,13 @@ def append(self, value): self.list.append(self.typecast_stor(value)) def extend(self, iter): - self.list.extend(map(lambda i: self.typecast_stor(i), iter)) + self.list.extend([self.typecast_stor(i) for i in iter]) def __setitem__(self, index, value): self.list[index] = self.typecast_stor(value) def __iter__(self): - for i in xrange(len(self.list)): + for i in range(len(self.list)): yield self[i] def __repr__(self): @@ -1211,7 +1213,7 @@ def hset(self, member, value): >>> h = Hash("foo") >>> h.hset("bar", "value") - 1L + 1 >>> h.clear() """ return self.db.hset(self.key, member, value) @@ -1225,7 +1227,7 @@ def hdel(self, *members): >>> h = Hash("foo") >>> h.hset("bar", "value") - 1L + 1 >>> h.hdel("bar") 1 >>> h.clear() @@ -1273,9 +1275,9 @@ def hincrby(self, field, increment=1): >>> h = Hash("foo") >>> h.hincrby("bar", 10) - 10L + 10 >>> h.hincrby("bar", 2) - 12L + 12 >>> h.clear() """ return self.db.hincrby(self.key, field, increment) diff --git a/redisco/containerstests.py b/redisco/containerstests.py index 34b2cdc96..18c24eb7b 100644 --- a/redisco/containerstests.py +++ b/redisco/containerstests.py @@ -1,6 +1,10 @@ +from __future__ import absolute_import import unittest import redisco from redisco import containers as cont +import six +from six.moves import range +from six.moves import zip class SetTestCase(unittest.TestCase): @@ -293,7 +297,7 @@ def test_common_operations(self): def test_pop_onto(self): a = cont.List('alpha') b = cont.List('beta') - a.extend(range(10)) + a.extend(list(range(10))) # test pop_onto a_snap = list(a.members) @@ -345,20 +349,20 @@ def tearDown(self): self.client.flushdb() def test_basic_types(self): - alpha = cont.TypedList('alpha', unicode, type_args=('UTF-8',)) + alpha = cont.TypedList('alpha', six.text_type) monies = u'\u0024\u00a2\u00a3\u00a5' alpha.append(monies) val = alpha[-1] self.assertEquals(monies, val) beta = cont.TypedList('beta', int) - for i in xrange(1000): + for i in range(1000): beta.append(i) for i, x in enumerate(beta): self.assertEquals(i, x) charlie = cont.TypedList('charlie', float) - for i in xrange(100): + for i in range(100): val = 1 * pow(10, i*-1) charlie.append(val) for i, x in enumerate(charlie): @@ -448,9 +452,9 @@ def test_basic(self): self.assertEqual({'name': "Richard Cypher", 'real_name': "Richard Rahl"}, h.dict) - self.assertEqual(['name', 'real_name'], h.keys()) + self.assertEqual(['name', 'real_name'], list(h.keys())) self.assertEqual(["Richard Cypher", "Richard Rahl"], - h.values()) + list(h.values())) del h['name'] pulled = self.client.hgetall('hkey') diff --git a/redisco/models/attributes.py b/redisco/models/attributes.py index c91c3ada0..12f351091 100644 --- a/redisco/models/attributes.py +++ b/redisco/models/attributes.py @@ -2,11 +2,15 @@ """ Defines the fields that can be added to redisco models. """ -import time +from __future__ import absolute_import + import sys +from calendar import timegm from datetime import datetime, date, timedelta + +import six from dateutil.tz import tzutc, tzlocal -from calendar import timegm + from redisco.containers import List from .exceptions import FieldValidationError, MissingID @@ -65,20 +69,21 @@ def __set__(self, instance, value): def typecast_for_read(self, value): """Typecasts the value for reading from Redis.""" # The redis client encodes all unicode data to utf-8 by default. - return value.decode('utf-8') + + return six.text_type(value) def typecast_for_storage(self, value): """Typecasts the value for storing to Redis.""" try: - return unicode(value) + return six.text_type(value) except UnicodeError: return value.decode('utf-8') def value_type(self): - return unicode + return six.text_type def acceptable_types(self): - return basestring + return six.string_types def validate(self, instance): val = getattr(instance, self.name) @@ -88,7 +93,7 @@ def validate(self, instance): errors.append((self.name, 'bad type',)) # validate first standard stuff if self.required: - if val is None or not unicode(val).strip(): + if val is None or not six.text_type(val).strip(): errors.append((self.name, 'required')) # validate uniquness if val and self.unique: @@ -161,13 +166,13 @@ def typecast_for_read(self, value): def typecast_for_storage(self, value): if value is None: return "0" - return unicode(value) + return six.text_type(value) def value_type(self): return int def acceptable_types(self): - return (int, long) + return six.integer_types class FloatField(Attribute): @@ -320,8 +325,8 @@ def __init__(self, target_type, self.required = required self.validator = validator self.default = default or [] - from base import Model - self._redisco_model = (isinstance(target_type, basestring) or + from .base import Model + self._redisco_model = (isinstance(target_type, six.string_types) or issubclass(target_type, Model)) def __get__(self, instance, owner): @@ -336,7 +341,7 @@ def __get__(self, instance, owner): if val is not None: klass = self.value_type() if self._redisco_model: - val = filter(lambda o: o is not None, [klass.objects.get_by_id(v) for v in val]) + val = [o for o in [klass.objects.get_by_id(v) for v in val] if o is not None] else: val = [klass(v) for v in val] self.__set__(instance, val) @@ -346,9 +351,9 @@ def __set__(self, instance, value): setattr(instance, '_' + self.name, value) def value_type(self): - if isinstance(self._target_type, basestring): + if isinstance(self._target_type, six.string_types): t = self._target_type - from base import get_model_from_key + from .base import get_model_from_key self._target_type = get_model_from_key(self._target_type) if self._target_type is None: raise ValueError("Unknown Redisco class %s" % t) @@ -495,7 +500,7 @@ def validate(self, instance): class Counter(IntegerField): def __init__(self, **kwargs): super(Counter, self).__init__(**kwargs) - if not kwargs.has_key('default') or self.default is None: + if 'default' not in kwargs or self.default is None: self.default = 0 def __set__(self, instance, value): diff --git a/redisco/models/base.py b/redisco/models/base.py index 28d9d44fa..551571971 100644 --- a/redisco/models/base.py +++ b/redisco/models/base.py @@ -1,18 +1,24 @@ +from __future__ import absolute_import + import time -from datetime import datetime, date +from datetime import datetime + +import six from dateutil.tz import tzutc + import redisco -from redisco.containers import Set, List, SortedSet, NonPersistentList +from redisco.containers import Set, List from .attributes import * +from .attributes import Counter +from .exceptions import FieldValidationError, MissingID, BadKeyError, WatchError from .key import Key from .managers import ManagerDescriptor, Manager -from .exceptions import FieldValidationError, MissingID, BadKeyError, WatchError -from .attributes import Counter __all__ = ['Model', 'from_key'] ZINDEXABLE = (IntegerField, DateTimeField, DateField, FloatField) + ############################## # Model Class Initialization # ############################## @@ -29,30 +35,32 @@ def _initialize_attributes(model_class, name, bases, attrs): for parent in bases: if not isinstance(parent, ModelBase): continue - for k, v in parent._attributes.iteritems(): + for k, v in six.iteritems(parent._attributes): model_class._attributes[k] = v - for k, v in attrs.iteritems(): + for k, v in six.iteritems(attrs): if isinstance(v, Attribute): model_class._attributes[k] = v v.name = v.name or k + def _initialize_referenced(model_class, attribute): """ Adds a property to the target of a reference field that returns the list of associated objects. """ + # this should be a descriptor def _related_objects(self): return (model_class.objects .filter(**{attribute.attname: self.id})) klass = attribute._target_type - if isinstance(klass, basestring): + if isinstance(klass, six.string_types): return (klass, model_class, attribute) else: related_name = (attribute.related_name or - model_class.__name__.lower() + '_set') + model_class.__name__.lower() + '_set') if not hasattr(klass, related_name): setattr(klass, related_name, property(_related_objects)) @@ -66,10 +74,10 @@ def _initialize_lists(model_class, name, bases, attrs): for parent in bases: if not isinstance(parent, ModelBase): continue - for k, v in parent._lists.iteritems(): + for k, v in six.iteritems(parent._lists): model_class._lists[k] = v - for k, v in attrs.iteritems(): + for k, v in six.iteritems(attrs): if isinstance(v, ListField): model_class._lists[k] = v v.name = v.name or k @@ -85,7 +93,7 @@ def _initialize_references(model_class, name, bases, attrs): for parent in bases: if not isinstance(parent, ModelBase): continue - for k, v in parent._references.iteritems(): + for k, v in six.iteritems(parent._references): model_class._references[k] = v # We skip updating the attributes since this is done # already at the parent construction and then copied back @@ -94,7 +102,7 @@ def _initialize_references(model_class, name, bases, attrs): if refd: deferred.append(refd) - for k, v in attrs.iteritems(): + for k, v in six.iteritems(attrs): if isinstance(v, ReferenceField): model_class._references[k] = v v.name = v.name or k @@ -116,14 +124,14 @@ def _initialize_indices(model_class, name, bases, attrs): for parent in bases: if not isinstance(parent, ModelBase): continue - for k, v in parent._attributes.iteritems(): + for k, v in six.iteritems(parent._attributes): if v.indexed: model_class._indices.append(k) - for k, v in parent._lists.iteritems(): + for k, v in six.iteritems(parent._lists): if v.indexed: model_class._indices.append(k) - for k, v in attrs.iteritems(): + for k, v in six.iteritems(attrs): if isinstance(v, (Attribute, ListField)) and v.indexed: model_class._indices.append(k) if model_class._meta['indices']: @@ -142,7 +150,7 @@ def _initialize_counters(model_class, name, bases, attrs): for c in parent._counters: model_class._counters.append(c) - for k, v in attrs.iteritems(): + for k, v in six.iteritems(attrs): if isinstance(v, Counter): # When subclassing, we want to override the attributes if k in model_class._counters: @@ -167,7 +175,7 @@ def _initialize_manager(model_class, name, bases, attrs): """ model_class.objects = ManagerDescriptor(Manager(model_class)) - for key, val in attrs.iteritems(): + for key, val in six.iteritems(attrs): if isinstance(val, type) and issubclass(val, Manager): attr_name = getattr(val, "__attr_name__", key.lower()) descriptor = ManagerDescriptor(val(model_class)) @@ -188,6 +196,7 @@ class ModelOptions(object): ... db = redis.Redis(host='localhost', port=29909) """ + def __init__(self, meta): self.meta = meta @@ -198,11 +207,13 @@ def get_field(self, field_name): return self.meta.__dict__[field_name] except KeyError: return None + __getitem__ = get_field _deferred_refs = [] + class ModelBase(type): """ Metaclass of the Model. @@ -230,9 +241,8 @@ def __init__(cls, name, bases, attrs): def __getitem__(self, id): return self.objects.get_by_id(id) -class Model(object): - __metaclass__ = ModelBase +class Model(six.with_metaclass(ModelBase, object)): def __init__(self, **kwargs): self.update_attributes(**kwargs) @@ -309,8 +319,8 @@ def update_attributes(self, **kwargs): >>> f.name 'Tesla' """ - attrs = self.attributes.values() + self.lists.values() \ - + self.references.values() + attrs = list(self.attributes.values()) + list(self.lists.values()) \ + + list(self.references.values()) for att in attrs: if att.name in kwargs: att.__set__(self, kwargs[att.name]) @@ -418,7 +428,6 @@ def decr(self, att, val=1): """ self.incr(att, -1 * val) - @property def attributes_dict(self): """ @@ -431,8 +440,8 @@ def attributes_dict(self): ... title = models.Attribute() ... >>> f = Foo(name="Einstein", title="Mr.") - >>> f.attributes_dict - {'name': 'Einstein', 'title': 'Mr.'} + >>> f.attributes_dict == {'name': 'Einstein', 'title': 'Mr.'} + True .. NOTE: the key ``id`` is present *only if* the object has been saved before. @@ -445,11 +454,10 @@ def attributes_dict(self): h[k] = getattr(self, k) for k in self.references.keys(): h[k] = getattr(self, k) - if 'id' not in self.attributes.keys() and not self.is_new(): + if 'id' not in list(self.attributes.keys()) and not self.is_new(): h['id'] = self.id return h - @property def id(self): """Returns the id of the instance. @@ -467,7 +475,7 @@ def id(self, val): """ self._id = str(val) stored_attrs = self.db.hgetall(self.key()) - attrs = self.attributes.values() + attrs = list(self.attributes.values()) for att in attrs: if att.name in stored_attrs and not isinstance(att, Counter): att.__set__(self, att.typecast_for_read(stored_attrs[att.name])) @@ -507,7 +515,8 @@ def references(self): @property def db(self): """Returns the Redis client used by the model.""" - return redisco.get_client() if not self._meta['db'] else self._meta['db'] + return redisco.get_client() if not self._meta['db'] else self._meta[ + 'db'] @property def errors(self): @@ -519,8 +528,8 @@ def errors(self): @property def fields(self): """Returns the list of field names of the model.""" - return (self.attributes.values() + self.lists.values() - + self.references.values()) + return (list(self.attributes.values()) + list(self.lists.values()) + + list(self.references.values())) @property def counters(self): @@ -534,8 +543,10 @@ def counters(self): @classmethod def exists(cls, id): """Checks if the model with id exists.""" - return bool((cls._meta['db'] or redisco.get_client()).exists(cls._key[str(id)]) or - (cls._meta['db'] or redisco.get_client()).sismember(cls._key['all'], str(id))) + return bool((cls._meta['db'] or redisco.get_client()).exists( + cls._key[str(id)]) or + (cls._meta['db'] or redisco.get_client()).sismember( + cls._key['all'], str(id))) ################### # Private methods # @@ -556,7 +567,7 @@ def _write(self, _new=False): self._update_indices(pipeline) h = {} # attributes - for k, v in self.attributes.iteritems(): + for k, v in six.iteritems(self.attributes): if isinstance(v, DateTimeField): if v.auto_now: setattr(self, k, datetime.now(tz=tzutc())) @@ -578,15 +589,15 @@ def _write(self, _new=False): v = v() if v: try: - h[index] = unicode(v) + h[index] = six.text_type(v) except UnicodeError: - h[index] = unicode(v.decode('utf-8')) + h[index] = six.text_type(v.decode('utf-8')) pipeline.delete(self.key()) if h: pipeline.hmset(self.key(), h) # lists - for k, v in self.lists.iteritems(): + for k, v in six.iteritems(self.lists): l = List(self.key()[k], pipeline=pipeline) l.clear() values = getattr(self, k) @@ -726,7 +737,6 @@ def __repr__(self): return "<%s %s>" % (self.__class__.__name__, self.attributes_dict) - def get_model_from_key(key): """Gets the model from a given key.""" _known_models = {} @@ -775,7 +785,8 @@ def lock(self): while True: try: pipe.watch(_lock_key) - if o.db.exists(_lock_key) and not self.lock_has_expired(o.db.get(_lock_key)): + if o.db.exists(_lock_key) and not self.lock_has_expired( + o.db.get(_lock_key)): continue pipe.multi() diff --git a/redisco/models/basetests.py b/redisco/models/basetests.py index 0c9de4a72..3cf29a57d 100644 --- a/redisco/models/basetests.py +++ b/redisco/models/basetests.py @@ -1,14 +1,18 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + import time -from threading import Thread -import redis -import redisco import unittest from datetime import date +from threading import Thread + +import six +from dateutil.tz import tzlocal + +import redisco from redisco import models from redisco.models import managers from redisco.models.base import Mutex -from dateutil.tz import tzlocal class Person(models.Model): @@ -81,17 +85,17 @@ def test_unicode(self): p = Person.objects.filter(first_name=u"Niña").first() self.assert_(p) - self.assert_(isinstance(p.full_name(), unicode)) + self.assert_(isinstance(p.full_name(), six.text_type)) self.assertEqual(u"Niña Jose", p.full_name()) def test_repr(self): person1 = Person(first_name="Granny", last_name="Goose") - self.assertEqual("", - repr(person1)) + self.assertEqual('Person', person1.__class__.__name__) + self.assertDictEqual({'first_name': 'Granny', 'last_name': 'Goose', 'active': False}, person1.attributes_dict) self.assert_(person1.save()) - self.assertEqual("", - repr(person1)) + self.assertEqual('Person:1', person1.key()) + self.assertDictEqual({'first_name': 'Granny', 'last_name': 'Goose', 'active': False, 'id': '1'}, person1.attributes_dict) def test_update(self): person1 = Person(first_name="Granny", last_name="Goose") diff --git a/redisco/models/exceptions.py b/redisco/models/exceptions.py index bf482742e..9f3d3c0fd 100755 --- a/redisco/models/exceptions.py +++ b/redisco/models/exceptions.py @@ -1,22 +1,27 @@ ########## # ERRORS # ########## +from __future__ import absolute_import from redis import WatchError + class Error(Exception): pass + class ValidationError(Error): pass + class MissingID(Error): pass + class AttributeNotIndexed(Error): pass -class FieldValidationError(Error): +class FieldValidationError(Error): def __init__(self, errors, *args, **kwargs): super(FieldValidationError, self).__init__(*args, **kwargs) self._errors = errors @@ -25,5 +30,6 @@ def __init__(self, errors, *args, **kwargs): def errors(self): return self._errors + class BadKeyError(Error): pass diff --git a/redisco/models/key.py b/redisco/models/key.py index 31325d8bf..a9c3c3e60 100644 --- a/redisco/models/key.py +++ b/redisco/models/key.py @@ -1,9 +1,14 @@ +from __future__ import absolute_import + +import six + try: - unicode + six.text_type except NameError: # Python 3 - basestring = unicode = str + six.string_types = six.text_type = str + -class Key(unicode): +class Key(six.text_type): def __getitem__(self, key): return Key(u"%s:%s" % (self, key)) diff --git a/redisco/models/managers.py b/redisco/models/managers.py index cac091629..1b923c322 100644 --- a/redisco/models/managers.py +++ b/redisco/models/managers.py @@ -1,5 +1,6 @@ from .modelset import ModelSet + ############ # Managers # ############ @@ -15,7 +16,6 @@ def __get__(self, instance, owner): class Manager(object): - def __init__(self, model_class): self.model_class = model_class @@ -45,5 +45,3 @@ def order(self, field): def zfilter(self, **kwargs): return self.get_model_set().zfilter(**kwargs) - - diff --git a/redisco/models/modelset.py b/redisco/models/modelset.py index 87e542901..043959529 100644 --- a/redisco/models/modelset.py +++ b/redisco/models/modelset.py @@ -1,11 +1,15 @@ """ Handles the queries. """ -from .attributes import IntegerField, DateTimeField +from __future__ import absolute_import + +import six + import redisco -from redisco.containers import SortedSet, Set, List, NonPersistentList -from .exceptions import AttributeNotIndexed +from redisco.containers import SortedSet, Set, List from .attributes import ZINDEXABLE +from .exceptions import AttributeNotIndexed + # Model Set class ModelSet(Set): @@ -31,7 +35,7 @@ def __getitem__(self, index): Will look in _set to get the id and simply return the instance of the model. """ if isinstance(index, slice): - return map(lambda id: self._get_item_with_id(id), self._set[index]) + return [self._get_item_with_id(id) for id in self._set[index]] else: id = self._set[index] if id: @@ -44,7 +48,7 @@ def __repr__(self): m = self._set[:30] else: m = self._set - s = map(lambda id: self._get_item_with_id(id), m) + s = [self._get_item_with_id(id) for id in m] return "%s" % s def __iter__(self): @@ -152,7 +156,7 @@ def exclude(self, **kwargs): >>> Foo(name="Edison", exclude_me=True).save() True >>> Foo.objects.exclude(exclude_me=True).first().name - u'Einstein' + 'Einstein' >>> [f.delete() for f in Foo.objects.all()] # doctest: +ELLIPSIS [...] """ @@ -186,9 +190,9 @@ def order(self, field): >>> Foo(name="Zztop").save() True >>> Foo.objects.all().order("-name").first().name - u'Zztop' + 'Zztop' >>> Foo.objects.all().order("name").first().name - u'Abba' + 'Abba' >>> [f.delete() for f in Foo.objects.all()] # doctest: +ELLIPSIS [...] """ @@ -257,7 +261,7 @@ def get_or_create(self, **kwargs): [...] """ opts = {} - for k, v in kwargs.iteritems(): + for k, v in six.iteritems(kwargs): if k in self.model_class._indices: opts[k] = v o = self.filter(**opts).first() @@ -308,7 +312,7 @@ def _add_set_filter(self, s): :return: the new Set """ indices = [] - for k, v in self._filters.iteritems(): + for k, v in six.iteritems(self._filters): index = self._build_key_from_filter_item(k, v) if k not in self.model_class._indices: raise AttributeNotIndexed( @@ -332,7 +336,7 @@ def _add_set_exclusions(self, s): :return: the new Set """ indices = [] - for k, v in self._exclusions.iteritems(): + for k, v in six.iteritems(self._exclusions): index = self._build_key_from_filter_item(k, v) if k not in self.model_class._indices: raise AttributeNotIndexed( @@ -354,7 +358,7 @@ def _add_zfilters(self, s): :return: a SortedSet with the ids. """ - k, v = self._zfilters[0].items()[0] + k, v = list(self._zfilters[0].items())[0] try: att, op = k.split('__') except ValueError: diff --git a/redisco/tests/__init__.py b/redisco/tests/__init__.py index 4868d4d3d..3685a5814 100644 --- a/redisco/tests/__init__.py +++ b/redisco/tests/__init__.py @@ -1,18 +1,27 @@ +from __future__ import absolute_import import os import unittest -from redisco.containerstests import (SetTestCase, ListTestCase, TypedListTestCase, - SortedSetTestCase, HashTestCase) -from redisco.models.basetests import (ModelTestCase, DateFieldTestCase, FloatFieldTestCase, - BooleanFieldTestCase, ListFieldTestCase, ReferenceFieldTestCase, - TimeDeltaFieldTestCase, DateTimeFieldTestCase, CounterFieldTestCase, - CharFieldTestCase, MutexTestCase) import redisco -REDIS_DB = int(os.environ.get('REDIS_DB', 15)) # WARNING TESTS FLUSHDB!!! +from redisco.containerstests import (SetTestCase, ListTestCase, + TypedListTestCase, + SortedSetTestCase, HashTestCase) +from redisco.models.basetests import (ModelTestCase, DateFieldTestCase, + FloatFieldTestCase, + BooleanFieldTestCase, ListFieldTestCase, + ReferenceFieldTestCase, + TimeDeltaFieldTestCase, + DateTimeFieldTestCase, + CounterFieldTestCase, + CharFieldTestCase, MutexTestCase) + +REDIS_DB = int(os.environ.get('REDIS_DB', 15)) # WARNING TESTS FLUSHDB!!! REDIS_PORT = int(os.environ.get('REDIS_PORT', 6379)) redisco.connection_setup(host="localhost", port=REDIS_PORT, db=REDIS_DB) -typed_list_suite = unittest.TestLoader().loadTestsFromTestCase(TypedListTestCase) +typed_list_suite = unittest.TestLoader().loadTestsFromTestCase( + TypedListTestCase) + def all_tests(): suite = unittest.TestSuite() diff --git a/requirements.txt b/requirements.txt index ed402347d..112c9c69e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ DateUtils==0.6.6 -hiredis==0.1.1 +hiredis==0.1.3 redis>=2.7.5 -redislite>=1.0.228 \ No newline at end of file diff --git a/setup.py b/setup.py index 2467e0560..83b3ea3e8 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python import os -version = '0.2.9' +version = '0.3.0' try: from setuptools import setup @@ -18,9 +18,6 @@ def read(fname): download_url='', long_description=read('README.rst'), install_requires=read('requirements.txt').splitlines(), - extras_require = { - 'redislite': ["redislite>=1.0.228"] - }, author='Tim Medina', author_email='iamteem@gmail.com', maintainer='Sebastien Requiem',