diff --git a/djangotoolbox/db/base.py b/djangotoolbox/db/base.py index ab7b9ab..89e1bb9 100644 --- a/djangotoolbox/db/base.py +++ b/djangotoolbox/db/base.py @@ -446,7 +446,7 @@ def _value_for_db_collection(self, value, field, field_kind, db_type, # Do convert filter parameters. if lookup: # Special case where we are looking for an empty list - if lookup == 'exact' and db_type == 'list' and value == u'[]': + if lookup == 'exact' and db_type == 'list' and value == '[]': return [] value = self._value_for_db(value, subfield, subkind, db_subtype, lookup) @@ -459,7 +459,7 @@ def _value_for_db_collection(self, value, field, field_kind, db_type, value = ( (key, self._value_for_db(subvalue, subfield, subkind, db_subtype, lookup)) - for key, subvalue in value.iteritems()) + for key, subvalue in value.items()) # Return just a dict, a once-flattened list; if db_type == 'dict': @@ -514,9 +514,9 @@ def _value_from_db_collection(self, value, field, field_kind, db_type): # Generator yielding pairs with deconverted values, the # "list" db_type stores keys and values interleaved. if db_type == 'list': - value = zip(value[::2], value[1::2]) + value = list(zip(value[::2], value[1::2])) else: - value = value.iteritems() + value = iter(value.items()) # DictField needs to hold a dict. return dict( @@ -575,7 +575,7 @@ def _value_for_db_model(self, value, field, field_kind, db_type, lookup): value = ( (subfield.column, self._value_for_db( subvalue, lookup=lookup, *self._convert_as(subfield, lookup))) - for subfield, subvalue in value.iteritems()) + for subfield, subvalue in value.items()) # Cast to a dict, interleave columns with values on a list, # serialize, or return a generator. @@ -603,7 +603,7 @@ def _value_from_db_model(self, value, field, field_kind, db_type): # Separate keys from values and create a dict or unpickle one. if db_type == 'list': - value = dict(zip(value[::2], value[1::2])) + value = dict(list(zip(value[::2], value[1::2]))) elif db_type == 'bytes' or db_type == 'string': value = pickle.loads(value) diff --git a/djangotoolbox/db/basecompiler.py b/djangotoolbox/db/basecompiler.py index d49a4f9..5c5eae9 100644 --- a/djangotoolbox/db/basecompiler.py +++ b/djangotoolbox/db/basecompiler.py @@ -233,7 +233,7 @@ def _decode_child(self, child): raise DatabaseError("This database doesn't support filtering " "on non-primary key ForeignKey fields.") - field = (f for f in opts.fields if f.column == column).next() + field = next((f for f in opts.fields if f.column == column)) assert field.rel is not None value = self._normalize_lookup_value( @@ -421,7 +421,7 @@ def execute_sql(self, result_type=MULTI): """ self.pre_sql_setup() - aggregates = self.query.aggregate_select.values() + aggregates = list(self.query.aggregate_select.values()) # Simulate a count(). if aggregates: @@ -547,9 +547,9 @@ def get_fields(self): only_load = self.deferred_to_columns() if only_load: db_table = self.query.model._meta.db_table - only_load = dict((k, v) for k, v in only_load.items() + only_load = dict((k, v) for k, v in list(only_load.items()) if v or k == db_table) - if len(only_load.keys()) > 1: + if len(list(only_load.keys())) > 1: raise DatabaseError("Multi-table inheritance is not " "supported by non-relational DBs %s." % repr(only_load)) diff --git a/djangotoolbox/db/utils.py b/djangotoolbox/db/utils.py index 9cf4271..1ad3fa2 100644 --- a/djangotoolbox/db/utils.py +++ b/djangotoolbox/db/utils.py @@ -16,10 +16,10 @@ def decimal_to_string(value, max_digits=16, decimal_places=0): # Handle sign separately. if value.is_signed(): - sign = u'-' + sign = '-' value = abs(value) else: - sign = u'' + sign = '' # Let Django quantize and cast to a string. value = format_number(value, max_digits, decimal_places) @@ -29,5 +29,5 @@ def decimal_to_string(value, max_digits=16, decimal_places=0): if n < 0: n = len(value) if n < max_digits - decimal_places: - value = u'0' * (max_digits - decimal_places - n) + value + value = '0' * (max_digits - decimal_places - n) + value return sign + value diff --git a/djangotoolbox/fields.py b/djangotoolbox/fields.py index c4e877b..1c4a24e 100644 --- a/djangotoolbox/fields.py +++ b/djangotoolbox/fields.py @@ -6,6 +6,7 @@ from django.db.models.fields.subclassing import Creator from django.db.utils import IntegrityError from django.db.models.fields.related import add_lazy_relation +import collections __all__ = ('RawField', 'ListField', 'SetField', 'DictField', @@ -58,7 +59,7 @@ def __init__(self, item_field=None, *args, **kwargs): # Ensure a new object is created every time the default is # accessed. - if default is not None and not callable(default): + if default is not None and not isinstance(default, collections.Callable): kwargs['default'] = lambda: self._type(default) super(AbstractIterableField, self).__init__(*args, **kwargs) @@ -66,7 +67,7 @@ def __init__(self, item_field=None, *args, **kwargs): # Either use the provided item_field or a RawField. if item_field is None: item_field = RawField() - elif callable(item_field): + elif isinstance(item_field, collections.Callable): item_field = item_field() self.item_field = item_field @@ -85,7 +86,7 @@ def contribute_to_class(self, cls, name): if item_metaclass and issubclass(item_metaclass, models.SubfieldBase): setattr(cls, self.name, Creator(self)) - if isinstance(self.item_field, models.ForeignKey) and isinstance(self.item_field.rel.to, basestring): + if isinstance(self.item_field, models.ForeignKey) and isinstance(self.item_field.rel.to, str): """ If rel.to is a string because the actual class is not yet defined, look up the actual class later. Refer to django.models.fields.related.RelatedField.contribute_to_class. @@ -176,7 +177,7 @@ class ListField(AbstractIterableField): def __init__(self, *args, **kwargs): self.ordering = kwargs.pop('ordering', None) - if self.ordering is not None and not callable(self.ordering): + if self.ordering is not None and not isinstance(self.ordering, collections.Callable): raise TypeError("'ordering' has to be a callable or None, " "not of type %r." % type(self.ordering)) super(ListField, self).__init__(*args, **kwargs) @@ -225,7 +226,7 @@ def get_internal_type(self): def _map(self, function, iterable, *args, **kwargs): return self._type((key, function(value, *args, **kwargs)) - for key, value in iterable.iteritems()) + for key, value in iterable.items()) def validate(self, values, model_instance): if not isinstance(values, dict): @@ -233,7 +234,7 @@ def validate(self, values, model_instance): type(values)) -class EmbeddedModelField(models.Field): +class EmbeddedModelField(models.Field, metaclass=models.SubfieldBase): """ Field that allows you to embed a model instance. @@ -245,7 +246,6 @@ class EmbeddedModelField(models.Field): the embedded instance (not just pre_save, get_db_prep_* and to_python). """ - __metaclass__ = models.SubfieldBase def __init__(self, embedded_model=None, *args, **kwargs): self.embedded_model = embedded_model @@ -271,7 +271,7 @@ def _set_model(self, model): our "model" attribute in its contribute_to_class method). """ self._model = model - if model is not None and isinstance(self.embedded_model, basestring): + if model is not None and isinstance(self.embedded_model, str): def _resolve_lookup(self_, resolved_model, model): self.embedded_model = resolved_model diff --git a/djangotoolbox/tests.py b/djangotoolbox/tests.py index c3bd8a8..c5e003d 100644 --- a/djangotoolbox/tests.py +++ b/djangotoolbox/tests.py @@ -1,4 +1,4 @@ -from __future__ import with_statement + from decimal import Decimal, InvalidOperation import time @@ -95,16 +95,16 @@ class EmbeddedModel(models.Model): class IterableFieldsTest(TestCase): floats = [5.3, 2.6, 9.1, 1.58] - names = [u'Kakashi', u'Naruto', u'Sasuke', u'Sakura'] + names = ['Kakashi', 'Naruto', 'Sasuke', 'Sakura'] unordered_ints = [4, 2, 6, 1] def setUp(self): - for i, float in zip(range(1, 5), IterableFieldsTest.floats): + for i, float in zip(list(range(1, 5)), IterableFieldsTest.floats): ListModel(integer=i, floating_point=float, names=IterableFieldsTest.names[:i]).save() def test_startswith(self): - self.assertEquals( + self.assertEqual( dict([(entity.pk, entity.names) for entity in ListModel.objects.filter(names__startswith='Sa')]), dict([(3, ['Kakashi', 'Naruto', 'Sasuke']), @@ -142,74 +142,74 @@ def test_ordering(self): self.assertLessEqual(f.ordering.calls, len(self.unordered_ints)) def test_gt(self): - self.assertEquals( + self.assertEqual( dict([(entity.pk, entity.names) for entity in ListModel.objects.filter(names__gt='Kakashi')]), - dict([(2, [u'Kakashi', u'Naruto']), - (3, [u'Kakashi', u'Naruto', u'Sasuke']), - (4, [u'Kakashi', u'Naruto', u'Sasuke', u'Sakura']), ])) + dict([(2, ['Kakashi', 'Naruto']), + (3, ['Kakashi', 'Naruto', 'Sasuke']), + (4, ['Kakashi', 'Naruto', 'Sasuke', 'Sakura']), ])) def test_lt(self): - self.assertEquals( + self.assertEqual( dict([(entity.pk, entity.names) for entity in ListModel.objects.filter(names__lt='Naruto')]), - dict([(1, [u'Kakashi']), - (2, [u'Kakashi', u'Naruto']), - (3, [u'Kakashi', u'Naruto', u'Sasuke']), - (4, [u'Kakashi', u'Naruto', u'Sasuke', u'Sakura']), ])) + dict([(1, ['Kakashi']), + (2, ['Kakashi', 'Naruto']), + (3, ['Kakashi', 'Naruto', 'Sasuke']), + (4, ['Kakashi', 'Naruto', 'Sasuke', 'Sakura']), ])) def test_gte(self): - self.assertEquals( + self.assertEqual( dict([(entity.pk, entity.names) for entity in ListModel.objects.filter(names__gte='Sakura')]), - dict([(3, [u'Kakashi', u'Naruto', u'Sasuke']), - (4, [u'Kakashi', u'Naruto', u'Sasuke', u'Sakura']), ])) + dict([(3, ['Kakashi', 'Naruto', 'Sasuke']), + (4, ['Kakashi', 'Naruto', 'Sasuke', 'Sakura']), ])) def test_lte(self): - self.assertEquals( + self.assertEqual( dict([(entity.pk, entity.names) for entity in ListModel.objects.filter(names__lte='Kakashi')]), - dict([(1, [u'Kakashi']), - (2, [u'Kakashi', u'Naruto']), - (3, [u'Kakashi', u'Naruto', u'Sasuke']), - (4, [u'Kakashi', u'Naruto', u'Sasuke', u'Sakura']), ])) + dict([(1, ['Kakashi']), + (2, ['Kakashi', 'Naruto']), + (3, ['Kakashi', 'Naruto', 'Sasuke']), + (4, ['Kakashi', 'Naruto', 'Sasuke', 'Sakura']), ])) def test_equals(self): - self.assertEquals([entity.names for entity in + self.assertEqual([entity.names for entity in ListModel.objects.filter(names='Sakura')], - [[u'Kakashi', u'Naruto', u'Sasuke', u'Sakura']]) + [['Kakashi', 'Naruto', 'Sasuke', 'Sakura']]) # Test with additonal pk filter (for DBs that have special pk # queries). query = ListModel.objects.filter(names='Sakura') - self.assertEquals(query.get(pk=query[0].pk).names, - [u'Kakashi', u'Naruto', u'Sasuke', u'Sakura']) + self.assertEqual(query.get(pk=query[0].pk).names, + ['Kakashi', 'Naruto', 'Sasuke', 'Sakura']) def test_is_null(self): - self.assertEquals(ListModel.objects.filter( + self.assertEqual(ListModel.objects.filter( names__isnull=True).count(), 0) def test_exclude(self): - self.assertEquals( + self.assertEqual( dict([(entity.pk, entity.names) for entity in ListModel.objects.all().exclude(names__lt='Sakura')]), - dict([(3, [u'Kakashi', u'Naruto', u'Sasuke']), - (4, [u'Kakashi', u'Naruto', u'Sasuke', u'Sakura']), ])) + dict([(3, ['Kakashi', 'Naruto', 'Sasuke']), + (4, ['Kakashi', 'Naruto', 'Sasuke', 'Sakura']), ])) def test_chained_filter(self): - self.assertEquals( + self.assertEqual( [entity.names for entity in ListModel.objects .filter(names='Sasuke').filter(names='Sakura')], [['Kakashi', 'Naruto', 'Sasuke', 'Sakura'], ]) - self.assertEquals( + self.assertEqual( [entity.names for entity in ListModel.objects .filter(names__startswith='Sa').filter(names='Sakura')], [['Kakashi', 'Naruto', 'Sasuke', 'Sakura']]) # Test across multiple columns. On app engine only one filter # is allowed to be an inequality filter. - self.assertEquals( + self.assertEqual( [entity.names for entity in ListModel.objects .filter(floating_point=9.1).filter(names__startswith='Sa')], [['Kakashi', 'Naruto', 'Sasuke'], ]) @@ -217,7 +217,7 @@ def test_chained_filter(self): def test_setfield(self): setdata = [1, 2, 3, 2, 1] # At the same time test value conversion. - SetModel(setfield=map(str, setdata)).save() + SetModel(setfield=list(map(str, setdata))).save() item = SetModel.objects.filter(setfield=3)[0] self.assertEqual(item.setfield, set(setdata)) # This shouldn't raise an error because the default value is @@ -228,7 +228,7 @@ def test_dictfield(self): DictModel(dictfield=dict(a=1, b='55', foo=3.14), auto_now={'a': None}).save() item = DictModel.objects.get() - self.assertEqual(item.dictfield, {u'a': 1, u'b': 55, u'foo': 3}) + self.assertEqual(item.dictfield, {'a': 1, 'b': 55, 'foo': 3}) dt = item.auto_now['a'] self.assertNotEqual(dt, None) @@ -245,7 +245,7 @@ def test_dictfield(self): @skip("GAE specific?") def test_Q_objects(self): - self.assertEquals( + self.assertEqual( [entity.names for entity in ListModel.objects .exclude(Q(names__lt='Sakura') | Q(names__gte='Sasuke'))], [['Kakashi', 'Naruto', 'Sasuke', 'Sakura']]) @@ -386,35 +386,35 @@ def test_error_messages(self): ({'simple': 42}, EmbeddedModel), ({'simple_untyped': 42}, models.Model), ({'typed_list': [EmbeddedModel()]}, SetModel)): - self.assertRaisesRegexp( + self.assertRaisesRegex( TypeError, "Expected instance of type %r." % expected, EmbeddedModelFieldModel(**kwargs).save) def test_typed_listfield(self): EmbeddedModelFieldModel.objects.create( - typed_list=[SetModel(setfield=range(3)), - SetModel(setfield=range(9))], - ordered_list=[Target(index=i) for i in xrange(5, 0, -1)]) + typed_list=[SetModel(setfield=list(range(3))), + SetModel(setfield=list(range(9)))], + ordered_list=[Target(index=i) for i in range(5, 0, -1)]) obj = EmbeddedModelFieldModel.objects.get() self.assertIn(5, obj.typed_list[1].setfield) self.assertEqual([target.index for target in obj.ordered_list], - range(1, 6)) + list(range(1, 6))) def test_untyped_listfield(self): EmbeddedModelFieldModel.objects.create(untyped_list=[ EmbeddedModel(someint=7), - OrderedListModel(ordered_ints=range(5, 0, -1)), + OrderedListModel(ordered_ints=list(range(5, 0, -1))), SetModel(setfield=[1, 2, 2, 3])]) instances = EmbeddedModelFieldModel.objects.get().untyped_list for instance, cls in zip(instances, [EmbeddedModel, OrderedListModel, SetModel]): self.assertIsInstance(instance, cls) self.assertNotEqual(instances[0].auto_now, None) - self.assertEqual(instances[1].ordered_ints, range(1, 6)) + self.assertEqual(instances[1].ordered_ints, list(range(1, 6))) def test_untyped_dict(self): EmbeddedModelFieldModel.objects.create(untyped_dict={ - 'a': SetModel(setfield=range(3)), + 'a': SetModel(setfield=list(range(3))), 'b': DictModel(dictfield={'a': 1, 'b': 2}), 'c': DictModel(dictfield={}, auto_now={'y': 1})}) data = EmbeddedModelFieldModel.objects.get().untyped_dict @@ -716,15 +716,15 @@ def test_filter(self): d = DecimalModel.objects.get(decimal=Decimal('5.0')) self.assertTrue(isinstance(d.decimal, Decimal)) - self.assertEquals(str(d.decimal), '5.00') + self.assertEqual(str(d.decimal), '5.00') d = DecimalModel.objects.get(decimal=Decimal('45.60')) - self.assertEquals(str(d.decimal), '45.60') + self.assertEqual(str(d.decimal), '45.60') # Filter argument should be converted to Decimal with 2 decimal #_places. d = DecimalModel.objects.get(decimal='0000345.67333333333333333') - self.assertEquals(str(d.decimal), '345.67') + self.assertEqual(str(d.decimal), '345.67') def test_order(self): """ @@ -733,7 +733,7 @@ def test_order(self): """ rows = DecimalModel.objects.all().order_by('decimal') values = list(d.decimal for d in rows) - self.assertEquals(values, sorted(values)) + self.assertEqual(values, sorted(values)) def test_sign_extend(self): DecimalModel(decimal=Decimal('-0.0')).save() @@ -766,12 +766,12 @@ def test_model_delete(self): def test_delete_all(self): DeleteModel.objects.all().delete() - self.assertEquals(0, DeleteModel.objects.all().count()) + self.assertEqual(0, DeleteModel.objects.all().count()) def test_delete_filtered(self): DeleteModel.objects.filter(deletable=True).delete() - self.assertEquals(5, DeleteModel.objects.all().count()) + self.assertEqual(5, DeleteModel.objects.all().count()) class M2MDeleteChildModel(models.Model): @@ -804,13 +804,13 @@ def test_model_delete(self): def test_delete_all(self): M2MDeleteModel.objects.all().delete() - self.assertEquals(0, M2MDeleteModel.objects.all().count()) + self.assertEqual(0, M2MDeleteModel.objects.all().count()) @expectedFailure def test_delete_filtered(self): M2MDeleteModel.objects.filter(deletable=True).delete() - self.assertEquals(5, M2MDeleteModel.objects.all().count()) + self.assertEqual(5, M2MDeleteModel.objects.all().count()) class QuerysetModel(models.Model): diff --git a/djangotoolbox/utils.py b/djangotoolbox/utils.py index e4d2ed9..570e8ac 100644 --- a/djangotoolbox/utils.py +++ b/djangotoolbox/utils.py @@ -1,3 +1,4 @@ +import collections def make_tls_property(default=None): """ Creates a class-wide instance property with a thread-specific @@ -37,7 +38,7 @@ def getattr_by_path(obj, attr, *default): if not hasattr(value, part) and len(default): return default[0] value = getattr(value, part) - if callable(value): + if isinstance(value, collections.Callable): value = value() return value