diff --git a/django/core/mail/backends/console.py b/django/core/mail/backends/console.py index fa71f3816ff..705497520a8 100644 --- a/django/core/mail/backends/console.py +++ b/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) diff --git a/django/core/mail/message.py b/django/core/mail/message.py index 01bb1d62d18..bed4966ef97 100644 --- a/django/core/mail/message.py +++ b/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 diff --git a/django/core/management/validation.py b/django/core/management/validation.py index 2eb6340de94..bb4fa552ffe 100644 --- a/django/core/management/validation.py +++ b/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 = [] diff --git a/django/db/__init__.py b/django/db/__init__.py index 3f9645a4522..7446a7fa607 100644 --- a/django/db/__init__.py +++ b/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') diff --git a/django/db/backends/util.py b/django/db/backends/util.py index 2f92a30c9ec..0766f877877 100644 --- a/django/db/backends/util.py +++ b/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): """ diff --git a/django/db/models/base.py b/django/db/models/base.py index 286f9b0de6e..4aa6cfa741f 100644 --- a/django/db/models/base.py +++ b/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 diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py index 73960f5e3c1..a52fa24a014 100644 --- a/django/db/models/deletion.py +++ b/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): diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 8b9c934bbbc..a71f4a33efe 100644 --- a/django/db/models/expressions.py +++ b/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): """ diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 8081cf3954f..ccb57da69e3 100644 --- a/django/db/models/fields/__init__.py +++ b/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 diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index aaf1fbb0ecc..50dc236902d 100644 --- a/django/db/models/fields/files.py +++ b/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 diff --git a/django/db/models/manager.py b/django/db/models/manager.py index 6a62c254ef1..9528d668121 100644 --- a/django/db/models/manager.py +++ b/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 diff --git a/django/db/models/options.py b/django/db/models/options.py index 10617dc9e99..6d8ab6f6d66 100644 --- a/django/db/models/options.py +++ b/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() diff --git a/django/db/models/query.py b/django/db/models/query.py index 0fc48f8a41c..58f9313e3da 100644 --- a/django/db/models/query.py +++ b/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): diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 80485e04827..69b19f18061 100644 --- a/django/db/models/query_utils.py +++ b/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 diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index ea89771ed1b..449c83b4953 100644 --- a/django/db/models/sql/query.py +++ b/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[:] diff --git a/django/db/transaction.py b/django/db/transaction.py index b5584dd8b99..cf7350c02f2 100644 --- a/django/db/transaction.py +++ b/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): diff --git a/django/dispatch/saferef.py b/django/dispatch/saferef.py index 8bcfd8a1406..f446447b464 100644 --- a/django/dispatch/saferef.py +++ b/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 diff --git a/django/forms/extras/widgets.py b/django/forms/extras/widgets.py index e8bacf2b1ed..d75e903f569 100644 --- a/django/forms/extras/widgets.py +++ b/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 diff --git a/django/forms/fields.py b/django/forms/fields.py index 4d7728f58c6..eebe0c44ffc 100644 --- a/django/forms/fields.py +++ b/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 diff --git a/django/forms/forms.py b/django/forms/forms.py index f776861948c..ad398c4cb7a 100644 --- a/django/forms/forms.py +++ b/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() diff --git a/django/forms/widgets.py b/django/forms/widgets.py index dd5868f4799..03152eaa249 100644 --- a/django/forms/widgets.py +++ b/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 diff --git a/django/http/__init__.py b/django/http/__init__.py index 714d552938d..62e327d9267 100644 --- a/django/http/__init__.py +++ b/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): diff --git a/django/middleware/common.py b/django/middleware/common.py index 2252c8f9f0d..eb145edf198 100644 --- a/django/middleware/common.py +++ b/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() diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index b5a85795b24..3247358ed50 100644 --- a/django/middleware/csrf.py +++ b/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): diff --git a/django/template/base.py b/django/template/base.py index 49ef0b85eb0..b8d6c139de2 100644 --- a/django/template/base.py +++ b/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 diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index d923d8a0e58..60fa59e04dd 100644 --- a/django/template/defaultfilters.py +++ b/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 diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py index 715542adf76..5b351ad0aa2 100644 --- a/django/template/loaders/cached.py +++ b/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) diff --git a/django/templatetags/cache.py b/django/templatetags/cache.py index 4fe3c3bb8d8..0440d10cb8c 100644 --- a/django/templatetags/cache.py +++ b/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: diff --git a/django/test/client.py b/django/test/client.py index dd0d811b029..15147c51fa8 100644 --- a/django/test/client.py +++ b/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: diff --git a/django/test/simple.py b/django/test/simple.py index d0b9a702570..41b2a421ed3 100644 --- a/django/test/simple.py +++ b/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 diff --git a/django/test/testcases.py b/django/test/testcases.py index 02cd00c27f2..81e028c3dc6 100644 --- a/django/test/testcases.py +++ b/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'(? 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): """ diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 7425ea2ce21..ff281b032a0 100644 --- a/django/utils/datastructures.py +++ b/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): """ diff --git a/django/utils/decorators.py b/django/utils/decorators.py index 17f2ea30b33..6099f882669 100644 --- a/django/utils/decorators.py +++ b/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): diff --git a/django/utils/functional.py b/django/utils/functional.py index ccfbcb0250b..5dc0a45e7a1 100644 --- a/django/utils/functional.py +++ b/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) diff --git a/django/utils/hashcompat.py b/django/utils/hashcompat.py deleted file mode 100644 index 4d9b76f3a67..00000000000 --- a/django/utils/hashcompat.py +++ /dev/null @@ -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 diff --git a/django/utils/itercompat.py b/django/utils/itercompat.py index d4ff2503c7f..b302e22b2a4 100644 --- a/django/utils/itercompat.py +++ b/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 diff --git a/django/utils/log.py b/django/utils/log.py index 4fb75b1504c..e4aec73ee87 100644 --- a/django/utils/log.py +++ b/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) diff --git a/django/utils/tree.py b/django/utils/tree.py index a6cfec27ad4..36b5977942c 100644 --- a/django/utils/tree.py +++ b/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): diff --git a/django/views/decorators/cache.py b/django/views/decorators/cache.py index a836ac5c28a..279649d35f5 100644 --- a/django/views/decorators/cache.py +++ b/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 diff --git a/django/views/decorators/csrf.py b/django/views/decorators/csrf.py index 578854a5297..19fa4f72188 100644 --- a/django/views/decorators/csrf.py +++ b/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" diff --git a/django/views/decorators/http.py b/django/views/decorators/http.py index fb3181e10ed..dc90cc348b3 100644 --- a/django/views/decorators/http.py +++ b/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 diff --git a/django/views/decorators/vary.py b/django/views/decorators/vary.py index a10896cccf3..939b8c34132 100644 --- a/django/views/decorators/vary.py +++ b/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 diff --git a/django/views/generic/base.py b/django/views/generic/base.py index d732af5d667..f2c6d3f9f57 100644 --- a/django/views/generic/base.py +++ b/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 diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index 2832bcbdd94..2f46c0b1aa6 100644 --- a/tests/regressiontests/cache/tests.py +++ b/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)) diff --git a/tests/regressiontests/comment_tests/tests/comment_form_tests.py b/tests/regressiontests/comment_tests/tests/comment_form_tests.py index 57dad3625cd..956ca53bfd7 100644 --- a/tests/regressiontests/comment_tests/tests/comment_form_tests.py +++ b/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) diff --git a/tests/regressiontests/dispatch/tests/test_dispatcher.py b/tests/regressiontests/dispatch/tests/test_dispatcher.py index 2ad5b0c67aa..a16d8e24f5a 100644 --- a/tests/regressiontests/dispatch/tests/test_dispatcher.py +++ b/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(): diff --git a/tests/regressiontests/extra_regress/models.py b/tests/regressiontests/extra_regress/models.py index 073157a38a7..11218202f03 100644 --- a/tests/regressiontests/extra_regress/models.py +++ b/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 diff --git a/tests/regressiontests/file_uploads/tests.py b/tests/regressiontests/file_uploads/tests.py index 6dba4731fd0..5da0a5fafc3 100644 --- a/tests/regressiontests/file_uploads/tests.py +++ b/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) diff --git a/tests/regressiontests/file_uploads/views.py b/tests/regressiontests/file_uploads/views.py index 9f4ce69b5fe..0fd0b6502dc 100644 --- a/tests/regressiontests/file_uploads/views.py +++ b/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() diff --git a/tests/regressiontests/forms/tests/widgets.py b/tests/regressiontests/forms/tests/widgets.py index 4c5aeb01479..0a8a87949ca 100644 --- a/tests/regressiontests/forms/tests/widgets.py +++ b/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 diff --git a/tests/regressiontests/introspection/tests.py b/tests/regressiontests/introspection/tests.py index 90dea9203c5..7481641fb56 100644 --- a/tests/regressiontests/introspection/tests.py +++ b/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): diff --git a/tests/regressiontests/utils/datastructures.py b/tests/regressiontests/utils/datastructures.py index 3d01ab8b6fe..6ae652cdba6 100644 --- a/tests/regressiontests/utils/datastructures.py +++ b/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 * diff --git a/tests/regressiontests/utils/simplelazyobject.py b/tests/regressiontests/utils/simplelazyobject.py index 9eea8b5901e..e29729df60a 100644 --- a/tests/regressiontests/utils/simplelazyobject.py +++ b/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):