From 4d7d33ed0d3349fa9e8ed4d00bb51a8c296e7d79 Mon Sep 17 00:00:00 2001 From: Diederik van der Boor Date: Mon, 8 Apr 2013 01:04:00 +0200 Subject: [PATCH] Port new code to Python 3 as well, fix six.with_metaclass() issues Many thanks to @atheiste for the big porting steps! --- .travis.yml | 1 + polymorphic/base.py | 12 +++++++++--- polymorphic/query_translate.py | 2 +- polymorphic/tests.py | 29 ++++++++++++++++++----------- polymorphic/tools_for_tests.py | 10 +++++----- tox.ini | 6 ++++++ 6 files changed, 40 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1248012..b02a3f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: python python: - "2.6" - "2.7" + - "3.3" env: - DJANGO=django==1.4.5 - DJANGO=django==1.5 diff --git a/polymorphic/base.py b/polymorphic/base.py index 9d959e6..95a124e 100644 --- a/polymorphic/base.py +++ b/polymorphic/base.py @@ -54,6 +54,13 @@ class PolymorphicModelBase(ModelBase): def __new__(self, model_name, bases, attrs): #print; print '###', model_name, '- bases:', bases + # Workaround compatibility issue with six.with_metaclass() and custom Django model metaclasses: + # Let Django fully ignore the class which is inserted in between. + if not attrs and model_name == 'NewBase': + attrs['__module__'] = 'django.utils.six' + attrs['Meta'] = type('Meta', (), {'abstract': True}) + return super(PolymorphicModelBase, self).__new__(self, model_name, bases, attrs) + # create new model new_class = self.call_superclass_new_method(model_name, bases, attrs) @@ -145,7 +152,7 @@ class PolymorphicModelBase(ModelBase): # The ordering in the base.__dict__ may randomly change depending on which method is added. # Make sure base_objects is on top, and 'objects' and '_default_manager' follow afterwards. # This makes sure that the _base_manager is also assigned properly. - add_managers = sorted(add_managers, key=lambda item: item[2].creation_counter, reverse=True) + add_managers = sorted(add_managers, key=lambda item: (item[1].startswith('_'), item[1])) return add_managers @classmethod @@ -178,9 +185,8 @@ class PolymorphicModelBase(ModelBase): # which is directly in the python path. To work around this we temporarily set # app_label here for PolymorphicModel. meta = attrs.get('Meta', None) - model_module_name = attrs['__module__'] do_app_label_workaround = (meta - and model_module_name == 'polymorphic' + and attrs['__module__'] == 'polymorphic' and model_name == 'PolymorphicModel' and getattr(meta, 'app_label', None) is None) diff --git a/polymorphic/query_translate.py b/polymorphic/query_translate.py index fe300b8..a5f8413 100644 --- a/polymorphic/query_translate.py +++ b/polymorphic/query_translate.py @@ -34,7 +34,7 @@ def translate_polymorphic_filter_definitions_in_kwargs(queryset_model, kwargs): Returns: a list of non-keyword-arguments (Q objects) to be added to the filter() query. """ additional_args = [] - for field_path, val in kwargs.items(): + for field_path, val in kwargs.copy().items(): # Python 3 needs copy new_expr = _translate_polymorphic_filter_definition(queryset_model, field_path, val) diff --git a/polymorphic/tests.py b/polymorphic/tests.py index a4d168b..2935368 100644 --- a/polymorphic/tests.py +++ b/polymorphic/tests.py @@ -2,6 +2,7 @@ """ Test Cases Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/ """ +from __future__ import print_function import uuid import re from django.db.models.query import QuerySet @@ -10,6 +11,7 @@ from django.test import TestCase from django.db.models import Q,Count from django.db import models from django.contrib.contenttypes.models import ContentType +from django.utils import six from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet from polymorphic import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent @@ -253,10 +255,10 @@ class PolymorphicTests(TestCase): o2 = DiamondXY.objects.get() if o2.field_b != 'b': - print - print '# known django model inheritance diamond problem detected' - print 'DiamondXY fields 1: field_b "{0}", field_x "{1}", field_y "{2}"'.format(o1.field_b, o1.field_x, o1.field_y) - print 'DiamondXY fields 2: field_b "{0}", field_x "{1}", field_y "{2}"'.format(o2.field_b, o2.field_x, o2.field_y) + print('') + print('# known django model inheritance diamond problem detected') + print('DiamondXY fields 1: field_b "{0}", field_x "{1}", field_y "{2}"'.format(o1.field_b, o1.field_x, o1.field_y)) + print('DiamondXY fields 2: field_b "{0}", field_x "{1}", field_y "{2}"'.format(o2.field_b, o2.field_x, o2.field_y)) def test_annotate_aggregate_order(self): @@ -363,16 +365,16 @@ class PolymorphicTests(TestCase): ]""" self.assertEqual(res, res_exp) #if (a.pk!= uuid.UUID or c.pk!= uuid.UUID): - # print - # print '# known inconstency with custom primary key field detected (django problem?)' + # print() + # print('# known inconstency with custom primary key field detected (django problem?)') a = UUIDPlainA.objects.create(field1='A1') b = UUIDPlainB.objects.create(field1='B1', field2='B2') c = UUIDPlainC.objects.create(field1='C1', field2='C2', field3='C3') qs = UUIDPlainA.objects.all() if a.pk!= uuid.UUID or c.pk!= uuid.UUID: - print - print '# known type inconstency with custom primary key field detected (django problem?)' + print('') + print('# known type inconstency with custom primary key field detected (django problem?)') def create_model2abcd(self): @@ -531,9 +533,14 @@ class PolymorphicTests(TestCase): ModelExtraExternal.objects.create(topic='extra2') ModelExtraExternal.objects.create(topic='extra3') objects = ModelExtraA.objects.extra(tables=["polymorphic_modelextraexternal"], select={"topic":"polymorphic_modelextraexternal.topic"}, where=["polymorphic_modelextraa.id = polymorphic_modelextraexternal.id"]) - self.assertEqual(repr(objects[0]), '') - self.assertEqual(repr(objects[1]), '') - self.assertEqual(repr(objects[2]), '') + if six.PY3: + self.assertEqual(repr(objects[0]), '') + self.assertEqual(repr(objects[1]), '') + self.assertEqual(repr(objects[2]), '') + else: + self.assertEqual(repr(objects[0]), '') + self.assertEqual(repr(objects[1]), '') + self.assertEqual(repr(objects[2]), '') self.assertEqual(len(objects), 3) diff --git a/polymorphic/tools_for_tests.py b/polymorphic/tools_for_tests.py index fd18230..b7c017d 100644 --- a/polymorphic/tools_for_tests.py +++ b/polymorphic/tools_for_tests.py @@ -6,7 +6,7 @@ import uuid from django import forms from django.db import models -from django.utils.encoding import smart_unicode +from django.utils.encoding import smart_text from django.utils import six class UUIDVersionError(Exception): @@ -98,7 +98,7 @@ class UUIDField(six.with_metaclass(models.SubfieldBase, models.CharField)): if isinstance(value, uuid.UUID): return value # attempt to parse a UUID - return uuid.UUID(smart_unicode(value)) + return uuid.UUID(smart_text(value)) # # If I do the following (returning a String instead of a UUID @@ -108,7 +108,7 @@ class UUIDField(six.with_metaclass(models.SubfieldBase, models.CharField)): #if not value: # return None #if isinstance(value, uuid.UUID): - # return smart_unicode(value) + # return smart_text(value) #else: # return value @@ -126,7 +126,7 @@ class UUIDField(six.with_metaclass(models.SubfieldBase, models.CharField)): def get_db_prep_value(self, value, connection, prepared): """Casts uuid.UUID values into the format expected by the back end for use in queries""" if isinstance(value, uuid.UUID): - return smart_unicode(value) + return smart_text(value) return value def value_to_string(self, obj): @@ -134,7 +134,7 @@ class UUIDField(six.with_metaclass(models.SubfieldBase, models.CharField)): if val is None: data = '' else: - data = smart_unicode(val) + data = smart_text(val) return data def formfield(self, **kwargs): diff --git a/tox.ini b/tox.ini index a0faa6f..b91ce1f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,6 @@ [tox] envlist= + py33-django15, py26-django15, py27-django15, py26-django14, @@ -31,3 +32,8 @@ basepython=python2.7 deps= django==1.4.5 +[testenv:py33-django15] +basepython=python3.3 +deps= + django==1.5 +