diff --git a/django/db/models/base.py b/django/db/models/base.py index ad80a863cb6ad..44efaa6e1d4a0 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -56,12 +56,12 @@ def __new__(cls, name, bases, attrs): new_class.add_to_class('_meta', Options(meta, **kwargs)) if not abstract: new_class.add_to_class('DoesNotExist', subclass_exception('DoesNotExist', - tuple(x.DoesNotExist - for x in parents if hasattr(x, '_meta') and not x._meta.abstract) + tuple([x.DoesNotExist + for x in parents if hasattr(x, '_meta') and not x._meta.abstract]) or (ObjectDoesNotExist,), module)) new_class.add_to_class('MultipleObjectsReturned', subclass_exception('MultipleObjectsReturned', - tuple(x.MultipleObjectsReturned - for x in parents if hasattr(x, '_meta') and not x._meta.abstract) + tuple([x.MultipleObjectsReturned + for x in parents if hasattr(x, '_meta') and not x._meta.abstract]) or (MultipleObjectsReturned,), module)) if base_meta and not base_meta.abstract: # Non-abstract child classes inherit some attributes from their diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index a0911830002eb..1f8a8a6771583 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -1,11 +1,15 @@ import datetime -import decimal import os import re import time import django.utils.copycompat as copy +try: + import decimal +except ImportError: + from django.utils import _decimal as decimal # for Python 2.3 + from django.db import connection from django.db.models import signals from django.db.models.query_utils import QueryWrapper @@ -437,7 +441,7 @@ def to_python(self, value): def get_db_prep_value(self, value): return self.to_python(value) - + def formfield(self, **kwargs): defaults = {'max_length': self.max_length} defaults.update(kwargs) diff --git a/django/db/models/loading.py b/django/db/models/loading.py index a1d83cd577cf1..ba34dd7c8bdff 100644 --- a/django/db/models/loading.py +++ b/django/db/models/loading.py @@ -148,8 +148,8 @@ def get_models(self, app_mod=None, include_deferred=False): model_list = [] for app in app_list: model_list.extend( - model for model in app.values() - if (not model._deferred or include_deferred) + [model for model in app.values() + if (not model._deferred or include_deferred)] ) return model_list diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index c5cb336f4ec32..058989457d4a9 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -12,6 +12,10 @@ from django.utils import tree from django.utils.datastructures import SortedDict +try: + sorted +except NameError: + from django.utils.itercompat import sorted # For Python 2.3. class CyclicDependency(Exception): """ diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index a94dca770fbab..dc16a4f44235d 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -18,8 +18,7 @@ from django.db.models.query_utils import select_related_descend from django.db.models.sql import aggregates as base_aggregates_module from django.db.models.sql.expressions import SQLEvaluator -from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode, - ExtraWhere, AND, OR) +from django.db.models.sql.where import WhereNode, Constraint, EverythingNode, ExtraWhere, AND, OR from django.core.exceptions import FieldError from datastructures import EmptyResultSet, Empty, MultiJoin from constants import * diff --git a/django/forms/fields.py b/django/forms/fields.py index 18174360f61e0..8d4fbc434be06 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -7,7 +7,6 @@ import re import time import urlparse -from decimal import Decimal, DecimalException try: from cStringIO import StringIO except ImportError: @@ -18,6 +17,16 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_unicode, smart_str +# Python 2.3 fallbacks +try: + from decimal import Decimal, DecimalException +except ImportError: + from django.utils._decimal import Decimal, DecimalException +try: + set +except NameError: + from sets import Set as set + from util import ErrorList, ValidationError from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget @@ -489,7 +498,7 @@ def clean(self, data, initial=None): return None elif not data and initial: return initial - + # Try to import PIL in either of the two ways it can end up installed. try: from PIL import Image diff --git a/django/forms/widgets.py b/django/forms/widgets.py index a39c19872e64b..36018979b8381 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -2,6 +2,11 @@ HTML Widget classes """ +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback + import django.utils.copycompat as copy from itertools import chain from django.conf import settings diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index af06bbef53a9d..206532780bf96 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -2,6 +2,11 @@ from django.utils.copycompat import deepcopy +# Python 2.3 doesn't have set as a builtin +try: + set +except NameError: + from sets import Set as set class MergeDict(object): """ diff --git a/django/utils/itercompat.py b/django/utils/itercompat.py index 4a28f81d8bc9d..6ae4854d09319 100644 --- a/django/utils/itercompat.py +++ b/django/utils/itercompat.py @@ -67,9 +67,15 @@ def is_iterable(x): else: return True -def sorted(in_value): +def sorted(in_value, key=None): "A naive implementation of sorted" - out_value = in_value[:] + if key: + out_value = [(key(item), index, item) for index, item in enumerate(in_value)] + else: + out_value = in_value[:] out_value.sort() - return out_value + if key: + return [item[2] for item in out_value] + else: + return out_value diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index c51323d843cfd..4291cc057a44c 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -141,12 +141,12 @@ def test_follow_redirect(self): def test_redirect_http(self): "GET a URL that redirects to an http URI" response = self.client.get('/test_client/http_redirect_view/',follow=True) - self.assertFalse(response.test_was_secure_request) + self.failIf(response.test_was_secure_request) def test_redirect_https(self): "GET a URL that redirects to an https URI" response = self.client.get('/test_client/https_redirect_view/',follow=True) - self.assertTrue(response.test_was_secure_request) + self.failUnless(response.test_was_secure_request) def test_notfound_response(self): "GET a URL that responds as '404:Not Found'" diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py index a858e2469fe55..cbba99b611754 100644 --- a/tests/regressiontests/datastructures/tests.py +++ b/tests/regressiontests/datastructures/tests.py @@ -42,7 +42,7 @@ True >>> sorted(mm.items(), key=lambda k: k[0]) [('key1', 'value1'), ('key2', 'value3'), ('key4', 'value6')] ->>> [(k,mm.getlist(k)) for k in sorted(mm)] +>>> [(k,mm.getlist(k)) for k in sorted(mm.keys())] [('key1', ['value1']), ('key2', ['value2', 'value3']), ('key4', ['value5', 'value6'])] ### MultiValueDict ########################################################## @@ -106,7 +106,17 @@ >>> d.pop('one', 'missing') 'missing' ->>> SortedDict((i, i) for i in xrange(3)) +This SortedDict test tests that it works properly when called with a +a generator expression. But having that syntax anywhere in the test +when run under Python 2.3 will cause a SyntaxError. Thus the rigamorale +with eval so that the test will run and test what it is intended to test +when run on Pythons > 2.3, but not cause a SyntaxError when run on Python 2.3. +>>> import sys +>>> if sys.version_info[0] == 2 and sys.version_info[1] == 3: +... arg = '[(i, i) for i in xrange(3)]' +... else: +... arg = '((i, i) for i in xrange(3))' +>>> SortedDict(eval(arg)) {0: 0, 1: 1, 2: 2} We don't know which item will be popped in popitem(), so we'll just check that @@ -171,3 +181,9 @@ 'Normal: a. Modified: *a' """ + +try: + sorted +except NameError: + from django.utils.itercompat import sorted # For Python 2.3 + diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py index d9e7bc62490f5..c2745b12657d2 100644 --- a/tests/regressiontests/defer_regress/models.py +++ b/tests/regressiontests/defer_regress/models.py @@ -6,6 +6,11 @@ from django.contrib.contenttypes.models import ContentType from django.db import connection, models +try: + sorted +except NameError: + from django.utils.itercompat import sorted # For Python 2.3 + class Item(models.Model): name = models.CharField(max_length=15) text = models.TextField(default="xyzzy") diff --git a/tests/regressiontests/generic_inline_admin/tests.py b/tests/regressiontests/generic_inline_admin/tests.py index 9b3e12d137a3b..2df4ca0c1d2e3 100644 --- a/tests/regressiontests/generic_inline_admin/tests.py +++ b/tests/regressiontests/generic_inline_admin/tests.py @@ -185,4 +185,4 @@ def test_no_deletion(self): inline = MediaPermanentInline(EpisodePermanent, fake_site) fake_request = object() formset = inline.get_formset(fake_request) - self.assertFalse(formset.can_delete) + self.failIf(formset.can_delete) diff --git a/tests/regressiontests/generic_relations_regress/tests.py b/tests/regressiontests/generic_relations_regress/tests.py index 45e86746e5b4d..e61618ae11257 100644 --- a/tests/regressiontests/generic_relations_regress/tests.py +++ b/tests/regressiontests/generic_relations_regress/tests.py @@ -63,12 +63,12 @@ def test_q_object_or(self): # search with a non-matching note and a matching org name qs = Contact.objects.filter(Q(notes__note__icontains=r'other note') | Q(organizations__name__icontains=r'org name')) - self.assertTrue(org_contact in qs) + self.failUnless(org_contact in qs) # search again, with the same query parameters, in reverse order qs = Contact.objects.filter( Q(organizations__name__icontains=r'org name') | Q(notes__note__icontains=r'other note')) - self.assertTrue(org_contact in qs) + self.failUnless(org_contact in qs) diff --git a/tests/regressiontests/localflavor/tests.py b/tests/regressiontests/localflavor/tests.py index 0ea3c52568e7a..7d1080fd8b8f4 100644 --- a/tests/regressiontests/localflavor/tests.py +++ b/tests/regressiontests/localflavor/tests.py @@ -5,19 +5,19 @@ class USLocalflavorTests(TestCase): def setUp(self): self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'}) - + def test_get_display_methods(self): """Test that the get_*_display() methods are added to the model instances.""" place = self.form.save() self.assertEqual(place.get_state_display(), 'Georgia') self.assertEqual(place.get_state_req_display(), 'North Carolina') - + def test_required(self): """Test that required USStateFields throw appropriate errors.""" form = PlaceForm({'state':'GA', 'name':'Place in GA'}) - self.assertFalse(form.is_valid()) + self.failIf(form.is_valid()) self.assertEqual(form.errors['state_req'], [u'This field is required.']) - + def test_field_blank_option(self): """Test that the empty option is there.""" state_select_html = """\