From 5368c3113ffcf712991db751f331ab9b6335b0e0 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 6 Apr 2013 15:55:22 -0700 Subject: [PATCH 1/5] Fix iterator/list problems for Python 3 support --- model_utils/managers.py | 2 +- model_utils/tests/tests.py | 6 +++--- model_utils/tracker.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/model_utils/managers.py b/model_utils/managers.py index 74cc7bc9..1f502ad0 100644 --- a/model_utils/managers.py +++ b/model_utils/managers.py @@ -38,7 +38,7 @@ def _clone(self, klass=None, setup=False, **kwargs): def annotate(self, *args, **kwargs): qset = super(InheritanceQuerySet, self).annotate(*args, **kwargs) - qset._annotated = [a.default_alias for a in args] + kwargs.keys() + qset._annotated = [a.default_alias for a in args] + list(kwargs.keys()) return qset diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index a8e6e3dd..7ea2e7a2 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -644,7 +644,7 @@ class ModelTrackerTestCase(TestCase): def assertHasChanged(self, **kwargs): tracker = kwargs.pop('tracker', self.tracker) - for field, value in kwargs.iteritems(): + for field, value in kwargs.items(): if value is None: self.assertRaises(FieldError, tracker.has_changed, field) else: @@ -652,7 +652,7 @@ def assertHasChanged(self, **kwargs): def assertPrevious(self, **kwargs): tracker = kwargs.pop('tracker', self.tracker) - for field, value in kwargs.iteritems(): + for field, value in kwargs.items(): self.assertEqual(tracker.previous(field), value) def assertChanged(self, **kwargs): @@ -664,7 +664,7 @@ def assertCurrent(self, **kwargs): self.assertEqual(tracker.current(), kwargs) def update_instance(self, **kwargs): - for field, value in kwargs.iteritems(): + for field, value in kwargs.items(): setattr(self.instance, field, value) self.instance.save() diff --git a/model_utils/tracker.py b/model_utils/tracker.py index d93dfea8..9a83f1de 100644 --- a/model_utils/tracker.py +++ b/model_utils/tracker.py @@ -74,6 +74,6 @@ def changed(self): """Returns dict of fields that changed since save (with old values)""" if not self.instance.pk: return {} - saved = self.saved_data.iteritems() + saved = self.saved_data.items() current = self.current() return dict((k, v) for k, v in saved if v != current[k]) From 0393e3791328d0d040794cb5b874ee898ac244b8 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 6 Apr 2013 15:55:42 -0700 Subject: [PATCH 2/5] Fix str/unicode problems in Python 3 Changes: - Use unicode_literals from the future for Python 2.6.5+ - Use six's text_type for proper unicode use in Python 2/3 --- model_utils/choices.py | 5 ++++- model_utils/fields.py | 6 ++++-- model_utils/managers.py | 1 + model_utils/models.py | 1 + model_utils/tests/tests.py | 19 ++++++++++--------- model_utils/tracker.py | 1 + 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/model_utils/choices.py b/model_utils/choices.py index 65097c76..b5177cdd 100644 --- a/model_utils/choices.py +++ b/model_utils/choices.py @@ -1,3 +1,6 @@ +from __future__ import unicode_literals + + class Choices(object): """ A class to encapsulate handy functionality for lists of choices @@ -72,4 +75,4 @@ def __getitem__(self, index): def __repr__(self): return '%s(%s)' % (self.__class__.__name__, - ', '.join(("%s" % str(i) for i in self._full))) + ', '.join(("%s" % repr(i) for i in self._full))) diff --git a/model_utils/fields.py b/model_utils/fields.py index 6dd64592..9178e1fd 100644 --- a/model_utils/fields.py +++ b/model_utils/fields.py @@ -1,7 +1,9 @@ +from __future__ import unicode_literals from datetime import datetime from django.db import models from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible try: @@ -128,6 +130,7 @@ def get_excerpt(content): return '\n'.join(default_excerpt) +@python_2_unicode_compatible class SplitText(object): def __init__(self, instance, field_name, excerpt_field_name): # instead of storing actual values store a reference to the instance @@ -153,8 +156,7 @@ def _get_has_more(self): return self.excerpt.strip() != self.content.strip() has_more = property(_get_has_more) - # allows display via templates without .content necessary - def __unicode__(self): + def __str__(self): return self.content class SplitDescriptor(object): diff --git a/model_utils/managers.py b/model_utils/managers.py index 1f502ad0..7c6c9f97 100644 --- a/model_utils/managers.py +++ b/model_utils/managers.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals import django from django.db import models from django.db.models.fields.related import OneToOneField diff --git a/model_utils/models.py b/model_utils/models.py index 40e7aa81..ff8b3752 100644 --- a/model_utils/models.py +++ b/model_utils/models.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals from datetime import datetime from django.db import models diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index 7ea2e7a2..f4d5c44c 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -1,4 +1,4 @@ -from __future__ import with_statement +from __future__ import unicode_literals, with_statement import pickle from datetime import datetime, timedelta @@ -6,6 +6,7 @@ import django from django.db import models from django.db.models.fields import FieldDoesNotExist +from django.utils.six import text_type from django.core.exceptions import ImproperlyConfigured, FieldError from django.test import TestCase @@ -47,8 +48,8 @@ def test_middle_of_line(self): class SplitFieldTests(TestCase): - full_text = u'summary\n\n\n\nmore' - excerpt = u'summary\n' + full_text = 'summary\n\n\n\nmore' + excerpt = 'summary\n' def setUp(self): @@ -57,7 +58,7 @@ def setUp(self): def test_unicode_content(self): - self.assertEquals(unicode(self.post.body), self.full_text) + self.assertEquals(text_type(self.post.body), self.full_text) def test_excerpt(self): @@ -85,17 +86,17 @@ def test_load_back(self): def test_assign_to_body(self): - new_text = u'different\n\n\n\nother' + new_text = 'different\n\n\n\nother' self.post.body = new_text self.post.save() - self.assertEquals(unicode(self.post.body), new_text) + self.assertEquals(text_type(self.post.body), new_text) def test_assign_to_content(self): - new_text = u'different\n\n\n\nother' + new_text = 'different\n\n\n\nother' self.post.body.content = new_text self.post.save() - self.assertEquals(unicode(self.post.body), new_text) + self.assertEquals(text_type(self.post.body), new_text) def test_assign_to_excerpt(self): @@ -118,7 +119,7 @@ def test_none(self): def test_assign_splittext(self): a = Article(title='Some Title') a.body = self.post.body - self.assertEquals(a.body.excerpt, u'summary\n') + self.assertEquals(a.body.excerpt, 'summary\n') def test_value_to_string(self): diff --git a/model_utils/tracker.py b/model_utils/tracker.py index 9a83f1de..a3664902 100644 --- a/model_utils/tracker.py +++ b/model_utils/tracker.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals from django.db import models from django.core.exceptions import FieldError From f7a7cf0a88d7d2f4eaefd42a1b0f9a300e1c247e Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Mon, 8 Apr 2013 22:37:45 -0700 Subject: [PATCH 3/5] Add Python 3 to tox and drop Django 1.2 and 1.3 --- tox.ini | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/tox.ini b/tox.ini index 55ea1760..d4f4ff78 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py26-1.2,py26-1.3,py26-1.4,py26,py26-trunk,py27-1.2,py27-1.3,py27-1.4,py27,py27-trunk,py27-nosouth +envlist=py26-1.4,py26,py26-trunk,py27-1.4,py27,py27-trunk,py27-nosouth,py33-1.5-nosouth,py33-trunk-nosouth [testenv] deps= @@ -8,20 +8,6 @@ deps= coverage==3.6 commands=coverage run -a --branch setup.py test -[testenv:py26-1.2] -basepython=python2.6 -deps= - django==1.2.7 - South==0.7.6 - coverage==3.6 - -[testenv:py26-1.3] -basepython=python2.6 -deps= - django==1.3.7 - South==0.7.6 - coverage==3.6 - [testenv:py26-1.4] basepython=python2.6 deps= @@ -36,32 +22,30 @@ deps= South==0.7.6 coverage==3.6 -[testenv:py27-1.2] +[testenv:py27-1.4] basepython=python2.7 deps= - django==1.2.7 + django==1.4.5 South==0.7.6 coverage==3.6 -[testenv:py27-1.3] +[testenv:py27-trunk] basepython=python2.7 deps= - django==1.3.7 + https://github.com/django/django/tarball/master South==0.7.6 coverage==3.6 -[testenv:py27-1.4] -basepython=python2.7 +[testenv:py33-1.5-nosouth] +basepython=python3.3 deps= - django==1.4.5 - South==0.7.6 + django==1.5.0 coverage==3.6 -[testenv:py27-trunk] -basepython=python2.7 +[testenv:py33-trunk-nosouth] +basepython=python3.3 deps= https://github.com/django/django/tarball/master - South==0.7.6 coverage==3.6 From 47f6dd6075de8e1589ff50d48ec725dc62b1c42a Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 6 Apr 2013 16:10:33 -0700 Subject: [PATCH 4/5] Add Python 3 to travis and drop Django 1.2 and 1.3 --- .travis.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0c003472..8031a3af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,13 +5,10 @@ python: - "2.7" env: - - DJANGO=Django==1.2.7 SOUTH=1 - - DJANGO=Django==1.3.7 SOUTH=1 - DJANGO=Django==1.4.5 SOUTH=1 - - DJANGO=Django==1.4.5 SOUTH=1 - - DJANGO=Django==1.5 SOUTH=1 + - DJANGO=Django==1.5.1 SOUTH=1 - DJANGO=https://github.com/django/django/tarball/master SOUTH=1 - - DJANGO=Django==1.4.5 SOUTH=0 + - DJANGO=Django==1.5.1 SOUTH=0 install: - pip install $DJANGO --use-mirrors @@ -20,4 +17,11 @@ install: script: coverage run -a --branch --include="model_utils/*" --omit="model_utils/tests/*" setup.py test +matrix: + include: + - python: 3.3 + env: DJANGO=Django==1.5.1 SOUTH=0 + - python: 3.3 + env: DJANGO=https://github.com/django/django/tarball/master SOUTH=0 + after_success: coveralls From 9c207e58ec46a64eff974aeb7237e99c831c5a75 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Tue, 9 Apr 2013 14:58:41 -0700 Subject: [PATCH 5/5] Fix repr tests for Python 2 (no string comparison) --- model_utils/tests/tests.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index f4d5c44c..fb7d9a23 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -199,10 +199,10 @@ def test_len(self): def test_repr(self): - self.assertEquals(repr(self.STATUS), - "Choices(" - "('DRAFT', 'DRAFT', 'DRAFT'), " - "('PUBLISHED', 'PUBLISHED', 'PUBLISHED'))") + self.assertEquals(repr(self.STATUS), "Choices" + repr(( + ('DRAFT', 'DRAFT', 'DRAFT'), + ('PUBLISHED', 'PUBLISHED', 'PUBLISHED'), + ))) def test_wrong_length_tuple(self): @@ -244,11 +244,11 @@ def test_len(self): def test_repr(self): - self.assertEquals(repr(self.STATUS), - "Choices(" - "('DRAFT', 'DRAFT', 'is draft'), " - "('PUBLISHED', 'PUBLISHED', 'is published'), " - "('DELETED', 'DELETED', 'DELETED'))") + self.assertEquals(repr(self.STATUS), "Choices" + repr(( + ('DRAFT', 'DRAFT', 'is draft'), + ('PUBLISHED', 'PUBLISHED', 'is published'), + ('DELETED', 'DELETED', 'DELETED'), + ))) @@ -280,12 +280,11 @@ def test_len(self): def test_repr(self): - self.assertEquals(repr(self.STATUS), - "Choices(" - "(0, 'DRAFT', 'is draft'), " - "(1, 'PUBLISHED', 'is published'), " - "(2, 'DELETED', 'is deleted'))") - + self.assertEquals(repr(self.STATUS), "Choices" + repr(( + (0, 'DRAFT', 'is draft'), + (1, 'PUBLISHED', 'is published'), + (2, 'DELETED', 'is deleted'), + ))) class InheritanceManagerTests(TestCase):