2
0
Эх сурвалжийг харах

Removed a bunch more Python 2.4 workarounds now that we don't support that version. Refs #15702 -- thanks to jonash for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15927 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Adrian Holovaty 14 жил өмнө
parent
commit
13864703bc
56 өөрчлөгдсөн 113 нэмэгдсэн , 326 устгасан
  1. 11 14
      django/core/mail/backends/console.py
  2. 1 4
      django/core/mail/message.py
  3. 0 5
      django/core/management/validation.py
  4. 0 1
      django/db/__init__.py
  5. 3 4
      django/db/backends/util.py
  6. 4 3
      django/db/models/base.py
  7. 2 3
      django/db/models/deletion.py
  8. 0 2
      django/db/models/expressions.py
  9. 1 2
      django/db/models/fields/__init__.py
  10. 0 2
      django/db/models/fields/files.py
  11. 1 1
      django/db/models/manager.py
  12. 0 5
      django/db/models/options.py
  13. 2 2
      django/db/models/query.py
  14. 0 1
      django/db/models/query_utils.py
  15. 7 7
      django/db/models/sql/query.py
  16. 4 17
      django/db/transaction.py
  17. 1 1
      django/dispatch/saferef.py
  18. 1 5
      django/forms/extras/widgets.py
  19. 1 1
      django/forms/fields.py
  20. 2 2
      django/forms/forms.py
  21. 3 2
      django/forms/widgets.py
  22. 1 1
      django/http/__init__.py
  23. 2 2
      django/middleware/common.py
  24. 3 4
      django/middleware/csrf.py
  25. 4 3
      django/template/base.py
  26. 2 5
      django/template/defaultfilters.py
  27. 2 2
      django/template/loaders/cached.py
  28. 2 2
      django/templatetags/cache.py
  29. 1 1
      django/test/client.py
  30. 0 6
      django/test/simple.py
  31. 3 12
      django/test/testcases.py
  32. 5 5
      django/utils/cache.py
  33. 0 14
      django/utils/copycompat.py
  34. 4 8
      django/utils/crypto.py
  35. 3 6
      django/utils/datastructures.py
  36. 1 4
      django/utils/decorators.py
  37. 3 58
      django/utils/functional.py
  38. 0 20
      django/utils/hashcompat.py
  39. 0 12
      django/utils/itercompat.py
  40. 4 33
      django/utils/log.py
  41. 3 3
      django/utils/tree.py
  42. 1 5
      django/views/decorators/cache.py
  43. 1 5
      django/views/decorators/csrf.py
  44. 1 5
      django/views/decorators/http.py
  45. 1 5
      django/views/decorators/vary.py
  46. 1 1
      django/views/generic/base.py
  47. 3 3
      tests/regressiontests/cache/tests.py
  48. 2 2
      tests/regressiontests/comment_tests/tests/comment_form_tests.py
  49. 0 1
      tests/regressiontests/dispatch/tests/test_dispatcher.py
  50. 1 2
      tests/regressiontests/extra_regress/models.py
  51. 4 3
      tests/regressiontests/file_uploads/tests.py
  52. 3 3
      tests/regressiontests/file_uploads/views.py
  53. 3 2
      tests/regressiontests/forms/tests/widgets.py
  54. 2 2
      tests/regressiontests/introspection/tests.py
  55. 2 1
      tests/regressiontests/utils/datastructures.py
  56. 1 1
      tests/regressiontests/utils/simplelazyobject.py

+ 11 - 14
django/core/mail/backends/console.py

@@ -18,20 +18,17 @@ class EmailBackend(BaseEmailBackend):
             return
         self._lock.acquire()
         try:
-            # The try-except is nested to allow for
-            # Python 2.4 support (Refs #12147)
-            try:
-                stream_created = self.open()
-                for message in email_messages:
-                    self.stream.write('%s\n' % message.message().as_string())
-                    self.stream.write('-'*79)
-                    self.stream.write('\n')
-                    self.stream.flush()  # flush after each message
-                if stream_created:
-                    self.close()
-            except:
-                if not self.fail_silently:
-                    raise
+            stream_created = self.open()
+            for message in email_messages:
+                self.stream.write('%s\n' % message.message().as_string())
+                self.stream.write('-'*79)
+                self.stream.write('\n')
+                self.stream.flush()  # flush after each message
+            if stream_created:
+                self.close()
+        except:
+            if not self.fail_silently:
+                raise
         finally:
             self._lock.release()
         return len(email_messages)

+ 1 - 4
django/core/mail/message.py

@@ -3,10 +3,7 @@ import os
 import random
 import time
 from email import Charset, Encoders
-try:
-    from email.generator import Generator
-except ImportError:
-    from email.Generator import Generator # TODO: Remove when remove Python 2.4 support
+from email.generator import Generator
 from email.MIMEText import MIMEText
 from email.MIMEMultipart import MIMEMultipart
 from email.MIMEBase import MIMEBase

+ 0 - 5
django/core/management/validation.py

@@ -4,11 +4,6 @@ from django.contrib.contenttypes.generic import GenericForeignKey, GenericRelati
 from django.core.management.color import color_style
 from django.utils.itercompat import is_iterable
 
-try:
-    any
-except NameError:
-    from django.utils.itercompat import any
-
 class ModelErrorCollection:
     def __init__(self, outfile=sys.stdout):
         self.errors = []

+ 0 - 1
django/db/__init__.py

@@ -3,7 +3,6 @@ from django.core import signals
 from django.core.exceptions import ImproperlyConfigured
 from django.db.utils import ConnectionHandler, ConnectionRouter, load_backend, DEFAULT_DB_ALIAS, \
                             DatabaseError, IntegrityError
-from django.utils.functional import curry
 
 __all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
     'IntegrityError', 'DEFAULT_DB_ALIAS')

+ 3 - 4
django/db/backends/util.py

@@ -1,8 +1,8 @@
 import datetime
 import decimal
+import hashlib
 from time import time
 
-from django.utils.hashcompat import md5_constructor
 from django.utils.log import getLogger
 
 
@@ -130,9 +130,8 @@ def truncate_name(name, length=None, hash_len=4):
     if length is None or len(name) <= length:
         return name
 
-    hash = md5_constructor(name).hexdigest()[:hash_len]
-
-    return '%s%s' % (name[:length-hash_len], hash)
+    hsh = hashlib.md5(name).hexdigest()[:hash_len]
+    return '%s%s' % (name[:length-hash_len], hsh)
 
 def format_number(value, max_digits, decimal_places):
     """

+ 4 - 3
django/db/models/base.py

@@ -1,5 +1,7 @@
-import types
+import copy
 import sys
+import types
+from functools import update_wrapper
 from itertools import izip
 
 import django.db.models.manager     # Imported to register signal handler.
@@ -17,8 +19,7 @@ from django.db import (connections, router, transaction, DatabaseError,
 from django.db.models import signals
 from django.db.models.loading import register_models, get_model
 from django.utils.translation import ugettext_lazy as _
-import django.utils.copycompat as copy
-from django.utils.functional import curry, update_wrapper
+from django.utils.functional import curry
 from django.utils.encoding import smart_str, force_unicode
 from django.utils.text import get_text_list, capfirst
 from django.conf import settings

+ 2 - 3
django/db/models/deletion.py

@@ -1,17 +1,16 @@
+from functools import wraps
 from operator import attrgetter
 
 from django.db import connections, transaction, IntegrityError
 from django.db.models import signals, sql
 from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE
 from django.utils.datastructures import SortedDict
-from django.utils.functional import wraps
 
 
 class ProtectedError(IntegrityError):
     def __init__(self, msg, protected_objects):
         self.protected_objects = protected_objects
-        # TODO change this to use super() when we drop Python 2.4
-        IntegrityError.__init__(self, msg, protected_objects)
+        super(ProtectedError, self).__init__(msg, protected_objects)
 
 
 def CASCADE(collector, field, sub_objs, using):

+ 0 - 2
django/db/models/expressions.py

@@ -1,7 +1,5 @@
 import datetime
-
 from django.utils import tree
-from django.utils.copycompat import deepcopy
 
 class ExpressionNode(tree.Node):
     """

+ 1 - 2
django/db/models/fields/__init__.py

@@ -1,3 +1,4 @@
+import copy
 import datetime
 import decimal
 import re
@@ -5,8 +6,6 @@ import time
 import math
 from itertools import tee
 
-import django.utils.copycompat as copy
-
 from django.db import connection
 from django.db.models.fields.subclassing import LegacyConnection
 from django.db.models.query_utils import QueryWrapper

+ 0 - 2
django/db/models/fields/files.py

@@ -1,8 +1,6 @@
 import datetime
 import os
 
-import django.utils.copycompat as copy
-
 from django.conf import settings
 from django.db.models.fields import Field
 from django.core.files.base import File, ContentFile

+ 1 - 1
django/db/models/manager.py

@@ -1,4 +1,4 @@
-from django.utils import copycompat as copy
+import copy
 from django.conf import settings
 from django.db import router
 from django.db.models.query import QuerySet, EmptyQuerySet, insert_query, RawQuerySet

+ 0 - 5
django/db/models/options.py

@@ -11,11 +11,6 @@ from django.utils.translation import activate, deactivate_all, get_language, str
 from django.utils.encoding import force_unicode, smart_str
 from django.utils.datastructures import SortedDict
 
-try:
-    all
-except NameError:
-    from django.utils.itercompat import all
-
 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
 get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip()
 

+ 2 - 2
django/db/models/query.py

@@ -2,6 +2,7 @@
 The main QuerySet implementation. This provides the public API for the ORM.
 """
 
+import copy
 from itertools import izip
 
 from django.db import connections, router, transaction, IntegrityError
@@ -11,7 +12,6 @@ from django.db.models.query_utils import (Q, select_related_descend,
     deferred_class_factory, InvalidQuery)
 from django.db.models.deletion import Collector
 from django.db.models import signals, sql
-from django.utils.copycompat import deepcopy
 
 # Used to control how many objects are worked with at once in some cases (e.g.
 # when deleting objects).
@@ -51,7 +51,7 @@ class QuerySet(object):
             if k in ('_iter','_result_cache'):
                 obj.__dict__[k] = None
             else:
-                obj.__dict__[k] = deepcopy(v, memo)
+                obj.__dict__[k] = copy.deepcopy(v, memo)
         return obj
 
     def __getstate__(self):

+ 0 - 1
django/db/models/query_utils.py

@@ -7,7 +7,6 @@ circular import difficulties.
 """
 
 import weakref
-from django.utils.copycompat import deepcopy
 
 from django.db.backends import util
 from django.utils import tree

+ 7 - 7
django/db/models/sql/query.py

@@ -7,7 +7,7 @@ databases). The abstraction barrier only works one way: this module has to know
 all about the internals of models in order to get the information it needs.
 """
 
-from django.utils.copycompat import deepcopy
+import copy
 from django.utils.tree import Node
 from django.utils.datastructures import SortedDict
 from django.utils.encoding import force_unicode
@@ -244,19 +244,19 @@ class Query(object):
         obj.dupe_avoidance = self.dupe_avoidance.copy()
         obj.select = self.select[:]
         obj.tables = self.tables[:]
-        obj.where = deepcopy(self.where, memo=memo)
+        obj.where = copy.deepcopy(self.where, memo=memo)
         obj.where_class = self.where_class
         if self.group_by is None:
             obj.group_by = None
         else:
             obj.group_by = self.group_by[:]
-        obj.having = deepcopy(self.having, memo=memo)
+        obj.having = copy.deepcopy(self.having, memo=memo)
         obj.order_by = self.order_by[:]
         obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
         obj.distinct = self.distinct
         obj.select_related = self.select_related
         obj.related_select_cols = []
-        obj.aggregates = deepcopy(self.aggregates, memo=memo)
+        obj.aggregates = copy.deepcopy(self.aggregates, memo=memo)
         if self.aggregate_select_mask is None:
             obj.aggregate_select_mask = None
         else:
@@ -279,7 +279,7 @@ class Query(object):
             obj._extra_select_cache = self._extra_select_cache.copy()
         obj.extra_tables = self.extra_tables
         obj.extra_order_by = self.extra_order_by
-        obj.deferred_loading = deepcopy(self.deferred_loading, memo=memo)
+        obj.deferred_loading = copy.deepcopy(self.deferred_loading, memo=memo)
         if self.filter_is_sticky and self.used_aliases:
             obj.used_aliases = self.used_aliases.copy()
         else:
@@ -476,7 +476,7 @@ class Query(object):
         # Now relabel a copy of the rhs where-clause and add it to the current
         # one.
         if rhs.where:
-            w = deepcopy(rhs.where)
+            w = copy.deepcopy(rhs.where)
             w.relabel_aliases(change_map)
             if not self.where:
                 # Since 'self' matches everything, add an explicit "include
@@ -497,7 +497,7 @@ class Query(object):
             if isinstance(col, (list, tuple)):
                 self.select.append((change_map.get(col[0], col[0]), col[1]))
             else:
-                item = deepcopy(col)
+                item = copy.deepcopy(col)
                 item.relabel_aliases(change_map)
                 self.select.append(item)
         self.select_fields = rhs.select_fields[:]

+ 4 - 17
django/db/transaction.py

@@ -11,12 +11,9 @@ called, a commit is made.
 Managed transactions don't do those commits, but will need some kind of manual
 or implicit commits or rollbacks.
 """
-import sys
 
-try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps  # Python 2.4 fallback.
+import sys
+from functools import wraps
 
 from django.conf import settings
 from django.db import connections, DEFAULT_DB_ALIAS
@@ -209,18 +206,8 @@ class Transaction(object):
     def __call__(self, func):
         @wraps(func)
         def inner(*args, **kwargs):
-            # Once we drop support for Python 2.4 this block should become:
-            # with self:
-            #     func(*args, **kwargs)
-            self.__enter__()
-            try:
-                res = func(*args, **kwargs)
-            except:
-                self.__exit__(*sys.exc_info())
-                raise
-            else:
-                self.__exit__(None, None, None)
-                return res
+            with self:
+                func(*args, **kwargs)
         return inner
 
 def _transaction_func(entering, exiting, using):

+ 1 - 1
django/dispatch/saferef.py

@@ -230,7 +230,7 @@ class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
         if target is not None:
             function = self.weakFunc()
             if function is not None:
-                # Using curry() would be another option, but it erases the
+                # Using partial() would be another option, but it erases the
                 # "signature" of the function. That is, after a function is
                 # curried, the inspect module can't be used to determine how
                 # many arguments the function expects, nor what keyword

+ 1 - 5
django/forms/extras/widgets.py

@@ -68,11 +68,7 @@ class SelectDateWidget(Widget):
                 if settings.USE_L10N:
                     try:
                         input_format = get_format('DATE_INPUT_FORMATS')[0]
-                        # Python 2.4 compatibility:
-                        #     v = datetime.datetime.strptime(value, input_format)
-                        # would be clearer, but datetime.strptime was added in
-                        # Python 2.5
-                        v = datetime.datetime(*(time.strptime(value, input_format)[0:6]))
+                        v = datetime.datetime.strptime(value, input_format)
                         year_val, month_val, day_val = v.year, v.month, v.day
                     except ValueError:
                         pass

+ 1 - 1
django/forms/fields.py

@@ -2,6 +2,7 @@
 Field classes.
 """
 
+import copy
 import datetime
 import os
 import re
@@ -16,7 +17,6 @@ except ImportError:
 
 from django.core.exceptions import ValidationError
 from django.core import validators
-import django.utils.copycompat as copy
 from django.utils import formats
 from django.utils.translation import ugettext_lazy as _
 from django.utils.encoding import smart_unicode, smart_str

+ 2 - 2
django/forms/forms.py

@@ -2,8 +2,8 @@
 Form classes
 """
 
+import copy
 from django.core.exceptions import ValidationError
-from django.utils.copycompat import deepcopy
 from django.utils.datastructures import SortedDict
 from django.utils.html import conditional_escape
 from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
@@ -89,7 +89,7 @@ class BaseForm(StrAndUnicode):
         # alter self.fields, we create self.fields here by copying base_fields.
         # Instances should always modify self.fields; they should not modify
         # self.base_fields.
-        self.fields = deepcopy(self.base_fields)
+        self.fields = copy.deepcopy(self.base_fields)
 
     def __unicode__(self):
         return self.as_table()

+ 3 - 2
django/forms/widgets.py

@@ -1,13 +1,14 @@
 """
 HTML Widget classes
 """
+
+import copy
 import datetime
-from itertools import chain
 import time
+from itertools import chain
 from urlparse import urljoin
 from util import flatatt
 
-import django.utils.copycompat as copy
 from django.conf import settings
 from django.utils.datastructures import MultiValueDict, MergeDict
 from django.utils.html import escape, conditional_escape

+ 1 - 1
django/http/__init__.py

@@ -364,7 +364,7 @@ class QueryDict(MultiValueDict):
         return result
 
     def __deepcopy__(self, memo):
-        import django.utils.copycompat as copy
+        import copy
         result = self.__class__('', mutable=True, encoding=self.encoding)
         memo[id(self)] = result
         for key, value in dict.items(self):

+ 2 - 2
django/middleware/common.py

@@ -1,3 +1,4 @@
+import hashlib
 import re
 
 from django.conf import settings
@@ -5,7 +6,6 @@ from django import http
 from django.core.mail import mail_managers
 from django.utils.http import urlquote
 from django.core import urlresolvers
-from django.utils.hashcompat import md5_constructor
 from django.utils.log import getLogger
 
 logger = getLogger('django.request')
@@ -113,7 +113,7 @@ class CommonMiddleware(object):
             if response.has_header('ETag'):
                 etag = response['ETag']
             else:
-                etag = '"%s"' % md5_constructor(response.content).hexdigest()
+                etag = '"%s"' % hashlib.md5(response.content).hexdigest()
             if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag:
                 cookies = response.cookies
                 response = http.HttpResponseNotModified()

+ 3 - 4
django/middleware/csrf.py

@@ -5,6 +5,7 @@ This module provides a middleware that implements protection
 against request forgeries from other sites.
 """
 
+import hashlib
 import itertools
 import re
 import random
@@ -12,7 +13,6 @@ import random
 from django.conf import settings
 from django.core.urlresolvers import get_callable
 from django.utils.cache import patch_vary_headers
-from django.utils.hashcompat import md5_constructor
 from django.utils.http import same_origin
 from django.utils.log import getLogger
 from django.utils.safestring import mark_safe
@@ -47,12 +47,11 @@ def _get_failure_view():
 
 
 def _get_new_csrf_key():
-    return md5_constructor("%s%s"
-                % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
+    return hashlib.md5("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
 
 
 def _make_legacy_session_token(session_id):
-    return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
+    return hashlib.md5(settings.SECRET_KEY + session_id).hexdigest()
 
 
 def get_token(request):

+ 4 - 3
django/template/base.py

@@ -1,12 +1,13 @@
 import imp
 import re
+from functools import partial
 from inspect import getargspec
 
 from django.conf import settings
 from django.template.context import Context, RequestContext, ContextPopException
 from django.utils.importlib import import_module
 from django.utils.itercompat import is_iterable
-from django.utils.functional import curry, Promise
+from django.utils.functional import Promise
 from django.utils.text import smart_split, unescape_string_literal, get_text_list
 from django.utils.encoding import smart_unicode, force_unicode, smart_str
 from django.utils.translation import ugettext_lazy
@@ -884,7 +885,7 @@ class Library(object):
                         func_args = resolved_vars
                     return func(*func_args)
 
-            compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
+            compile_func = partial(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
             compile_func.__doc__ = func.__doc__
             self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
             return func
@@ -936,7 +937,7 @@ class Library(object):
                         new_context['csrf_token'] = csrf_token
                     return self.nodelist.render(new_context)
 
-            compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
+            compile_func = partial(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
             compile_func.__doc__ = func.__doc__
             self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
             return func

+ 2 - 5
django/template/defaultfilters.py

@@ -1,12 +1,9 @@
 """Default variable filters."""
 
 import re
-from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
 import random as random_module
-try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps  # Python 2.4 fallback.
+from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
+from functools import wraps
 
 from django.template.base import Variable, Library
 from django.conf import settings

+ 2 - 2
django/template/loaders/cached.py

@@ -3,10 +3,10 @@ Wrapper class that takes a list of template loaders as an argument and attempts
 to load templates from them in order, caching the result.
 """
 
+import hashlib
 from django.core.exceptions import ImproperlyConfigured
 from django.template.base import TemplateDoesNotExist
 from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin
-from django.utils.hashcompat import sha_constructor
 from django.utils.importlib import import_module
 
 class Loader(BaseLoader):
@@ -38,7 +38,7 @@ class Loader(BaseLoader):
         key = template_name
         if template_dirs:
             # If template directories were specified, use a hash to differentiate
-            key = '-'.join([template_name, sha_constructor('|'.join(template_dirs)).hexdigest()])
+            key = '-'.join([template_name, hashlib.sha1('|'.join(template_dirs)).hexdigest()])
 
         if key not in self.template_cache:
             template, origin = self.find_template(template_name, template_dirs)

+ 2 - 2
django/templatetags/cache.py

@@ -1,9 +1,9 @@
+import hashlib
 from django.template import Library, Node, TemplateSyntaxError, Variable, VariableDoesNotExist
 from django.template import resolve_variable
 from django.core.cache import cache
 from django.utils.encoding import force_unicode
 from django.utils.http import urlquote
-from django.utils.hashcompat import md5_constructor
 
 register = Library()
 
@@ -24,7 +24,7 @@ class CacheNode(Node):
         except (ValueError, TypeError):
             raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time)
         # Build a unicode key for this fragment and all vary-on's.
-        args = md5_constructor(u':'.join([urlquote(resolve_variable(var, context)) for var in self.vary_on]))
+        args = hashlib.md5(u':'.join([urlquote(resolve_variable(var, context)) for var in self.vary_on]))
         cache_key = 'template.cache.%s.%s' % (self.fragment_name, args.hexdigest())
         value = cache.get(cache_key)
         if value is None:

+ 1 - 1
django/test/client.py

@@ -1,11 +1,11 @@
 import urllib
-from urlparse import urlparse, urlunparse, urlsplit
 import sys
 import os
 import re
 import mimetypes
 import warnings
 from copy import copy
+from urlparse import urlparse, urlunparse, urlsplit
 try:
     from cStringIO import StringIO
 except ImportError:

+ 0 - 6
django/test/simple.py

@@ -7,12 +7,6 @@ from django.test.utils import setup_test_environment, teardown_test_environment
 from django.test.testcases import OutputChecker, DocTestRunner, TestCase
 from django.utils import unittest
 
-try:
-    all
-except NameError:
-    from django.utils.itercompat import all
-
-
 __all__ = ('DjangoTestRunner', 'DjangoTestSuiteRunner', 'run_tests')
 
 # The module name for tests outside models.py

+ 3 - 12
django/test/testcases.py

@@ -1,5 +1,6 @@
 import re
 import sys
+from functools import wraps
 from urlparse import urlsplit, urlunsplit
 from xml.dom.minidom import parseString, Node
 
@@ -16,21 +17,13 @@ from django.test.client import Client
 from django.test.utils import get_warnings_state, restore_warnings_state
 from django.utils import simplejson, unittest as ut2
 from django.utils.encoding import smart_str
-from django.utils.functional import wraps
 
 __all__ = ('DocTestRunner', 'OutputChecker', 'TestCase', 'TransactionTestCase',
            'skipIfDBFeature', 'skipUnlessDBFeature')
 
-
-try:
-    all
-except NameError:
-    from django.utils.itercompat import all
-
 normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
 normalize_decimals = lambda s: re.sub(r"Decimal\('(\d+(\.\d*)?)'\)", lambda m: "Decimal(\"%s\")" % m.groups()[0], s)
 
-
 def to_list(value):
     """
     Puts value into a list if it's not already one.
@@ -550,11 +543,9 @@ class TransactionTestCase(ut2.TestCase):
 
 def connections_support_transactions():
     """
-    Returns True if all connections support transactions.  This is messy
-    because 2.4 doesn't support any or all.
+    Returns True if all connections support transactions.
     """
-    return all(conn.features.supports_transactions
-        for conn in connections.all())
+    return all(conn.features.supports_transactions for conn in connections.all())
 
 class TestCase(TransactionTestCase):
     """

+ 5 - 5
django/utils/cache.py

@@ -17,6 +17,7 @@ An example: i18n middleware would need to distinguish caches by the
 "Accept-language" header.
 """
 
+import hashlib
 import re
 import time
 
@@ -24,7 +25,6 @@ from django.conf import settings
 from django.core.cache import get_cache
 from django.utils.encoding import smart_str, iri_to_uri
 from django.utils.http import http_date
-from django.utils.hashcompat import md5_constructor
 from django.utils.translation import get_language
 from django.http import HttpRequest
 
@@ -102,7 +102,7 @@ def patch_response_headers(response, cache_timeout=None):
     if cache_timeout < 0:
         cache_timeout = 0 # Can't have max-age negative
     if settings.USE_ETAGS and not response.has_header('ETag'):
-        response['ETag'] = '"%s"' % md5_constructor(response.content).hexdigest()
+        response['ETag'] = '"%s"' % hashlib.md5(response.content).hexdigest()
     if not response.has_header('Last-Modified'):
         response['Last-Modified'] = http_date()
     if not response.has_header('Expires'):
@@ -155,19 +155,19 @@ def _i18n_cache_key_suffix(request, cache_key):
 
 def _generate_cache_key(request, method, headerlist, key_prefix):
     """Returns a cache key from the headers given in the header list."""
-    ctx = md5_constructor()
+    ctx = hashlib.md5()
     for header in headerlist:
         value = request.META.get(header, None)
         if value is not None:
             ctx.update(value)
-    path = md5_constructor(iri_to_uri(request.get_full_path()))
+    path = hashlib.md5(iri_to_uri(request.get_full_path()))
     cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
         key_prefix, request.method, path.hexdigest(), ctx.hexdigest())
     return _i18n_cache_key_suffix(request, cache_key)
 
 def _generate_cache_header_key(key_prefix, request):
     """Returns a cache key for the header cache."""
-    path = md5_constructor(iri_to_uri(request.get_full_path()))
+    path = hashlib.md5(iri_to_uri(request.get_full_path()))
     cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
         key_prefix, path.hexdigest())
     return _i18n_cache_key_suffix(request, cache_key)

+ 0 - 14
django/utils/copycompat.py

@@ -1,14 +0,0 @@
-"""
-Fixes Python 2.4's failure to deepcopy unbound functions.
-"""
-
-import copy
-import types
-
-# Monkeypatch copy's deepcopy registry to handle functions correctly.
-if (hasattr(copy, '_deepcopy_dispatch') and types.FunctionType not in copy._deepcopy_dispatch):
-    copy._deepcopy_dispatch[types.FunctionType] = copy._deepcopy_atomic
-
-# Pose as the copy module now.
-del copy, types
-from copy import *

+ 4 - 8
django/utils/crypto.py

@@ -1,11 +1,10 @@
 """
 Django's standard crypto functions and utilities.
 """
-import hmac
 
+import hashlib
+import hmac
 from django.conf import settings
-from django.utils.hashcompat import sha_constructor, sha_hmac
-
 
 def salted_hmac(key_salt, value, secret=None):
     """
@@ -20,16 +19,13 @@ def salted_hmac(key_salt, value, secret=None):
     # We need to generate a derived key from our base key.  We can do this by
     # passing the key_salt and our base key through a pseudo-random function and
     # SHA1 works nicely.
-
-    key = sha_constructor(key_salt + secret).digest()
+    key = hashlib.sha1(key_salt + secret).digest()
 
     # If len(key_salt + secret) > sha_constructor().block_size, the above
     # line is redundant and could be replaced by key = key_salt + secret, since
     # the hmac module does the same thing for keys longer than the block size.
     # However, we need to ensure that we *always* do this.
-
-    return hmac.new(key, msg=value, digestmod=sha_hmac)
-
+    return hmac.new(key, msg=value, digestmod=hashlib.sha1)
 
 def constant_time_compare(val1, val2):
     """

+ 3 - 6
django/utils/datastructures.py

@@ -1,8 +1,6 @@
+import copy
 from types import GeneratorType
 
-from django.utils.copycompat import copy, deepcopy
-
-
 class MergeDict(object):
     """
     A simple class for creating new "virtual" dictionaries that actually look
@@ -127,7 +125,7 @@ class SortedDict(dict):
                     seen.add(key)
 
     def __deepcopy__(self, memo):
-        return self.__class__([(key, deepcopy(value, memo))
+        return self.__class__([(key, copy.deepcopy(value, memo))
                                for key, value in self.iteritems()])
 
     def __setitem__(self, key, value):
@@ -269,7 +267,6 @@ class MultiValueDict(dict):
         ])
 
     def __deepcopy__(self, memo=None):
-        import django.utils.copycompat as copy
         if memo is None:
             memo = {}
         result = self.__class__()
@@ -365,7 +362,7 @@ class MultiValueDict(dict):
 
     def copy(self):
         """Returns a shallow copy of this object."""
-        return copy(self)
+        return copy.copy(self)
 
     def update(self, *args, **kwargs):
         """

+ 1 - 4
django/utils/decorators.py

@@ -1,9 +1,6 @@
 "Functions that help with dynamically creating decorators for views."
 
-try:
-    from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS
-except ImportError:
-    from django.utils.functional import wraps, update_wrapper, WRAPPER_ASSIGNMENTS  # Python 2.4 fallback.
+from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS
 
 class classonlymethod(classmethod):
     def __get__(self, instance, owner):

+ 3 - 58
django/utils/functional.py

@@ -49,66 +49,13 @@
 # agrees to be bound by the terms and conditions of this License
 # Agreement.
 
+from functools import wraps
 
 def curry(_curried_func, *args, **kwargs):
     def _curried(*moreargs, **morekwargs):
         return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
     return _curried
 
-### Begin from Python 2.5 functools.py ########################################
-
-# Summary of changes made to the Python 2.5 code below:
-#   * swapped ``partial`` for ``curry`` to maintain backwards-compatibility
-#     in Django.
-
-# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation.
-# All Rights Reserved.
-
-###############################################################################
-
-# update_wrapper() and wraps() are tools to help write
-# wrapper functions that can handle naive introspection
-
-WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
-WRAPPER_UPDATES = ('__dict__',)
-def update_wrapper(wrapper,
-                   wrapped,
-                   assigned = WRAPPER_ASSIGNMENTS,
-                   updated = WRAPPER_UPDATES):
-    """Update a wrapper function to look like the wrapped function
-
-       wrapper is the function to be updated
-       wrapped is the original function
-       assigned is a tuple naming the attributes assigned directly
-       from the wrapped function to the wrapper function (defaults to
-       functools.WRAPPER_ASSIGNMENTS)
-       updated is a tuple naming the attributes off the wrapper that
-       are updated with the corresponding attribute from the wrapped
-       function (defaults to functools.WRAPPER_UPDATES)
-    """
-    for attr in assigned:
-        setattr(wrapper, attr, getattr(wrapped, attr))
-    for attr in updated:
-        getattr(wrapper, attr).update(getattr(wrapped, attr))
-    # Return the wrapper so this can be used as a decorator via curry()
-    return wrapper
-
-def wraps(wrapped,
-          assigned = WRAPPER_ASSIGNMENTS,
-          updated = WRAPPER_UPDATES):
-    """Decorator factory to apply update_wrapper() to a wrapper function
-
-       Returns a decorator that invokes update_wrapper() with the decorated
-       function as the wrapper argument and the arguments to wraps() as the
-       remaining arguments. Default arguments are as for update_wrapper().
-       This is a convenience function to simplify applying curry() to
-       update_wrapper().
-    """
-    return curry(update_wrapper, wrapped=wrapped,
-                 assigned=assigned, updated=updated)
-
-### End from Python 2.5 functools.py ##########################################
-
 def memoize(func, cache, num_args):
     """
     Wrap a function so that results for any argument tuple are stored in
@@ -343,10 +290,8 @@ class SimpleLazyObject(LazyObject):
             memo[id(self)] = result
             return result
         else:
-            # Changed to use deepcopy from copycompat, instead of copy
-            # For Python 2.4.
-            from django.utils.copycompat import deepcopy
-            return deepcopy(self._wrapped, memo)
+            import copy
+            return copy.deepcopy(self._wrapped, memo)
 
     # Need to pretend to be the wrapped class, for the sake of objects that care
     # about this (especially in equality tests)

+ 0 - 20
django/utils/hashcompat.py

@@ -1,20 +0,0 @@
-"""
-The md5 and sha modules are deprecated since Python 2.5, replaced by the
-hashlib module containing both hash algorithms. Here, we provide a common
-interface to the md5 and sha constructors, depending on system version.
-"""
-
-import sys
-if sys.version_info >= (2, 5):
-    import hashlib
-    md5_constructor = hashlib.md5
-    md5_hmac = md5_constructor
-    sha_constructor = hashlib.sha1
-    sha_hmac = sha_constructor
-else:
-    import md5
-    md5_constructor = md5.new
-    md5_hmac = md5
-    import sha
-    sha_constructor = sha.new
-    sha_hmac = sha

+ 0 - 12
django/utils/itercompat.py

@@ -31,15 +31,3 @@ def is_iterable(x):
         return False
     else:
         return True
-
-def all(iterable):
-    for item in iterable:
-        if not item:
-            return False
-    return True
-
-def any(iterable):
-    for item in iterable:
-        if item:
-            return True
-    return False

+ 4 - 33
django/utils/log.py

@@ -18,27 +18,7 @@ try:
 except ImportError:
     from django.utils.dictconfig import dictConfig
 
-if sys.version_info < (2, 5):
-    class LoggerCompat(object):
-        def __init__(self, logger):
-            self._logger = logger
-
-        def __getattr__(self, name):
-            val = getattr(self._logger, name)
-            if callable(val):
-                def _wrapper(*args, **kwargs):
-                    # Python 2.4 logging module doesn't support 'extra' parameter to
-                    # methods of Logger
-                    kwargs.pop('extra', None)
-                    return val(*args, **kwargs)
-                return _wrapper
-            else:
-                return val
-
-    def getLogger(name=None):
-        return LoggerCompat(logging.getLogger(name=name))
-else:
-    getLogger = logging.getLogger
+getLogger = logging.getLogger
 
 # Ensure the creation of the Django logger
 # with a null handler. This ensures we don't get any
@@ -49,7 +29,7 @@ if not logger.handlers:
 
 class AdminEmailHandler(logging.Handler):
     def __init__(self, include_html=False):
-        logging.Handler.__init__(self)        
+        logging.Handler.__init__(self)
         self.include_html = include_html
 
     """An exception log handler that e-mails log entries to site admins.
@@ -63,15 +43,7 @@ class AdminEmailHandler(logging.Handler):
         from django.views.debug import ExceptionReporter
 
         try:
-            if sys.version_info < (2,5):
-                # A nasty workaround required because Python 2.4's logging
-                # module doesn't support passing in extra context.
-                # For this handler, the only extra data we need is the
-                # request, and that's in the top stack frame.
-                request = record.exc_info[2].tb_frame.f_locals['request']
-            else:
-                request = record.request
-
+            request = record.request
             subject = '%s (%s IP): %s' % (
                 record.levelname,
                 (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'),
@@ -97,5 +69,4 @@ class AdminEmailHandler(logging.Handler):
         message = "%s\n\n%s" % (stack_trace, request_repr)
         reporter = ExceptionReporter(request, is_email=True, *exc_info)
         html_message = self.include_html and reporter.get_traceback_html() or None
-        mail.mail_admins(subject, message, fail_silently=True,
-                         html_message=html_message)
+        mail.mail_admins(subject, message, fail_silently=True, html_message=html_message)

+ 3 - 3
django/utils/tree.py

@@ -3,7 +3,7 @@ A class for storing a tree graph. Primarily used for filter constructs in the
 ORM.
 """
 
-from django.utils.copycompat import deepcopy
+import copy
 
 class Node(object):
     """
@@ -58,8 +58,8 @@ class Node(object):
         """
         obj = Node(connector=self.connector, negated=self.negated)
         obj.__class__ = self.__class__
-        obj.children = deepcopy(self.children, memodict)
-        obj.subtree_parents = deepcopy(self.subtree_parents, memodict)
+        obj.children = copy.deepcopy(self.children, memodict)
+        obj.subtree_parents = copy.deepcopy(self.subtree_parents, memodict)
         return obj
 
     def __len__(self):

+ 1 - 5
django/views/decorators/cache.py

@@ -1,8 +1,4 @@
-try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps  # Python 2.4 fallback.
-
+from functools import wraps
 from django.utils.decorators import decorator_from_middleware_with_args, available_attrs
 from django.utils.cache import patch_cache_control, add_never_cache_headers
 from django.middleware.cache import CacheMiddleware

+ 1 - 5
django/views/decorators/csrf.py

@@ -1,10 +1,6 @@
 from django.middleware.csrf import CsrfViewMiddleware
 from django.utils.decorators import decorator_from_middleware, available_attrs
-
-try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps  # Python 2.4 fallback.
+from functools import wraps
 
 csrf_protect = decorator_from_middleware(CsrfViewMiddleware)
 csrf_protect.__name__ = "csrf_protect"

+ 1 - 5
django/views/decorators/http.py

@@ -2,13 +2,9 @@
 Decorators for views based on HTTP headers.
 """
 
-try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps  # Python 2.4 fallback.
-
 from calendar import timegm
 from datetime import timedelta
+from functools import wraps
 
 from django.utils.decorators import decorator_from_middleware, available_attrs
 from django.utils.http import http_date, parse_http_date_safe, parse_etags, quote_etag

+ 1 - 5
django/views/decorators/vary.py

@@ -1,8 +1,4 @@
-try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps  # Python 2.4 fallback.
-
+from functools import wraps
 from django.utils.cache import patch_vary_headers
 from django.utils.decorators import available_attrs
 

+ 1 - 1
django/views/generic/base.py

@@ -1,8 +1,8 @@
+from functools import update_wrapper
 from django import http
 from django.core.exceptions import ImproperlyConfigured
 from django.template import RequestContext, loader
 from django.template.response import TemplateResponse
-from django.utils.functional import update_wrapper
 from django.utils.log import getLogger
 from django.utils.decorators import classonlymethod
 

+ 3 - 3
tests/regressiontests/cache/tests.py

@@ -3,6 +3,7 @@
 # Unit tests for cache framework
 # Uses whatever cache backend is set in the test settings file.
 
+import hashlib
 import os
 import tempfile
 import time
@@ -19,7 +20,6 @@ from django.test.utils import get_warnings_state, restore_warnings_state
 from django.utils import translation
 from django.utils import unittest
 from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key
-from django.utils.hashcompat import md5_constructor
 from django.views.decorators.cache import cache_page
 
 from regressiontests.cache.models import Poll, expensive_calculation
@@ -850,7 +850,7 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
         """Test that keys are hashed into subdirectories correctly"""
         self.cache.set("foo", "bar")
         key = self.cache.make_key("foo")
-        keyhash = md5_constructor(key).hexdigest()
+        keyhash = hashlib.md5(key).hexdigest()
         keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
         self.assertTrue(os.path.exists(keypath))
 
@@ -860,7 +860,7 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
         """
         self.cache.set("foo", "bar")
         key = self.cache.make_key("foo")
-        keyhash = md5_constructor(key).hexdigest()
+        keyhash = hashlib.md5(key).hexdigest()
         keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
         self.assertTrue(os.path.exists(keypath))
 

+ 2 - 2
tests/regressiontests/comment_tests/tests/comment_form_tests.py

@@ -1,9 +1,9 @@
+import hashlib
 import time
 
 from django.conf import settings
 from django.contrib.comments.forms import CommentForm
 from django.contrib.comments.models import Comment
-from django.utils.hashcompat import sha_constructor
 
 from regressiontests.comment_tests.models import Article
 from regressiontests.comment_tests.tests import CommentTestCase
@@ -57,7 +57,7 @@ class CommentFormTests(CommentTestCase):
 
         # The Django 1.2 method hard-coded here:
         info = (content_type, object_pk, timestamp, settings.SECRET_KEY)
-        security_hash = sha_constructor("".join(info)).hexdigest()
+        security_hash = hashlib.sha1("".join(info)).hexdigest()
 
         d['security_hash'] = security_hash
         f = CommentForm(a, data=d)

+ 0 - 1
tests/regressiontests/dispatch/tests/test_dispatcher.py

@@ -3,7 +3,6 @@ import sys
 
 from django.dispatch import Signal
 from django.utils import unittest
-import django.utils.copycompat as copy
 
 if sys.platform.startswith('java'):
     def garbage_collect():

+ 1 - 2
tests/regressiontests/extra_regress/models.py

@@ -1,7 +1,6 @@
+import copy
 import datetime
 
-import django.utils.copycompat as copy
-
 from django.contrib.auth.models import User
 from django.db import models
 

+ 4 - 3
tests/regressiontests/file_uploads/tests.py

@@ -1,5 +1,7 @@
 #! -*- coding: utf-8 -*-
+
 import errno
+import hashlib
 import os
 import shutil
 from StringIO import StringIO
@@ -10,7 +12,6 @@ from django.http.multipartparser import MultiPartParser
 from django.test import TestCase, client
 from django.utils import simplejson
 from django.utils import unittest
-from django.utils.hashcompat import sha_constructor
 
 from models import FileModel, temp_storage, UPLOAD_TO
 import uploadhandler
@@ -46,10 +47,10 @@ class FileUploadTests(TestCase):
 
         for key in post_data.keys():
             try:
-                post_data[key + '_hash'] = sha_constructor(post_data[key].read()).hexdigest()
+                post_data[key + '_hash'] = hashlib.sha1(post_data[key].read()).hexdigest()
                 post_data[key].seek(0)
             except AttributeError:
-                post_data[key + '_hash'] = sha_constructor(post_data[key]).hexdigest()
+                post_data[key + '_hash'] = hashlib.sha1(post_data[key]).hexdigest()
 
         response = self.client.post('/file_uploads/verify/', post_data)
 

+ 3 - 3
tests/regressiontests/file_uploads/views.py

@@ -1,10 +1,10 @@
+import hashlib
 import os
 from django.core.files.uploadedfile import UploadedFile
 from django.http import HttpResponse, HttpResponseServerError
 from django.utils import simplejson
 from models import FileModel, UPLOAD_TO
 from uploadhandler import QuotaUploadHandler, ErroringUploadHandler
-from django.utils.hashcompat import sha_constructor
 from tests import UNICODE_FILENAME
 
 def file_upload_view(request):
@@ -37,9 +37,9 @@ def file_upload_view_verify(request):
             continue
         submitted_hash = form_data[key + '_hash']
         if isinstance(value, UploadedFile):
-            new_hash = sha_constructor(value.read()).hexdigest()
+            new_hash = hashlib.sha1(value.read()).hexdigest()
         else:
-            new_hash = sha_constructor(value).hexdigest()
+            new_hash = hashlib.sha1(value).hexdigest()
         if new_hash != submitted_hash:
             return HttpResponseServerError()
 

+ 3 - 2
tests/regressiontests/forms/tests/widgets.py

@@ -1,13 +1,14 @@
 # -*- coding: utf-8 -*-
+
+import copy
 import datetime
-from decimal import Decimal
 import re
 import time
+from decimal import Decimal
 from django.conf import settings
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.forms import *
 from django.forms.widgets import RadioFieldRenderer
-from django.utils import copycompat as copy
 from django.utils import formats
 from django.utils.safestring import mark_safe
 from django.utils.translation import activate, deactivate

+ 2 - 2
tests/regressiontests/introspection/tests.py

@@ -1,7 +1,7 @@
+from functools import update_wrapper
 from django.conf import settings
 from django.db import connection, DEFAULT_DB_ALIAS
 from django.test import TestCase, skipUnlessDBFeature
-from django.utils import functional
 
 from models import Reporter, Article
 
@@ -23,7 +23,7 @@ def ignore_not_implemented(func):
             return func(*args, **kwargs)
         except NotImplementedError:
             return None
-    functional.update_wrapper(_inner, func)
+    update_wrapper(_inner, func)
     return _inner
 
 class IgnoreNotimplementedError(type):

+ 2 - 1
tests/regressiontests/utils/datastructures.py

@@ -1,10 +1,11 @@
 """
 Tests for stuff in django.utils.datastructures.
 """
+
+import copy
 import pickle
 import unittest
 
-from django.utils.copycompat import copy
 from django.utils.datastructures import *
 
 

+ 1 - 1
tests/regressiontests/utils/simplelazyobject.py

@@ -1,6 +1,6 @@
+import copy
 import unittest
 
-import django.utils.copycompat as copy
 from django.utils.functional import SimpleLazyObject
 
 class _ComplexObject(object):