From bcf072d7faa7ad36cacf5688a1264970138778af Mon Sep 17 00:00:00 2001 From: Menno Visser Date: Tue, 3 Feb 2015 13:24:04 +0100 Subject: [PATCH 1/5] Initial py3 compatibility. --- djangotoolbox/fields.py | 7 ++++--- djangotoolbox/test.py | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/djangotoolbox/fields.py b/djangotoolbox/fields.py index c4e877b..9f854d4 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 +from django.utils import six __all__ = ('RawField', 'ListField', 'SetField', 'DictField', @@ -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, six.string_types): """ 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. @@ -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 six.iteritems(iterable)) def validate(self, values, model_instance): if not isinstance(values, dict): @@ -271,7 +272,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, six.string_types): def _resolve_lookup(self_, resolved_model, model): self.embedded_model = resolved_model diff --git a/djangotoolbox/test.py b/djangotoolbox/test.py index 32ffcb7..8228acb 100644 --- a/djangotoolbox/test.py +++ b/djangotoolbox/test.py @@ -42,14 +42,14 @@ def validate_state(self, columns, *state_table): current_state = object_list_to_table( columns, self.model.all())[1:] if not equal_lists(current_state, state_table): - print "DB state not valid:" - print "Current state:" - print columns + print("DB state not valid:") + print("Current state:") + print(columns) for state in current_state: - print state - print "Should be:" + print(state) + print("Should be:") for state in state_table: - print state + print(state) self.fail("DB state not valid.") From dec8003e17eaa2fd1a06265ba376c9af537249fd Mon Sep 17 00:00:00 2001 From: Menno Visser Date: Tue, 3 Feb 2015 15:30:07 +0100 Subject: [PATCH 2/5] Py3 compatibility --- djangotoolbox/db/base.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/djangotoolbox/db/base.py b/djangotoolbox/db/base.py index 5dfa881..74cb133 100644 --- a/djangotoolbox/db/base.py +++ b/djangotoolbox/db/base.py @@ -1,5 +1,5 @@ -from django.utils.six.moves import cPickle as pickle import datetime +import six from django.conf import settings from django.db.backends import ( @@ -11,8 +11,16 @@ BaseDatabaseIntrospection) from django.db.utils import DatabaseError from django.utils.functional import Promise -from django.utils.safestring import EscapeString, EscapeUnicode, SafeString, \ - SafeUnicode +from django.utils.safestring import EscapeString, SafeString + +if six.PY3: + import pickle + EscapeUnicode = EscapeString + SafeUnicode = SafeString + StandardError = Exception +else: + import cPickle as pickle + from django.utils.safestring import EscapeUnicode, SafeUnicode from django.utils import timezone from .creation import NonrelDatabaseCreation From 8e7ba8da4ed1f7e66bdb240f0dffc04549923d98 Mon Sep 17 00:00:00 2001 From: Menno Visser Date: Tue, 3 Feb 2015 17:50:08 +0100 Subject: [PATCH 3/5] Python 3 compatibility changes --- djangotoolbox/db/base.py | 6 +++--- djangotoolbox/db/basecompiler.py | 7 ++++--- djangotoolbox/fields.py | 8 +++----- djangotoolbox/tests.py | 17 +++++++++-------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/djangotoolbox/db/base.py b/djangotoolbox/db/base.py index 74cb133..b459312 100644 --- a/djangotoolbox/db/base.py +++ b/djangotoolbox/db/base.py @@ -443,7 +443,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 six.iteritems(value)) # Return just a dict, a once-flattened list; if db_type == 'dict': @@ -500,7 +500,7 @@ def _value_from_db_collection(self, value, field, field_kind, db_type): if db_type == 'list': value = zip(value[::2], value[1::2]) else: - value = value.iteritems() + value = six.iteritems(value) # DictField needs to hold a dict. return dict( @@ -559,7 +559,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 six.iteritems(value)) # Cast to a dict, interleave columns with values on a list, # serialize, or return a generator. diff --git a/djangotoolbox/db/basecompiler.py b/djangotoolbox/db/basecompiler.py index 71d9ec8..73f712a 100644 --- a/djangotoolbox/db/basecompiler.py +++ b/djangotoolbox/db/basecompiler.py @@ -10,6 +10,7 @@ from django.db.models.sql.where import AND, OR from django.db.utils import DatabaseError, IntegrityError from django.utils.tree import Node +from django.utils import six from django.db import connections try: @@ -209,7 +210,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 = six.next(f for f in opts.fields if f.column == column) assert field.rel is not None value = self._normalize_lookup_value( @@ -388,7 +389,7 @@ def execute_sql(self, result_type=MULTI): Handles SQL-like aggregate queries. This class only emulates COUNT by using abstract NonrelQuery.count method. """ - aggregates = self.query.aggregate_select.values() + aggregates = list(self.query.aggregate_select.values()) # Simulate a count(). if aggregates: @@ -503,7 +504,7 @@ def get_fields(self): db_table = self.query.model._meta.db_table only_load = dict((k, v) for k, v in only_load.items() if v or k == db_table) - if len(only_load.keys()) > 1: + if len(only_load) > 1: raise DatabaseError("Multi-table inheritance is not " "supported by non-relational DBs %s." % repr(only_load)) diff --git a/djangotoolbox/fields.py b/djangotoolbox/fields.py index 9f854d4..5c6c0aa 100644 --- a/djangotoolbox/fields.py +++ b/djangotoolbox/fields.py @@ -82,8 +82,8 @@ def contribute_to_class(self, cls, name): super(AbstractIterableField, self).contribute_to_class(cls, name) # If items' field uses SubfieldBase we also need to. - item_metaclass = getattr(self.item_field, '__metaclass__', None) - if item_metaclass and issubclass(item_metaclass, models.SubfieldBase): + item_metaclass = type(self.item_field) + if 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, six.string_types): @@ -234,7 +234,7 @@ def validate(self, values, model_instance): type(values)) -class EmbeddedModelField(models.Field): +class EmbeddedModelField(six.with_metaclass(models.SubfieldBase, models.Field)): """ Field that allows you to embed a model instance. @@ -246,8 +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 kwargs.setdefault('default', None) diff --git a/djangotoolbox/tests.py b/djangotoolbox/tests.py index c3bd8a8..d1db411 100644 --- a/djangotoolbox/tests.py +++ b/djangotoolbox/tests.py @@ -10,6 +10,7 @@ from django.dispatch.dispatcher import receiver from django.test import TestCase from django.utils.unittest import expectedFailure, skip +from django.utils import six from .fields import ListField, SetField, DictField, EmbeddedModelField @@ -217,7 +218,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 @@ -392,29 +393,29 @@ def test_error_messages(self): 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 six.moves.xrange(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 From f3cc40879cbceca045110410e963711da6ab6d01 Mon Sep 17 00:00:00 2001 From: Menno Visser Date: Tue, 24 Feb 2015 16:29:11 +0100 Subject: [PATCH 4/5] Py3 compatibility: Fixed up imports based on feedback. --- djangotoolbox/db/base.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/djangotoolbox/db/base.py b/djangotoolbox/db/base.py index b459312..e499ff9 100644 --- a/djangotoolbox/db/base.py +++ b/djangotoolbox/db/base.py @@ -1,5 +1,6 @@ +from django.utils.six.moves import cPickle as pickle import datetime -import six +from django.utils import six from django.conf import settings from django.db.backends import ( @@ -14,12 +15,10 @@ from django.utils.safestring import EscapeString, SafeString if six.PY3: - import pickle EscapeUnicode = EscapeString SafeUnicode = SafeString StandardError = Exception else: - import cPickle as pickle from django.utils.safestring import EscapeUnicode, SafeUnicode from django.utils import timezone From 6590de12dfa8c27b620c43cd94ba97cd85513ef7 Mon Sep 17 00:00:00 2001 From: Menno Visser Date: Tue, 24 Feb 2015 16:32:10 +0100 Subject: [PATCH 5/5] Py3 compatibility: found a few more locations where compatibility was fixed. --- djangotoolbox/db/base.py | 2 +- djangotoolbox/db/basecompiler.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/djangotoolbox/db/base.py b/djangotoolbox/db/base.py index e499ff9..1c74c9e 100644 --- a/djangotoolbox/db/base.py +++ b/djangotoolbox/db/base.py @@ -336,7 +336,7 @@ def _value_for_db(self, value, field, field_kind, db_type, lookup): # Also research cases of database operations not done # through the sql.Query. if isinstance(value, Promise): - value = unicode(value) + value = six.text_type(value) # Django wraps strings marked as safe or needed escaping, # convert them to just strings for type-inspecting back-ends. diff --git a/djangotoolbox/db/basecompiler.py b/djangotoolbox/db/basecompiler.py index 73f712a..571815c 100644 --- a/djangotoolbox/db/basecompiler.py +++ b/djangotoolbox/db/basecompiler.py @@ -339,7 +339,9 @@ def _matches_filters(self, entity, filters): def _order_in_memory(self, lhs, rhs): for field, ascending in self.compiler._get_ordering(): column = field.column - result = cmp(lhs.get(column), rhs.get(column)) + left = lhs.get(column) + right = rhs.get(column) + result = -1 if left < right else (0 if left == right else 1) if result != 0: return result if ascending else -result return 0