From 904b82759cce6f85d06564c42123da030da52341 Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Fri, 28 Jun 2013 17:53:49 +0100 Subject: [PATCH 01/47] Fix Python 3 support --- django/db/models/fields/related.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index c708bff49f..1ad4bf4706 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -1166,7 +1166,7 @@ class ForeignKey(ForeignObject): rel = self.rel if self.rel.field_name: kwargs['to_field'] = self.rel.field_name - if isinstance(self.rel.to, basestring): + if isinstance(self.rel.to, six.string_types): kwargs['to'] = self.rel.to else: kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name) @@ -1393,7 +1393,7 @@ class ManyToManyField(RelatedField): del kwargs['help_text'] # Rel needs more work. rel = self.rel - if isinstance(self.rel.to, basestring): + if isinstance(self.rel.to, six.string_types): kwargs['to'] = self.rel.to else: kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name) From e1dd24d2f6dd3464ab50593320a7eb2325d6c196 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Fri, 28 Jun 2013 15:33:20 -0400 Subject: [PATCH 02/47] Added missing deprecation note for model permission methods. refs #20642. --- docs/internals/deprecation.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 9672746717..5513c79966 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -404,6 +404,9 @@ these changes. forms for ManyToMany model fields will not be performed by Django anymore either at the model or forms layer. +* The ``Model._meta.get_(add|change|delete)_permission`` methods will + be removed. + 2.0 --- From 4f6be9a0c43050500af598527e1453d27c5c5b85 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 13:10:34 -0300 Subject: [PATCH 03/47] Removed warnings level handling code as per deprecation TL. --- django/test/testcases.py | 19 ------------------- django/test/utils.py | 25 ------------------------- tests/test_utils/tests.py | 31 ------------------------------- 3 files changed, 75 deletions(-) diff --git a/django/test/testcases.py b/django/test/testcases.py index 984d21ce18..4300df2856 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -162,10 +162,6 @@ class SimpleTestCase(ut2.TestCase): # Can be overridden in derived classes. client_class = Client - _warn_txt = ("save_warnings_state/restore_warnings_state " - "django.test.*TestCase methods are deprecated. Use Python's " - "warnings.catch_warnings context manager instead.") - def __call__(self, result=None): """ Wrapper around default __call__ method to perform common Django test @@ -225,21 +221,6 @@ class SimpleTestCase(ut2.TestCase): settings.ROOT_URLCONF = self._old_root_urlconf clear_url_caches() - def save_warnings_state(self): - """ - Saves the state of the warnings module - """ - warnings.warn(self._warn_txt, DeprecationWarning, stacklevel=2) - self._warnings_state = warnings.filters[:] - - def restore_warnings_state(self): - """ - Restores the state of the warnings module to the state - saved by save_warnings_state() - """ - warnings.warn(self._warn_txt, DeprecationWarning, stacklevel=2) - warnings.filters = self._warnings_state[:] - def settings(self, **kwargs): """ A context manager that temporarily sets a setting and reverts diff --git a/django/test/utils.py b/django/test/utils.py index 591c588933..6be9070009 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -124,31 +124,6 @@ def teardown_test_environment(): del mail.outbox -warn_txt = ("get_warnings_state/restore_warnings_state functions from " - "django.test.utils are deprecated. Use Python's warnings.catch_warnings() " - "context manager instead.") - - -def get_warnings_state(): - """ - Returns an object containing the state of the warnings module - """ - # There is no public interface for doing this, but this implementation of - # get_warnings_state and restore_warnings_state appears to work on Python - # 2.4 to 2.7. - warnings.warn(warn_txt, DeprecationWarning, stacklevel=2) - return warnings.filters[:] - - -def restore_warnings_state(state): - """ - Restores the state of the warnings module when passed an object that was - returned by get_warnings_state() - """ - warnings.warn(warn_txt, DeprecationWarning, stacklevel=2) - warnings.filters = state[:] - - def get_runner(settings, test_runner_class=None): if not test_runner_class: test_runner_class = settings.TEST_RUNNER diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 5f7fb05eb4..b51ba7bf3b 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -270,37 +270,6 @@ class AssertTemplateUsedContextManagerTests(TestCase): render_to_string('template_used/alternative.html') -class SaveRestoreWarningState(TestCase): - def test_save_restore_warnings_state(self): - """ - Ensure save_warnings_state/restore_warnings_state work correctly. - """ - # In reality this test could be satisfied by many broken implementations - # of save_warnings_state/restore_warnings_state (e.g. just - # warnings.resetwarnings()) , but it is difficult to test more. - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - - self.save_warnings_state() - - class MyWarning(Warning): - pass - - # Add a filter that causes an exception to be thrown, so we can catch it - warnings.simplefilter("error", MyWarning) - self.assertRaises(Warning, lambda: warnings.warn("warn", MyWarning)) - - # Now restore. - self.restore_warnings_state() - # After restoring, we shouldn't get an exception. But we don't want a - # warning printed either, so we have to silence the warning. - warnings.simplefilter("ignore", MyWarning) - warnings.warn("warn", MyWarning) - - # Remove the filter we just added. - self.restore_warnings_state() - - class HTMLEqualTests(TestCase): def test_html_parser(self): element = parse_html('

Hello

') From f02a703ca6867806fbef71dd8d64d9a19975fe6c Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 13:13:31 -0300 Subject: [PATCH 04/47] Removed AuthenticationForm.check_for_test_cookie() as per deprecation TL. --- django/contrib/auth/forms.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index 43f5303b63..beff18c0f2 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -200,10 +200,6 @@ class AuthenticationForm(forms.Form): ) return self.cleaned_data - def check_for_test_cookie(self): - warnings.warn("check_for_test_cookie is deprecated; ensure your login " - "view is CSRF-protected.", DeprecationWarning) - def get_user_id(self): if self.user_cache: return self.user_cache.id From 57d46bcde3a473ea2d61f4f466d870e85d7b5eb4 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 13:14:49 -0300 Subject: [PATCH 05/47] Removed daily_cleanup.py script as per deprecation TL. --- django/bin/daily_cleanup.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100755 django/bin/daily_cleanup.py diff --git a/django/bin/daily_cleanup.py b/django/bin/daily_cleanup.py deleted file mode 100755 index 5a2ce210f4..0000000000 --- a/django/bin/daily_cleanup.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -""" -Daily cleanup job. - -Can be run as a cronjob to clean out old data from the database (only expired -sessions at the moment). -""" - -import warnings - -from django.core import management - -if __name__ == "__main__": - warnings.warn( - "The `daily_cleanup` script has been deprecated " - "in favor of `django-admin.py clearsessions`.", - DeprecationWarning) - management.call_command('clearsessions') From da6d303df4a9e5d51a6575b073980199d79508b8 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 13:22:28 -0300 Subject: [PATCH 06/47] Removed django.utils.simplejson as per deprecation TL. --- django/utils/simplejson.py | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 django/utils/simplejson.py diff --git a/django/utils/simplejson.py b/django/utils/simplejson.py deleted file mode 100644 index d4032a6b34..0000000000 --- a/django/utils/simplejson.py +++ /dev/null @@ -1,31 +0,0 @@ -# Django 1.5 only supports Python >= 2.6, where the standard library includes -# the json module. Previous version of Django shipped a copy for Python < 2.6. - -# For backwards compatibility, we're keeping an importable json module -# at this location, with the same lookup sequence. - -# Avoid shadowing the simplejson module -from __future__ import absolute_import - -import warnings -warnings.warn("django.utils.simplejson is deprecated; use json instead.", - DeprecationWarning, stacklevel=2) - -try: - import simplejson -except ImportError: - use_simplejson = False -else: - # The system-installed version has priority providing it is either not an - # earlier version or it contains the C speedups. - from json import __version__ as stdlib_json_version - use_simplejson = (hasattr(simplejson, '_speedups') or - simplejson.__version__.split('.') >= stdlib_json_version.split('.')) - -# Make sure we copy over the version. See #17071 -if use_simplejson: - from simplejson import * - from simplejson import __version__ -else: - from json import * - from json import __version__ From bb33ee5e7bf5325696e80e6d87e7614a9607a1b7 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 13:37:14 -0300 Subject: [PATCH 07/47] Removed django.utils.itercompat.product() as per deprecation TL. --- django/utils/itercompat.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/django/utils/itercompat.py b/django/utils/itercompat.py index c50dcfb779..1df1e57cb2 100644 --- a/django/utils/itercompat.py +++ b/django/utils/itercompat.py @@ -5,9 +5,7 @@ these implementations if necessary. """ import collections -import itertools import sys -import warnings def is_iterable(x): @@ -29,8 +27,3 @@ def is_iterator(x): if sys.version_info >= (2, 7): return isinstance(x, collections.Iterator) return isinstance(x, collections.Iterator) and hasattr(x, '__iter__') - -def product(*args, **kwds): - warnings.warn("django.utils.itercompat.product is deprecated; use the native version instead", - DeprecationWarning, stacklevel=2) - return itertools.product(*args, **kwds) From f73d04dda9f1a96acdab77dacd7bdeaad66ad736 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 13:38:04 -0300 Subject: [PATCH 08/47] Removed 'cleanup' management command as per deprecation TL. --- django/core/management/commands/cleanup.py | 11 ----------- docs/ref/django-admin.txt | 12 ------------ docs/releases/1.5-beta-1.txt | 2 +- docs/releases/1.5.txt | 2 +- 4 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 django/core/management/commands/cleanup.py diff --git a/django/core/management/commands/cleanup.py b/django/core/management/commands/cleanup.py deleted file mode 100644 index e158ebb541..0000000000 --- a/django/core/management/commands/cleanup.py +++ /dev/null @@ -1,11 +0,0 @@ -import warnings - -from django.contrib.sessions.management.commands import clearsessions - - -class Command(clearsessions.Command): - def handle_noargs(self, **options): - warnings.warn( - "The `cleanup` command has been deprecated in favor of `clearsessions`.", - DeprecationWarning) - super(Command, self).handle_noargs(**options) diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index c2c11e6db4..caee368e63 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -102,18 +102,6 @@ is compatible with the current version of Django. Upon finding things that are incompatible or require notifying the user, it issues a series of warnings. -cleanup -------- - -.. django-admin:: cleanup - -Can be run as a cronjob or directly to clean out old data from the database -(only expired sessions at the moment). - -.. versionchanged:: 1.5 - - :djadmin:`cleanup` is deprecated. Use :djadmin:`clearsessions` instead. - compilemessages --------------- diff --git a/docs/releases/1.5-beta-1.txt b/docs/releases/1.5-beta-1.txt index 57b13ea6ce..a1c9edd890 100644 --- a/docs/releases/1.5-beta-1.txt +++ b/docs/releases/1.5-beta-1.txt @@ -689,7 +689,7 @@ framework. ``cleanup`` management command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :djadmin:`cleanup` management command has been deprecated and replaced by +The ``cleanup`` management command has been deprecated and replaced by :djadmin:`clearsessions`. ``daily_cleanup.py`` script diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index a1b046776b..2bb3095297 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -788,7 +788,7 @@ the built-in :func:`itertools.product` instead. ``cleanup`` management command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :djadmin:`cleanup` management command has been deprecated and replaced by +The ``cleanup`` management command has been deprecated and replaced by :djadmin:`clearsessions`. ``daily_cleanup.py`` script From 5e08b792eff2a3ca340ad5a8a568c665e52ec39e Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 14:50:06 -0300 Subject: [PATCH 09/47] Stop auto-correcting string INSTALLED_APPS, TEMPLATE_DIRS settings, as per deprecation TL. --- django/conf/__init__.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/django/conf/__init__.py b/django/conf/__init__.py index 61584391cd..b1f40af80a 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -132,19 +132,9 @@ class Settings(BaseSettings): % (self.SETTINGS_MODULE, e) ) - # Settings that should be converted into tuples if they're mistakenly entered - # as strings. - tuple_settings = ("INSTALLED_APPS", "TEMPLATE_DIRS") - for setting in dir(mod): if setting == setting.upper(): setting_value = getattr(mod, setting) - if setting in tuple_settings and \ - isinstance(setting_value, six.string_types): - warnings.warn("The %s setting must be a tuple. Please fix your " - "settings, as auto-correction is now deprecated." % setting, - DeprecationWarning, stacklevel=2) - setting_value = (setting_value,) # In case the user forgot the comma. setattr(self, setting, setting_value) if not self.SECRET_KEY: From c196564132a351549245a5d795391ad1ea56195c Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 15:13:06 -0300 Subject: [PATCH 10/47] Removed custom profile model functionality as per deprecation TL. --- django/contrib/auth/models.py | 32 ------------------- django/contrib/auth/tests/test_models.py | 39 +----------------------- docs/ref/contrib/auth.txt | 14 --------- docs/ref/settings.txt | 16 ---------- docs/releases/1.5-alpha-1.txt | 8 ++--- docs/releases/1.5-beta-1.txt | 8 ++--- docs/releases/1.5.txt | 8 ++--- docs/topics/auth/customizing.txt | 34 --------------------- tests/multiple_database/tests.py | 21 ------------- 9 files changed, 13 insertions(+), 167 deletions(-) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 5fec99a783..16c6357c40 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -413,38 +413,6 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin): """ send_mail(subject, message, from_email, [self.email]) - def get_profile(self): - """ - Returns site-specific profile for this user. Raises - SiteProfileNotAvailable if this site does not allow profiles. - """ - warnings.warn("The use of AUTH_PROFILE_MODULE to define user profiles has been deprecated.", - DeprecationWarning, stacklevel=2) - if not hasattr(self, '_profile_cache'): - from django.conf import settings - if not getattr(settings, 'AUTH_PROFILE_MODULE', False): - raise SiteProfileNotAvailable( - 'You need to set AUTH_PROFILE_MODULE in your project ' - 'settings') - try: - app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.') - except ValueError: - raise SiteProfileNotAvailable( - 'app_label and model_name should be separated by a dot in ' - 'the AUTH_PROFILE_MODULE setting') - try: - model = models.get_model(app_label, model_name) - if model is None: - raise SiteProfileNotAvailable( - 'Unable to load the profile model, check ' - 'AUTH_PROFILE_MODULE in your project settings') - self._profile_cache = model._default_manager.using( - self._state.db).get(user__id__exact=self.id) - self._profile_cache.user = self - except (ImportError, ImproperlyConfigured): - raise SiteProfileNotAvailable - return self._profile_cache - class User(AbstractUser): """ diff --git a/django/contrib/auth/tests/test_models.py b/django/contrib/auth/tests/test_models.py index b0a4559b42..cd3a13b76d 100644 --- a/django/contrib/auth/tests/test_models.py +++ b/django/contrib/auth/tests/test_models.py @@ -1,46 +1,9 @@ -import warnings - -from django.conf import settings from django.contrib.auth import get_user_model -from django.contrib.auth.models import (Group, User, SiteProfileNotAvailable, - UserManager) +from django.contrib.auth.models import (Group, User, UserManager) from django.contrib.auth.tests.utils import skipIfCustomUser from django.db.models.signals import post_save from django.test import TestCase from django.test.utils import override_settings -from django.utils import six - - -@skipIfCustomUser -@override_settings(USE_TZ=False, AUTH_PROFILE_MODULE='') -class ProfileTestCase(TestCase): - - def test_site_profile_not_available(self): - user = User.objects.create(username='testclient') - - # calling get_profile without AUTH_PROFILE_MODULE set - del settings.AUTH_PROFILE_MODULE - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - with six.assertRaisesRegex(self, SiteProfileNotAvailable, - "You need to set AUTH_PROFILE_MODULE in your project"): - user.get_profile() - - # Bad syntax in AUTH_PROFILE_MODULE: - settings.AUTH_PROFILE_MODULE = 'foobar' - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - with six.assertRaisesRegex(self, SiteProfileNotAvailable, - "app_label and model_name should be separated by a dot"): - user.get_profile() - - # module that doesn't exist - settings.AUTH_PROFILE_MODULE = 'foo.bar' - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - with six.assertRaisesRegex(self, SiteProfileNotAvailable, - "Unable to load the profile model"): - user.get_profile() @skipIfCustomUser diff --git a/docs/ref/contrib/auth.txt b/docs/ref/contrib/auth.txt index afbc6ec048..dfda8add4b 100644 --- a/docs/ref/contrib/auth.txt +++ b/docs/ref/contrib/auth.txt @@ -218,20 +218,6 @@ Methods Sends an email to the user. If ``from_email`` is ``None``, Django uses the :setting:`DEFAULT_FROM_EMAIL`. - .. method:: get_profile() - - .. deprecated:: 1.5 - With the introduction of :ref:`custom User models `, - the use of :setting:`AUTH_PROFILE_MODULE` to define a single profile - model is no longer supported. See the - :doc:`Django 1.5 release notes` for more information. - - Returns a site-specific profile for this user. Raises - ``django.contrib.auth.models.SiteProfileNotAvailable`` if the - current site doesn't allow profiles, or - :exc:`django.core.exceptions.ObjectDoesNotExist` if the user does not - have a profile. - Manager methods --------------- diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 215931768c..736edbb826 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -2049,22 +2049,6 @@ A tuple of authentication backend classes (as strings) to use when attempting to authenticate a user. See the :ref:`authentication backends documentation ` for details. -.. setting:: AUTH_PROFILE_MODULE - -AUTH_PROFILE_MODULE -------------------- - -.. deprecated:: 1.5 - With the introduction of :ref:`custom User models `, - the use of :setting:`AUTH_PROFILE_MODULE` to define a single profile - model is no longer supported. See the - :doc:`Django 1.5 release notes` for more information. - -Default: Not defined - -The site-specific user profile model used by this site. See -:ref:`User profiles `. - .. setting:: AUTH_USER_MODEL AUTH_USER_MODEL diff --git a/docs/releases/1.5-alpha-1.txt b/docs/releases/1.5-alpha-1.txt index 2588b85306..9ff4ca44db 100644 --- a/docs/releases/1.5-alpha-1.txt +++ b/docs/releases/1.5-alpha-1.txt @@ -575,8 +575,8 @@ Miscellaneous Features deprecated in 1.5 ========================== -:setting:`AUTH_PROFILE_MODULE` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``AUTH_PROFILE_MODULE`` setting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ With the introduction of :ref:`custom User models `, there is no longer any need for a built-in mechanism to store user profile data. @@ -584,8 +584,8 @@ no longer any need for a built-in mechanism to store user profile data. You can still define user profiles models that have a one-to-one relation with the User model - in fact, for many applications needing to associate data with a User account, this will be an appropriate design pattern to follow. However, -the :setting:`AUTH_PROFILE_MODULE` setting, and the -:meth:`~django.contrib.auth.models.User.get_profile()` method for accessing +the ``AUTH_PROFILE_MODULE`` setting, and the +``django.contrib.auth.models.User.get_profile()`` method for accessing the user profile model, should not be used any longer. Streaming behavior of :class:`~django.http.HttpResponse` diff --git a/docs/releases/1.5-beta-1.txt b/docs/releases/1.5-beta-1.txt index a1c9edd890..9c3b9e78e6 100644 --- a/docs/releases/1.5-beta-1.txt +++ b/docs/releases/1.5-beta-1.txt @@ -627,8 +627,8 @@ Features deprecated in 1.5 .. _simplejson-deprecation-beta-1: -:setting:`AUTH_PROFILE_MODULE` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``AUTH_PROFILE_MODULE`` +~~~~~~~~~~~~~~~~~~~~~~~ With the introduction of :ref:`custom User models `, there is no longer any need for a built-in mechanism to store user profile data. @@ -636,8 +636,8 @@ no longer any need for a built-in mechanism to store user profile data. You can still define user profiles models that have a one-to-one relation with the User model - in fact, for many applications needing to associate data with a User account, this will be an appropriate design pattern to follow. However, -the :setting:`AUTH_PROFILE_MODULE` setting, and the -:meth:`~django.contrib.auth.models.User.get_profile()` method for accessing +the ``AUTH_PROFILE_MODULE`` setting, and the +``django.contrib.auth.models.User.get_profile()`` method for accessing the user profile model, should not be used any longer. Streaming behavior of :class:`~django.http.HttpResponse` diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 2bb3095297..3948c867a3 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -732,8 +732,8 @@ deprecation schedule. Direct use of python markup libraries or 3rd party tag libraries is preferred to Django maintaining this functionality in the framework. -:setting:`AUTH_PROFILE_MODULE` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``AUTH_PROFILE_MODULE`` +~~~~~~~~~~~~~~~~~~~~~~~ With the introduction of :ref:`custom User models `, there is no longer any need for a built-in mechanism to store user profile data. @@ -741,8 +741,8 @@ no longer any need for a built-in mechanism to store user profile data. You can still define user profiles models that have a one-to-one relation with the User model - in fact, for many applications needing to associate data with a User account, this will be an appropriate design pattern to follow. However, -the :setting:`AUTH_PROFILE_MODULE` setting, and the -:meth:`~django.contrib.auth.models.User.get_profile()` method for accessing +the ``AUTH_PROFILE_MODULE`` setting, and the +``django.contrib.auth.models.User.get_profile()`` method for accessing the user profile model, should not be used any longer. Streaming behavior of :class:`~django.http.HttpResponse` diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt index ee1aedbd8c..24c622f77f 100644 --- a/docs/topics/auth/customizing.txt +++ b/docs/topics/auth/customizing.txt @@ -354,40 +354,6 @@ model and adding the related fields may be your better option. However existing links to the default User model within your project's apps may justify the extra database load. -.. _auth-profiles: - -.. deprecated:: 1.5 - With the introduction of :ref:`custom User models `, - the use of :setting:`AUTH_PROFILE_MODULE` to define a single profile - model is no longer supported. See the - :doc:`Django 1.5 release notes` for more information. - -Prior to 1.5, a single profile model could be specified site-wide with the -setting :setting:`AUTH_PROFILE_MODULE` with a string consisting of the -following items, separated by a dot: - -1. The name of the application (case sensitive) in which the user - profile model is defined (in other words, the - name which was passed to :djadmin:`manage.py startapp ` to create - the application). - -2. The name of the model (not case sensitive) class. - -For example, if the profile model was a class named ``UserProfile`` and was -defined inside an application named ``accounts``, the appropriate setting would -be:: - - AUTH_PROFILE_MODULE = 'accounts.UserProfile' - -When a user profile model has been defined and specified in this manner, each -:class:`~django.contrib.auth.models.User` object will have a method -- -:class:`~django.contrib.auth.models.User.get_profile()` -- which returns the -instance of the user profile model associated with that -:class:`~django.contrib.auth.models.User`. - -The method :class:`~django.contrib.auth.models.User.get_profile()` -does not create a profile if one does not exist. - .. _auth-custom-user: Substituting a custom User model diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index a0d937ce81..12a6379ca0 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -3,9 +3,7 @@ from __future__ import absolute_import, unicode_literals import datetime import pickle from operator import attrgetter -import warnings -from django.conf import settings from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.core import management @@ -1626,25 +1624,6 @@ class AuthTestCase(TestCase): command_output = new_io.getvalue().strip() self.assertTrue('"email": "alice@example.com"' in command_output) - -@override_settings(AUTH_PROFILE_MODULE='multiple_database.UserProfile') -class UserProfileTestCase(TestCase): - - def test_user_profiles(self): - alice = User.objects.create_user('alice', 'alice@example.com') - bob = User.objects.db_manager('other').create_user('bob', 'bob@example.com') - - alice_profile = UserProfile(user=alice, flavor='chocolate') - alice_profile.save() - - bob_profile = UserProfile(user=bob, flavor='crunchy frog') - bob_profile.save() - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.assertEqual(alice.get_profile().flavor, 'chocolate') - self.assertEqual(bob.get_profile().flavor, 'crunchy frog') - class AntiPetRouter(object): # A router that only expresses an opinion on syncdb, # passing pets to the 'other' database From 6ba69c8456461aa30f415331b948f7f92dfcf211 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 20:00:50 -0300 Subject: [PATCH 11/47] Removed 'depth' .select_related() argument as per deprecation TL. --- django/db/models/query.py | 9 ------ docs/ref/models/querysets.txt | 16 ---------- tests/select_related/tests.py | 58 ++++++++--------------------------- 3 files changed, 13 insertions(+), 70 deletions(-) diff --git a/django/db/models/query.py b/django/db/models/query.py index 27a87a3f65..34d8eaa6ff 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -5,7 +5,6 @@ The main QuerySet implementation. This provides the public API for the ORM. import copy import itertools import sys -import warnings from django.conf import settings from django.core import exceptions @@ -648,10 +647,6 @@ class QuerySet(object): If select_related(None) is called, the list is cleared. """ - if 'depth' in kwargs: - warnings.warn('The "depth" keyword argument has been deprecated.\n' - 'Use related field names instead.', DeprecationWarning, stacklevel=2) - depth = kwargs.pop('depth', 0) if kwargs: raise TypeError('Unexpected keyword arguments to select_related: %s' % (list(kwargs),)) @@ -659,13 +654,9 @@ class QuerySet(object): if fields == (None,): obj.query.select_related = False elif fields: - if depth: - raise TypeError('Cannot pass both "depth" and fields to select_related()') obj.query.add_select_related(fields) else: obj.query.select_related = True - if depth: - obj.query.max_depth = depth return obj def prefetch_related(self, *lookups): diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 2788143899..6fd8a42dee 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -764,8 +764,6 @@ You can refer to any :class:`~django.db.models.ForeignKey` or :class:`~django.db.models.OneToOneField` relation in the list of fields passed to ``select_related()``. This includes foreign keys that have ``null=True`` (which are omitted in a no-parameter ``select_related()`` call). -It's an error to use both a list of fields and the ``depth`` parameter in the -same ``select_related()`` call; they are conflicting options. You can also refer to the reverse direction of a :class:`~django.db.models.OneToOneField` in the list of fields passed to @@ -781,20 +779,6 @@ If you need to clear the list of related fields added by past calls of >>> without_relations = queryset.select_related(None) -.. deprecated:: 1.5 - The ``depth`` parameter to ``select_related()`` has been deprecated. You - should replace it with the use of the ``(*fields)`` listing specific - related fields instead as documented above. - -A depth limit of relationships to follow can also be specified:: - - b = Book.objects.select_related(depth=1).get(id=4) - p = b.author # Doesn't hit the database. - c = p.hometown # Requires a database call. - -A :class:`~django.db.models.OneToOneField` is not traversed in the reverse -direction if you are performing a depth-based ``select_related()`` call. - prefetch_related ~~~~~~~~~~~~~~~~ diff --git a/tests/select_related/tests.py b/tests/select_related/tests.py index baa141d123..e6723eac9b 100644 --- a/tests/select_related/tests.py +++ b/tests/select_related/tests.py @@ -1,7 +1,5 @@ from __future__ import absolute_import, unicode_literals -import warnings - from django.test import TestCase from .models import Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species @@ -55,9 +53,7 @@ class SelectRelatedTests(TestCase): extra queries """ with self.assertNumQueries(1): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - person = Species.objects.select_related(depth=10).get(name="sapiens") + person = Species.objects.select_related('genus__family__order__klass__phylum__kingdom__domain').get(name="sapiens") domain = person.genus.family.order.klass.phylum.kingdom.domain self.assertEqual(domain.name, 'Eukaryota') @@ -91,53 +87,27 @@ class SelectRelatedTests(TestCase): 'Hominidae', ]) - def test_depth(self, depth=1, expected=7): - """ - The "depth" argument to select_related() will stop the descent at a - particular level. - """ - # Notice: one fewer queries than above because of depth=1 - with self.assertNumQueries(expected): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - pea = Species.objects.select_related(depth=depth).get(name="sativum") - self.assertEqual( - pea.genus.family.order.klass.phylum.kingdom.domain.name, - 'Eukaryota' - ) - - def test_larger_depth(self): - """ - The "depth" argument to select_related() will stop the descent at a - particular level. This tests a larger depth value. - """ - self.test_depth(depth=5, expected=3) - def test_list_with_depth(self): """ - The "depth" argument to select_related() will stop the descent at a - particular level. This can be used on lists as well. + Passing a relationship field lookup specifier to select_related() will + stop the descent at a particular level. This can be used on lists as + well. """ with self.assertNumQueries(5): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - world = Species.objects.all().select_related(depth=2) - orders = [o.genus.family.order.name for o in world] + world = Species.objects.all().select_related('genus__family') + orders = [o.genus.family.order.name for o in world] self.assertEqual(sorted(orders), ['Agaricales', 'Diptera', 'Fabales', 'Primates']) def test_select_related_with_extra(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - s = Species.objects.all().select_related(depth=1)\ - .extra(select={'a': 'select_related_species.id + 10'})[0] + s = Species.objects.all().select_related()\ + .extra(select={'a': 'select_related_species.id + 10'})[0] self.assertEqual(s.id + 10, s.a) def test_certain_fields(self): """ The optional fields passed to select_related() control which related - models we pull in. This allows for smaller queries and can act as an - alternative (or, in addition to) the depth parameter. + models we pull in. This allows for smaller queries. In this case, we explicitly say to select the 'genus' and 'genus.family' models, leading to the same number of queries as before. @@ -166,12 +136,10 @@ class SelectRelatedTests(TestCase): self.assertEqual(s, 'Diptera') def test_depth_fields_fails(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.assertRaises(TypeError, - Species.objects.select_related, - 'genus__family__order', depth=4 - ) + self.assertRaises(TypeError, + Species.objects.select_related, + 'genus__family__order', depth=4 + ) def test_none_clears_list(self): queryset = Species.objects.select_related('genus').select_related(None) From 8eadbc5a03d06f5bfedfa3fad35ad0801d2ab6ff Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 20:50:36 -0300 Subject: [PATCH 12/47] Removed 'mimetype' arguments from a few places, as per deprecation TL. This includes HttpResponse and co. __init__() methods, django.shortcuts.render_to_response() and the index(), sitemap() sitemap app views. --- django/contrib/sitemaps/views.py | 16 ++-------------- django/http/response.py | 7 +------ django/shortcuts/__init__.py | 6 ------ django/template/response.py | 10 ++++------ docs/internals/deprecation.txt | 2 +- docs/ref/template-response.txt | 6 ++---- 6 files changed, 10 insertions(+), 37 deletions(-) diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py index 95bc7eac54..63551a925b 100644 --- a/django/contrib/sitemaps/views.py +++ b/django/contrib/sitemaps/views.py @@ -19,13 +19,7 @@ def x_robots_tag(func): @x_robots_tag def index(request, sitemaps, template_name='sitemap_index.xml', content_type='application/xml', - sitemap_url_name='django.contrib.sitemaps.views.sitemap', - mimetype=None): - - if mimetype: - warnings.warn("The mimetype keyword argument is deprecated, use " - "content_type instead", DeprecationWarning, stacklevel=2) - content_type = mimetype + sitemap_url_name='django.contrib.sitemaps.views.sitemap'): req_protocol = 'https' if request.is_secure() else 'http' req_site = get_current_site(request) @@ -47,13 +41,7 @@ def index(request, sitemaps, @x_robots_tag def sitemap(request, sitemaps, section=None, - template_name='sitemap.xml', content_type='application/xml', - mimetype=None): - - if mimetype: - warnings.warn("The mimetype keyword argument is deprecated, use " - "content_type instead", DeprecationWarning, stacklevel=2) - content_type = mimetype + template_name='sitemap.xml', content_type='application/xml'): req_protocol = 'https' if request.is_secure() else 'http' req_site = get_current_site(request) diff --git a/django/http/response.py b/django/http/response.py index 784f21174e..552161c212 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -98,7 +98,7 @@ class HttpResponseBase(six.Iterator): status_code = 200 reason_phrase = None # Use default reason phrase for status code. - def __init__(self, content_type=None, status=None, reason=None, mimetype=None): + def __init__(self, content_type=None, status=None, reason=None): # _headers is a mapping of the lower-case name to the original case of # the header (required for working with legacy systems) and the header # value. Both the name of the header and its value are ASCII strings. @@ -108,11 +108,6 @@ class HttpResponseBase(six.Iterator): # This parameter is set by the handler. It's necessary to preserve the # historical behavior of request_finished. self._handler_class = None - if mimetype: - warnings.warn("Using mimetype keyword argument is deprecated, use" - " content_type instead", - DeprecationWarning, stacklevel=2) - content_type = mimetype if not content_type: content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, self._charset) diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py index 21bd7a06d2..828b6d2d37 100644 --- a/django/shortcuts/__init__.py +++ b/django/shortcuts/__init__.py @@ -20,12 +20,6 @@ def render_to_response(*args, **kwargs): """ httpresponse_kwargs = {'content_type': kwargs.pop('content_type', None)} - mimetype = kwargs.pop('mimetype', None) - if mimetype: - warnings.warn("The mimetype keyword argument is deprecated, use " - "content_type instead", DeprecationWarning, stacklevel=2) - httpresponse_kwargs['content_type'] = mimetype - return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs) def render(request, *args, **kwargs): diff --git a/django/template/response.py b/django/template/response.py index 3b3b41331a..aa4f12af44 100644 --- a/django/template/response.py +++ b/django/template/response.py @@ -10,8 +10,7 @@ class ContentNotRenderedError(Exception): class SimpleTemplateResponse(HttpResponse): rendering_attrs = ['template_name', 'context_data', '_post_render_callbacks'] - def __init__(self, template, context=None, content_type=None, status=None, - mimetype=None): + def __init__(self, template, context=None, content_type=None, status=None): # It would seem obvious to call these next two members 'template' and # 'context', but those names are reserved as part of the test Client # API. To avoid the name collision, we use tricky-to-debug problems @@ -23,8 +22,7 @@ class SimpleTemplateResponse(HttpResponse): # content argument doesn't make sense here because it will be replaced # with rendered template so we always pass empty string in order to # prevent errors and provide shorter signature. - super(SimpleTemplateResponse, self).__init__('', content_type, status, - mimetype) + super(SimpleTemplateResponse, self).__init__('', content_type, status) # _is_rendered tracks whether the template and context has been baked # into a final response. @@ -139,7 +137,7 @@ class TemplateResponse(SimpleTemplateResponse): ['_request', '_current_app'] def __init__(self, request, template, context=None, content_type=None, - status=None, mimetype=None, current_app=None): + status=None, current_app=None): # self.request gets over-written by django.test.client.Client - and # unlike context_data and template_name the _request should not # be considered part of the public API. @@ -148,7 +146,7 @@ class TemplateResponse(SimpleTemplateResponse): # having to avoid needing to create the RequestContext directly self._current_app = current_app super(TemplateResponse, self).__init__( - template, context, content_type, status, mimetype) + template, context, content_type, status) def resolve_context(self, context): """Convert context data into a full RequestContext object diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 5513c79966..ee38df12a1 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -295,7 +295,7 @@ these changes. :class:`~django.template.response.TemplateResponse`, will be removed. ``content_type`` should be used instead. This also applies to the :func:`~django.shortcuts.render_to_response` shortcut and - the sitemamp views, :func:`~django.contrib.sitemaps.views.index` and + the sitemap views, :func:`~django.contrib.sitemaps.views.index` and :func:`~django.contrib.sitemaps.views.sitemap`. * When :class:`~django.http.HttpResponse` is instantiated with an iterator, diff --git a/docs/ref/template-response.txt b/docs/ref/template-response.txt index 4f34d150ed..a8fb8dfee6 100644 --- a/docs/ref/template-response.txt +++ b/docs/ref/template-response.txt @@ -82,8 +82,7 @@ Methods deprecated), but since this is actually the value included in the HTTP ``Content-Type`` header, it can also include the character set encoding, which makes it more than just a MIME type specification. If - ``mimetype`` is specified (not ``None``), that value is used. - Otherwise, ``content_type`` is used. If neither is given, + ``content_type`` is specified, then its value is used. Otherwise, :setting:`DEFAULT_CONTENT_TYPE` is used. @@ -175,8 +174,7 @@ Methods deprecated), but since this is actually the value included in the HTTP ``Content-Type`` header, it can also include the character set encoding, which makes it more than just a MIME type specification. If - ``mimetype`` is specified (not ``None``), that value is used. - Otherwise, ``content_type`` is used. If neither is given, + ``content_type`` is specified, then its value is used. Otherwise, :setting:`DEFAULT_CONTENT_TYPE` is used. ``current_app`` From 7379d9aceab01e007966b5fe1f47ba7590deb887 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 22:38:13 -0300 Subject: [PATCH 13/47] Removed insert(), value_for_insert() SortedDict methods deprecated in Django 1.5. --- django/utils/datastructures.py | 26 ------------------------ docs/ref/utils.txt | 14 ------------- tests/utils_tests/test_datastructures.py | 15 -------------- 3 files changed, 55 deletions(-) diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index a211323320..3b1638392c 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -1,5 +1,4 @@ import copy -import warnings from django.utils import six @@ -217,31 +216,6 @@ class SortedDict(dict): self.keyOrder.append(key) return super(SortedDict, self).setdefault(key, default) - def value_for_index(self, index): - """Returns the value of the item at the given zero-based index.""" - # This, and insert() are deprecated because they cannot be implemented - # using collections.OrderedDict (Python 2.7 and up), which we'll - # eventually switch to - warnings.warn( - "SortedDict.value_for_index is deprecated", DeprecationWarning, - stacklevel=2 - ) - return self[self.keyOrder[index]] - - def insert(self, index, key, value): - """Inserts the key, value pair before the item with the given index.""" - warnings.warn( - "SortedDict.insert is deprecated", DeprecationWarning, - stacklevel=2 - ) - if key in self.keyOrder: - n = self.keyOrder.index(key) - del self.keyOrder[n] - if n < index: - index -= 1 - self.keyOrder.insert(index, key) - super(SortedDict, self).__setitem__(key, value) - def copy(self): """Returns a copy of this object.""" # This way of initializing the copy means it works for subclasses, too. diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 8d722829fb..6fa829ef22 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -107,20 +107,6 @@ to distinguish caches by the ``Accept-language`` header. The :class:`django.utils.datastructures.SortedDict` class is a dictionary that keeps its keys in the order in which they're inserted. - ``SortedDict`` adds two additional methods to the standard Python ``dict`` - class: - - .. method:: insert(index, key, value) - - .. deprecated:: 1.5 - - Inserts the key, value pair before the item with the given index. - - .. method:: value_for_index(index) - - .. deprecated:: 1.5 - - Returns the value of the item at the given zero-based index. Creating a new SortedDict ------------------------- diff --git a/tests/utils_tests/test_datastructures.py b/tests/utils_tests/test_datastructures.py index 5829e7c2d7..5563a0fc4f 100644 --- a/tests/utils_tests/test_datastructures.py +++ b/tests/utils_tests/test_datastructures.py @@ -4,7 +4,6 @@ Tests for stuff in django.utils.datastructures. import copy import pickle -import warnings from django.test import SimpleTestCase from django.utils.datastructures import (DictWrapper, ImmutableList, @@ -134,20 +133,6 @@ class SortedDictTests(SimpleTestCase): self.assertEqual(list(reversed(self.d1)), [9, 1, 7]) self.assertEqual(list(reversed(self.d2)), [7, 0, 9, 1]) - def test_insert(self): - d = SortedDict() - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - d.insert(0, "hello", "world") - assert w[0].category is DeprecationWarning - - def test_value_for_index(self): - d = SortedDict({"a": 3}) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - self.assertEqual(d.value_for_index(0), 3) - assert w[0].category is DeprecationWarning - class MergeDictTests(SimpleTestCase): From 425a4292083c7355a2a4ef9676f605a9c3b8fe70 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 22:42:10 -0300 Subject: [PATCH 14/47] Made GeoDjango GeometryField stop accepting a 'null' keyword argument as per its deprecation in 1.5. --- django/contrib/gis/forms/fields.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/django/contrib/gis/forms/fields.py b/django/contrib/gis/forms/fields.py index 59e725926c..2fb8b545e0 100644 --- a/django/contrib/gis/forms/fields.py +++ b/django/contrib/gis/forms/fields.py @@ -1,7 +1,5 @@ from __future__ import unicode_literals -import warnings - from django import forms from django.utils import six from django.utils.translation import ugettext_lazy as _ @@ -34,10 +32,6 @@ class GeometryField(forms.Field): # defaults (e.g., allow None). self.srid = kwargs.pop('srid', None) self.geom_type = kwargs.pop('geom_type', self.geom_type) - if 'null' in kwargs: - kwargs.pop('null', True) - warnings.warn("Passing 'null' keyword argument to GeometryField is deprecated.", - DeprecationWarning, stacklevel=2) super(GeometryField, self).__init__(**kwargs) self.widget.attrs['geom_type'] = self.geom_type From 24bbf1367a1eed9443571622ab74c2c3e3d3db11 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 28 Jun 2013 23:05:00 -0300 Subject: [PATCH 15/47] Removed django.utils.encoding.StrAndUnicode class, deprecated in Django 1.5. --- django/utils/encoding.py | 24 ------------------------ docs/ref/utils.txt | 13 +------------ docs/releases/1.5-alpha-1.txt | 2 +- docs/releases/1.5-beta-1.txt | 2 +- docs/releases/1.5.txt | 2 +- 5 files changed, 4 insertions(+), 39 deletions(-) diff --git a/django/utils/encoding.py b/django/utils/encoding.py index adab0d0712..aa0218ca6b 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -8,7 +8,6 @@ try: from urllib.parse import quote except ImportError: # Python 2 from urllib import quote -import warnings from django.utils.functional import Promise from django.utils import six @@ -23,29 +22,6 @@ class DjangoUnicodeDecodeError(UnicodeDecodeError): return '%s. You passed in %r (%s)' % (original, self.obj, type(self.obj)) -class StrAndUnicode(object): - """ - A class that derives __str__ from __unicode__. - - On Python 2, __str__ returns the output of __unicode__ encoded as a UTF-8 - bytestring. On Python 3, __str__ returns the output of __unicode__. - - Useful as a mix-in. If you support Python 2 and 3 with a single code base, - you can inherit this mix-in and just define __unicode__. - """ - def __init__(self, *args, **kwargs): - warnings.warn("StrAndUnicode is deprecated. Define a __str__ method " - "and apply the @python_2_unicode_compatible decorator " - "instead.", DeprecationWarning, stacklevel=2) - super(StrAndUnicode, self).__init__(*args, **kwargs) - - if six.PY3: - def __str__(self): - return self.__unicode__() - else: - def __str__(self): - return self.__unicode__().encode('utf-8') - def python_2_unicode_compatible(klass): """ A decorator that defines __unicode__ and __str__ methods under Python 2. diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 6fa829ef22..c29c210be7 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -188,18 +188,7 @@ The functions defined in this module share the following properties: ========================= .. module:: django.utils.encoding - :synopsis: A series of helper classes and function to manage character encoding. - -.. class:: StrAndUnicode - - A class that derives ``__str__`` from ``__unicode__``. - - On Python 2, ``__str__`` returns the output of ``__unicode__`` encoded as - a UTF-8 bytestring. On Python 3, ``__str__`` returns the output of - ``__unicode__``. - - Useful as a mix-in. If you support Python 2 and 3 with a single code base, - you can inherit this mix-in and just define ``__unicode__``. + :synopsis: A series of helper function to manage character encoding. .. function:: python_2_unicode_compatible diff --git a/docs/releases/1.5-alpha-1.txt b/docs/releases/1.5-alpha-1.txt index 9ff4ca44db..15169131df 100644 --- a/docs/releases/1.5-alpha-1.txt +++ b/docs/releases/1.5-alpha-1.txt @@ -615,7 +615,7 @@ explicitly. ``django.utils.encoding.StrAndUnicode`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :class:`~django.utils.encoding.StrAndUnicode` mix-in has been deprecated. +The ``django.utils.encoding.StrAndUnicode`` mix-in has been deprecated. Define a ``__str__`` method and apply the :func:`~django.utils.encoding.python_2_unicode_compatible` decorator instead. diff --git a/docs/releases/1.5-beta-1.txt b/docs/releases/1.5-beta-1.txt index 9c3b9e78e6..69c591ac03 100644 --- a/docs/releases/1.5-beta-1.txt +++ b/docs/releases/1.5-beta-1.txt @@ -668,7 +668,7 @@ If you rely on features added to :mod:`simplejson` after it became Python's ``django.utils.encoding.StrAndUnicode`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :class:`~django.utils.encoding.StrAndUnicode` mix-in has been deprecated. +The ``django.utils.encoding.StrAndUnicode`` mix-in has been deprecated. Define a ``__str__`` method and apply the :func:`~django.utils.encoding.python_2_unicode_compatible` decorator instead. diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 3948c867a3..a4b03bc197 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -775,7 +775,7 @@ If you rely on features added to :mod:`simplejson` after it became Python's ``django.utils.encoding.StrAndUnicode`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :class:`~django.utils.encoding.StrAndUnicode` mix-in has been deprecated. +The ``django.utils.encoding.StrAndUnicode`` mix-in has been deprecated. Define a ``__str__`` method and apply the :func:`~django.utils.encoding.python_2_unicode_compatible` decorator instead. From adc6f38867dd5b57c9e1c50395eec01f6bdd651f Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sat, 29 Jun 2013 10:50:04 +0200 Subject: [PATCH 16/47] Fixed 1.6 release notes. --- docs/releases/1.6.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 1fd98e1271..4219edb1a0 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -41,9 +41,9 @@ Simplified default project and app templates The default templates used by :djadmin:`startproject` and :djadmin:`startapp` have been simplified and modernized. The :doc:`admin ` is now enabled by default in new projects; the -:doc:`sites ` framework no longer is. :ref:`Language -detection ` and :ref:`clickjacking -prevention ` are turned on. +:doc:`sites ` framework no longer is. :ref:`clickjacking +prevention ` is now on and the database defaults to +SQLite. If the default templates don't suit your tastes, you can use :ref:`custom project and app templates `. From c8756e17fbd5293ee1e0582201c41e2febc58ae1 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 29 Jun 2013 11:40:31 +0200 Subject: [PATCH 17/47] Removed obsolete comment. Refs #20079. Thanks Gavin Wahl. --- django/contrib/auth/models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 16c6357c40..5e3c6451f9 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -14,7 +14,6 @@ from django.utils.translation import ugettext_lazy as _ from django.utils import timezone from django.contrib import auth -# UNUSABLE_PASSWORD is still imported here for backwards compatibility from django.contrib.auth.hashers import ( check_password, make_password, is_password_usable) from django.contrib.auth.signals import user_logged_in From 6118d6d1c982e428cf398ac998eb9f0baba15bad Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 29 Jun 2013 11:52:29 +0200 Subject: [PATCH 18/47] More import removals Following the series of commits removing deprecated features in Django 1.7, here are some more unneeded imports removed and other minor cleanups. --- django/contrib/auth/forms.py | 2 -- django/contrib/auth/models.py | 6 ------ django/contrib/auth/tests/test_models.py | 2 +- django/contrib/sitemaps/views.py | 1 - django/shortcuts/__init__.py | 2 -- django/test/testcases.py | 1 - docs/ref/utils.txt | 2 +- tests/.coveragerc | 2 +- tests/test_utils/tests.py | 1 - 9 files changed, 3 insertions(+), 16 deletions(-) diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index beff18c0f2..2d7a7c14d4 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -1,7 +1,5 @@ from __future__ import unicode_literals -import warnings - from django import forms from django.forms.util import flatatt from django.template import loader diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 5e3c6451f9..cf3d37e5c3 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -1,8 +1,6 @@ from __future__ import unicode_literals import re -import warnings -from django.core.exceptions import ImproperlyConfigured from django.core.mail import send_mail from django.core import validators from django.db import models @@ -31,10 +29,6 @@ def update_last_login(sender, user, **kwargs): user_logged_in.connect(update_last_login) -class SiteProfileNotAvailable(Exception): - pass - - class PermissionManager(models.Manager): def get_by_natural_key(self, codename, app_label, model): return self.get( diff --git a/django/contrib/auth/tests/test_models.py b/django/contrib/auth/tests/test_models.py index cd3a13b76d..fa20775a8d 100644 --- a/django/contrib/auth/tests/test_models.py +++ b/django/contrib/auth/tests/test_models.py @@ -1,5 +1,5 @@ from django.contrib.auth import get_user_model -from django.contrib.auth.models import (Group, User, UserManager) +from django.contrib.auth.models import Group, User, UserManager from django.contrib.auth.tests.utils import skipIfCustomUser from django.db.models.signals import post_save from django.test import TestCase diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py index 63551a925b..7a94fb355d 100644 --- a/django/contrib/sitemaps/views.py +++ b/django/contrib/sitemaps/views.py @@ -1,4 +1,3 @@ -import warnings from functools import wraps from django.contrib.sites.models import get_current_site diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py index 828b6d2d37..b096efdb44 100644 --- a/django/shortcuts/__init__.py +++ b/django/shortcuts/__init__.py @@ -3,8 +3,6 @@ This module collects helper functions and classes that "span" multiple levels of MVC. In other words, these functions/classes introduce controlled coupling for convenience's sake. """ -import warnings - from django.template import loader, RequestContext from django.http import HttpResponse, Http404 from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect diff --git a/django/test/testcases.py b/django/test/testcases.py index 4300df2856..53b15158ca 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -15,7 +15,6 @@ except ImportError: # Python 2 import select import socket import threading -import warnings from django.conf import settings from django.contrib.staticfiles.handlers import StaticFilesHandler diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index c29c210be7..d7be7a4735 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -188,7 +188,7 @@ The functions defined in this module share the following properties: ========================= .. module:: django.utils.encoding - :synopsis: A series of helper function to manage character encoding. + :synopsis: A series of helper functions to manage character encoding. .. function:: python_2_unicode_compatible diff --git a/tests/.coveragerc b/tests/.coveragerc index cd7fee3c89..1b4c087f14 100644 --- a/tests/.coveragerc +++ b/tests/.coveragerc @@ -1,5 +1,5 @@ [run] -omit = */django/contrib/*/tests*,*/django/utils/unittest*,*/django/utils/simplejson*,*/django/utils/importlib.py,*/django/test/_doctest.py,*/django/core/servers/fastcgi.py,*/django/utils/autoreload.py,*/django/utils/dictconfig.py +omit = */django/contrib/*/tests*,*/django/utils/unittest*,*/django/utils/importlib.py,*/django/test/_doctest.py,*/django/core/servers/fastcgi.py,*/django/utils/autoreload.py,*/django/utils/dictconfig.py [html] directory = coverage_html diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index b51ba7bf3b..8c6c15e357 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, unicode_literals -import warnings from django.db import connection from django.forms import EmailField, IntegerField From b5f709e6f4c67020bedb141b9b18c5cd1e05f829 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sat, 29 Jun 2013 16:28:05 +0200 Subject: [PATCH 19/47] Removed comment from setup.cfg which broke newer wheel versions. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 330eff6977..4c25b840b5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,4 +6,4 @@ install-script = scripts/rpm-install.sh license-file = LICENSE [wheel] -universal = 1 # use py2.py3 tag for pure-python dist +universal = 1 From d51b7794bfd1ddfd92f71f71d07daf931421c5f7 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Sat, 29 Jun 2013 12:22:15 -0300 Subject: [PATCH 20/47] Removed django.contrib.auth.views.password_reset_confirm_uidb36() view to finish its accelerated deprecation schedule. --- django/contrib/auth/tests/test_views.py | 22 +--------------------- django/contrib/auth/urls.py | 3 --- django/contrib/auth/views.py | 9 --------- 3 files changed, 1 insertion(+), 33 deletions(-) diff --git a/django/contrib/auth/tests/test_views.py b/django/contrib/auth/tests/test_views.py index ba06a6af4d..b939dff058 100644 --- a/django/contrib/auth/tests/test_views.py +++ b/django/contrib/auth/tests/test_views.py @@ -13,7 +13,7 @@ from django.core import mail from django.core.urlresolvers import reverse, NoReverseMatch from django.http import QueryDict, HttpRequest from django.utils.encoding import force_text -from django.utils.http import int_to_base36, urlsafe_base64_decode, urlquote +from django.utils.http import urlquote from django.utils._os import upath from django.test import TestCase from django.test.utils import override_settings, patch_logger @@ -193,16 +193,6 @@ class PasswordResetTest(AuthViewsTestCase): # redirect to a 'complete' page: self.assertContains(response, "Please enter your new password") - def test_confirm_valid_base36(self): - # Remove in Django 1.7 - url, path = self._test_confirm_start() - path_parts = path.strip("/").split("/") - # construct an old style (base36) URL by converting the base64 ID - path_parts[1] = int_to_base36(int(urlsafe_base64_decode(path_parts[1]))) - response = self.client.get("/%s/%s-%s/" % tuple(path_parts)) - # redirect to a 'complete' page: - self.assertContains(response, "Please enter your new password") - def test_confirm_invalid(self): url, path = self._test_confirm_start() # Let's munge the token in the path, but keep the same length, @@ -217,21 +207,11 @@ class PasswordResetTest(AuthViewsTestCase): response = self.client.get('/reset/123456/1-1/') self.assertContains(response, "The password reset link was invalid") - def test_confirm_invalid_user_base36(self): - # Remove in Django 1.7 - response = self.client.get('/reset/123456-1-1/') - self.assertContains(response, "The password reset link was invalid") - def test_confirm_overflow_user(self): # Ensure that we get a 200 response for a base36 user id that overflows int response = self.client.get('/reset/zzzzzzzzzzzzz/1-1/') self.assertContains(response, "The password reset link was invalid") - def test_confirm_overflow_user_base36(self): - # Remove in Django 1.7 - response = self.client.get('/reset/zzzzzzzzzzzzz-1-1/') - self.assertContains(response, "The password reset link was invalid") - def test_confirm_invalid_post(self): # Same as test_confirm_invalid, but trying # to do a POST instead. diff --git a/django/contrib/auth/urls.py b/django/contrib/auth/urls.py index 801d133437..85b8b2869d 100644 --- a/django/contrib/auth/urls.py +++ b/django/contrib/auth/urls.py @@ -12,9 +12,6 @@ urlpatterns = patterns('', url(r'^password_change/done/$', 'django.contrib.auth.views.password_change_done', name='password_change_done'), url(r'^password_reset/$', 'django.contrib.auth.views.password_reset', name='password_reset'), url(r'^password_reset/done/$', 'django.contrib.auth.views.password_reset_done', name='password_reset_done'), - # Support old style base36 password reset links; remove in Django 1.7 - url(r'^reset/(?P[0-9A-Za-z]{1,13})-(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', - 'django.contrib.auth.views.password_reset_confirm_uidb36'), url(r'^reset/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', 'django.contrib.auth.views.password_reset_confirm', name='password_reset_confirm'), diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index e9affb33cd..b731a2b3d1 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -228,15 +228,6 @@ def password_reset_confirm(request, uidb64=None, token=None, return TemplateResponse(request, template_name, context, current_app=current_app) -def password_reset_confirm_uidb36(request, uidb36=None, **kwargs): - # Support old password reset URLs that used base36 encoded user IDs. - # Remove in Django 1.7 - try: - uidb64 = force_text(urlsafe_base64_encode(force_bytes(base36_to_int(uidb36)))) - except ValueError: - uidb64 = '1' # dummy invalid ID (incorrect padding for base64) - return password_reset_confirm(request, uidb64=uidb64, **kwargs) - def password_reset_complete(request, template_name='registration/password_reset_complete.html', current_app=None, extra_context=None): From ea3fe78a9d742904f6902cdc353a11d795418105 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 27 Jun 2013 10:58:05 +0200 Subject: [PATCH 21/47] Fixed #20660 -- Do not try to delete an unset FieldFile Thanks stanislas.guerra at gmail.com for the report and Baptiste Mispelon for the review. --- django/db/models/fields/files.py | 2 ++ tests/model_fields/tests.py | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index 0a913e908b..7663c8ab90 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -96,6 +96,8 @@ class FieldFile(File): save.alters_data = True def delete(self, save=True): + if not self: + return # Only close the file if it's already open, which we know by the # presence of self._file if hasattr(self, '_file'): diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index ccff8b8cfa..6abeed8c42 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -432,6 +432,17 @@ class FileFieldTests(unittest.TestCase): field.save_form_data(d, 'else.txt') self.assertEqual(d.myfile, 'else.txt') + def test_delete_when_file_unset(self): + """ + Calling delete on an unset FileField should not call the file deletion + process, but fail silently (#20660). + """ + d = Document() + try: + d.myfile.delete() + except OSError: + self.fail("Deleting an unset FileField should not raise OSError.") + class BinaryFieldTests(test.TestCase): binary_data = b'\x00\x46\xFE' From 7fbab3ebaf8b60bbe847b772f895df47067a60d3 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 27 Jun 2013 10:59:30 +0200 Subject: [PATCH 22/47] Do not allow FileSystemStorage.delete to receive an empty name Refs #20660. --- django/core/files/storage.py | 1 + tests/file_storage/tests.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/django/core/files/storage.py b/django/core/files/storage.py index 977b6a68a8..5d301a317c 100644 --- a/django/core/files/storage.py +++ b/django/core/files/storage.py @@ -231,6 +231,7 @@ class FileSystemStorage(Storage): return name def delete(self, name): + assert name, "The name argument is not allowed to be empty." name = self.path(name) # If the file exists, delete it from the filesystem. # Note that there is a race between os.path.exists and os.remove: diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index 6c3c559660..e6caabf9d9 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -364,6 +364,14 @@ class FileStorageTests(unittest.TestCase): with self.assertRaises(IOError): self.storage.save('error.file', f1) + def test_delete_no_name(self): + """ + Calling delete with an empty name should not try to remove the base + storage directory, but fail loudly (#20660). + """ + with self.assertRaises(AssertionError): + self.storage.delete('') + class CustomStorage(FileSystemStorage): def get_available_name(self, name): From 59b0c48ce27951048146358739baf08056c5e821 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 29 Jun 2013 18:44:41 +0200 Subject: [PATCH 23/47] Fixed #18592 -- Prevented crash when accessing MySQL _last_executed Thanks reames at asymmetricventures.com for the report. --- django/db/backends/mysql/base.py | 2 +- tests/backends/tests.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index ea687715e4..d10be94f43 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -284,7 +284,7 @@ class DatabaseOperations(BaseDatabaseOperations): # With MySQLdb, cursor objects have an (undocumented) "_last_executed" # attribute where the exact query sent to the database is saved. # See MySQLdb/cursors.py in the source distribution. - return force_text(cursor._last_executed, errors='replace') + return force_text(getattr(cursor, '_last_executed', None), errors='replace') def no_limit_value(self): # 2**64 - 1, as recommended by the MySQL documentation diff --git a/tests/backends/tests.py b/tests/backends/tests.py index c1a26df7fc..488f8d518b 100644 --- a/tests/backends/tests.py +++ b/tests/backends/tests.py @@ -164,6 +164,17 @@ class DateQuotingTest(TestCase): @override_settings(DEBUG=True) class LastExecutedQueryTest(TestCase): + def test_last_executed_query(self): + """ + last_executed_query should not raise an exception even if no previous + query has been run. + """ + cursor = connection.cursor() + try: + connection.ops.last_executed_query(cursor, '', ()) + except Exception: + self.fail("'last_executed_query' should not raise an exception.") + def test_debug_sql(self): list(models.Reporter.objects.filter(first_name="test")) sql = connection.queries[-1]['sql'].lower() From 8b9b8d3bda09eb1b447631182d06c6c5e51425f6 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 29 Jun 2013 15:41:57 +0200 Subject: [PATCH 24/47] Removed compatibility code for streaming responses. This code provided a deprecation path for old-style streaming responses. Refs #6527, #7581. --- django/http/__init__.py | 8 ++--- django/http/response.py | 61 ++----------------------------------- django/views/static.py | 8 ++--- tests/httpwrappers/tests.py | 25 ++++----------- 4 files changed, 16 insertions(+), 86 deletions(-) diff --git a/django/http/__init__.py b/django/http/__init__.py index 46afa34ee7..be7b24b2e2 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -2,9 +2,9 @@ from django.http.cookie import SimpleCookie, parse_cookie from django.http.request import (HttpRequest, QueryDict, UnreadablePostError, build_request_repr) from django.http.response import (HttpResponse, StreamingHttpResponse, - CompatibleStreamingHttpResponse, HttpResponsePermanentRedirect, - HttpResponseRedirect, HttpResponseNotModified, HttpResponseBadRequest, - HttpResponseForbidden, HttpResponseNotFound, HttpResponseNotAllowed, - HttpResponseGone, HttpResponseServerError, Http404, BadHeaderError) + HttpResponseRedirect, HttpResponsePermanentRedirect, + HttpResponseNotModified, HttpResponseBadRequest, HttpResponseForbidden, + HttpResponseNotFound, HttpResponseNotAllowed, HttpResponseGone, + HttpResponseServerError, Http404, BadHeaderError) from django.http.utils import (fix_location_header, conditional_content_removal, fix_IE_for_attach, fix_IE_for_vary) diff --git a/django/http/response.py b/django/http/response.py index 552161c212..1f0849c0bf 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -2,7 +2,6 @@ from __future__ import absolute_import, unicode_literals import datetime import time -import warnings from email.header import Header try: from urllib.parse import urlparse @@ -332,53 +331,27 @@ class HttpResponse(HttpResponseBase): else: __str__ = serialize - def _consume_content(self): - # If the response was instantiated with an iterator, when its content - # is accessed, the iterator is going be exhausted and the content - # loaded in memory. At this point, it's better to abandon the original - # iterator and save the content for later reuse. This is a temporary - # solution. See the comment in __iter__ below for the long term plan. - if self._base_content_is_iter: - self.content = b''.join(self.make_bytes(e) for e in self._container) - @property def content(self): - self._consume_content() return b''.join(self.make_bytes(e) for e in self._container) @content.setter def content(self, value): if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)): - self._container = value - self._base_content_is_iter = True if hasattr(value, 'close'): self._closable_objects.append(value) - else: - self._container = [value] - self._base_content_is_iter = False + value = b''.join(self.make_bytes(e) for e in value) + self._container = [value] def __iter__(self): - # Raise a deprecation warning only if the content wasn't consumed yet, - # because the response may be intended to be streamed. - # Once the deprecation completes, iterators should be consumed upon - # assignment rather than upon access. The _consume_content method - # should be removed. See #6527. - if self._base_content_is_iter: - warnings.warn( - 'Creating streaming responses with `HttpResponse` is ' - 'deprecated. Use `StreamingHttpResponse` instead ' - 'if you need the streaming behavior.', - DeprecationWarning, stacklevel=2) if not hasattr(self, '_iterator'): self._iterator = iter(self._container) return self def write(self, content): - self._consume_content() self._container.append(content) def tell(self): - self._consume_content() return len(self.content) @@ -416,35 +389,6 @@ class StreamingHttpResponse(HttpResponseBase): self._closable_objects.append(value) -class CompatibleStreamingHttpResponse(StreamingHttpResponse): - """ - This class maintains compatibility with middleware that doesn't know how - to handle the content of a streaming response by exposing a `content` - attribute that will consume and cache the content iterator when accessed. - - These responses will stream only if no middleware attempts to access the - `content` attribute. Otherwise, they will behave like a regular response, - and raise a `DeprecationWarning`. - """ - @property - def content(self): - warnings.warn( - 'Accessing the `content` attribute on a streaming response is ' - 'deprecated. Use the `streaming_content` attribute instead.', - DeprecationWarning, stacklevel=2) - content = b''.join(self) - self.streaming_content = [content] - return content - - @content.setter - def content(self, content): - warnings.warn( - 'Accessing the `content` attribute on a streaming response is ' - 'deprecated. Use the `streaming_content` attribute instead.', - DeprecationWarning, stacklevel=2) - self.streaming_content = [content] - - class HttpResponseRedirectBase(HttpResponse): allowed_schemes = ['http', 'https', 'ftp'] @@ -478,7 +422,6 @@ class HttpResponseNotModified(HttpResponse): if value: raise AttributeError("You cannot set content to a 304 (Not Modified) response") self._container = [] - self._base_content_is_iter = False class HttpResponseBadRequest(HttpResponse): diff --git a/django/views/static.py b/django/views/static.py index 12af130796..470999bb9d 100644 --- a/django/views/static.py +++ b/django/views/static.py @@ -14,8 +14,8 @@ try: except ImportError: # Python 2 from urllib import unquote -from django.http import (CompatibleStreamingHttpResponse, Http404, - HttpResponse, HttpResponseRedirect, HttpResponseNotModified) +from django.http import (Http404, HttpResponse, HttpResponseRedirect, + HttpResponseNotModified, StreamingHttpResponse) from django.template import loader, Template, Context, TemplateDoesNotExist from django.utils.http import http_date, parse_http_date from django.utils.translation import ugettext as _, ugettext_noop @@ -63,8 +63,8 @@ def serve(request, path, document_root=None, show_indexes=False): return HttpResponseNotModified() content_type, encoding = mimetypes.guess_type(fullpath) content_type = content_type or 'application/octet-stream' - response = CompatibleStreamingHttpResponse(open(fullpath, 'rb'), - content_type=content_type) + response = StreamingHttpResponse(open(fullpath, 'rb'), + content_type=content_type) response["Last-Modified"] = http_date(statobj.st_mtime) if stat.S_ISREG(statobj.st_mode): response["Content-Length"] = statobj.st_size diff --git a/tests/httpwrappers/tests.py b/tests/httpwrappers/tests.py index 194232e92f..673409fd99 100644 --- a/tests/httpwrappers/tests.py +++ b/tests/httpwrappers/tests.py @@ -324,19 +324,10 @@ class HttpResponseTests(unittest.TestCase): r.content = [1, 2, 3] self.assertEqual(r.content, b'123') - #test retrieval explicitly using iter (deprecated) and odd inputs + #test odd inputs r = HttpResponse() r.content = ['1', '2', 3, '\u079e'] - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", DeprecationWarning) - my_iter = iter(r) - self.assertEqual(w[0].category, DeprecationWarning) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", DeprecationWarning) - result = list(my_iter) - self.assertEqual(w[0].category, DeprecationWarning) #'\xde\x9e' == unichr(1950).encode('utf-8') - self.assertEqual(result, [b'1', b'2', b'3', b'\xde\x9e']) self.assertEqual(r.content, b'123\xde\x9e') #with Content-Encoding header @@ -344,9 +335,8 @@ class HttpResponseTests(unittest.TestCase): r['Content-Encoding'] = 'winning' r.content = [b'abc', b'def'] self.assertEqual(r.content, b'abcdef') - r.content = ['\u079e'] self.assertRaises(TypeError if six.PY3 else UnicodeEncodeError, - getattr, r, 'content') + setattr, r, 'content', ['\u079e']) # .content can safely be accessed multiple times. r = HttpResponse(iter(['hello', 'world'])) @@ -358,15 +348,12 @@ class HttpResponseTests(unittest.TestCase): # accessing .content still works self.assertEqual(r.content, b'helloworld') - # XXX accessing .content doesn't work if the response was iterated first - # XXX change this when the deprecation completes in HttpResponse + # Accessing .content also works if the response was iterated first. r = HttpResponse(iter(['hello', 'world'])) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.assertEqual(b''.join(r), b'helloworld') - self.assertEqual(r.content, b'') # not the expected result! + self.assertEqual(b''.join(r), b'helloworld') + self.assertEqual(r.content, b'helloworld') - # additional content can be written to the response. + # Additional content can be written to the response. r = HttpResponse(iter(['hello', 'world'])) self.assertEqual(r.content, b'helloworld') r.write('!') From acd7b34aafe352ef604edcb73f75041c5cbba6b9 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 29 Jun 2013 18:34:41 +0200 Subject: [PATCH 25/47] Advanced deprecation warnings for Django 1.7. --- django/conf/urls/shortcut.py | 2 +- django/contrib/admin/options.py | 2 +- django/contrib/admin/views/main.py | 8 ++++---- django/contrib/comments/__init__.py | 2 +- django/contrib/comments/templatetags/comments.py | 2 +- django/contrib/contenttypes/generic.py | 2 +- django/db/__init__.py | 4 ++-- django/db/backends/creation.py | 2 +- django/db/models/fields/related.py | 4 ++-- django/db/models/manager.py | 4 ++-- django/db/models/options.py | 8 ++++---- django/db/transaction.py | 14 +++++++------- django/db/utils.py | 2 +- django/forms/forms.py | 2 +- django/forms/models.py | 6 +++--- django/forms/widgets.py | 2 +- django/middleware/cache.py | 2 +- django/middleware/common.py | 2 +- django/middleware/doc.py | 2 +- django/middleware/transaction.py | 2 +- django/template/defaulttags.py | 4 ++-- django/test/_doctest.py | 2 +- django/test/simple.py | 2 +- django/utils/image.py | 2 +- django/views/defaults.py | 2 +- django/views/generic/edit.py | 2 +- tests/admin_scripts/app_with_import/models.py | 6 +++--- tests/admin_scripts/tests.py | 1 - tests/cache/tests.py | 4 ++-- tests/deprecation/tests.py | 2 +- tests/generic_views/test_edit.py | 6 +++--- tests/middleware/tests.py | 10 +++++----- tests/model_forms/tests.py | 4 ++-- tests/model_forms_regress/tests.py | 4 ++-- tests/runtests.py | 2 +- tests/template_tests/tests.py | 2 +- tests/test_runner/tests.py | 4 ++-- tests/test_suite_override/tests.py | 4 ++-- tests/test_utils/tests.py | 4 ++-- tests/transactions/tests.py | 8 ++++---- tests/transactions_regress/tests.py | 12 ++++++------ 41 files changed, 80 insertions(+), 81 deletions(-) diff --git a/django/conf/urls/shortcut.py b/django/conf/urls/shortcut.py index c00d176ad6..08ecd90aa3 100644 --- a/django/conf/urls/shortcut.py +++ b/django/conf/urls/shortcut.py @@ -3,7 +3,7 @@ import warnings from django.conf.urls import patterns warnings.warn("django.conf.urls.shortcut will be removed in Django 1.8.", - PendingDeprecationWarning) + DeprecationWarning) urlpatterns = patterns('django.views', (r'^(?P\d+)/(?P.*)/$', 'defaults.shortcut'), diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index fd516cb512..9c92f7ae9c 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -76,7 +76,7 @@ csrf_protect_m = method_decorator(csrf_protect) class RenameBaseModelAdminMethods(forms.MediaDefiningClass, RenameMethodsBase): renamed_methods = ( - ('queryset', 'get_queryset', PendingDeprecationWarning), + ('queryset', 'get_queryset', DeprecationWarning), ) diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 56462dece8..f766031bf8 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -52,7 +52,7 @@ def _is_changelist_popup(request): warnings.warn( "The `%s` GET parameter has been renamed to `%s`." % (IS_LEGACY_POPUP_VAR, IS_POPUP_VAR), - PendingDeprecationWarning, 2) + DeprecationWarning, 2) return True return False @@ -60,7 +60,7 @@ def _is_changelist_popup(request): class RenameChangeListMethods(RenameMethodsBase): renamed_methods = ( - ('get_query_set', 'get_queryset', PendingDeprecationWarning), + ('get_query_set', 'get_queryset', DeprecationWarning), ) @@ -115,14 +115,14 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)): def root_query_set(self): warnings.warn("`ChangeList.root_query_set` is deprecated, " "use `root_queryset` instead.", - PendingDeprecationWarning, 2) + DeprecationWarning, 2) return self.root_queryset @property def query_set(self): warnings.warn("`ChangeList.query_set` is deprecated, " "use `queryset` instead.", - PendingDeprecationWarning, 2) + DeprecationWarning, 2) return self.queryset def get_filters_params(self, params=None): diff --git a/django/contrib/comments/__init__.py b/django/contrib/comments/__init__.py index 007b77ad7b..0b3fcebc51 100644 --- a/django/contrib/comments/__init__.py +++ b/django/contrib/comments/__init__.py @@ -6,7 +6,7 @@ from django.contrib.comments.models import Comment from django.contrib.comments.forms import CommentForm from django.utils.importlib import import_module -warnings.warn("django.contrib.comments is deprecated and will be removed before Django 1.8.", PendingDeprecationWarning) +warnings.warn("django.contrib.comments is deprecated and will be removed before Django 1.8.", DeprecationWarning) DEFAULT_COMMENTS_APP = 'django.contrib.comments' diff --git a/django/contrib/comments/templatetags/comments.py b/django/contrib/comments/templatetags/comments.py index d8eed76ad6..2b2cea5f20 100644 --- a/django/contrib/comments/templatetags/comments.py +++ b/django/contrib/comments/templatetags/comments.py @@ -12,7 +12,7 @@ register = template.Library() class RenameBaseCommentNodeMethods(RenameMethodsBase): renamed_methods = ( - ('get_query_set', 'get_queryset', PendingDeprecationWarning), + ('get_query_set', 'get_queryset', DeprecationWarning), ) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index 26db4ab171..ab7c29f3e7 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -25,7 +25,7 @@ from django.utils.encoding import smart_text class RenameGenericForeignKeyMethods(RenameMethodsBase): renamed_methods = ( - ('get_prefetch_query_set', 'get_prefetch_queryset', PendingDeprecationWarning), + ('get_prefetch_query_set', 'get_prefetch_queryset', DeprecationWarning), ) diff --git a/django/db/__init__.py b/django/db/__init__.py index 2421ddeab8..0d7fbe2a34 100644 --- a/django/db/__init__.py +++ b/django/db/__init__.py @@ -49,7 +49,7 @@ class DefaultBackendProxy(object): @cached_property def _backend(self): warnings.warn("Accessing django.db.backend is deprecated.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) return load_backend(connections[DEFAULT_DB_ALIAS].settings_dict['ENGINE']) def __getattr__(self, item): @@ -66,7 +66,7 @@ backend = DefaultBackendProxy() def close_connection(**kwargs): warnings.warn( "close_connection is superseded by close_old_connections.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) # Avoid circular imports from django.db import transaction for conn in connections: diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py index c9e5c83ade..6f66cfb7ca 100644 --- a/django/db/backends/creation.py +++ b/django/db/backends/creation.py @@ -465,7 +465,7 @@ class BaseDatabaseCreation(object): """ warnings.warn( "set_autocommit was moved from BaseDatabaseCreation to " - "BaseDatabaseWrapper.", PendingDeprecationWarning, stacklevel=2) + "BaseDatabaseWrapper.", DeprecationWarning, stacklevel=2) return self.connection.set_autocommit(True) def sql_table_creation_suffix(self): diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 1ad4bf4706..d3bfb338fb 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -144,8 +144,8 @@ class RelatedField(Field): class RenameRelatedObjectDescriptorMethods(RenameMethodsBase): renamed_methods = ( - ('get_query_set', 'get_queryset', PendingDeprecationWarning), - ('get_prefetch_query_set', 'get_prefetch_queryset', PendingDeprecationWarning), + ('get_query_set', 'get_queryset', DeprecationWarning), + ('get_prefetch_query_set', 'get_prefetch_queryset', DeprecationWarning), ) diff --git a/django/db/models/manager.py b/django/db/models/manager.py index a1aa79f809..6817c9c8ee 100644 --- a/django/db/models/manager.py +++ b/django/db/models/manager.py @@ -50,8 +50,8 @@ signals.class_prepared.connect(ensure_default_manager) class RenameManagerMethods(RenameMethodsBase): renamed_methods = ( - ('get_query_set', 'get_queryset', PendingDeprecationWarning), - ('get_prefetch_query_set', 'get_prefetch_queryset', PendingDeprecationWarning), + ('get_query_set', 'get_queryset', DeprecationWarning), + ('get_prefetch_query_set', 'get_prefetch_queryset', DeprecationWarning), ) diff --git a/django/db/models/options.py b/django/db/models/options.py index ad25de4a3e..c6005f379d 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -130,7 +130,7 @@ class Options(object): """ warnings.warn( "Options.module_name has been deprecated in favor of model_name", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) return self.model_name def _prepare(self, model): @@ -421,7 +421,7 @@ class Options(object): warnings.warn( "`Options.get_add_permission` has been deprecated in favor " "of `django.contrib.auth.get_permission_codename`.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) return 'add_%s' % self.model_name def get_change_permission(self): @@ -432,7 +432,7 @@ class Options(object): warnings.warn( "`Options.get_change_permission` has been deprecated in favor " "of `django.contrib.auth.get_permission_codename`.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) return 'change_%s' % self.model_name def get_delete_permission(self): @@ -443,7 +443,7 @@ class Options(object): warnings.warn( "`Options.get_delete_permission` has been deprecated in favor " "of `django.contrib.auth.get_permission_codename`.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) return 'delete_%s' % self.model_name def get_all_related_objects(self, local_only=False, include_hidden=False, diff --git a/django/db/transaction.py b/django/db/transaction.py index 95b9ae165e..6031ce96c8 100644 --- a/django/db/transaction.py +++ b/django/db/transaction.py @@ -101,19 +101,19 @@ def set_clean(using=None): def is_managed(using=None): warnings.warn("'is_managed' is deprecated.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) def managed(flag=True, using=None): warnings.warn("'managed' no longer serves a purpose.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) def commit_unless_managed(using=None): warnings.warn("'commit_unless_managed' is now a no-op.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) def rollback_unless_managed(using=None): warnings.warn("'rollback_unless_managed' is now a no-op.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) ############### # Public APIs # @@ -430,7 +430,7 @@ def autocommit(using=None): your settings file and want the default behavior in some view functions. """ warnings.warn("autocommit is deprecated in favor of set_autocommit.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) def entering(using): enter_transaction_management(managed=False, using=using) @@ -448,7 +448,7 @@ def commit_on_success(using=None): control in Web apps. """ warnings.warn("commit_on_success is deprecated in favor of atomic.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) def entering(using): enter_transaction_management(using=using) @@ -478,7 +478,7 @@ def commit_manually(using=None): themselves. """ warnings.warn("commit_manually is deprecated in favor of set_autocommit.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) def entering(using): enter_transaction_management(using=using) diff --git a/django/db/utils.py b/django/db/utils.py index bd7e10d24c..acb838f940 100644 --- a/django/db/utils.py +++ b/django/db/utils.py @@ -174,7 +174,7 @@ class ConnectionHandler(object): if settings.TRANSACTIONS_MANAGED: warnings.warn( "TRANSACTIONS_MANAGED is deprecated. Use AUTOCOMMIT instead.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) conn.setdefault('AUTOCOMMIT', False) conn.setdefault('AUTOCOMMIT', True) conn.setdefault('ENGINE', 'django.db.backends.dummy') diff --git a/django/forms/forms.py b/django/forms/forms.py index b25eeb30a4..6d59271d17 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -352,7 +352,7 @@ class BaseForm(object): if hasattr(field.widget, '_has_changed'): warnings.warn("The _has_changed method on widgets is deprecated," " define it at field level instead.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) if field.widget._has_changed(initial_value, data_value): self._changed_data.append(name) elif field._has_changed(initial_value, data_value): diff --git a/django/forms/models.py b/django/forms/models.py index 821f64199b..a0c3009d38 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -260,7 +260,7 @@ class ModelFormMetaclass(type): warnings.warn("Creating a ModelForm without either the 'fields' attribute " "or the 'exclude' attribute is deprecated - form %s " "needs updating" % name, - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) if opts.fields == ALL_FIELDS: # sentinel for fields_for_model to indicate "get the list of @@ -513,7 +513,7 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None, getattr(Meta, 'exclude', None) is None): warnings.warn("Calling modelform_factory without defining 'fields' or " "'exclude' explicitly is deprecated", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) # Instatiate type(form) in order to use the same metaclass as form. return type(form)(class_name, (form,), form_class_attrs) @@ -796,7 +796,7 @@ def modelformset_factory(model, form=ModelForm, formfield_callback=None, getattr(meta, 'exclude', exclude) is None): warnings.warn("Calling modelformset_factory without defining 'fields' or " "'exclude' explicitly is deprecated", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) form = modelform_factory(model, form=form, fields=fields, exclude=exclude, formfield_callback=formfield_callback, diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 38d1b99b0d..a92e5a56ce 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -636,7 +636,7 @@ class RadioChoiceInput(ChoiceInput): class RadioInput(RadioChoiceInput): def __init__(self, *args, **kwargs): msg = "RadioInput has been deprecated. Use RadioChoiceInput instead." - warnings.warn(msg, PendingDeprecationWarning, stacklevel=2) + warnings.warn(msg, DeprecationWarning, stacklevel=2) super(RadioInput, self).__init__(*args, **kwargs) diff --git a/django/middleware/cache.py b/django/middleware/cache.py index e13a8c3918..55b61358ea 100644 --- a/django/middleware/cache.py +++ b/django/middleware/cache.py @@ -199,7 +199,7 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware): if self.cache_anonymous_only: msg = "CACHE_MIDDLEWARE_ANONYMOUS_ONLY has been deprecated and will be removed in Django 1.8." - warnings.warn(msg, PendingDeprecationWarning, stacklevel=1) + warnings.warn(msg, DeprecationWarning, stacklevel=1) self.cache = get_cache(self.cache_alias, **cache_kwargs) self.cache_timeout = self.cache.default_timeout diff --git a/django/middleware/common.py b/django/middleware/common.py index 2c76c47756..8f5923ac4a 100644 --- a/django/middleware/common.py +++ b/django/middleware/common.py @@ -110,7 +110,7 @@ class CommonMiddleware(object): if settings.SEND_BROKEN_LINK_EMAILS: warnings.warn("SEND_BROKEN_LINK_EMAILS is deprecated. " "Use BrokenLinkEmailsMiddleware instead.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) BrokenLinkEmailsMiddleware().process_response(request, response) if settings.USE_ETAGS: diff --git a/django/middleware/doc.py b/django/middleware/doc.py index 1af7b6150a..a2f00b6ded 100644 --- a/django/middleware/doc.py +++ b/django/middleware/doc.py @@ -1,6 +1,6 @@ """XViewMiddleware has been moved to django.contrib.admindocs.middleware.""" import warnings -warnings.warn(__doc__, PendingDeprecationWarning, stacklevel=2) +warnings.warn(__doc__, DeprecationWarning, stacklevel=2) from django.contrib.admindocs.middleware import XViewMiddleware diff --git a/django/middleware/transaction.py b/django/middleware/transaction.py index 95cc9a21f3..510176758a 100644 --- a/django/middleware/transaction.py +++ b/django/middleware/transaction.py @@ -14,7 +14,7 @@ class TransactionMiddleware(object): def __init__(self): warnings.warn( "TransactionMiddleware is deprecated in favor of ATOMIC_REQUESTS.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) if connection.settings_dict['ATOMIC_REQUESTS']: raise MiddlewareNotUsed diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 959de3dea1..5b2a1b9501 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -568,7 +568,7 @@ def cycle(parser, token, escape=False): "'The `cycle` template tag is changing to escape its arguments; " "the non-autoescaping version is deprecated. Load it " "from the `future` tag library to start using the new behavior.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) # Note: This returns the exact same node on each {% cycle name %} call; # that is, the node object returned from {% cycle a b c as name %} and the @@ -712,7 +712,7 @@ def firstof(parser, token, escape=False): "'The `firstof` template tag is changing to escape its arguments; " "the non-autoescaping version is deprecated. Load it " "from the `future` tag library to start using the new behavior.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) bits = token.split_contents()[1:] if len(bits) < 1: diff --git a/django/test/_doctest.py b/django/test/_doctest.py index 5381cff2f0..50d772cfad 100644 --- a/django/test/_doctest.py +++ b/django/test/_doctest.py @@ -54,7 +54,7 @@ import warnings warnings.warn( "The django.test._doctest module is deprecated; " "use the doctest module from the Python standard library instead.", - PendingDeprecationWarning) + DeprecationWarning) __docformat__ = 'reStructuredText en' diff --git a/django/test/simple.py b/django/test/simple.py index f28b8a2830..874cfdff39 100644 --- a/django/test/simple.py +++ b/django/test/simple.py @@ -21,7 +21,7 @@ __all__ = ('DjangoTestSuiteRunner',) warnings.warn( "The django.test.simple module and DjangoTestSuiteRunner are deprecated; " "use django.test.runner.DiscoverRunner instead.", - PendingDeprecationWarning) + DeprecationWarning) # The module name for tests outside models.py TEST_MODULE = 'tests' diff --git a/django/utils/image.py b/django/utils/image.py index d251ab9d0b..2fd0c6e6b0 100644 --- a/django/utils/image.py +++ b/django/utils/image.py @@ -139,7 +139,7 @@ def _detect_image_library(): warnings.warn( "Support for the PIL will be removed in Django 1.8. Please " + "uninstall it & install Pillow instead.", - PendingDeprecationWarning + DeprecationWarning ) return PILImage, PIL_imaging, PILImageFile diff --git a/django/views/defaults.py b/django/views/defaults.py index c8a62fc753..e9d0f16a33 100644 --- a/django/views/defaults.py +++ b/django/views/defaults.py @@ -83,6 +83,6 @@ def shortcut(request, content_type_id, object_id): warnings.warn( "django.views.defaults.shortcut will be removed in Django 1.8. " "Import it from django.contrib.contenttypes.views instead.", - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) from django.contrib.contenttypes.views import shortcut as real_shortcut return real_shortcut(request, content_type_id, object_id) diff --git a/django/views/generic/edit.py b/django/views/generic/edit.py index fccacf0bd3..0a8301f0db 100644 --- a/django/views/generic/edit.py +++ b/django/views/generic/edit.py @@ -113,7 +113,7 @@ class ModelFormMixin(FormMixin, SingleObjectMixin): if self.fields is None: warnings.warn("Using ModelFormMixin (base class of %s) without " "the 'fields' attribute is deprecated." % self.__class__.__name__, - PendingDeprecationWarning) + DeprecationWarning) return model_forms.modelform_factory(model, fields=self.fields) diff --git a/tests/admin_scripts/app_with_import/models.py b/tests/admin_scripts/app_with_import/models.py index 17a892bd17..89125c386f 100644 --- a/tests/admin_scripts/app_with_import/models.py +++ b/tests/admin_scripts/app_with_import/models.py @@ -1,8 +1,8 @@ from django.db import models -from django.contrib.comments.models import Comment +from django.contrib.auth.models import User # Regression for #13368. This is an example of a model # that imports a class that has an abstract base class. -class CommentScore(models.Model): - comment = models.OneToOneField(Comment, primary_key=True) +class UserProfile(models.Model): + user = models.OneToOneField(User, primary_key=True) diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 2f399acb23..2b772598d3 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -1079,7 +1079,6 @@ class ManageValidate(AdminScriptTestCase): "manage.py validate does not raise errors when an app imports a base class that itself has an abstract base" self.write_settings('settings.py', apps=['admin_scripts.app_with_import', - 'django.contrib.comments', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sites'], diff --git a/tests/cache/tests.py b/tests/cache/tests.py index 7413a4aae6..060f27e6e2 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -28,7 +28,7 @@ from django.middleware.cache import (FetchFromCacheMiddleware, from django.template import Template from django.template.response import TemplateResponse from django.test import TestCase, TransactionTestCase, RequestFactory -from django.test.utils import override_settings, IgnorePendingDeprecationWarningsMixin +from django.test.utils import override_settings, IgnoreDeprecationWarningsMixin from django.utils import six, timezone, translation, unittest from django.utils.cache import (patch_vary_headers, get_cache_key, learn_cache_key, patch_cache_control, patch_response_headers) @@ -1594,7 +1594,7 @@ def hello_world_view(request, value): }, }, ) -class CacheMiddlewareTest(IgnorePendingDeprecationWarningsMixin, TestCase): +class CacheMiddlewareTest(IgnoreDeprecationWarningsMixin, TestCase): def setUp(self): super(CacheMiddlewareTest, self).setUp() diff --git a/tests/deprecation/tests.py b/tests/deprecation/tests.py index df752b3149..fda780c7e6 100644 --- a/tests/deprecation/tests.py +++ b/tests/deprecation/tests.py @@ -8,7 +8,7 @@ from django.utils.deprecation import RenameMethodsBase class RenameManagerMethods(RenameMethodsBase): renamed_methods = ( - ('old', 'new', PendingDeprecationWarning), + ('old', 'new', DeprecationWarning), ) diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py index 84d18ebcb2..982d9e5778 100644 --- a/tests/generic_views/test_edit.py +++ b/tests/generic_views/test_edit.py @@ -146,7 +146,7 @@ class CreateViewTests(TestCase): def test_create_view_all_fields(self): with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", PendingDeprecationWarning) + warnings.simplefilter("always", DeprecationWarning) class MyCreateView(CreateView): model = Author @@ -160,7 +160,7 @@ class CreateViewTests(TestCase): def test_create_view_without_explicit_fields(self): with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", PendingDeprecationWarning) + warnings.simplefilter("always", DeprecationWarning) class MyCreateView(CreateView): model = Author @@ -171,7 +171,7 @@ class CreateViewTests(TestCase): ['name', 'slug']) # but with a warning: - self.assertEqual(w[0].category, PendingDeprecationWarning) + self.assertEqual(w[0].category, DeprecationWarning) class UpdateViewTests(TestCase): diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py index 20645cf91f..f3165f939f 100644 --- a/tests/middleware/tests.py +++ b/tests/middleware/tests.py @@ -18,7 +18,7 @@ from django.middleware.http import ConditionalGetMiddleware from django.middleware.gzip import GZipMiddleware from django.middleware.transaction import TransactionMiddleware from django.test import TransactionTestCase, TestCase, RequestFactory -from django.test.utils import override_settings, IgnorePendingDeprecationWarningsMixin +from django.test.utils import override_settings, IgnoreDeprecationWarningsMixin from django.utils import six from django.utils.encoding import force_str from django.utils.six.moves import xrange @@ -249,7 +249,7 @@ class CommonMiddlewareTest(TestCase): request = self._get_request('regular_url/that/does/not/exist') request.META['HTTP_REFERER'] = '/another/url/' with warnings.catch_warnings(): - warnings.simplefilter("ignore", PendingDeprecationWarning) + warnings.simplefilter("ignore", DeprecationWarning) response = self.client.get(request.path) CommonMiddleware().process_response(request, response) self.assertEqual(len(mail.outbox), 1) @@ -261,7 +261,7 @@ class CommonMiddlewareTest(TestCase): def test_404_error_reporting_no_referer(self): request = self._get_request('regular_url/that/does/not/exist') with warnings.catch_warnings(): - warnings.simplefilter("ignore", PendingDeprecationWarning) + warnings.simplefilter("ignore", DeprecationWarning) response = self.client.get(request.path) CommonMiddleware().process_response(request, response) self.assertEqual(len(mail.outbox), 0) @@ -273,7 +273,7 @@ class CommonMiddlewareTest(TestCase): request = self._get_request('foo_url/that/does/not/exist/either') request.META['HTTP_REFERER'] = '/another/url/' with warnings.catch_warnings(): - warnings.simplefilter("ignore", PendingDeprecationWarning) + warnings.simplefilter("ignore", DeprecationWarning) response = self.client.get(request.path) CommonMiddleware().process_response(request, response) self.assertEqual(len(mail.outbox), 0) @@ -703,7 +703,7 @@ class ETagGZipMiddlewareTest(TestCase): self.assertNotEqual(gzip_etag, nogzip_etag) -class TransactionMiddlewareTest(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class TransactionMiddlewareTest(IgnoreDeprecationWarningsMixin, TransactionTestCase): """ Test the transaction middleware. """ diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 39be824798..2c3caef016 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -266,7 +266,7 @@ class ModelFormBaseTest(TestCase): def test_missing_fields_attribute(self): with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", PendingDeprecationWarning) + warnings.simplefilter("always", DeprecationWarning) class MissingFieldsForm(forms.ModelForm): class Meta: @@ -276,7 +276,7 @@ class ModelFormBaseTest(TestCase): # if a warning has been seen already, the catch_warnings won't # have recorded it. The following line therefore will not work reliably: - # self.assertEqual(w[0].category, PendingDeprecationWarning) + # self.assertEqual(w[0].category, DeprecationWarning) # Until end of the deprecation cycle, should still create the # form as before: diff --git a/tests/model_forms_regress/tests.py b/tests/model_forms_regress/tests.py index 39ae857219..29d66a2cf0 100644 --- a/tests/model_forms_regress/tests.py +++ b/tests/model_forms_regress/tests.py @@ -566,10 +566,10 @@ class CustomMetaclassTestCase(TestCase): class TestTicket19733(TestCase): def test_modelform_factory_without_fields(self): with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", PendingDeprecationWarning) + warnings.simplefilter("always", DeprecationWarning) # This should become an error once deprecation cycle is complete. form = modelform_factory(Person) - self.assertEqual(w[0].category, PendingDeprecationWarning) + self.assertEqual(w[0].category, DeprecationWarning) def test_modelform_factory_with_all_fields(self): form = modelform_factory(Person, fields="__all__") diff --git a/tests/runtests.py b/tests/runtests.py index da4592ecc0..b604155190 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -109,7 +109,7 @@ def setup(verbosity, test_labels): # Load all the ALWAYS_INSTALLED_APPS. with warnings.catch_warnings(): - warnings.filterwarnings('ignore', 'django.contrib.comments is deprecated and will be removed before Django 1.8.', PendingDeprecationWarning) + warnings.filterwarnings('ignore', 'django.contrib.comments is deprecated and will be removed before Django 1.8.', DeprecationWarning) get_apps() # Load all the test model apps. diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index 76712a09a6..ff31795fff 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -534,7 +534,7 @@ class TemplateTests(TransRealMixin, TestCase): try: with warnings.catch_warnings(): # Ignore pending deprecations of the old syntax of the 'cycle' and 'firstof' tags. - warnings.filterwarnings("ignore", category=PendingDeprecationWarning, module='django.template.base') + warnings.filterwarnings("ignore", category=DeprecationWarning, module='django.template.base') test_template = loader.get_template(name) except ShouldNotExecuteException: failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Template loading invoked method that shouldn't have been invoked." % (is_cached, invalid_str, template_debug, name)) diff --git a/tests/test_runner/tests.py b/tests/test_runner/tests.py index 0dc7a0155a..53a268e823 100644 --- a/tests/test_runner/tests.py +++ b/tests/test_runner/tests.py @@ -11,7 +11,7 @@ from django.core.management import call_command from django import db from django.test import runner, TestCase, TransactionTestCase, skipUnlessDBFeature from django.test.testcases import connections_support_transactions -from django.test.utils import IgnorePendingDeprecationWarningsMixin +from django.test.utils import IgnoreDeprecationWarningsMixin from django.utils import unittest from django.utils.importlib import import_module @@ -225,7 +225,7 @@ class Ticket17477RegressionTests(AdminScriptTestCase): self.assertNoOutput(err) -class ModulesTestsPackages(IgnorePendingDeprecationWarningsMixin, unittest.TestCase): +class ModulesTestsPackages(IgnoreDeprecationWarningsMixin, unittest.TestCase): def test_get_tests(self): "Check that the get_tests helper function can find tests in a directory" from django.test.simple import get_tests diff --git a/tests/test_suite_override/tests.py b/tests/test_suite_override/tests.py index 35ca2b060b..6050e0c509 100644 --- a/tests/test_suite_override/tests.py +++ b/tests/test_suite_override/tests.py @@ -1,5 +1,5 @@ from django.db.models import get_app -from django.test.utils import IgnorePendingDeprecationWarningsMixin +from django.test.utils import IgnoreDeprecationWarningsMixin from django.utils import unittest @@ -9,7 +9,7 @@ def suite(): return testSuite -class SuiteOverrideTest(IgnorePendingDeprecationWarningsMixin, unittest.TestCase): +class SuiteOverrideTest(IgnoreDeprecationWarningsMixin, unittest.TestCase): def test_suite_override(self): """ Validate that you can define a custom suite when running tests with diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 8c6c15e357..ec98a845a0 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -7,7 +7,7 @@ from django.http import HttpResponse from django.template.loader import render_to_string from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature from django.test.html import HTMLParseError, parse_html -from django.test.utils import CaptureQueriesContext, IgnorePendingDeprecationWarningsMixin +from django.test.utils import CaptureQueriesContext, IgnoreDeprecationWarningsMixin from django.utils import six from django.utils import unittest from django.utils.unittest import skip @@ -591,7 +591,7 @@ class AssertFieldOutputTests(SimpleTestCase): self.assertFieldOutput(MyCustomField, {}, {}, empty_value=None) -class DoctestNormalizerTest(IgnorePendingDeprecationWarningsMixin, SimpleTestCase): +class DoctestNormalizerTest(IgnoreDeprecationWarningsMixin, SimpleTestCase): def test_normalizer(self): from django.test.simple import make_doctest diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py index 756fa40abd..2da4d9fc9a 100644 --- a/tests/transactions/tests.py +++ b/tests/transactions/tests.py @@ -4,7 +4,7 @@ import sys from django.db import connection, transaction, DatabaseError, IntegrityError from django.test import TransactionTestCase, skipUnlessDBFeature -from django.test.utils import IgnorePendingDeprecationWarningsMixin +from django.test.utils import IgnoreDeprecationWarningsMixin from django.utils import six from django.utils.unittest import skipIf, skipUnless @@ -350,7 +350,7 @@ class AtomicMiscTests(TransactionTestCase): transaction.atomic(Callable()) -class TransactionTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class TransactionTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): available_apps = ['transactions'] @@ -508,7 +508,7 @@ class TransactionTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCas ) -class TransactionRollbackTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class TransactionRollbackTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): available_apps = ['transactions'] @@ -528,7 +528,7 @@ class TransactionRollbackTests(IgnorePendingDeprecationWarningsMixin, Transactio self.assertRaises(IntegrityError, execute_bad_sql) transaction.rollback() -class TransactionContextManagerTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class TransactionContextManagerTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): available_apps = ['transactions'] diff --git a/tests/transactions_regress/tests.py b/tests/transactions_regress/tests.py index 8078f1d128..7db32fa4f3 100644 --- a/tests/transactions_regress/tests.py +++ b/tests/transactions_regress/tests.py @@ -4,7 +4,7 @@ from django.db import (connection, connections, transaction, DEFAULT_DB_ALIAS, D IntegrityError) from django.db.transaction import commit_on_success, commit_manually, TransactionManagementError from django.test import TransactionTestCase, skipUnlessDBFeature -from django.test.utils import override_settings, IgnorePendingDeprecationWarningsMixin +from django.test.utils import override_settings, IgnoreDeprecationWarningsMixin from django.utils.unittest import skipIf, skipUnless from .models import Mod, M2mA, M2mB, SubMod @@ -30,7 +30,7 @@ class ModelInheritanceTests(TransactionTestCase): self.assertEqual(SubMod.objects.count(), 1) self.assertEqual(Mod.objects.count(), 1) -class TestTransactionClosing(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class TestTransactionClosing(IgnoreDeprecationWarningsMixin, TransactionTestCase): """ Tests to make sure that transactions are properly closed when they should be, and aren't left pending after operations @@ -194,7 +194,7 @@ class TestTransactionClosing(IgnorePendingDeprecationWarningsMixin, TransactionT (connection.settings_dict['NAME'] == ':memory:' or not connection.settings_dict['NAME']), 'Test uses multiple connections, but in-memory sqlite does not support this') -class TestNewConnection(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class TestNewConnection(IgnoreDeprecationWarningsMixin, TransactionTestCase): """ Check that new connections don't have special behaviour. """ @@ -242,7 +242,7 @@ class TestNewConnection(IgnorePendingDeprecationWarningsMixin, TransactionTestCa @skipUnless(connection.vendor == 'postgresql', "This test only valid for PostgreSQL") -class TestPostgresAutocommitAndIsolation(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class TestPostgresAutocommitAndIsolation(IgnoreDeprecationWarningsMixin, TransactionTestCase): """ Tests to make sure psycopg2's autocommit mode and isolation level is restored after entering and leaving transaction management. @@ -326,7 +326,7 @@ class TestPostgresAutocommitAndIsolation(IgnorePendingDeprecationWarningsMixin, self.assertTrue(connection.autocommit) -class TestManyToManyAddTransaction(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class TestManyToManyAddTransaction(IgnoreDeprecationWarningsMixin, TransactionTestCase): available_apps = ['transactions_regress'] @@ -344,7 +344,7 @@ class TestManyToManyAddTransaction(IgnorePendingDeprecationWarningsMixin, Transa self.assertEqual(a.others.count(), 1) -class SavepointTest(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): +class SavepointTest(IgnoreDeprecationWarningsMixin, TransactionTestCase): available_apps = ['transactions_regress'] From 74151765073ce280cc5df0bfe411971c6212a0ff Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 29 Jun 2013 18:56:00 +0200 Subject: [PATCH 26/47] Simplified description of HttpResponse() Related to 8b9b8d3b. --- docs/ref/request-response.txt | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 578418b4ee..f1c5702d36 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -555,28 +555,12 @@ file-like object:: Passing iterators ~~~~~~~~~~~~~~~~~ -Finally, you can pass ``HttpResponse`` an iterator rather than strings. If you -use this technique, the iterator should return strings. +Finally, you can pass ``HttpResponse`` an iterator rather than strings. +``HttpResponse`` will consume the iterator immediately, store its content as a +string, and discard it. -Passing an iterator as content to :class:`HttpResponse` creates a -streaming response if (and only if) no middleware accesses the -:attr:`HttpResponse.content` attribute before the response is returned. - -.. versionchanged:: 1.5 - - This technique is fragile and was deprecated in Django 1.5. If you need the - response to be streamed from the iterator to the client, you should use the - :class:`StreamingHttpResponse` class instead. - - As of Django 1.7, when :class:`HttpResponse` is instantiated with an - iterator, it will consume it immediately, store the response content as a - string, and discard the iterator. - -.. versionchanged:: 1.5 - - You can now use :class:`HttpResponse` as a file-like object even if it was - instantiated with an iterator. Django will consume and save the content of - the iterator on first access. +If you need the response to be streamed from the iterator to the client, you +must use the :class:`StreamingHttpResponse` class instead. Setting header fields ~~~~~~~~~~~~~~~~~~~~~ From 3fd0ee5b46807c18c731045b4f5b360b5b091c61 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sat, 29 Jun 2013 14:14:32 -0400 Subject: [PATCH 27/47] Fixed #20677 - Typos in generic_inlineformset_factory docs. Thanks Riley Strong for the report. --- django/contrib/contenttypes/generic.py | 4 ++-- docs/ref/contrib/contenttypes.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index ab7c29f3e7..04d4894b8a 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -434,8 +434,8 @@ def generic_inlineformset_factory(model, form=ModelForm, """ Returns a ``GenericInlineFormSet`` for the given kwargs. - You must provide ``ct_field`` and ``object_id`` if they different from the - defaults ``content_type`` and ``object_id`` respectively. + You must provide ``ct_field`` and ``fk_field`` if they are different from + the defaults ``content_type`` and ``object_id`` respectively. """ opts = model._meta # if there is no field called `ct_field` let the exception propagate diff --git a/docs/ref/contrib/contenttypes.txt b/docs/ref/contrib/contenttypes.txt index de9c5dcbd6..199401c64a 100644 --- a/docs/ref/contrib/contenttypes.txt +++ b/docs/ref/contrib/contenttypes.txt @@ -506,9 +506,9 @@ information. Returns a ``GenericInlineFormSet`` using :func:`~django.forms.models.modelformset_factory`. - You must provide ``ct_field`` and ``object_id`` if they different from the - defaults, ``content_type`` and ``object_id`` respectively. Other parameters - are similar to those documented in + You must provide ``ct_field`` and ``fk_field`` if they are different from + the defaults, ``content_type`` and ``object_id`` respectively. Other + parameters are similar to those documented in :func:`~django.forms.models.modelformset_factory` and :func:`~django.forms.models.inlineformset_factory`. From 64cdea68e71829905da6374a066d1700375255ec Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Sat, 29 Jun 2013 18:19:20 -0300 Subject: [PATCH 28/47] Report wrongly-typed settings and abort, as originally planned. Thanks Claude for the heads up. Refs #12493 and commit 5e08b792. --- django/conf/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/django/conf/__init__.py b/django/conf/__init__.py index b1f40af80a..c98443be1a 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -132,9 +132,17 @@ class Settings(BaseSettings): % (self.SETTINGS_MODULE, e) ) + tuple_settings = ("INSTALLED_APPS", "TEMPLATE_DIRS") + for setting in dir(mod): if setting == setting.upper(): setting_value = getattr(mod, setting) + + if setting in tuple_settings and \ + isinstance(setting_value, six.string_types): + raise ImproperlyConfigured("The %s setting must be a tuple. " + "Please fix your settings." % setting) + setattr(self, setting, setting_value) if not self.SECRET_KEY: From 5ff2ffa3304b2e49e17257575327f9fc08ce227e Mon Sep 17 00:00:00 2001 From: Matt Robenolt Date: Sat, 29 Jun 2013 23:26:10 -0700 Subject: [PATCH 29/47] Define the SessionStore inside __init__ instead of process_request It's unnecessary to run this on every request, since technically, settings *should be* immutable. --- django/contrib/sessions/middleware.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py index 9f65255f47..8bc2d37dd3 100644 --- a/django/contrib/sessions/middleware.py +++ b/django/contrib/sessions/middleware.py @@ -6,10 +6,13 @@ from django.utils.http import cookie_date from django.utils.importlib import import_module class SessionMiddleware(object): - def process_request(self, request): + def __init__(self): engine = import_module(settings.SESSION_ENGINE) + self.SessionStore = engine.SessionStore + + def process_request(self, request): session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None) - request.session = engine.SessionStore(session_key) + request.session = self.SessionStore(session_key) def process_response(self, request, response): """ From 2c406818058e88a907e24bfd244418d2a55619d7 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 30 Jun 2013 14:17:33 +0200 Subject: [PATCH 30/47] Stopped calling loaddata with commit=False. This was a stealth option only used by the tests, and it isn't useful any more since `atomic` provides nested transactions. --- tests/fixtures/tests.py | 56 +++++++++++++-------------- tests/fixtures_model_package/tests.py | 7 ++-- tests/fixtures_regress/tests.py | 32 --------------- tests/proxy_models/tests.py | 2 +- 4 files changed, 31 insertions(+), 66 deletions(-) diff --git a/tests/fixtures/tests.py b/tests/fixtures/tests.py index d0942afdb7..6f2218b19e 100644 --- a/tests/fixtures/tests.py +++ b/tests/fixtures/tests.py @@ -56,7 +56,7 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): def test_loading_and_dumping(self): Site.objects.all().delete() # Load fixture 1. Single JSON file, with two objects. - management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1.json', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -87,7 +87,7 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): self._dumpdata_assert(['fixtures.Category', 'sites'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]') # Load fixture 2. JSON file imported by default. Overwrites some existing objects - management.call_command('loaddata', 'fixture2.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture2.json', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -95,7 +95,7 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): ]) # Load fixture 3, XML format. - management.call_command('loaddata', 'fixture3.xml', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture3.xml', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -104,14 +104,14 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): ]) # Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne. - management.call_command('loaddata', 'fixture6.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture6.json', verbosity=0) self.assertQuerysetEqual(Tag.objects.all(), [ ' tagged "copyright">', ' tagged "law">', ], ordered=False) # Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne. - management.call_command('loaddata', 'fixture7.xml', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture7.xml', verbosity=0) self.assertQuerysetEqual(Tag.objects.all(), [ ' tagged "copyright">', ' tagged "legal">', @@ -120,7 +120,7 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): ], ordered=False) # Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany. - management.call_command('loaddata', 'fixture8.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture8.json', verbosity=0) self.assertQuerysetEqual(Visa.objects.all(), [ '', '', @@ -128,7 +128,7 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): ], ordered=False) # Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany. - management.call_command('loaddata', 'fixture9.xml', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture9.xml', verbosity=0) self.assertQuerysetEqual(Visa.objects.all(), [ '', '', @@ -143,15 +143,13 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): # Loading a fixture that doesn't exist emits a warning with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") - management.call_command('loaddata', 'unknown.json', verbosity=0, - commit=False) + management.call_command('loaddata', 'unknown.json', verbosity=0) self.assertEqual(len(w), 1) self.assertTrue(w[0].message, "No fixture named 'unknown' found.") # An attempt to load a nonexistent 'initial_data' fixture isn't an error with warnings.catch_warnings(record=True) as w: - management.call_command('loaddata', 'initial_data.json', verbosity=0, - commit=False) + management.call_command('loaddata', 'initial_data.json', verbosity=0) self.assertEqual(len(w), 0) # object list is unaffected @@ -178,7 +176,7 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): def test_dumpdata_with_excludes(self): # Load fixture1 which has a site, two articles, and a category Site.objects.all().delete() - management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1.json', verbosity=0) # Excluding fixtures app should only leave sites self._dumpdata_assert( @@ -226,8 +224,8 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): self._dumpdata_assert(['fixtures.Spy'], '[{"pk": %d, "model": "fixtures.spy", "fields": {"cover_blown": true}}, {"pk": %d, "model": "fixtures.spy", "fields": {"cover_blown": false}}]' % (spy2.pk, spy1.pk), use_base_manager=True) def test_dumpdata_with_pks(self): - management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) - management.call_command('loaddata', 'fixture2.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1.json', verbosity=0) + management.call_command('loaddata', 'fixture2.json', verbosity=0) self._dumpdata_assert( ['fixtures.Article'], '[{"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}]', @@ -266,21 +264,21 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): def test_compress_format_loading(self): # Load fixture 4 (compressed), using format specification - management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture4.json', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', ]) def test_compressed_specified_loading(self): # Load fixture 5 (compressed), using format *and* compression specification - management.call_command('loaddata', 'fixture5.json.zip', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture5.json.zip', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', ]) def test_compressed_loading(self): # Load fixture 5 (compressed), only compression specification - management.call_command('loaddata', 'fixture5.zip', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture5.zip', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', ]) @@ -288,13 +286,13 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): def test_ambiguous_compressed_fixture(self): # The name "fixture5" is ambigous, so loading it will raise an error with self.assertRaises(management.CommandError) as cm: - management.call_command('loaddata', 'fixture5', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture5', verbosity=0) self.assertIn("Multiple fixtures named 'fixture5'", cm.exception.args[0]) def test_db_loading(self): # Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly - management.call_command('loaddata', 'db_fixture_1', verbosity=0, commit=False) - management.call_command('loaddata', 'db_fixture_2', verbosity=0, commit=False) + management.call_command('loaddata', 'db_fixture_1', verbosity=0) + management.call_command('loaddata', 'db_fixture_2', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -312,13 +310,13 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): if connection.vendor == 'mysql': connection.cursor().execute("SET sql_mode = 'TRADITIONAL'") with self.assertRaises(IntegrityError) as cm: - management.call_command('loaddata', 'invalid.json', verbosity=0, commit=False) + management.call_command('loaddata', 'invalid.json', verbosity=0) self.assertIn("Could not load fixtures.Article(pk=1):", cm.exception.args[0]) def test_loading_using(self): # Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly - management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default', commit=False) - management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default', commit=False) + management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default') + management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default') self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -327,18 +325,18 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): def test_unmatched_identifier_loading(self): # Try to load db fixture 3. This won't load because the database identifier doesn't match with warnings.catch_warnings(record=True): - management.call_command('loaddata', 'db_fixture_3', verbosity=0, commit=False) + management.call_command('loaddata', 'db_fixture_3', verbosity=0) with warnings.catch_warnings(record=True): - management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default', commit=False) + management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default') self.assertQuerysetEqual(Article.objects.all(), []) def test_output_formats(self): # Load back in fixture 1, we need the articles from it - management.call_command('loaddata', 'fixture1', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1', verbosity=0) # Try to load fixture 6 using format discovery - management.call_command('loaddata', 'fixture6', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture6', verbosity=0) self.assertQuerysetEqual(Tag.objects.all(), [ ' tagged "copyright">', ' tagged "law">' @@ -364,7 +362,7 @@ class FixtureTransactionTests(DumpDataAssertMixin, TransactionTestCase): @skipUnlessDBFeature('supports_forward_references') def test_format_discovery(self): # Load fixture 1 again, using format discovery - management.call_command('loaddata', 'fixture1', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -386,7 +384,7 @@ class FixtureTransactionTests(DumpDataAssertMixin, TransactionTestCase): self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}]') # Load fixture 4 (compressed), using format discovery - management.call_command('loaddata', 'fixture4', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture4', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', diff --git a/tests/fixtures_model_package/tests.py b/tests/fixtures_model_package/tests.py index dbcc721d8f..4b00e6dfb9 100644 --- a/tests/fixtures_model_package/tests.py +++ b/tests/fixtures_model_package/tests.py @@ -60,7 +60,6 @@ class TestNoInitialDataLoading(TransactionTestCase): 'flush', verbosity=0, interactive=False, - commit=False, load_initial_data=False ) self.assertQuerysetEqual(Book.objects.all(), []) @@ -83,7 +82,7 @@ class FixtureTestCase(TestCase): def test_loaddata(self): "Fixtures can load data into models defined in packages" # Load fixture 1. Single JSON file, with two objects - management.call_command("loaddata", "fixture1.json", verbosity=0, commit=False) + management.call_command("loaddata", "fixture1.json", verbosity=0) self.assertQuerysetEqual( Article.objects.all(), [ "Time to reform copyright", @@ -94,7 +93,7 @@ class FixtureTestCase(TestCase): # Load fixture 2. JSON file imported by default. Overwrites some # existing objects - management.call_command("loaddata", "fixture2.json", verbosity=0, commit=False) + management.call_command("loaddata", "fixture2.json", verbosity=0) self.assertQuerysetEqual( Article.objects.all(), [ "Django conquers world!", @@ -107,7 +106,7 @@ class FixtureTestCase(TestCase): # Load a fixture that doesn't exist with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") - management.call_command("loaddata", "unknown.json", verbosity=0, commit=False) + management.call_command("loaddata", "unknown.json", verbosity=0) self.assertEqual(len(w), 1) self.assertTrue(w[0].message, "No fixture named 'unknown' found.") diff --git a/tests/fixtures_regress/tests.py b/tests/fixtures_regress/tests.py index 0f6ac65976..a9d67ec9a2 100644 --- a/tests/fixtures_regress/tests.py +++ b/tests/fixtures_regress/tests.py @@ -45,7 +45,6 @@ class TestFixtures(TestCase): 'loaddata', 'sequence', verbosity=0, - commit=False ) # Create a new animal. Without a sequence reset, this new object @@ -84,7 +83,6 @@ class TestFixtures(TestCase): 'sequence_extra', ignore=True, verbosity=0, - commit=False ) self.assertEqual(Animal.specimens.all()[0].name, 'Lion') @@ -98,7 +96,6 @@ class TestFixtures(TestCase): 'sequence_extra_xml', ignore=True, verbosity=0, - commit=False ) self.assertEqual(Animal.specimens.all()[0].name, 'Wolf') @@ -113,7 +110,6 @@ class TestFixtures(TestCase): 'loaddata', 'pretty.xml', verbosity=0, - commit=False ) self.assertEqual(Stuff.objects.all()[0].name, None) self.assertEqual(Stuff.objects.all()[0].owner, None) @@ -129,7 +125,6 @@ class TestFixtures(TestCase): 'loaddata', 'pretty.xml', verbosity=0, - commit=False ) self.assertEqual(Stuff.objects.all()[0].name, '') self.assertEqual(Stuff.objects.all()[0].owner, None) @@ -152,7 +147,6 @@ class TestFixtures(TestCase): 'loaddata', load_absolute_path, verbosity=0, - commit=False ) self.assertEqual(Absolute.load_count, 1) @@ -168,7 +162,6 @@ class TestFixtures(TestCase): 'loaddata', 'bad_fixture1.unkn', verbosity=0, - commit=False, ) @override_settings(SERIALIZATION_MODULES={'unkn': 'unexistent.path'}) @@ -182,7 +175,6 @@ class TestFixtures(TestCase): 'loaddata', 'bad_fixture1.unkn', verbosity=0, - commit=False, ) def test_invalid_data(self): @@ -197,7 +189,6 @@ class TestFixtures(TestCase): 'loaddata', 'bad_fixture2.xml', verbosity=0, - commit=False, ) def test_invalid_data_no_ext(self): @@ -212,7 +203,6 @@ class TestFixtures(TestCase): 'loaddata', 'bad_fixture2', verbosity=0, - commit=False, ) def test_empty(self): @@ -226,7 +216,6 @@ class TestFixtures(TestCase): 'loaddata', 'empty', verbosity=0, - commit=False, ) def test_error_message(self): @@ -240,7 +229,6 @@ class TestFixtures(TestCase): 'bad_fixture2', 'animal', verbosity=0, - commit=False, ) def test_pg_sequence_resetting_checks(self): @@ -253,7 +241,6 @@ class TestFixtures(TestCase): 'loaddata', 'model-inheritance.json', verbosity=0, - commit=False ) self.assertEqual(Parent.objects.all()[0].id, 1) self.assertEqual(Child.objects.all()[0].id, 1) @@ -270,7 +257,6 @@ class TestFixtures(TestCase): 'loaddata', 'big-fixture.json', verbosity=0, - commit=False ) articles = Article.objects.exclude(id=9) self.assertEqual( @@ -297,7 +283,6 @@ class TestFixtures(TestCase): 'loaddata', 'animal.xml', verbosity=0, - commit=False, ) self.assertEqual( self.pre_save_checks, @@ -319,13 +304,11 @@ class TestFixtures(TestCase): 'loaddata', 'animal.xml', verbosity=0, - commit=False, ) management.call_command( 'loaddata', 'sequence.json', verbosity=0, - commit=False, ) animal = Animal( name='Platypus', @@ -390,7 +373,6 @@ class TestFixtures(TestCase): 'loaddata', 'forward_ref.json', verbosity=0, - commit=False ) self.assertEqual(Book.objects.all()[0].id, 1) self.assertEqual(Person.objects.all()[0].id, 4) @@ -405,7 +387,6 @@ class TestFixtures(TestCase): 'loaddata', 'forward_ref_bad_data.json', verbosity=0, - commit=False, ) _cur_dir = os.path.dirname(os.path.abspath(upath(__file__))) @@ -422,7 +403,6 @@ class TestFixtures(TestCase): 'forward_ref_1.json', 'forward_ref_2.json', verbosity=0, - commit=False ) self.assertEqual(Book.objects.all()[0].id, 1) self.assertEqual(Person.objects.all()[0].id, 4) @@ -437,7 +417,6 @@ class TestFixtures(TestCase): management.call_command( 'loaddata', verbosity=0, - commit=False, ) def test_loaddata_not_existant_fixture_file(self): @@ -447,7 +426,6 @@ class TestFixtures(TestCase): 'loaddata', 'this_fixture_doesnt_exist', verbosity=2, - commit=False, stdout=stdout_output, ) self.assertTrue("No fixture 'this_fixture_doesnt_exist' in" in @@ -465,13 +443,11 @@ class NaturalKeyFixtureTests(TestCase): 'loaddata', 'model-inheritance.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'nk-inheritance.json', verbosity=0, - commit=False ) self.assertEqual( NKChild.objects.get(pk=1).data, @@ -492,19 +468,16 @@ class NaturalKeyFixtureTests(TestCase): 'loaddata', 'model-inheritance.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'nk-inheritance.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'nk-inheritance2.xml', verbosity=0, - commit=False ) self.assertEqual( NKChild.objects.get(pk=2).data, @@ -524,7 +497,6 @@ class NaturalKeyFixtureTests(TestCase): 'loaddata', 'forward_ref_lookup.json', verbosity=0, - commit=False ) stdout = StringIO() @@ -662,19 +634,16 @@ class NaturalKeyFixtureTests(TestCase): 'loaddata', 'non_natural_1.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'forward_ref_lookup.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'non_natural_2.xml', verbosity=0, - commit=False ) books = Book.objects.all() self.assertEqual( @@ -696,7 +665,6 @@ class TestTicket11101(TransactionTestCase): 'loaddata', 'thingy.json', verbosity=0, - commit=False ) self.assertEqual(Thingy.objects.count(), 1) transaction.rollback() diff --git a/tests/proxy_models/tests.py b/tests/proxy_models/tests.py index 77d2ba9a74..5cc5ef5478 100644 --- a/tests/proxy_models/tests.py +++ b/tests/proxy_models/tests.py @@ -358,7 +358,7 @@ class ProxyModelTests(TestCase): ) def test_proxy_load_from_fixture(self): - management.call_command('loaddata', 'mypeople.json', verbosity=0, commit=False) + management.call_command('loaddata', 'mypeople.json', verbosity=0) p = MyPerson.objects.get(pk=100) self.assertEqual(p.name, 'Elvis Presley') From dd9c6bc359a799fcbed647055b596239956a472a Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 30 Jun 2013 15:57:00 +0200 Subject: [PATCH 31/47] Introduced getters for connection.autocommit and .needs_rollback. They ensure that the attributes aren't accessed in conditions where they don't contain a valid value. Fixed #20666. --- django/db/backends/__init__.py | 34 +++++++++++++++++++++++----------- django/db/transaction.py | 12 ++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 9abb9a9637..a1fd923faf 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -204,7 +204,7 @@ class BaseDatabaseWrapper(object): def _savepoint_allowed(self): # Savepoints cannot be created outside a transaction - return self.features.uses_savepoints and not self.autocommit + return self.features.uses_savepoints and not self.get_autocommit() ##### Generic savepoint management methods ##### @@ -279,15 +279,13 @@ class BaseDatabaseWrapper(object): """ self.validate_no_atomic_block() - self.ensure_connection() - self.transaction_state.append(managed) if not managed and self.is_dirty() and not forced: self.commit() self.set_clean() - if managed == self.autocommit: + if managed == self.get_autocommit(): self.set_autocommit(not managed) def leave_transaction_management(self): @@ -298,8 +296,6 @@ class BaseDatabaseWrapper(object): """ self.validate_no_atomic_block() - self.ensure_connection() - if self.transaction_state: del self.transaction_state[-1] else: @@ -313,14 +309,21 @@ class BaseDatabaseWrapper(object): if self._dirty: self.rollback() - if managed == self.autocommit: + if managed == self.get_autocommit(): self.set_autocommit(not managed) raise TransactionManagementError( "Transaction managed block ended with pending COMMIT/ROLLBACK") - if managed == self.autocommit: + if managed == self.get_autocommit(): self.set_autocommit(not managed) + def get_autocommit(self): + """ + Check the autocommit state. + """ + self.ensure_connection() + return self.autocommit + def set_autocommit(self, autocommit): """ Enable or disable autocommit. @@ -330,13 +333,22 @@ class BaseDatabaseWrapper(object): self._set_autocommit(autocommit) self.autocommit = autocommit + def get_rollback(self): + """ + Get the "needs rollback" flag -- for *advanced use* only. + """ + if not self.in_atomic_block: + raise TransactionManagementError( + "The rollback flag doesn't work outside of an 'atomic' block.") + return self.needs_rollback + def set_rollback(self, rollback): """ Set or unset the "needs rollback" flag -- for *advanced use* only. """ if not self.in_atomic_block: raise TransactionManagementError( - "needs_rollback doesn't work outside of an 'atomic' block.") + "The rollback flag doesn't work outside of an 'atomic' block.") self.needs_rollback = rollback def validate_no_atomic_block(self): @@ -370,7 +382,7 @@ class BaseDatabaseWrapper(object): to decide in a managed block of code to decide whether there are open changes waiting for commit. """ - if not self.autocommit: + if not self.get_autocommit(): self._dirty = True def set_clean(self): @@ -436,7 +448,7 @@ class BaseDatabaseWrapper(object): if self.connection is not None: # If the application didn't restore the original autocommit setting, # don't take chances, drop the connection. - if self.autocommit != self.settings_dict['AUTOCOMMIT']: + if self.get_autocommit() != self.settings_dict['AUTOCOMMIT']: self.close() return diff --git a/django/db/transaction.py b/django/db/transaction.py index 6031ce96c8..96be981e7b 100644 --- a/django/db/transaction.py +++ b/django/db/transaction.py @@ -123,7 +123,7 @@ def get_autocommit(using=None): """ Get the autocommit status of the connection. """ - return get_connection(using).autocommit + return get_connection(using).get_autocommit() def set_autocommit(autocommit, using=None): """ @@ -175,7 +175,7 @@ def get_rollback(using=None): """ Gets the "needs rollback" flag -- for *advanced use* only. """ - return get_connection(using).needs_rollback + return get_connection(using).get_rollback() def set_rollback(rollback, using=None): """ @@ -229,15 +229,11 @@ class Atomic(object): def __enter__(self): connection = get_connection(self.using) - # Ensure we have a connection to the database before testing - # autocommit status. - connection.ensure_connection() - if not connection.in_atomic_block: # Reset state when entering an outermost atomic block. connection.commit_on_exit = True connection.needs_rollback = False - if not connection.autocommit: + if not connection.get_autocommit(): # Some database adapters (namely sqlite3) don't handle # transactions and savepoints properly when autocommit is off. # Turning autocommit back on isn't an option; it would trigger @@ -500,7 +496,7 @@ def commit_on_success_unless_managed(using=None, savepoint=False): legacy behavior. """ connection = get_connection(using) - if connection.autocommit or connection.in_atomic_block: + if connection.get_autocommit() or connection.in_atomic_block: return atomic(using, savepoint) else: def entering(using): From b9ebca6a2881f8f0a141a87adac869ae87f9df29 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 30 Jun 2013 23:32:23 -0700 Subject: [PATCH 32/47] Removed a comment that wasn't adding a value. In the intervening years, RelatedField has become less of a hack (though it still is one). Anyone who wants to can re-instate the comment, but please add more details. --- django/db/models/fields/related.py | 1 - 1 file changed, 1 deletion(-) diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index d3bfb338fb..ff222e1b85 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -91,7 +91,6 @@ def do_pending_lookups(sender, **kwargs): signals.class_prepared.connect(do_pending_lookups) -#HACK class RelatedField(Field): def db_type(self, connection): '''By default related field will not have a column From 8b8b2f2fc7a4a79a7a18305c77d287809004c59e Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 1 Jul 2013 11:44:59 +0200 Subject: [PATCH 33/47] Updated FAQ on Python versions to explain 2 vs 3. Required the latest version for each Python series to minimize bookkeeping in the future. Forward-port of c1d8f3b from stable/1.6.x. --- docs/faq/install.txt | 49 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/docs/faq/install.txt b/docs/faq/install.txt index d221f93d02..2c7c35299b 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -38,22 +38,6 @@ PostgreSQL fans, and MySQL_, `SQLite 3`_, and Oracle_ are also supported. .. _`SQLite 3`: http://www.sqlite.org/ .. _Oracle: http://www.oracle.com/ -Do I lose anything by using Python 2.6 versus newer Python versions, such as Python 2.7? ----------------------------------------------------------------------------------------- - -Not in the core framework. Currently, Django itself officially supports -Python 2.6 (2.6.5 or higher), 2.7, 3.2.3 or higher. However, newer versions of -Python are often faster, have more features, and are better supported. If you -use a newer version of Python you will also have access to some APIs that -aren't available under older versions of Python. - -Third-party applications for use with Django are, of course, free to set their -own version requirements. - -All else being equal, we recommend that you use the latest 2.7 or 3.x release. -This will let you take advantage of the numerous improvements and optimizations -to the Python language since version 2.6. - What Python version can I use with Django? ------------------------------------------ @@ -65,20 +49,35 @@ Django version Python versions 1.2 2.4, 2.5, 2.6, 2.7 1.3 2.4, 2.5, 2.6, 2.7 1.4 2.5, 2.6, 2.7 -1.5 2.6.5, 2.7 and 3.2.3, 3.3 (experimental) -**1.6** **2.6.5, 2.7** and **3.2.3, 3.3** -*1.7 (future)* *2.7, 3.3 (to be confirmed)* +1.5 2.6, 2.7 and 3.2, 3.3 (experimental) +**1.6** **2.6, 2.7** and **3.2, 3.3** +*1.7 (future)* *2.7* and *3.2, 3.3* ============== =============== -Can I use Django with Python 3? -------------------------------- +For a given series of Python versions, only the latest release is officially +supported. For instance, at the time of writing (July 1st, 2013), the latest +release in the 2.7 series is 2.7.5. -Yes, you can! - -Django 1.5 introduced experimental support for Python 3.2.3 and above. +What Python version should I use with Django? +--------------------------------------------- As of Django 1.6, Python 3 support is considered stable and you can safely use -it in production. See also :doc:`/topics/python3`. +it in production. See also :doc:`/topics/python3`. However, the community is +still in the process of migrating third-party packages and applications to +Python 3. + +If you're starting a new project, and the dependencies you plan to use work on +Python 3, you should use Python 3. If they don't, consider contributing to the +porting efforts, or stick to Python 2. + +Since newer versions of Python are often faster, have more features, and are +better supported, all else being equal, we recommend that you use the latest +2.x.y or 3.x.y release. + +You don't lose anything in Django by using an older release, but you don't take +advantage of the improvements and optimizations in newer Python releases. +Third-party applications for use with Django are, of course, free to set their +own version requirements. Will Django run under shared hosting (like TextDrive or Dreamhost)? ------------------------------------------------------------------- From 3fd8eb1a276fa452293112562463898de7351139 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 1 Jul 2013 11:52:00 +0200 Subject: [PATCH 34/47] Stopped branding Python 3 support as experimental. Forward-port of 0de21a6 from stable/1.6.x. --- docs/intro/install.txt | 5 ++--- docs/topics/install.txt | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/intro/install.txt b/docs/intro/install.txt index 3cbc8d88ab..40375e5eca 100644 --- a/docs/intro/install.txt +++ b/docs/intro/install.txt @@ -9,9 +9,8 @@ that'll work while you walk through the introduction. Install Python -------------- -Being a Python Web framework, Django requires Python. It works with any Python -version from 2.6.5 to 2.7. It also features experimental support for versions -3.2 and 3.3. All these versions of Python include a lightweight database +Being a Python Web framework, Django requires Python. It works with Python 2.6, +2.7, 3.2 or 3.3. All these versions of Python include a lightweight database called SQLite_ so you won't need to set up a database just yet. .. _sqlite: http://sqlite.org/ diff --git a/docs/topics/install.txt b/docs/topics/install.txt index 1c99dc5d5a..505d51a846 100644 --- a/docs/topics/install.txt +++ b/docs/topics/install.txt @@ -7,10 +7,8 @@ This document will get you up and running with Django. Install Python ============== -Being a Python Web framework, Django requires Python. - -It works with any Python version from 2.6.5 to 2.7. It also features -experimental support for versions from 3.2.3 to 3.3. +Being a Python Web framework, Django requires Python. It works with Python 2.6, +2.7, 3.2 or 3.3. Get Python at http://www.python.org. If you're running Linux or Mac OS X, you probably already have it installed. From a763915a03579868c6abdc5cdffbb4c398dc52ee Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 1 Jul 2013 12:01:25 +0200 Subject: [PATCH 35/47] Bumped minimum Python version requirement to 2.7 in Django 1.7. --- INSTALL | 3 +-- docs/faq/install.txt | 8 ++++---- docs/intro/install.txt | 10 +++++----- docs/topics/install.txt | 4 ++-- setup.py | 1 - 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/INSTALL b/INSTALL index 5ae2b26900..92be6da650 100644 --- a/INSTALL +++ b/INSTALL @@ -1,6 +1,6 @@ Thanks for downloading Django. -To install it, make sure you have Python 2.6 or greater installed. Then run +To install it, make sure you have Python 2.7 or greater installed. Then run this command from the command prompt: python setup.py install @@ -12,7 +12,6 @@ site-packages directory, which is located wherever your Python installation lives. Some places you might check are: /usr/lib/python2.7/site-packages (Unix, Python 2.7) - /usr/lib/python2.6/site-packages (Unix, Python 2.6) C:\\PYTHON\site-packages (Windows) For more detailed instructions, see docs/intro/install.txt. diff --git a/docs/faq/install.txt b/docs/faq/install.txt index 2c7c35299b..be45012961 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -16,8 +16,8 @@ How do I get started? What are Django's prerequisites? -------------------------------- -Django requires Python, specifically Python 2.6.5 - 2.7.x, or 3.2.3 and above. -No other Python libraries are required for basic Django usage. +Django requires Python, specifically Python 2.7 or 3.2 and above. No other +Python libraries are required for basic Django usage. For a development environment -- if you just want to experiment with Django -- you don't need to have a separate Web server installed; Django comes with its @@ -50,8 +50,8 @@ Django version Python versions 1.3 2.4, 2.5, 2.6, 2.7 1.4 2.5, 2.6, 2.7 1.5 2.6, 2.7 and 3.2, 3.3 (experimental) -**1.6** **2.6, 2.7** and **3.2, 3.3** -*1.7 (future)* *2.7* and *3.2, 3.3* +1.6 2.6, 2.7 and 3.2, 3.3 +**1.7** **2.7** and **3.2, 3.3** ============== =============== For a given series of Python versions, only the latest release is officially diff --git a/docs/intro/install.txt b/docs/intro/install.txt index 40375e5eca..3ef59dcafb 100644 --- a/docs/intro/install.txt +++ b/docs/intro/install.txt @@ -9,9 +9,9 @@ that'll work while you walk through the introduction. Install Python -------------- -Being a Python Web framework, Django requires Python. It works with Python 2.6, -2.7, 3.2 or 3.3. All these versions of Python include a lightweight database -called SQLite_ so you won't need to set up a database just yet. +Being a Python Web framework, Django requires Python. It works with Python 2.7, +3.2 or 3.3. All these versions of Python include a lightweight database called +SQLite_ so you won't need to set up a database just yet. .. _sqlite: http://sqlite.org/ @@ -28,8 +28,8 @@ probably already have it installed. You can verify that Python is installed by typing ``python`` from your shell; you should see something like:: - Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48) - [GCC 4.4.5] on linux2 + Python 2.7.3 (default, Jan 2 2013, 13:56:14) + [GCC 4.7.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> diff --git a/docs/topics/install.txt b/docs/topics/install.txt index 505d51a846..9cf02d96de 100644 --- a/docs/topics/install.txt +++ b/docs/topics/install.txt @@ -7,8 +7,8 @@ This document will get you up and running with Django. Install Python ============== -Being a Python Web framework, Django requires Python. It works with Python 2.6, -2.7, 3.2 or 3.3. +Being a Python Web framework, Django requires Python. It works with Python 2.7, +3.2 or 3.3. Get Python at http://www.python.org. If you're running Linux or Mac OS X, you probably already have it installed. diff --git a/setup.py b/setup.py index 2c64868d00..6278afa116 100644 --- a/setup.py +++ b/setup.py @@ -101,7 +101,6 @@ setup( 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', From d5589b4cd3238c4bf4063ddd8239d22b37caa7d3 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 1 Jul 2013 12:02:17 +0200 Subject: [PATCH 36/47] Removed some conditional code only needed under Python 2.6. --- django/conf/__init__.py | 12 ++++-------- django/db/backends/sqlite3/base.py | 10 ++-------- django/db/models/fields/related.py | 4 +--- django/db/utils.py | 7 +------ 4 files changed, 8 insertions(+), 25 deletions(-) diff --git a/django/conf/__init__.py b/django/conf/__init__.py index c98443be1a..c4b9634d83 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -59,14 +59,10 @@ class LazySettings(LazyObject): Setup logging from LOGGING_CONFIG and LOGGING settings. """ if not sys.warnoptions: - try: - # Route warnings through python logging - logging.captureWarnings(True) - # Allow DeprecationWarnings through the warnings filters - warnings.simplefilter("default", DeprecationWarning) - except AttributeError: - # No captureWarnings on Python 2.6, DeprecationWarnings are on anyway - pass + # Route warnings through python logging + logging.captureWarnings(True) + # Allow DeprecationWarnings through the warnings filters + warnings.simplefilter("default", DeprecationWarning) if self.LOGGING_CONFIG: from django.utils.log import DEFAULT_LOGGING diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 92dbf354ae..4345790b06 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -78,14 +78,8 @@ Database.register_converter(str("decimal"), decoder(util.typecast_decimal)) Database.register_adapter(datetime.datetime, adapt_datetime_with_timezone_support) Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) -if Database.version_info >= (2, 4, 1): - # Starting in 2.4.1, the str type is not accepted anymore, therefore, - # we convert all str objects to Unicode - # As registering a adapter for a primitive type causes a small - # slow-down, this adapter is only registered for sqlite3 versions - # needing it (Python 2.6 and up). - Database.register_adapter(str, lambda s: s.decode('utf-8')) - Database.register_adapter(SafeBytes, lambda s: s.decode('utf-8')) +Database.register_adapter(str, lambda s: s.decode('utf-8')) +Database.register_adapter(SafeBytes, lambda s: s.decode('utf-8')) class DatabaseFeatures(BaseDatabaseFeatures): # SQLite cannot handle us only partially reading from a cursor's result set diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index ff222e1b85..1e7e73ddbe 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -1362,9 +1362,7 @@ class ManyToManyField(RelatedField): assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name) except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT assert isinstance(to, six.string_types), "%s(%r) is invalid. First parameter to ManyToManyField must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT) - # Python 2.6 and earlier require dictionary keys to be of str type, - # not unicode and class names must be ASCII (in Python 2.x), so we - # forcibly coerce it here (breaks early if there's a problem). + # Class names must be ASCII in Python 2.x, so we forcibly coerce it here to break early if there's a problem. to = str(to) kwargs['verbose_name'] = kwargs.get('verbose_name', None) diff --git a/django/db/utils.py b/django/db/utils.py index acb838f940..36b89d9acf 100644 --- a/django/db/utils.py +++ b/django/db/utils.py @@ -85,12 +85,7 @@ class DatabaseErrorWrapper(object): ): db_exc_type = getattr(self.wrapper.Database, dj_exc_type.__name__) if issubclass(exc_type, db_exc_type): - # Under Python 2.6, exc_value can still be a string. - try: - args = tuple(exc_value.args) - except AttributeError: - args = (exc_value,) - dj_exc_value = dj_exc_type(*args) + dj_exc_value = dj_exc_type(*exc_value.args) dj_exc_value.__cause__ = exc_value # Only set the 'errors_occurred' flag for errors that may make # the connection unusable. From 88de53d4a86548016f245a1413b856aa334bc737 Mon Sep 17 00:00:00 2001 From: Baptiste Mispelon Date: Mon, 1 Jul 2013 14:05:49 +0200 Subject: [PATCH 37/47] Fixed #20659 -- Fixed PublisherDetail in CBV topic documentation. Thanks to tudor.prodan, susan, and Tim Graham for the report and reviews. --- docs/topics/class-based-views/mixins.txt | 25 ++++++++++++------------ 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt index 84d7417233..df1a5505a5 100644 --- a/docs/topics/class-based-views/mixins.txt +++ b/docs/topics/class-based-views/mixins.txt @@ -286,18 +286,18 @@ One way to do this is to combine :class:`ListView` with for the paginated list of books can hang off the publisher found as the single object. In order to do this, we need to have two different querysets: -``Publisher`` queryset for use in - :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()` - We'll set the ``model`` attribute on the view and rely on the default - implementation of ``get_object()`` to fetch the correct ``Publisher`` - object. - ``Book`` queryset for use by :class:`~django.views.generic.list.ListView` - The default implementation of ``get_queryset()`` uses the ``model`` attribute - to construct the queryset. This conflicts with our use of this attribute - for ``get_object()`` so we'll override that method and have it return - the queryset of ``Book`` objects linked to the ``Publisher`` we're looking - at. + Since we have access to the ``Publisher`` whose books we want to list, we + simply override ``get_queryset()`` and use the ``Publisher``'s + :ref:`reverse foreign key manager`. + +``Publisher`` queryset for use in :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()` + We'll rely on the default implementation of ``get_object()`` to fetch the + correct ``Publisher`` object. + However, we need to explicitly pass a ``queryset`` argument because + otherwise the default implementation of ``get_object()`` would call + ``get_queryset()`` which we have overridden to return ``Book`` objects + instead of ``Publisher`` ones. .. note:: @@ -317,12 +317,11 @@ Now we can write a new ``PublisherDetail``:: from books.models import Publisher class PublisherDetail(SingleObjectMixin, ListView): - model = Publisher # for SingleObjectMixin.get_object paginate_by = 2 template_name = "books/publisher_detail.html" def get(self, request, *args, **kwargs): - self.object = self.get_object() + self.object = self.get_object(queryset=Publisher.objects.all()) return super(PublisherDetail, self).get(request, *args, **kwargs) def get_context_data(self, **kwargs): From 7f264e02f4480c49d1440be882416a10951c2165 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 1 Jul 2013 13:53:06 +0200 Subject: [PATCH 38/47] Fixed #20680 -- Deprecated django.utils.unittest. Refs #19204. --- django/utils/unittest/__init__.py | 79 +- django/utils/unittest/__main__.py | 10 - django/utils/unittest/case.py | 1076 ----------------- django/utils/unittest/collector.py | 9 - django/utils/unittest/compatibility.py | 64 - django/utils/unittest/loader.py | 322 ----- django/utils/unittest/main.py | 241 ---- django/utils/unittest/result.py | 183 --- django/utils/unittest/runner.py | 206 ---- django/utils/unittest/signals.py | 57 - django/utils/unittest/suite.py | 287 ----- django/utils/unittest/util.py | 99 -- docs/internals/deprecation.txt | 5 + docs/releases/1.7.txt | 41 + .../django_unittest_classes_hierarchy.graffle | 250 +--- .../django_unittest_classes_hierarchy.pdf | Bin 51979 -> 37750 bytes .../django_unittest_classes_hierarchy.svg | 2 +- docs/topics/testing/overview.txt | 33 +- 18 files changed, 81 insertions(+), 2883 deletions(-) delete mode 100644 django/utils/unittest/__main__.py delete mode 100644 django/utils/unittest/case.py delete mode 100644 django/utils/unittest/collector.py delete mode 100644 django/utils/unittest/compatibility.py delete mode 100644 django/utils/unittest/loader.py delete mode 100644 django/utils/unittest/main.py delete mode 100644 django/utils/unittest/result.py delete mode 100644 django/utils/unittest/runner.py delete mode 100644 django/utils/unittest/signals.py delete mode 100644 django/utils/unittest/suite.py delete mode 100644 django/utils/unittest/util.py create mode 100644 docs/releases/1.7.txt diff --git a/django/utils/unittest/__init__.py b/django/utils/unittest/__init__.py index ac852a3757..37634e0b37 100644 --- a/django/utils/unittest/__init__.py +++ b/django/utils/unittest/__init__.py @@ -1,80 +1,9 @@ -""" -unittest2 +import warnings -unittest2 is a backport of the new features added to the unittest testing -framework in Python 2.7. It is tested to run on Python 2.4 - 2.6. - -To use unittest2 instead of unittest simply replace ``import unittest`` with -``import unittest2``. - - -Copyright (c) 1999-2003 Steve Purcell -Copyright (c) 2003-2010 Python Software Foundation -This module is free software, and you may redistribute it and/or modify -it under the same terms as Python itself, so long as this copyright message -and disclaimer are retained in their original form. - -IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -""" - -import sys - -# Django hackery to load the appropriate version of unittest +warnings.warn("django.utils.unittest will be removed in Django 1.9.", + PendingDeprecationWarning) try: - # check the system path first from unittest2 import * except ImportError: - if sys.version_info >= (2,7): - # unittest2 features are native in Python 2.7 - from unittest import * - else: - # otherwise use our bundled version - __all__ = ['TestResult', 'TestCase', 'TestSuite', - 'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main', - 'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless', - 'expectedFailure', 'TextTestResult', '__version__', 'collector'] - - __version__ = '0.5.1' - - # Expose obsolete functions for backwards compatibility - __all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases']) - - - from django.utils.unittest.collector import collector - from django.utils.unittest.result import TestResult - from django.utils.unittest.case import \ - TestCase, FunctionTestCase, SkipTest, skip, skipIf,\ - skipUnless, expectedFailure - - from django.utils.unittest.suite import BaseTestSuite, TestSuite - from django.utils.unittest.loader import \ - TestLoader, defaultTestLoader, makeSuite, getTestCaseNames,\ - findTestCases - - from django.utils.unittest.main import TestProgram, main, main_ - from django.utils.unittest.runner import TextTestRunner, TextTestResult - - try: - from django.utils.unittest.signals import\ - installHandler, registerResult, removeResult, removeHandler - except ImportError: - # Compatibility with platforms that don't have the signal module - pass - else: - __all__.extend(['installHandler', 'registerResult', 'removeResult', - 'removeHandler']) - - # deprecated - _TextTestResult = TextTestResult - - __unittest = True + from unittest import * diff --git a/django/utils/unittest/__main__.py b/django/utils/unittest/__main__.py deleted file mode 100644 index 68b893d139..0000000000 --- a/django/utils/unittest/__main__.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Main entry point""" - -import sys -if sys.argv[0].endswith("__main__.py"): - sys.argv[0] = "unittest2" - -__unittest = True - -from django.utils.unittest.main import main_ -main_() diff --git a/django/utils/unittest/case.py b/django/utils/unittest/case.py deleted file mode 100644 index fffd3c2524..0000000000 --- a/django/utils/unittest/case.py +++ /dev/null @@ -1,1076 +0,0 @@ -"""Test case implementation""" - -import sys -import difflib -import pprint -import re -import unittest -import warnings - -from django.utils.unittest import result -from django.utils.unittest.util import\ - safe_repr, safe_str, strclass,\ - unorderable_list_difference - -from django.utils.unittest.compatibility import wraps - -__unittest = True - - -DIFF_OMITTED = ('\nDiff is %s characters long. ' - 'Set self.maxDiff to None to see it.') - -class SkipTest(Exception): - """ - Raise this exception in a test to skip it. - - Usually you can use TestResult.skip() or one of the skipping decorators - instead of raising this directly. - """ - -class _ExpectedFailure(Exception): - """ - Raise this when a test is expected to fail. - - This is an implementation detail. - """ - - def __init__(self, exc_info): - # can't use super because Python 2.4 exceptions are old style - Exception.__init__(self) - self.exc_info = exc_info - -class _UnexpectedSuccess(Exception): - """ - The test was supposed to fail, but it didn't! - """ - -def _id(obj): - return obj - -def skip(reason): - """ - Unconditionally skip a test. - """ - def decorator(test_item): - if not (isinstance(test_item, type) and issubclass(test_item, TestCase)): - @wraps(test_item) - def skip_wrapper(*args, **kwargs): - raise SkipTest(reason) - test_item = skip_wrapper - - test_item.__unittest_skip__ = True - test_item.__unittest_skip_why__ = reason - return test_item - return decorator - -def skipIf(condition, reason): - """ - Skip a test if the condition is true. - """ - if condition: - return skip(reason) - return _id - -def skipUnless(condition, reason): - """ - Skip a test unless the condition is true. - """ - if not condition: - return skip(reason) - return _id - - -def expectedFailure(func): - @wraps(func) - def wrapper(*args, **kwargs): - try: - func(*args, **kwargs) - except Exception: - raise _ExpectedFailure(sys.exc_info()) - raise _UnexpectedSuccess - return wrapper - - -class _AssertRaisesContext(object): - """A context manager used to implement TestCase.assertRaises* methods.""" - - def __init__(self, expected, test_case, expected_regexp=None): - self.expected = expected - self.failureException = test_case.failureException - self.expected_regexp = expected_regexp - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, tb): - if exc_type is None: - try: - exc_name = self.expected.__name__ - except AttributeError: - exc_name = str(self.expected) - raise self.failureException( - "%s not raised" % (exc_name,)) - if not issubclass(exc_type, self.expected): - # let unexpected exceptions pass through - return False - self.exception = exc_value # store for later retrieval - if self.expected_regexp is None: - return True - - expected_regexp = self.expected_regexp - if isinstance(expected_regexp, basestring): - expected_regexp = re.compile(expected_regexp) - if not expected_regexp.search(str(exc_value)): - raise self.failureException('"%s" does not match "%s"' % - (expected_regexp.pattern, str(exc_value))) - return True - - -class _TypeEqualityDict(object): - - def __init__(self, testcase): - self.testcase = testcase - self._store = {} - - def __setitem__(self, key, value): - self._store[key] = value - - def __getitem__(self, key): - value = self._store[key] - if isinstance(value, basestring): - return getattr(self.testcase, value) - return value - - def get(self, key, default=None): - if key in self._store: - return self[key] - return default - - -class TestCase(unittest.TestCase): - """A class whose instances are single test cases. - - By default, the test code itself should be placed in a method named - 'runTest'. - - If the fixture may be used for many test cases, create as - many test methods as are needed. When instantiating such a TestCase - subclass, specify in the constructor arguments the name of the test method - that the instance is to execute. - - Test authors should subclass TestCase for their own tests. Construction - and deconstruction of the test's environment ('fixture') can be - implemented by overriding the 'setUp' and 'tearDown' methods respectively. - - If it is necessary to override the __init__ method, the base class - __init__ method must always be called. It is important that subclasses - should not change the signature of their __init__ method, since instances - of the classes are instantiated automatically by parts of the framework - in order to be run. - """ - - # This attribute determines which exception will be raised when - # the instance's assertion methods fail; test methods raising this - # exception will be deemed to have 'failed' rather than 'errored' - - failureException = AssertionError - - # This attribute sets the maximum length of a diff in failure messages - # by assert methods using difflib. It is looked up as an instance attribute - # so can be configured by individual tests if required. - - maxDiff = 80*8 - - # This attribute determines whether long messages (including repr of - # objects used in assert methods) will be printed on failure in *addition* - # to any explicit message passed. - - longMessage = True - - # Attribute used by TestSuite for classSetUp - - _classSetupFailed = False - - def __init__(self, methodName='runTest'): - """Create an instance of the class that will use the named test - method when executed. Raises a ValueError if the instance does - not have a method with the specified name. - """ - self._testMethodName = methodName - self._resultForDoCleanups = None - try: - testMethod = getattr(self, methodName) - except AttributeError: - raise ValueError("no such test method in %s: %s" % \ - (self.__class__, methodName)) - self._testMethodDoc = testMethod.__doc__ - self._cleanups = [] - - # Map types to custom assertEqual functions that will compare - # instances of said type in more detail to generate a more useful - # error message. - self._type_equality_funcs = _TypeEqualityDict(self) - self.addTypeEqualityFunc(dict, 'assertDictEqual') - self.addTypeEqualityFunc(list, 'assertListEqual') - self.addTypeEqualityFunc(tuple, 'assertTupleEqual') - self.addTypeEqualityFunc(set, 'assertSetEqual') - self.addTypeEqualityFunc(frozenset, 'assertSetEqual') - self.addTypeEqualityFunc(unicode, 'assertMultiLineEqual') - - def addTypeEqualityFunc(self, typeobj, function): - """Add a type specific assertEqual style function to compare a type. - - This method is for use by TestCase subclasses that need to register - their own type equality functions to provide nicer error messages. - - Args: - typeobj: The data type to call this function on when both values - are of the same type in assertEqual(). - function: The callable taking two arguments and an optional - msg= argument that raises self.failureException with a - useful error message when the two arguments are not equal. - """ - self._type_equality_funcs[typeobj] = function - - def addCleanup(self, function, *args, **kwargs): - """Add a function, with arguments, to be called when the test is - completed. Functions added are called on a LIFO basis and are - called after tearDown on test failure or success. - - Cleanup items are called even if setUp fails (unlike tearDown).""" - self._cleanups.append((function, args, kwargs)) - - @classmethod - def setUpClass(cls): - "Hook method for setting up class fixture before running tests in the class." - - @classmethod - def tearDownClass(cls): - "Hook method for deconstructing the class fixture after running all tests in the class." - - def countTestCases(self): - return 1 - - def defaultTestResult(self): - return result.TestResult() - - def shortDescription(self): - """Returns a one-line description of the test, or None if no - description has been provided. - - The default implementation of this method returns the first line of - the specified test method's docstring. - """ - doc = self._testMethodDoc - return doc and doc.split("\n")[0].strip() or None - - - def id(self): - return "%s.%s" % (strclass(self.__class__), self._testMethodName) - - def __eq__(self, other): - if type(self) is not type(other): - return NotImplemented - - return self._testMethodName == other._testMethodName - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((type(self), self._testMethodName)) - - def __str__(self): - return "%s (%s)" % (self._testMethodName, strclass(self.__class__)) - - def __repr__(self): - return "<%s testMethod=%s>" % \ - (strclass(self.__class__), self._testMethodName) - - def _addSkip(self, result, reason): - addSkip = getattr(result, 'addSkip', None) - if addSkip is not None: - addSkip(self, reason) - else: - warnings.warn("Use of a TestResult without an addSkip method is deprecated", - DeprecationWarning, 2) - result.addSuccess(self) - - def run(self, result=None): - orig_result = result - if result is None: - result = self.defaultTestResult() - startTestRun = getattr(result, 'startTestRun', None) - if startTestRun is not None: - startTestRun() - - self._resultForDoCleanups = result - result.startTest(self) - - testMethod = getattr(self, self._testMethodName) - - if (getattr(self.__class__, "__unittest_skip__", False) or - getattr(testMethod, "__unittest_skip__", False)): - # If the class or method was skipped. - try: - skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') - or getattr(testMethod, '__unittest_skip_why__', '')) - self._addSkip(result, skip_why) - finally: - result.stopTest(self) - return - try: - success = False - try: - self.setUp() - except SkipTest as e: - self._addSkip(result, str(e)) - except Exception: - result.addError(self, sys.exc_info()) - else: - try: - testMethod() - except self.failureException: - result.addFailure(self, sys.exc_info()) - except _ExpectedFailure as e: - addExpectedFailure = getattr(result, 'addExpectedFailure', None) - if addExpectedFailure is not None: - addExpectedFailure(self, e.exc_info) - else: - warnings.warn("Use of a TestResult without an addExpectedFailure method is deprecated", - DeprecationWarning) - result.addSuccess(self) - except _UnexpectedSuccess: - addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None) - if addUnexpectedSuccess is not None: - addUnexpectedSuccess(self) - else: - warnings.warn("Use of a TestResult without an addUnexpectedSuccess method is deprecated", - DeprecationWarning) - result.addFailure(self, sys.exc_info()) - except SkipTest as e: - self._addSkip(result, str(e)) - except Exception: - result.addError(self, sys.exc_info()) - else: - success = True - - try: - self.tearDown() - except Exception: - result.addError(self, sys.exc_info()) - success = False - - cleanUpSuccess = self.doCleanups() - success = success and cleanUpSuccess - if success: - result.addSuccess(self) - finally: - result.stopTest(self) - if orig_result is None: - stopTestRun = getattr(result, 'stopTestRun', None) - if stopTestRun is not None: - stopTestRun() - - def doCleanups(self): - """Execute all cleanup functions. Normally called for you after - tearDown.""" - result = self._resultForDoCleanups - ok = True - while self._cleanups: - function, args, kwargs = self._cleanups.pop(-1) - try: - function(*args, **kwargs) - except Exception: - ok = False - result.addError(self, sys.exc_info()) - return ok - - def __call__(self, *args, **kwds): - return self.run(*args, **kwds) - - def debug(self): - """Run the test without collecting errors in a TestResult""" - self.setUp() - getattr(self, self._testMethodName)() - self.tearDown() - while self._cleanups: - function, args, kwargs = self._cleanups.pop(-1) - function(*args, **kwargs) - - def skipTest(self, reason): - """Skip this test.""" - raise SkipTest(reason) - - def fail(self, msg=None): - """Fail immediately, with the given message.""" - raise self.failureException(msg) - - def assertFalse(self, expr, msg=None): - "Fail the test if the expression is true." - if expr: - msg = self._formatMessage(msg, "%s is not False" % safe_repr(expr)) - raise self.failureException(msg) - - def assertTrue(self, expr, msg=None): - """Fail the test unless the expression is true.""" - if not expr: - msg = self._formatMessage(msg, "%s is not True" % safe_repr(expr)) - raise self.failureException(msg) - - def _formatMessage(self, msg, standardMsg): - """Honour the longMessage attribute when generating failure messages. - If longMessage is False this means: - * Use only an explicit message if it is provided - * Otherwise use the standard message for the assert - - If longMessage is True: - * Use the standard message - * If an explicit message is provided, plus ' : ' and the explicit message - """ - if not self.longMessage: - return msg or standardMsg - if msg is None: - return standardMsg - try: - return '%s : %s' % (standardMsg, msg) - except UnicodeDecodeError: - return '%s : %s' % (safe_str(standardMsg), safe_str(msg)) - - - def assertRaises(self, excClass, callableObj=None, *args, **kwargs): - """Fail unless an exception of class excClass is thrown - by callableObj when invoked with arguments args and keyword - arguments kwargs. If a different type of exception is - thrown, it will not be caught, and the test case will be - deemed to have suffered an error, exactly as for an - unexpected exception. - - If called with callableObj omitted or None, will return a - context object used like this:: - - with self.assertRaises(SomeException): - do_something() - - The context manager keeps a reference to the exception as - the 'exception' attribute. This allows you to inspect the - exception after the assertion:: - - with self.assertRaises(SomeException) as cm: - do_something() - the_exception = cm.exception - self.assertEqual(the_exception.error_code, 3) - """ - if callableObj is None: - return _AssertRaisesContext(excClass, self) - try: - callableObj(*args, **kwargs) - except excClass: - return - - if hasattr(excClass,'__name__'): - excName = excClass.__name__ - else: - excName = str(excClass) - raise self.failureException("%s not raised" % excName) - - def _getAssertEqualityFunc(self, first, second): - """Get a detailed comparison function for the types of the two args. - - Returns: A callable accepting (first, second, msg=None) that will - raise a failure exception if first != second with a useful human - readable error message for those types. - """ - # - # NOTE(gregory.p.smith): I considered isinstance(first, type(second)) - # and vice versa. I opted for the conservative approach in case - # subclasses are not intended to be compared in detail to their super - # class instances using a type equality func. This means testing - # subtypes won't automagically use the detailed comparison. Callers - # should use their type specific assertSpamEqual method to compare - # subclasses if the detailed comparison is desired and appropriate. - # See the discussion in http://bugs.python.org/issue2578. - # - if type(first) is type(second): - asserter = self._type_equality_funcs.get(type(first)) - if asserter is not None: - return asserter - - return self._baseAssertEqual - - def _baseAssertEqual(self, first, second, msg=None): - """The default assertEqual implementation, not type specific.""" - if not first == second: - standardMsg = '%s != %s' % (safe_repr(first), safe_repr(second)) - msg = self._formatMessage(msg, standardMsg) - raise self.failureException(msg) - - def assertEqual(self, first, second, msg=None): - """Fail if the two objects are unequal as determined by the '==' - operator. - """ - assertion_func = self._getAssertEqualityFunc(first, second) - assertion_func(first, second, msg=msg) - - def assertNotEqual(self, first, second, msg=None): - """Fail if the two objects are equal as determined by the '==' - operator. - """ - if not first != second: - msg = self._formatMessage(msg, '%s == %s' % (safe_repr(first), - safe_repr(second))) - raise self.failureException(msg) - - def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None): - """Fail if the two objects are unequal as determined by their - difference rounded to the given number of decimal places - (default 7) and comparing to zero, or by comparing that the - between the two objects is more than the given delta. - - Note that decimal places (from zero) are usually not the same - as significant digits (measured from the most signficant digit). - - If the two objects compare equal then they will automatically - compare almost equal. - """ - if first == second: - # shortcut - return - if delta is not None and places is not None: - raise TypeError("specify delta or places not both") - - if delta is not None: - if abs(first - second) <= delta: - return - - standardMsg = '%s != %s within %s delta' % (safe_repr(first), - safe_repr(second), - safe_repr(delta)) - else: - if places is None: - places = 7 - - if round(abs(second-first), places) == 0: - return - - standardMsg = '%s != %s within %r places' % (safe_repr(first), - safe_repr(second), - places) - msg = self._formatMessage(msg, standardMsg) - raise self.failureException(msg) - - def assertNotAlmostEqual(self, first, second, places=None, msg=None, delta=None): - """Fail if the two objects are equal as determined by their - difference rounded to the given number of decimal places - (default 7) and comparing to zero, or by comparing that the - between the two objects is less than the given delta. - - Note that decimal places (from zero) are usually not the same - as significant digits (measured from the most signficant digit). - - Objects that are equal automatically fail. - """ - if delta is not None and places is not None: - raise TypeError("specify delta or places not both") - if delta is not None: - if not (first == second) and abs(first - second) > delta: - return - standardMsg = '%s == %s within %s delta' % (safe_repr(first), - safe_repr(second), - safe_repr(delta)) - else: - if places is None: - places = 7 - if not (first == second) and round(abs(second-first), places) != 0: - return - standardMsg = '%s == %s within %r places' % (safe_repr(first), - safe_repr(second), - places) - - msg = self._formatMessage(msg, standardMsg) - raise self.failureException(msg) - - # Synonyms for assertion methods - - # The plurals are undocumented. Keep them that way to discourage use. - # Do not add more. Do not remove. - # Going through a deprecation cycle on these would annoy many people. - assertEquals = assertEqual - assertNotEquals = assertNotEqual - assertAlmostEquals = assertAlmostEqual - assertNotAlmostEquals = assertNotAlmostEqual - assert_ = assertTrue - - # These fail* assertion method names are pending deprecation and will - # be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578 - def _deprecate(original_func): - def deprecated_func(*args, **kwargs): - warnings.warn( - ('Please use %s instead.' % original_func.__name__), - PendingDeprecationWarning, 2) - return original_func(*args, **kwargs) - return deprecated_func - - failUnlessEqual = _deprecate(assertEqual) - failIfEqual = _deprecate(assertNotEqual) - failUnlessAlmostEqual = _deprecate(assertAlmostEqual) - failIfAlmostEqual = _deprecate(assertNotAlmostEqual) - failUnless = _deprecate(assertTrue) - failUnlessRaises = _deprecate(assertRaises) - failIf = _deprecate(assertFalse) - - def assertSequenceEqual(self, seq1, seq2, - msg=None, seq_type=None, max_diff=80*8): - """An equality assertion for ordered sequences (like lists and tuples). - - For the purposes of this function, a valid ordered sequence type is one - which can be indexed, has a length, and has an equality operator. - - Args: - seq1: The first sequence to compare. - seq2: The second sequence to compare. - seq_type: The expected datatype of the sequences, or None if no - datatype should be enforced. - msg: Optional message to use on failure instead of a list of - differences. - max_diff: Maximum size off the diff, larger diffs are not shown - """ - if seq_type is not None: - seq_type_name = seq_type.__name__ - if not isinstance(seq1, seq_type): - raise self.failureException('First sequence is not a %s: %s' - % (seq_type_name, safe_repr(seq1))) - if not isinstance(seq2, seq_type): - raise self.failureException('Second sequence is not a %s: %s' - % (seq_type_name, safe_repr(seq2))) - else: - seq_type_name = "sequence" - - differing = None - try: - len1 = len(seq1) - except (TypeError, NotImplementedError): - differing = 'First %s has no length. Non-sequence?' % ( - seq_type_name) - - if differing is None: - try: - len2 = len(seq2) - except (TypeError, NotImplementedError): - differing = 'Second %s has no length. Non-sequence?' % ( - seq_type_name) - - if differing is None: - if seq1 == seq2: - return - - seq1_repr = repr(seq1) - seq2_repr = repr(seq2) - if len(seq1_repr) > 30: - seq1_repr = seq1_repr[:30] + '...' - if len(seq2_repr) > 30: - seq2_repr = seq2_repr[:30] + '...' - elements = (seq_type_name.capitalize(), seq1_repr, seq2_repr) - differing = '%ss differ: %s != %s\n' % elements - - for i in xrange(min(len1, len2)): - try: - item1 = seq1[i] - except (TypeError, IndexError, NotImplementedError): - differing += ('\nUnable to index element %d of first %s\n' % - (i, seq_type_name)) - break - - try: - item2 = seq2[i] - except (TypeError, IndexError, NotImplementedError): - differing += ('\nUnable to index element %d of second %s\n' % - (i, seq_type_name)) - break - - if item1 != item2: - differing += ('\nFirst differing element %d:\n%s\n%s\n' % - (i, item1, item2)) - break - else: - if (len1 == len2 and seq_type is None and - type(seq1) != type(seq2)): - # The sequences are the same, but have differing types. - return - - if len1 > len2: - differing += ('\nFirst %s contains %d additional ' - 'elements.\n' % (seq_type_name, len1 - len2)) - try: - differing += ('First extra element %d:\n%s\n' % - (len2, seq1[len2])) - except (TypeError, IndexError, NotImplementedError): - differing += ('Unable to index element %d ' - 'of first %s\n' % (len2, seq_type_name)) - elif len1 < len2: - differing += ('\nSecond %s contains %d additional ' - 'elements.\n' % (seq_type_name, len2 - len1)) - try: - differing += ('First extra element %d:\n%s\n' % - (len1, seq2[len1])) - except (TypeError, IndexError, NotImplementedError): - differing += ('Unable to index element %d ' - 'of second %s\n' % (len1, seq_type_name)) - standardMsg = differing - diffMsg = '\n' + '\n'.join( - difflib.ndiff(pprint.pformat(seq1).splitlines(), - pprint.pformat(seq2).splitlines())) - - standardMsg = self._truncateMessage(standardMsg, diffMsg) - msg = self._formatMessage(msg, standardMsg) - self.fail(msg) - - def _truncateMessage(self, message, diff): - max_diff = self.maxDiff - if max_diff is None or len(diff) <= max_diff: - return message + diff - return message + (DIFF_OMITTED % len(diff)) - - def assertListEqual(self, list1, list2, msg=None): - """A list-specific equality assertion. - - Args: - list1: The first list to compare. - list2: The second list to compare. - msg: Optional message to use on failure instead of a list of - differences. - - """ - self.assertSequenceEqual(list1, list2, msg, seq_type=list) - - def assertTupleEqual(self, tuple1, tuple2, msg=None): - """A tuple-specific equality assertion. - - Args: - tuple1: The first tuple to compare. - tuple2: The second tuple to compare. - msg: Optional message to use on failure instead of a list of - differences. - """ - self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple) - - def assertSetEqual(self, set1, set2, msg=None): - """A set-specific equality assertion. - - Args: - set1: The first set to compare. - set2: The second set to compare. - msg: Optional message to use on failure instead of a list of - differences. - - assertSetEqual uses ducktyping to support - different types of sets, and is optimized for sets specifically - (parameters must support a difference method). - """ - try: - difference1 = set1.difference(set2) - except TypeError as e: - self.fail('invalid type when attempting set difference: %s' % e) - except AttributeError as e: - self.fail('first argument does not support set difference: %s' % e) - - try: - difference2 = set2.difference(set1) - except TypeError as e: - self.fail('invalid type when attempting set difference: %s' % e) - except AttributeError as e: - self.fail('second argument does not support set difference: %s' % e) - - if not (difference1 or difference2): - return - - lines = [] - if difference1: - lines.append('Items in the first set but not the second:') - for item in difference1: - lines.append(repr(item)) - if difference2: - lines.append('Items in the second set but not the first:') - for item in difference2: - lines.append(repr(item)) - - standardMsg = '\n'.join(lines) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertIn(self, member, container, msg=None): - """Just like self.assertTrue(a in b), but with a nicer default message.""" - if member not in container: - standardMsg = '%s not found in %s' % (safe_repr(member), - safe_repr(container)) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertNotIn(self, member, container, msg=None): - """Just like self.assertTrue(a not in b), but with a nicer default message.""" - if member in container: - standardMsg = '%s unexpectedly found in %s' % (safe_repr(member), - safe_repr(container)) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertIs(self, expr1, expr2, msg=None): - """Just like self.assertTrue(a is b), but with a nicer default message.""" - if expr1 is not expr2: - standardMsg = '%s is not %s' % (safe_repr(expr1), safe_repr(expr2)) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertIsNot(self, expr1, expr2, msg=None): - """Just like self.assertTrue(a is not b), but with a nicer default message.""" - if expr1 is expr2: - standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertDictEqual(self, d1, d2, msg=None): - self.assertTrue(isinstance(d1, dict), 'First argument is not a dictionary') - self.assertTrue(isinstance(d2, dict), 'Second argument is not a dictionary') - - if d1 != d2: - standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True)) - diff = ('\n' + '\n'.join(difflib.ndiff( - pprint.pformat(d1).splitlines(), - pprint.pformat(d2).splitlines()))) - standardMsg = self._truncateMessage(standardMsg, diff) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertDictContainsSubset(self, expected, actual, msg=None): - """Checks whether actual is a superset of expected.""" - missing = [] - mismatched = [] - for key, value in expected.iteritems(): - if key not in actual: - missing.append(key) - elif value != actual[key]: - mismatched.append('%s, expected: %s, actual: %s' % - (safe_repr(key), safe_repr(value), - safe_repr(actual[key]))) - - if not (missing or mismatched): - return - - standardMsg = '' - if missing: - standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in - missing) - if mismatched: - if standardMsg: - standardMsg += '; ' - standardMsg += 'Mismatched values: %s' % ','.join(mismatched) - - self.fail(self._formatMessage(msg, standardMsg)) - - def assertItemsEqual(self, expected_seq, actual_seq, msg=None): - """An unordered sequence specific comparison. It asserts that - expected_seq and actual_seq contain the same elements. It is - the equivalent of:: - - self.assertEqual(sorted(expected_seq), sorted(actual_seq)) - - Raises with an error message listing which elements of expected_seq - are missing from actual_seq and vice versa if any. - - Asserts that each element has the same count in both sequences. - Example: - - [0, 1, 1] and [1, 0, 1] compare equal. - - [0, 0, 1] and [0, 1] compare unequal. - """ - try: - expected = sorted(expected_seq) - actual = sorted(actual_seq) - except TypeError: - # Unsortable items (example: set(), complex(), ...) - expected = list(expected_seq) - actual = list(actual_seq) - missing, unexpected = unorderable_list_difference( - expected, actual, ignore_duplicate=False - ) - else: - return self.assertSequenceEqual(expected, actual, msg=msg) - - errors = [] - if missing: - errors.append('Expected, but missing:\n %s' % - safe_repr(missing)) - if unexpected: - errors.append('Unexpected, but present:\n %s' % - safe_repr(unexpected)) - if errors: - standardMsg = '\n'.join(errors) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertMultiLineEqual(self, first, second, msg=None): - """Assert that two multi-line strings are equal.""" - self.assertTrue(isinstance(first, basestring), ( - 'First argument is not a string')) - self.assertTrue(isinstance(second, basestring), ( - 'Second argument is not a string')) - - if first != second: - standardMsg = '%s != %s' % (safe_repr(first, True), safe_repr(second, True)) - diff = '\n' + ''.join(difflib.ndiff(first.splitlines(True), - second.splitlines(True))) - standardMsg = self._truncateMessage(standardMsg, diff) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertLess(self, a, b, msg=None): - """Just like self.assertTrue(a < b), but with a nicer default message.""" - if not a < b: - standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b)) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertLessEqual(self, a, b, msg=None): - """Just like self.assertTrue(a <= b), but with a nicer default message.""" - if not a <= b: - standardMsg = '%s not less than or equal to %s' % (safe_repr(a), safe_repr(b)) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertGreater(self, a, b, msg=None): - """Just like self.assertTrue(a > b), but with a nicer default message.""" - if not a > b: - standardMsg = '%s not greater than %s' % (safe_repr(a), safe_repr(b)) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertGreaterEqual(self, a, b, msg=None): - """Just like self.assertTrue(a >= b), but with a nicer default message.""" - if not a >= b: - standardMsg = '%s not greater than or equal to %s' % (safe_repr(a), safe_repr(b)) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertIsNone(self, obj, msg=None): - """Same as self.assertTrue(obj is None), with a nicer default message.""" - if obj is not None: - standardMsg = '%s is not None' % (safe_repr(obj),) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertIsNotNone(self, obj, msg=None): - """Included for symmetry with assertIsNone.""" - if obj is None: - standardMsg = 'unexpectedly None' - self.fail(self._formatMessage(msg, standardMsg)) - - def assertIsInstance(self, obj, cls, msg=None): - """Same as self.assertTrue(isinstance(obj, cls)), with a nicer - default message.""" - if not isinstance(obj, cls): - standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertNotIsInstance(self, obj, cls, msg=None): - """Included for symmetry with assertIsInstance.""" - if isinstance(obj, cls): - standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertRaisesRegexp(self, expected_exception, expected_regexp, - callable_obj=None, *args, **kwargs): - """Asserts that the message in a raised exception matches a regexp. - - Args: - expected_exception: Exception class expected to be raised. - expected_regexp: Regexp (re pattern object or string) expected - to be found in error message. - callable_obj: Function to be called. - args: Extra args. - kwargs: Extra kwargs. - """ - if callable_obj is None: - return _AssertRaisesContext(expected_exception, self, expected_regexp) - try: - callable_obj(*args, **kwargs) - except expected_exception as exc_value: - if isinstance(expected_regexp, basestring): - expected_regexp = re.compile(expected_regexp) - if not expected_regexp.search(str(exc_value)): - raise self.failureException('"%s" does not match "%s"' % - (expected_regexp.pattern, str(exc_value))) - else: - if hasattr(expected_exception, '__name__'): - excName = expected_exception.__name__ - else: - excName = str(expected_exception) - raise self.failureException("%s not raised" % excName) - - def assertRegexpMatches(self, text, expected_regexp, msg=None): - """Fail the test unless the text matches the regular expression.""" - if isinstance(expected_regexp, basestring): - expected_regexp = re.compile(expected_regexp) - if not expected_regexp.search(text): - msg = msg or "Regexp didn't match" - msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text) - raise self.failureException(msg) - - def assertNotRegexpMatches(self, text, unexpected_regexp, msg=None): - """Fail the test if the text matches the regular expression.""" - if isinstance(unexpected_regexp, basestring): - unexpected_regexp = re.compile(unexpected_regexp) - match = unexpected_regexp.search(text) - if match: - msg = msg or "Regexp matched" - msg = '%s: %r matches %r in %r' % (msg, - text[match.start():match.end()], - unexpected_regexp.pattern, - text) - raise self.failureException(msg) - -class FunctionTestCase(TestCase): - """A test case that wraps a test function. - - This is useful for slipping pre-existing test functions into the - unittest framework. Optionally, set-up and tidy-up functions can be - supplied. As with TestCase, the tidy-up ('tearDown') function will - always be called if the set-up ('setUp') function ran successfully. - """ - - def __init__(self, testFunc, setUp=None, tearDown=None, description=None): - super(FunctionTestCase, self).__init__() - self._setUpFunc = setUp - self._tearDownFunc = tearDown - self._testFunc = testFunc - self._description = description - - def setUp(self): - if self._setUpFunc is not None: - self._setUpFunc() - - def tearDown(self): - if self._tearDownFunc is not None: - self._tearDownFunc() - - def runTest(self): - self._testFunc() - - def id(self): - return self._testFunc.__name__ - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - - return self._setUpFunc == other._setUpFunc and \ - self._tearDownFunc == other._tearDownFunc and \ - self._testFunc == other._testFunc and \ - self._description == other._description - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((type(self), self._setUpFunc, self._tearDownFunc, - self._testFunc, self._description)) - - def __str__(self): - return "%s (%s)" % (strclass(self.__class__), - self._testFunc.__name__) - - def __repr__(self): - return "<%s testFunc=%s>" % (strclass(self.__class__), - self._testFunc) - - def shortDescription(self): - if self._description is not None: - return self._description - doc = self._testFunc.__doc__ - return doc and doc.split("\n")[0].strip() or None diff --git a/django/utils/unittest/collector.py b/django/utils/unittest/collector.py deleted file mode 100644 index 0f76fc3404..0000000000 --- a/django/utils/unittest/collector.py +++ /dev/null @@ -1,9 +0,0 @@ -import os -import sys -from django.utils.unittest.loader import defaultTestLoader - -def collector(): - # import __main__ triggers code re-execution - __main__ = sys.modules['__main__'] - setupDir = os.path.abspath(os.path.dirname(__main__.__file__)) - return defaultTestLoader.discover(setupDir) diff --git a/django/utils/unittest/compatibility.py b/django/utils/unittest/compatibility.py deleted file mode 100644 index a0dc499cbf..0000000000 --- a/django/utils/unittest/compatibility.py +++ /dev/null @@ -1,64 +0,0 @@ -import os -import sys - -try: - from functools import wraps -except ImportError: - # only needed for Python 2.4 - def wraps(_): - def _wraps(func): - return func - return _wraps - -__unittest = True - -def _relpath_nt(path, start=os.path.curdir): - """Return a relative version of a path""" - - if not path: - raise ValueError("no path specified") - start_list = os.path.abspath(start).split(os.path.sep) - path_list = os.path.abspath(path).split(os.path.sep) - if start_list[0].lower() != path_list[0].lower(): - unc_path, rest = os.path.splitunc(path) - unc_start, rest = os.path.splitunc(start) - if bool(unc_path) ^ bool(unc_start): - raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" - % (path, start)) - else: - raise ValueError("path is on drive %s, start on drive %s" - % (path_list[0], start_list[0])) - # Work out how much of the filepath is shared by start and path. - for i in range(min(len(start_list), len(path_list))): - if start_list[i].lower() != path_list[i].lower(): - break - else: - i += 1 - - rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return os.path.curdir - return os.path.join(*rel_list) - -# default to posixpath definition -def _relpath_posix(path, start=os.path.curdir): - """Return a relative version of a path""" - - if not path: - raise ValueError("no path specified") - - start_list = os.path.abspath(start).split(os.path.sep) - path_list = os.path.abspath(path).split(os.path.sep) - - # Work out how much of the filepath is shared by start and path. - i = len(os.path.commonprefix([start_list, path_list])) - - rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return os.path.curdir - return os.path.join(*rel_list) - -if os.path is sys.modules.get('ntpath'): - relpath = _relpath_nt -else: - relpath = _relpath_posix diff --git a/django/utils/unittest/loader.py b/django/utils/unittest/loader.py deleted file mode 100644 index 695bac40ef..0000000000 --- a/django/utils/unittest/loader.py +++ /dev/null @@ -1,322 +0,0 @@ -"""Loading unittests.""" - -import os -import re -import sys -import traceback -import types -import unittest - -from fnmatch import fnmatch - -from django.utils.unittest import case, suite - -try: - from os.path import relpath -except ImportError: - from django.utils.unittest.compatibility import relpath - -__unittest = True - - -def _CmpToKey(mycmp): - 'Convert a cmp= function into a key= function' - class K(object): - def __init__(self, obj): - self.obj = obj - def __lt__(self, other): - return mycmp(self.obj, other.obj) == -1 - return K - - -# what about .pyc or .pyo (etc) -# we would need to avoid loading the same tests multiple times -# from '.py', '.pyc' *and* '.pyo' -VALID_MODULE_NAME = re.compile(r'[_a-z]\w*\.py$', re.IGNORECASE) - - -def _make_failed_import_test(name, suiteClass): - message = 'Failed to import test module: %s' % name - if hasattr(traceback, 'format_exc'): - # Python 2.3 compatibility - # format_exc returns two frames of discover.py as well - message += '\n%s' % traceback.format_exc() - return _make_failed_test('ModuleImportFailure', name, ImportError(message), - suiteClass) - -def _make_failed_load_tests(name, exception, suiteClass): - return _make_failed_test('LoadTestsFailure', name, exception, suiteClass) - -def _make_failed_test(classname, methodname, exception, suiteClass): - def testFailure(self): - raise exception - attrs = {methodname: testFailure} - TestClass = type(classname, (case.TestCase,), attrs) - return suiteClass((TestClass(methodname),)) - - -class TestLoader(unittest.TestLoader): - """ - This class is responsible for loading tests according to various criteria - and returning them wrapped in a TestSuite - """ - testMethodPrefix = 'test' - sortTestMethodsUsing = cmp - suiteClass = suite.TestSuite - _top_level_dir = None - - def loadTestsFromTestCase(self, testCaseClass): - """Return a suite of all tests cases contained in testCaseClass""" - if issubclass(testCaseClass, suite.TestSuite): - raise TypeError("Test cases should not be derived from TestSuite." - " Maybe you meant to derive from TestCase?") - testCaseNames = self.getTestCaseNames(testCaseClass) - if not testCaseNames and hasattr(testCaseClass, 'runTest'): - testCaseNames = ['runTest'] - loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames)) - return loaded_suite - - def loadTestsFromModule(self, module, use_load_tests=True): - """Return a suite of all tests cases contained in the given module""" - tests = [] - for name in dir(module): - obj = getattr(module, name) - if isinstance(obj, type) and issubclass(obj, unittest.TestCase): - tests.append(self.loadTestsFromTestCase(obj)) - - load_tests = getattr(module, 'load_tests', None) - tests = self.suiteClass(tests) - if use_load_tests and load_tests is not None: - try: - return load_tests(self, tests, None) - except Exception as e: - return _make_failed_load_tests(module.__name__, e, - self.suiteClass) - return tests - - def loadTestsFromName(self, name, module=None): - """Return a suite of all tests cases given a string specifier. - - The name may resolve either to a module, a test case class, a - test method within a test case class, or a callable object which - returns a TestCase or TestSuite instance. - - The method optionally resolves the names relative to a given module. - """ - parts = name.split('.') - if module is None: - parts_copy = parts[:] - while parts_copy: - try: - module = __import__('.'.join(parts_copy)) - break - except ImportError: - del parts_copy[-1] - if not parts_copy: - raise - parts = parts[1:] - obj = module - for part in parts: - parent, obj = obj, getattr(obj, part) - - if isinstance(obj, types.ModuleType): - return self.loadTestsFromModule(obj) - elif isinstance(obj, type) and issubclass(obj, unittest.TestCase): - return self.loadTestsFromTestCase(obj) - elif (isinstance(obj, types.UnboundMethodType) and - isinstance(parent, type) and - issubclass(parent, unittest.TestCase)): - return self.suiteClass([parent(obj.__name__)]) - elif isinstance(obj, unittest.TestSuite): - return obj - elif hasattr(obj, '__call__'): - test = obj() - if isinstance(test, unittest.TestSuite): - return test - elif isinstance(test, unittest.TestCase): - return self.suiteClass([test]) - else: - raise TypeError("calling %s returned %s, not a test" % - (obj, test)) - else: - raise TypeError("don't know how to make test from: %s" % obj) - - def loadTestsFromNames(self, names, module=None): - """Return a suite of all tests cases found using the given sequence - of string specifiers. See 'loadTestsFromName()'. - """ - suites = [self.loadTestsFromName(name, module) for name in names] - return self.suiteClass(suites) - - def getTestCaseNames(self, testCaseClass): - """Return a sorted sequence of method names found within testCaseClass - """ - def isTestMethod(attrname, testCaseClass=testCaseClass, - prefix=self.testMethodPrefix): - return attrname.startswith(prefix) and \ - hasattr(getattr(testCaseClass, attrname), '__call__') - testFnNames = filter(isTestMethod, dir(testCaseClass)) - if self.sortTestMethodsUsing: - testFnNames.sort(key=_CmpToKey(self.sortTestMethodsUsing)) - return testFnNames - - def discover(self, start_dir, pattern='test*.py', top_level_dir=None): - """Find and return all test modules from the specified start - directory, recursing into subdirectories to find them. Only test files - that match the pattern will be loaded. (Using shell style pattern - matching.) - - All test modules must be importable from the top level of the project. - If the start directory is not the top level directory then the top - level directory must be specified separately. - - If a test package name (directory with '__init__.py') matches the - pattern then the package will be checked for a 'load_tests' function. If - this exists then it will be called with loader, tests, pattern. - - If load_tests exists then discovery does *not* recurse into the package, - load_tests is responsible for loading all tests in the package. - - The pattern is deliberately not stored as a loader attribute so that - packages can continue discovery themselves. top_level_dir is stored so - load_tests does not need to pass this argument in to loader.discover(). - """ - set_implicit_top = False - if top_level_dir is None and self._top_level_dir is not None: - # make top_level_dir optional if called from load_tests in a package - top_level_dir = self._top_level_dir - elif top_level_dir is None: - set_implicit_top = True - top_level_dir = start_dir - - top_level_dir = os.path.abspath(top_level_dir) - - if not top_level_dir in sys.path: - # all test modules must be importable from the top level directory - # should we *unconditionally* put the start directory in first - # in sys.path to minimise likelihood of conflicts between installed - # modules and development versions? - sys.path.insert(0, top_level_dir) - self._top_level_dir = top_level_dir - - is_not_importable = False - if os.path.isdir(os.path.abspath(start_dir)): - start_dir = os.path.abspath(start_dir) - if start_dir != top_level_dir: - is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py')) - else: - # support for discovery from dotted module names - try: - __import__(start_dir) - except ImportError: - is_not_importable = True - else: - the_module = sys.modules[start_dir] - top_part = start_dir.split('.')[0] - start_dir = os.path.abspath(os.path.dirname((the_module.__file__))) - if set_implicit_top: - self._top_level_dir = os.path.abspath(os.path.dirname(os.path.dirname(sys.modules[top_part].__file__))) - sys.path.remove(top_level_dir) - - if is_not_importable: - raise ImportError('Start directory is not importable: %r' % start_dir) - - tests = list(self._find_tests(start_dir, pattern)) - return self.suiteClass(tests) - - def _get_name_from_path(self, path): - path = os.path.splitext(os.path.normpath(path))[0] - - _relpath = relpath(path, self._top_level_dir) - assert not os.path.isabs(_relpath), "Path must be within the project" - assert not _relpath.startswith('..'), "Path must be within the project" - - name = _relpath.replace(os.path.sep, '.') - return name - - def _get_module_from_name(self, name): - __import__(name) - return sys.modules[name] - - def _match_path(self, path, full_path, pattern): - # override this method to use alternative matching strategy - return fnmatch(path, pattern) - - def _find_tests(self, start_dir, pattern): - """Used by discovery. Yields test suites it loads.""" - paths = os.listdir(start_dir) - - for path in paths: - full_path = os.path.join(start_dir, path) - if os.path.isfile(full_path): - if not VALID_MODULE_NAME.match(path): - # valid Python identifiers only - continue - if not self._match_path(path, full_path, pattern): - continue - # if the test file matches, load it - name = self._get_name_from_path(full_path) - try: - module = self._get_module_from_name(name) - except: - yield _make_failed_import_test(name, self.suiteClass) - else: - mod_file = os.path.abspath(getattr(module, '__file__', full_path)) - realpath = os.path.splitext(mod_file)[0] - fullpath_noext = os.path.splitext(full_path)[0] - if realpath.lower() != fullpath_noext.lower(): - module_dir = os.path.dirname(realpath) - mod_name = os.path.splitext(os.path.basename(full_path))[0] - expected_dir = os.path.dirname(full_path) - msg = ("%r module incorrectly imported from %r. Expected %r. " - "Is this module globally installed?") - raise ImportError(msg % (mod_name, module_dir, expected_dir)) - yield self.loadTestsFromModule(module) - elif os.path.isdir(full_path): - if not os.path.isfile(os.path.join(full_path, '__init__.py')): - continue - - load_tests = None - tests = None - if fnmatch(path, pattern): - # only check load_tests if the package directory itself matches the filter - name = self._get_name_from_path(full_path) - package = self._get_module_from_name(name) - load_tests = getattr(package, 'load_tests', None) - tests = self.loadTestsFromModule(package, use_load_tests=False) - - if load_tests is None: - if tests is not None: - # tests loaded from package file - yield tests - # recurse into the package - for test in self._find_tests(full_path, pattern): - yield test - else: - try: - yield load_tests(self, tests, pattern) - except Exception as e: - yield _make_failed_load_tests(package.__name__, e, - self.suiteClass) - -defaultTestLoader = TestLoader() - - -def _makeLoader(prefix, sortUsing, suiteClass=None): - loader = TestLoader() - loader.sortTestMethodsUsing = sortUsing - loader.testMethodPrefix = prefix - if suiteClass: - loader.suiteClass = suiteClass - return loader - -def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp): - return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass) - -def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, - suiteClass=suite.TestSuite): - return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass) - -def findTestCases(module, prefix='test', sortUsing=cmp, - suiteClass=suite.TestSuite): - return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module) diff --git a/django/utils/unittest/main.py b/django/utils/unittest/main.py deleted file mode 100644 index 659310babf..0000000000 --- a/django/utils/unittest/main.py +++ /dev/null @@ -1,241 +0,0 @@ -"""Unittest main program""" - -import sys -import os -import types - -from django.utils.unittest import loader, runner -try: - from django.utils.unittest.signals import installHandler -except ImportError: - installHandler = None - -__unittest = True - -FAILFAST = " -f, --failfast Stop on first failure\n" -CATCHBREAK = " -c, --catch Catch control-C and display results\n" -BUFFEROUTPUT = " -b, --buffer Buffer stdout and stderr during test runs\n" - -USAGE_AS_MAIN = """\ -Usage: %(progName)s [options] [tests] - -Options: - -h, --help Show this message - -v, --verbose Verbose output - -q, --quiet Minimal output -%(failfast)s%(catchbreak)s%(buffer)s -Examples: - %(progName)s test_module - run tests from test_module - %(progName)s test_module.TestClass - run tests from - test_module.TestClass - %(progName)s test_module.TestClass.test_method - run specified test method - -[tests] can be a list of any number of test modules, classes and test -methods. - -Alternative Usage: %(progName)s discover [options] - -Options: - -v, --verbose Verbose output -%(failfast)s%(catchbreak)s%(buffer)s -s directory Directory to start discovery ('.' default) - -p pattern Pattern to match test files ('test*.py' default) - -t directory Top level directory of project (default to - start directory) - -For test discovery all test modules must be importable from the top -level directory of the project. -""" - -USAGE_FROM_MODULE = """\ -Usage: %(progName)s [options] [test] [...] - -Options: - -h, --help Show this message - -v, --verbose Verbose output - -q, --quiet Minimal output -%(failfast)s%(catchbreak)s%(buffer)s -Examples: - %(progName)s - run default set of tests - %(progName)s MyTestSuite - run suite 'MyTestSuite' - %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething - %(progName)s MyTestCase - run all 'test*' test methods - in MyTestCase -""" - - -class TestProgram(object): - """A command-line program that runs a set of tests; this is primarily - for making test modules conveniently executable. - """ - USAGE = USAGE_FROM_MODULE - - # defaults for testing - failfast = catchbreak = buffer = progName = None - - def __init__(self, module='__main__', defaultTest=None, - argv=None, testRunner=None, - testLoader=loader.defaultTestLoader, exit=True, - verbosity=1, failfast=None, catchbreak=None, buffer=None): - if isinstance(module, basestring): - self.module = __import__(module) - for part in module.split('.')[1:]: - self.module = getattr(self.module, part) - else: - self.module = module - if argv is None: - argv = sys.argv - - self.exit = exit - self.verbosity = verbosity - self.failfast = failfast - self.catchbreak = catchbreak - self.buffer = buffer - self.defaultTest = defaultTest - self.testRunner = testRunner - self.testLoader = testLoader - self.progName = os.path.basename(argv[0]) - self.parseArgs(argv) - self.runTests() - - def usageExit(self, msg=None): - if msg: - print(msg) - usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '', - 'buffer': ''} - if self.failfast != False: - usage['failfast'] = FAILFAST - if self.catchbreak != False and installHandler is not None: - usage['catchbreak'] = CATCHBREAK - if self.buffer != False: - usage['buffer'] = BUFFEROUTPUT - print(self.USAGE % usage) - sys.exit(2) - - def parseArgs(self, argv): - if len(argv) > 1 and argv[1].lower() == 'discover': - self._do_discovery(argv[2:]) - return - - import getopt - long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer'] - try: - options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts) - for opt, value in options: - if opt in ('-h','-H','--help'): - self.usageExit() - if opt in ('-q','--quiet'): - self.verbosity = 0 - if opt in ('-v','--verbose'): - self.verbosity = 2 - if opt in ('-f','--failfast'): - if self.failfast is None: - self.failfast = True - # Should this raise an exception if -f is not valid? - if opt in ('-c','--catch'): - if self.catchbreak is None and installHandler is not None: - self.catchbreak = True - # Should this raise an exception if -c is not valid? - if opt in ('-b','--buffer'): - if self.buffer is None: - self.buffer = True - # Should this raise an exception if -b is not valid? - if len(args) == 0 and self.defaultTest is None: - # createTests will load tests from self.module - self.testNames = None - elif len(args) > 0: - self.testNames = args - if __name__ == '__main__': - # to support python -m unittest ... - self.module = None - else: - self.testNames = (self.defaultTest,) - self.createTests() - except getopt.error as msg: - self.usageExit(msg) - - def createTests(self): - if self.testNames is None: - self.test = self.testLoader.loadTestsFromModule(self.module) - else: - self.test = self.testLoader.loadTestsFromNames(self.testNames, - self.module) - - def _do_discovery(self, argv, Loader=loader.TestLoader): - # handle command line args for test discovery - self.progName = '%s discover' % self.progName - import optparse - parser = optparse.OptionParser() - parser.prog = self.progName - parser.add_option('-v', '--verbose', dest='verbose', default=False, - help='Verbose output', action='store_true') - if self.failfast != False: - parser.add_option('-f', '--failfast', dest='failfast', default=False, - help='Stop on first fail or error', - action='store_true') - if self.catchbreak != False and installHandler is not None: - parser.add_option('-c', '--catch', dest='catchbreak', default=False, - help='Catch ctrl-C and display results so far', - action='store_true') - if self.buffer != False: - parser.add_option('-b', '--buffer', dest='buffer', default=False, - help='Buffer stdout and stderr during tests', - action='store_true') - parser.add_option('-s', '--start-directory', dest='start', default='.', - help="Directory to start discovery ('.' default)") - parser.add_option('-p', '--pattern', dest='pattern', default='test*.py', - help="Pattern to match tests ('test*.py' default)") - parser.add_option('-t', '--top-level-directory', dest='top', default=None, - help='Top level directory of project (defaults to start directory)') - - options, args = parser.parse_args(argv) - if len(args) > 3: - self.usageExit() - - for name, value in zip(('start', 'pattern', 'top'), args): - setattr(options, name, value) - - # only set options from the parsing here - # if they weren't set explicitly in the constructor - if self.failfast is None: - self.failfast = options.failfast - if self.catchbreak is None and installHandler is not None: - self.catchbreak = options.catchbreak - if self.buffer is None: - self.buffer = options.buffer - - if options.verbose: - self.verbosity = 2 - - start_dir = options.start - pattern = options.pattern - top_level_dir = options.top - - loader = Loader() - self.test = loader.discover(start_dir, pattern, top_level_dir) - - def runTests(self): - if self.catchbreak: - installHandler() - if self.testRunner is None: - self.testRunner = runner.TextTestRunner - if isinstance(self.testRunner, (type, types.ClassType)): - try: - testRunner = self.testRunner(verbosity=self.verbosity, - failfast=self.failfast, - buffer=self.buffer) - except TypeError: - # didn't accept the verbosity, buffer or failfast arguments - testRunner = self.testRunner() - else: - # it is assumed to be a TestRunner instance - testRunner = self.testRunner - self.result = testRunner.run(self.test) - if self.exit: - sys.exit(not self.result.wasSuccessful()) - -main = TestProgram - -def main_(): - TestProgram.USAGE = USAGE_AS_MAIN - main(module=None) - diff --git a/django/utils/unittest/result.py b/django/utils/unittest/result.py deleted file mode 100644 index 2d2a1ada95..0000000000 --- a/django/utils/unittest/result.py +++ /dev/null @@ -1,183 +0,0 @@ -"""Test result object""" - -import sys -import traceback -import unittest - -from StringIO import StringIO - -from django.utils.unittest import util -from django.utils.unittest.compatibility import wraps - -__unittest = True - -def failfast(method): - @wraps(method) - def inner(self, *args, **kw): - if getattr(self, 'failfast', False): - self.stop() - return method(self, *args, **kw) - return inner - - -STDOUT_LINE = '\nStdout:\n%s' -STDERR_LINE = '\nStderr:\n%s' - -class TestResult(unittest.TestResult): - """Holder for test result information. - - Test results are automatically managed by the TestCase and TestSuite - classes, and do not need to be explicitly manipulated by writers of tests. - - Each instance holds the total number of tests run, and collections of - failures and errors that occurred among those test runs. The collections - contain tuples of (testcase, exceptioninfo), where exceptioninfo is the - formatted traceback of the error that occurred. - """ - _previousTestClass = None - _moduleSetUpFailed = False - - def __init__(self): - self.failfast = False - self.failures = [] - self.errors = [] - self.testsRun = 0 - self.skipped = [] - self.expectedFailures = [] - self.unexpectedSuccesses = [] - self.shouldStop = False - self.buffer = False - self._stdout_buffer = None - self._stderr_buffer = None - self._original_stdout = sys.stdout - self._original_stderr = sys.stderr - self._mirrorOutput = False - - def startTest(self, test): - "Called when the given test is about to be run" - self.testsRun += 1 - self._mirrorOutput = False - if self.buffer: - if self._stderr_buffer is None: - self._stderr_buffer = StringIO() - self._stdout_buffer = StringIO() - sys.stdout = self._stdout_buffer - sys.stderr = self._stderr_buffer - - def startTestRun(self): - """Called once before any tests are executed. - - See startTest for a method called before each test. - """ - - def stopTest(self, test): - """Called when the given test has been run""" - if self.buffer: - if self._mirrorOutput: - output = sys.stdout.getvalue() - error = sys.stderr.getvalue() - if output: - if not output.endswith('\n'): - output += '\n' - self._original_stdout.write(STDOUT_LINE % output) - if error: - if not error.endswith('\n'): - error += '\n' - self._original_stderr.write(STDERR_LINE % error) - - sys.stdout = self._original_stdout - sys.stderr = self._original_stderr - self._stdout_buffer.seek(0) - self._stdout_buffer.truncate() - self._stderr_buffer.seek(0) - self._stderr_buffer.truncate() - self._mirrorOutput = False - - - def stopTestRun(self): - """Called once after all tests are executed. - - See stopTest for a method called after each test. - """ - - @failfast - def addError(self, test, err): - """Called when an error has occurred. 'err' is a tuple of values as - returned by sys.exc_info(). - """ - self.errors.append((test, self._exc_info_to_string(err, test))) - self._mirrorOutput = True - - @failfast - def addFailure(self, test, err): - """Called when an error has occurred. 'err' is a tuple of values as - returned by sys.exc_info().""" - self.failures.append((test, self._exc_info_to_string(err, test))) - self._mirrorOutput = True - - def addSuccess(self, test): - "Called when a test has completed successfully" - pass - - def addSkip(self, test, reason): - """Called when a test is skipped.""" - self.skipped.append((test, reason)) - - def addExpectedFailure(self, test, err): - """Called when an expected failure/error occured.""" - self.expectedFailures.append( - (test, self._exc_info_to_string(err, test))) - - @failfast - def addUnexpectedSuccess(self, test): - """Called when a test was expected to fail, but succeed.""" - self.unexpectedSuccesses.append(test) - - def wasSuccessful(self): - "Tells whether or not this result was a success" - return (len(self.failures) + len(self.errors) == 0) - - def stop(self): - "Indicates that the tests should be aborted" - self.shouldStop = True - - def _exc_info_to_string(self, err, test): - """Converts a sys.exc_info()-style tuple of values into a string.""" - exctype, value, tb = err - # Skip test runner traceback levels - while tb and self._is_relevant_tb_level(tb): - tb = tb.tb_next - if exctype is test.failureException: - # Skip assert*() traceback levels - length = self._count_relevant_tb_levels(tb) - msgLines = traceback.format_exception(exctype, value, tb, length) - else: - msgLines = traceback.format_exception(exctype, value, tb) - - if self.buffer: - output = sys.stdout.getvalue() - error = sys.stderr.getvalue() - if output: - if not output.endswith('\n'): - output += '\n' - msgLines.append(STDOUT_LINE % output) - if error: - if not error.endswith('\n'): - error += '\n' - msgLines.append(STDERR_LINE % error) - return ''.join(msgLines) - - def _is_relevant_tb_level(self, tb): - return '__unittest' in tb.tb_frame.f_globals - - def _count_relevant_tb_levels(self, tb): - length = 0 - while tb and not self._is_relevant_tb_level(tb): - length += 1 - tb = tb.tb_next - return length - - def __repr__(self): - return "<%s run=%i errors=%i failures=%i>" % \ - (util.strclass(self.__class__), self.testsRun, len(self.errors), - len(self.failures)) diff --git a/django/utils/unittest/runner.py b/django/utils/unittest/runner.py deleted file mode 100644 index 242173ee31..0000000000 --- a/django/utils/unittest/runner.py +++ /dev/null @@ -1,206 +0,0 @@ -"""Running tests""" - -import sys -import time -import unittest - -from django.utils.unittest import result - -try: - from django.utils.unittest.signals import registerResult -except ImportError: - def registerResult(_): - pass - -__unittest = True - - -class _WritelnDecorator(object): - """Used to decorate file-like objects with a handy 'writeln' method""" - def __init__(self,stream): - self.stream = stream - - def __getattr__(self, attr): - if attr in ('stream', '__getstate__'): - raise AttributeError(attr) - return getattr(self.stream,attr) - - def writeln(self, arg=None): - if arg: - self.write(arg) - self.write('\n') # text-mode streams translate to \r\n if needed - - -class TextTestResult(result.TestResult): - """A test result class that can print formatted text results to a stream. - - Used by TextTestRunner. - """ - separator1 = '=' * 70 - separator2 = '-' * 70 - - def __init__(self, stream, descriptions, verbosity): - super(TextTestResult, self).__init__() - self.stream = stream - self.showAll = verbosity > 1 - self.dots = verbosity == 1 - self.descriptions = descriptions - - def getDescription(self, test): - doc_first_line = test.shortDescription() - if self.descriptions and doc_first_line: - return '\n'.join((str(test), doc_first_line)) - else: - return str(test) - - def startTest(self, test): - super(TextTestResult, self).startTest(test) - if self.showAll: - self.stream.write(self.getDescription(test)) - self.stream.write(" ... ") - self.stream.flush() - - def addSuccess(self, test): - super(TextTestResult, self).addSuccess(test) - if self.showAll: - self.stream.writeln("ok") - elif self.dots: - self.stream.write('.') - self.stream.flush() - - def addError(self, test, err): - super(TextTestResult, self).addError(test, err) - if self.showAll: - self.stream.writeln("ERROR") - elif self.dots: - self.stream.write('E') - self.stream.flush() - - def addFailure(self, test, err): - super(TextTestResult, self).addFailure(test, err) - if self.showAll: - self.stream.writeln("FAIL") - elif self.dots: - self.stream.write('F') - self.stream.flush() - - def addSkip(self, test, reason): - super(TextTestResult, self).addSkip(test, reason) - if self.showAll: - self.stream.writeln("skipped %r" % (reason,)) - elif self.dots: - self.stream.write("s") - self.stream.flush() - - def addExpectedFailure(self, test, err): - super(TextTestResult, self).addExpectedFailure(test, err) - if self.showAll: - self.stream.writeln("expected failure") - elif self.dots: - self.stream.write("x") - self.stream.flush() - - def addUnexpectedSuccess(self, test): - super(TextTestResult, self).addUnexpectedSuccess(test) - if self.showAll: - self.stream.writeln("unexpected success") - elif self.dots: - self.stream.write("u") - self.stream.flush() - - def printErrors(self): - if self.dots or self.showAll: - self.stream.writeln() - self.printErrorList('ERROR', self.errors) - self.printErrorList('FAIL', self.failures) - - def printErrorList(self, flavour, errors): - for test, err in errors: - self.stream.writeln(self.separator1) - self.stream.writeln("%s: %s" % (flavour, self.getDescription(test))) - self.stream.writeln(self.separator2) - self.stream.writeln("%s" % err) - - def stopTestRun(self): - super(TextTestResult, self).stopTestRun() - self.printErrors() - - -class TextTestRunner(unittest.TextTestRunner): - """A test runner class that displays results in textual form. - - It prints out the names of tests as they are run, errors as they - occur, and a summary of the results at the end of the test run. - """ - resultclass = TextTestResult - - def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1, - failfast=False, buffer=False, resultclass=None): - self.stream = _WritelnDecorator(stream) - self.descriptions = descriptions - self.verbosity = verbosity - self.failfast = failfast - self.buffer = buffer - if resultclass is not None: - self.resultclass = resultclass - - def _makeResult(self): - return self.resultclass(self.stream, self.descriptions, self.verbosity) - - def run(self, test): - "Run the given test case or test suite." - result = self._makeResult() - result.failfast = self.failfast - result.buffer = self.buffer - registerResult(result) - - startTime = time.time() - startTestRun = getattr(result, 'startTestRun', None) - if startTestRun is not None: - startTestRun() - try: - test(result) - finally: - stopTestRun = getattr(result, 'stopTestRun', None) - if stopTestRun is not None: - stopTestRun() - else: - result.printErrors() - stopTime = time.time() - timeTaken = stopTime - startTime - if hasattr(result, 'separator2'): - self.stream.writeln(result.separator2) - run = result.testsRun - self.stream.writeln("Ran %d test%s in %.3fs" % - (run, run != 1 and "s" or "", timeTaken)) - self.stream.writeln() - - expectedFails = unexpectedSuccesses = skipped = 0 - try: - results = map(len, (result.expectedFailures, - result.unexpectedSuccesses, - result.skipped)) - expectedFails, unexpectedSuccesses, skipped = results - except AttributeError: - pass - infos = [] - if not result.wasSuccessful(): - self.stream.write("FAILED") - failed, errored = map(len, (result.failures, result.errors)) - if failed: - infos.append("failures=%d" % failed) - if errored: - infos.append("errors=%d" % errored) - else: - self.stream.write("OK") - if skipped: - infos.append("skipped=%d" % skipped) - if expectedFails: - infos.append("expected failures=%d" % expectedFails) - if unexpectedSuccesses: - infos.append("unexpected successes=%d" % unexpectedSuccesses) - if infos: - self.stream.writeln(" (%s)" % (", ".join(infos),)) - else: - self.stream.write("\n") - return result diff --git a/django/utils/unittest/signals.py b/django/utils/unittest/signals.py deleted file mode 100644 index f1731ea13d..0000000000 --- a/django/utils/unittest/signals.py +++ /dev/null @@ -1,57 +0,0 @@ -import signal -import weakref - -from django.utils.unittest.compatibility import wraps - -__unittest = True - - -class _InterruptHandler(object): - def __init__(self, default_handler): - self.called = False - self.default_handler = default_handler - - def __call__(self, signum, frame): - installed_handler = signal.getsignal(signal.SIGINT) - if installed_handler is not self: - # if we aren't the installed handler, then delegate immediately - # to the default handler - self.default_handler(signum, frame) - - if self.called: - self.default_handler(signum, frame) - self.called = True - for result in _results.keys(): - result.stop() - -_results = weakref.WeakKeyDictionary() -def registerResult(result): - _results[result] = 1 - -def removeResult(result): - return bool(_results.pop(result, None)) - -_interrupt_handler = None -def installHandler(): - global _interrupt_handler - if _interrupt_handler is None: - default_handler = signal.getsignal(signal.SIGINT) - _interrupt_handler = _InterruptHandler(default_handler) - signal.signal(signal.SIGINT, _interrupt_handler) - - -def removeHandler(method=None): - if method is not None: - @wraps(method) - def inner(*args, **kwargs): - initial = signal.getsignal(signal.SIGINT) - removeHandler() - try: - return method(*args, **kwargs) - finally: - signal.signal(signal.SIGINT, initial) - return inner - - global _interrupt_handler - if _interrupt_handler is not None: - signal.signal(signal.SIGINT, _interrupt_handler.default_handler) diff --git a/django/utils/unittest/suite.py b/django/utils/unittest/suite.py deleted file mode 100644 index da8ac2e2a8..0000000000 --- a/django/utils/unittest/suite.py +++ /dev/null @@ -1,287 +0,0 @@ -"""TestSuite""" - -import sys -import unittest -from django.utils.unittest import case, util - -__unittest = True - - -class BaseTestSuite(unittest.TestSuite): - """A simple test suite that doesn't provide class or module shared fixtures. - """ - def __init__(self, tests=()): - self._tests = [] - self.addTests(tests) - - def __repr__(self): - return "<%s tests=%s>" % (util.strclass(self.__class__), list(self)) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return list(self) == list(other) - - def __ne__(self, other): - return not self == other - - # Can't guarantee hash invariant, so flag as unhashable - __hash__ = None - - def __iter__(self): - return iter(self._tests) - - def countTestCases(self): - cases = 0 - for test in self: - cases += test.countTestCases() - return cases - - def addTest(self, test): - # sanity checks - if not hasattr(test, '__call__'): - raise TypeError("%r is not callable" % (repr(test),)) - if isinstance(test, type) and issubclass(test, - (case.TestCase, TestSuite)): - raise TypeError("TestCases and TestSuites must be instantiated " - "before passing them to addTest()") - self._tests.append(test) - - def addTests(self, tests): - if isinstance(tests, basestring): - raise TypeError("tests must be an iterable of tests, not a string") - for test in tests: - self.addTest(test) - - def run(self, result): - for test in self: - if result.shouldStop: - break - test(result) - return result - - def __call__(self, *args, **kwds): - return self.run(*args, **kwds) - - def debug(self): - """Run the tests without collecting errors in a TestResult""" - for test in self: - test.debug() - - -class TestSuite(BaseTestSuite): - """A test suite is a composite test consisting of a number of TestCases. - - For use, create an instance of TestSuite, then add test case instances. - When all tests have been added, the suite can be passed to a test - runner, such as TextTestRunner. It will run the individual test cases - in the order in which they were added, aggregating the results. When - subclassing, do not forget to call the base class constructor. - """ - - - def run(self, result): - self._wrapped_run(result) - self._tearDownPreviousClass(None, result) - self._handleModuleTearDown(result) - return result - - def debug(self): - """Run the tests without collecting errors in a TestResult""" - debug = _DebugResult() - self._wrapped_run(debug, True) - self._tearDownPreviousClass(None, debug) - self._handleModuleTearDown(debug) - - ################################ - # private methods - def _wrapped_run(self, result, debug=False): - for test in self: - if result.shouldStop: - break - - if _isnotsuite(test): - self._tearDownPreviousClass(test, result) - self._handleModuleFixture(test, result) - self._handleClassSetUp(test, result) - result._previousTestClass = test.__class__ - - if (getattr(test.__class__, '_classSetupFailed', False) or - getattr(result, '_moduleSetUpFailed', False)): - continue - - if hasattr(test, '_wrapped_run'): - test._wrapped_run(result, debug) - elif not debug: - test(result) - else: - test.debug() - - def _handleClassSetUp(self, test, result): - previousClass = getattr(result, '_previousTestClass', None) - currentClass = test.__class__ - if currentClass == previousClass: - return - if result._moduleSetUpFailed: - return - if getattr(currentClass, "__unittest_skip__", False): - return - - try: - currentClass._classSetupFailed = False - except TypeError: - # test may actually be a function - # so its class will be a builtin-type - pass - - setUpClass = getattr(currentClass, 'setUpClass', None) - if setUpClass is not None: - try: - setUpClass() - except Exception as e: - if isinstance(result, _DebugResult): - raise - currentClass._classSetupFailed = True - className = util.strclass(currentClass) - errorName = 'setUpClass (%s)' % className - self._addClassOrModuleLevelException(result, e, errorName) - - def _get_previous_module(self, result): - previousModule = None - previousClass = getattr(result, '_previousTestClass', None) - if previousClass is not None: - previousModule = previousClass.__module__ - return previousModule - - - def _handleModuleFixture(self, test, result): - previousModule = self._get_previous_module(result) - currentModule = test.__class__.__module__ - if currentModule == previousModule: - return - - self._handleModuleTearDown(result) - - - result._moduleSetUpFailed = False - try: - module = sys.modules[currentModule] - except KeyError: - return - setUpModule = getattr(module, 'setUpModule', None) - if setUpModule is not None: - try: - setUpModule() - except Exception as e: - if isinstance(result, _DebugResult): - raise - result._moduleSetUpFailed = True - errorName = 'setUpModule (%s)' % currentModule - self._addClassOrModuleLevelException(result, e, errorName) - - def _addClassOrModuleLevelException(self, result, exception, errorName): - error = _ErrorHolder(errorName) - addSkip = getattr(result, 'addSkip', None) - if addSkip is not None and isinstance(exception, case.SkipTest): - addSkip(error, str(exception)) - else: - result.addError(error, sys.exc_info()) - - def _handleModuleTearDown(self, result): - previousModule = self._get_previous_module(result) - if previousModule is None: - return - if result._moduleSetUpFailed: - return - - try: - module = sys.modules[previousModule] - except KeyError: - return - - tearDownModule = getattr(module, 'tearDownModule', None) - if tearDownModule is not None: - try: - tearDownModule() - except Exception as e: - if isinstance(result, _DebugResult): - raise - errorName = 'tearDownModule (%s)' % previousModule - self._addClassOrModuleLevelException(result, e, errorName) - - def _tearDownPreviousClass(self, test, result): - previousClass = getattr(result, '_previousTestClass', None) - currentClass = test.__class__ - if currentClass == previousClass: - return - if getattr(previousClass, '_classSetupFailed', False): - return - if getattr(result, '_moduleSetUpFailed', False): - return - if getattr(previousClass, "__unittest_skip__", False): - return - - tearDownClass = getattr(previousClass, 'tearDownClass', None) - if tearDownClass is not None: - try: - tearDownClass() - except Exception as e: - if isinstance(result, _DebugResult): - raise - className = util.strclass(previousClass) - errorName = 'tearDownClass (%s)' % className - self._addClassOrModuleLevelException(result, e, errorName) - - -class _ErrorHolder(object): - """ - Placeholder for a TestCase inside a result. As far as a TestResult - is concerned, this looks exactly like a unit test. Used to insert - arbitrary errors into a test suite run. - """ - # Inspired by the ErrorHolder from Twisted: - # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py - - # attribute used by TestResult._exc_info_to_string - failureException = None - - def __init__(self, description): - self.description = description - - def id(self): - return self.description - - def shortDescription(self): - return None - - def __repr__(self): - return "" % (self.description,) - - def __str__(self): - return self.id() - - def run(self, result): - # could call result.addError(...) - but this test-like object - # shouldn't be run anyway - pass - - def __call__(self, result): - return self.run(result) - - def countTestCases(self): - return 0 - -def _isnotsuite(test): - "A crude way to tell apart testcases and suites with duck-typing" - try: - iter(test) - except TypeError: - return True - return False - - -class _DebugResult(object): - "Used by the TestSuite to hold previous class when running in debug." - _previousTestClass = None - _moduleSetUpFailed = False - shouldStop = False diff --git a/django/utils/unittest/util.py b/django/utils/unittest/util.py deleted file mode 100644 index c45d008cc8..0000000000 --- a/django/utils/unittest/util.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Various utility functions.""" - -__unittest = True - - -_MAX_LENGTH = 80 -def safe_repr(obj, short=False): - try: - result = repr(obj) - except Exception: - result = object.__repr__(obj) - if not short or len(result) < _MAX_LENGTH: - return result - return result[:_MAX_LENGTH] + ' [truncated]...' - -def safe_str(obj): - try: - return str(obj) - except Exception: - return object.__str__(obj) - -def strclass(cls): - return "%s.%s" % (cls.__module__, cls.__name__) - -def sorted_list_difference(expected, actual): - """Finds elements in only one or the other of two, sorted input lists. - - Returns a two-element tuple of lists. The first list contains those - elements in the "expected" list but not in the "actual" list, and the - second contains those elements in the "actual" list but not in the - "expected" list. Duplicate elements in either input list are ignored. - """ - i = j = 0 - missing = [] - unexpected = [] - while True: - try: - e = expected[i] - a = actual[j] - if e < a: - missing.append(e) - i += 1 - while expected[i] == e: - i += 1 - elif e > a: - unexpected.append(a) - j += 1 - while actual[j] == a: - j += 1 - else: - i += 1 - try: - while expected[i] == e: - i += 1 - finally: - j += 1 - while actual[j] == a: - j += 1 - except IndexError: - missing.extend(expected[i:]) - unexpected.extend(actual[j:]) - break - return missing, unexpected - -def unorderable_list_difference(expected, actual, ignore_duplicate=False): - """Same behavior as sorted_list_difference but - for lists of unorderable items (like dicts). - - As it does a linear search per item (remove) it - has O(n*n) performance. - """ - missing = [] - unexpected = [] - while expected: - item = expected.pop() - try: - actual.remove(item) - except ValueError: - missing.append(item) - if ignore_duplicate: - for lst in expected, actual: - try: - while True: - lst.remove(item) - except ValueError: - pass - if ignore_duplicate: - while actual: - item = actual.pop() - unexpected.append(item) - try: - while True: - actual.remove(item) - except ValueError: - pass - return missing, unexpected - - # anything left in actual is unexpected - return missing, actual diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index ee38df12a1..7503506ecc 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -407,6 +407,11 @@ these changes. * The ``Model._meta.get_(add|change|delete)_permission`` methods will be removed. +1.9 +--- + +* ``django.utils.unittest`` will be removed. + 2.0 --- diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt new file mode 100644 index 0000000000..2707b2896e --- /dev/null +++ b/docs/releases/1.7.txt @@ -0,0 +1,41 @@ +============================================ +Django 1.7 release notes - UNDER DEVELOPMENT +============================================ + +Welcome to Django 1.7! + +These release notes cover the `new features`_, as well as some `backwards +incompatible changes`_ you'll want to be aware of when upgrading from Django +1.6 or older versions. We've also dropped some features, which are detailed in +:doc:`our deprecation plan `, and we've `begun the +deprecation process for some features`_. + +.. _`new features`: `What's new in Django 1.7`_ +.. _`backwards incompatible changes`: `Backwards incompatible changes in 1.7`_ +.. _`begun the deprecation process for some features`: `Features deprecated in 1.7`_ + +What's new in Django 1.7 +======================== + +Backwards incompatible changes in 1.7 +===================================== + +.. warning:: + + In addition to the changes outlined in this section, be sure to review the + :doc:`deprecation plan ` for any features that + have been removed. If you haven't updated your code within the + deprecation timeline for a given feature, its removal may appear as a + backwards incompatible change. + +Features deprecated in 1.7 +========================== + +``django.utils.unittest`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +``django.utils.unittest`` provided uniform access to the ``unittest2`` library +on all Python versions. Since ``unittest2`` became the standard library's +:mod:`unittest` module in Python 2.7, and Django 1.7 drops support for older +Python versions, this module isn't useful anymore. It has been deprecated. Use +:mod:`unittest` instead. diff --git a/docs/topics/testing/_images/django_unittest_classes_hierarchy.graffle b/docs/topics/testing/_images/django_unittest_classes_hierarchy.graffle index 7211c0f3be..c0edad721d 100644 --- a/docs/topics/testing/_images/django_unittest_classes_hierarchy.graffle +++ b/docs/topics/testing/_images/django_unittest_classes_hierarchy.graffle @@ -7,14 +7,14 @@ ApplicationVersion com.omnigroup.OmniGrafflePro - 139.16.0.171715 + 139.15.0.171074 AutoAdjust BackgroundGraphic Bounds - {{0, 0}, {559.28997802734375, 782.8900146484375}} + {{0, 0}, {559, 783}} Class SolidGraphic ID @@ -96,53 +96,6 @@ 2 - - Class - LineGraphic - Head - - ID - 12 - Info - 1 - - ID - 27 - OrthogonalBarAutomatic - - OrthogonalBarPoint - {0, 0} - OrthogonalBarPosition - -1 - Points - - {135, 270} - {369, 225} - - Style - - stroke - - HeadArrow - UMLInheritance - HeadScale - 0.79999995231628418 - Legacy - - LineType - 2 - TailArrow - 0 - - - Tail - - ID - 26 - Position - 0.5 - - Class LineGraphic @@ -162,7 +115,7 @@ Points {135, 315} - {135, 225} + {135, 261} Style @@ -274,144 +227,7 @@ Bounds - {{378, 252}, {81, 27}} - Class - ShapedGraphic - FontInfo - - Font - Helvetica - Size - 12 - - ID - 22 - Shape - NoteShape - Style - - stroke - - Color - - b - 0 - g - 0.501961 - r - 0 - - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;\red0\green128\blue0;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\i\fs24 \cf2 Python < 2.7} - VerticalPad - 0 - - TextRelativeArea - {{0, 0}, {1, 1}} - - - Bounds - {{45, 252}, {81, 27}} - Class - ShapedGraphic - FontInfo - - Font - Helvetica - Size - 12 - - ID - 20 - Shape - NoteShape - Style - - stroke - - Color - - b - 0 - g - 0.501961 - r - 0 - - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;\red0\green128\blue0;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\i\fs24 \cf2 Python \uc0\u8805 2.7} - VerticalPad - 0 - - - - Bounds - {{288, 198}, {162, 27}} - Class - ShapedGraphic - ID - 12 - Magnets - - {0, 1} - {0, -1} - {1, 0} - {-1, 0} - - Shape - Rectangle - Style - - fill - - FillType - 2 - GradientAngle - 90 - GradientColor - - w - 0.666667 - - - stroke - - CornerRadius - 5 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 TestCase} - - - - Bounds - {{54, 198}, {162, 27}} + {{54, 234}, {162, 27}} Class ShapedGraphic ID @@ -448,7 +264,7 @@ Text Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 + {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc @@ -495,7 +311,7 @@ Text Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 + {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc @@ -542,7 +358,7 @@ Text Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 + {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc @@ -589,7 +405,7 @@ Text Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 + {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc @@ -636,7 +452,7 @@ Text Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 + {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc @@ -660,7 +476,7 @@ Align 2 Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 + {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr @@ -672,7 +488,7 @@ Bounds - {{18, 153}, {225, 90}} + {{18, 216}, {468, 63}} Class ShapedGraphic ID @@ -686,40 +502,16 @@ Align 2 Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} + {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier-Oblique;\f1\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr -\f0\fs24 \cf0 django.utils.unittest\ -= unittest (standard library)} - - TextPlacement - 0 - - - Bounds - {{261, 153}, {225, 90}} - Class - ShapedGraphic - ID - 19 - Shape - Rectangle - Style - - Text - - Align - 2 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr +\f0\i\fs24 \cf0 standard library\ -\f0\fs24 \cf0 django.utils.unittest\ -= unittest2 (bundled copy)} +\f1\i0 \ +\ +unittest} TextPlacement 0 @@ -777,7 +569,7 @@ MasterSheets ModificationDate - 2012-12-16 19:08:28 +0000 + 2013-07-01 11:48:06 +0000 Modifier Aymeric Augustin NotesVisible @@ -808,7 +600,7 @@ NSPaperSize size - {595.28997802734375, 841.8900146484375} + {595, 842} NSPrintReverseOrientation @@ -853,7 +645,7 @@ ExpandedCanvases Frame - {{9, 4}, {694, 874}} + {{613, 284}, {694, 774}} ListView OutlineWidth @@ -867,7 +659,7 @@ SidebarWidth 120 VisibleRegion - {{0, 0}, {559, 735}} + {{0, 0}, {545, 620}} Zoom 1 ZoomValues diff --git a/docs/topics/testing/_images/django_unittest_classes_hierarchy.pdf b/docs/topics/testing/_images/django_unittest_classes_hierarchy.pdf index cedaba22ac7f5e26690d43ddf9aa081c2c28eedb..0835aec9ba8c5a18f8c711bd2334e53071a8464a 100644 GIT binary patch delta 17565 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 delta 27782 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 -2012-12-16 19:08ZCanevas 1Calque 1django.utils.unittest= unittest2 (bundled copy)django.utils.unittest= unittest (standard library)django.testLiveServerTestCaseTestCaseTransactionTestCaseSimpleTestCaseTestCaseTestCasePython ≥ 2.7Python < 2.7 +2013-07-01 11:48ZCanevas 1Calque 1standard libraryunittestdjango.testLiveServerTestCaseTestCaseTransactionTestCaseSimpleTestCaseTestCase diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index d56b1be20f..9cf805e371 100644 --- a/docs/topics/testing/overview.txt +++ b/docs/topics/testing/overview.txt @@ -21,27 +21,15 @@ module defines tests using a class-based approach. .. admonition:: unittest2 + .. deprecated:: 1.7 + Python 2.7 introduced some major changes to the ``unittest`` library, - adding some extremely useful features. To ensure that every Django - project can benefit from these new features, Django ships with a - copy of unittest2_, a copy of Python 2.7's ``unittest``, backported for - Python 2.6 compatibility. + adding some extremely useful features. To ensure that every Django project + could benefit from these new features, Django used to ship with a copy of + Python 2.7's ``unittest`` backported for Python 2.6 compatibility. - To access this library, Django provides the - ``django.utils.unittest`` module alias. If you are using Python - 2.7, or you have installed ``unittest2`` locally, Django will map the alias - to it. Otherwise, Django will use its own bundled version of ``unittest2``. - - To use this alias, simply use:: - - from django.utils import unittest - - wherever you would have historically used:: - - import unittest - - If you want to continue to use the legacy ``unittest`` library, you can -- - you just won't get any of the nice new ``unittest2`` features. + Since Django no longer supports Python versions older than 2.7, + ``django.utils.unittest`` is deprecated. Simply use ``unittest``. .. _unittest2: http://pypi.python.org/pypi/unittest2 @@ -849,13 +837,10 @@ Normal Python unit test classes extend a base class of .. figure:: _images/django_unittest_classes_hierarchy.* :alt: Hierarchy of Django unit testing classes (TestCase subclasses) :width: 508 - :height: 391 + :height: 328 Hierarchy of Django unit testing classes -Regardless of the version of Python you're using, if you've installed -``unittest2``, ``django.utils.unittest`` will point to that library. - SimpleTestCase ~~~~~~~~~~~~~~ @@ -905,7 +890,7 @@ features like: then you should use :class:`~django.test.TransactionTestCase` or :class:`~django.test.TestCase` instead. -``SimpleTestCase`` inherits from ``django.utils.unittest.TestCase``. +``SimpleTestCase`` inherits from ``unittest.TestCase``. TransactionTestCase ~~~~~~~~~~~~~~~~~~~ From cfcf4b3605f9653e4e056088d89932b2a0e4281b Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 1 Jul 2013 14:22:27 +0200 Subject: [PATCH 39/47] Stopped using django.utils.unittest in the test suite. Refs #20680. --- django/contrib/admin/tests.py | 2 +- django/contrib/admindocs/tests/test_fields.py | 3 ++- django/contrib/auth/tests/test_hashers.py | 5 +++-- django/contrib/auth/tests/test_tokens.py | 4 ++-- django/contrib/auth/tests/utils.py | 3 ++- django/contrib/formtools/tests/tests.py | 2 +- django/contrib/gis/gdal/tests/test_driver.py | 5 +++-- django/contrib/gis/gdal/tests/test_ds.py | 4 ++-- django/contrib/gis/gdal/tests/test_envelope.py | 7 ++++--- django/contrib/gis/gdal/tests/test_geom.py | 4 ++-- django/contrib/gis/gdal/tests/test_srs.py | 5 +++-- django/contrib/gis/geoip/tests.py | 5 +++-- django/contrib/gis/geos/tests/test_geos.py | 4 ++-- .../gis/geos/tests/test_geos_mutation.py | 4 ++-- django/contrib/gis/geos/tests/test_io.py | 2 +- .../contrib/gis/geos/tests/test_mutable_list.py | 4 +++- django/contrib/gis/tests/distapp/tests.py | 3 ++- django/contrib/gis/tests/geo3d/tests.py | 2 +- django/contrib/gis/tests/geoadmin/tests.py | 3 ++- django/contrib/gis/tests/geoapp/test_feeds.py | 2 +- django/contrib/gis/tests/geoapp/test_regress.py | 2 +- .../contrib/gis/tests/geoapp/test_sitemaps.py | 2 +- django/contrib/gis/tests/geoapp/tests.py | 2 +- django/contrib/gis/tests/geogapp/tests.py | 2 +- django/contrib/gis/tests/inspectapp/tests.py | 2 +- django/contrib/gis/tests/layermap/tests.py | 6 +++--- django/contrib/gis/tests/relatedapp/tests.py | 3 ++- django/contrib/gis/tests/test_geoforms.py | 7 ++++--- django/contrib/gis/tests/test_measure.py | 3 ++- django/contrib/gis/tests/test_spatialrefsys.py | 3 ++- django/contrib/humanize/tests.py | 2 +- django/contrib/messages/tests/base.py | 3 ++- .../contrib/messages/tests/test_middleware.py | 3 ++- django/contrib/sessions/tests.py | 2 +- django/contrib/sitemaps/tests/test_flatpages.py | 3 ++- django/contrib/sitemaps/tests/test_http.py | 2 +- django/test/runner.py | 4 ++-- django/test/testcases.py | 17 +++++++++-------- docs/topics/testing/advanced.txt | 2 +- docs/topics/testing/overview.txt | 4 ++-- tests/admin_scripts/tests.py | 4 ++-- tests/admin_widgets/tests.py | 2 +- tests/aggregation_regress/tests.py | 1 - tests/app_loading/tests.py | 3 ++- tests/bash_completion/tests.py | 2 +- tests/bug639/tests.py | 2 +- tests/bug8245/tests.py | 3 ++- tests/builtin_server/tests.py | 2 +- tests/db_typecasts/tests.py | 2 +- tests/decorators/tests.py | 2 +- tests/defaultfilters/tests.py | 3 ++- tests/dispatch/tests/test_dispatcher.py | 2 +- tests/dispatch/tests/test_saferef.py | 3 ++- tests/file_storage/tests.py | 2 +- tests/file_uploads/tests.py | 2 +- tests/files/tests.py | 2 +- tests/forms_tests/tests/test_validators.py | 3 ++- tests/generic_views/test_base.py | 2 +- tests/generic_views/test_dates.py | 2 +- tests/generic_views/test_edit.py | 2 +- tests/httpwrappers/tests.py | 2 +- tests/indexes/tests.py | 3 ++- tests/inspectdb/tests.py | 2 +- tests/introspection/tests.py | 3 ++- tests/invalid_models/tests.py | 3 +-- tests/logging_tests/tests.py | 2 +- tests/max_lengths/tests.py | 4 ++-- tests/middleware/tests.py | 2 +- tests/model_fields/test_imagefield.py | 2 +- tests/model_fields/tests.py | 2 +- tests/model_forms/tests.py | 2 +- tests/model_forms_regress/tests.py | 2 +- tests/model_inheritance_regress/tests.py | 4 ++-- tests/model_regress/tests.py | 2 +- tests/modeladmin/tests.py | 3 ++- tests/pagination/tests.py | 2 +- tests/queries/tests.py | 2 +- tests/requests/tests.py | 6 +++--- tests/resolve_url/tests.py | 3 ++- tests/select_for_update/tests.py | 2 +- tests/select_related_onetoone/tests.py | 3 ++- tests/serializers/tests.py | 2 +- tests/serializers_regress/tests.py | 4 ++-- tests/settings_tests/tests.py | 3 ++- tests/str/tests.py | 2 +- tests/template_tests/loaders.py | 7 ++++--- tests/template_tests/test_callables.py | 3 ++- tests/template_tests/test_context.py | 4 +++- tests/template_tests/test_custom.py | 3 ++- tests/template_tests/test_nodelist.py | 3 ++- tests/template_tests/test_parser.py | 3 ++- tests/template_tests/test_smartif.py | 3 ++- tests/template_tests/test_unicode.py | 3 ++- tests/template_tests/tests.py | 2 +- tests/test_runner/test_discover_runner.py | 2 +- tests/test_runner/tests.py | 4 ++-- tests/test_suite_override/tests.py | 3 ++- tests/test_utils/tests.py | 5 +++-- tests/timezones/tests.py | 2 +- tests/transactions/tests.py | 2 +- tests/transactions_regress/tests.py | 3 ++- tests/urlpatterns_reverse/tests.py | 4 +++- tests/utils_tests/test_archive.py | 2 +- tests/utils_tests/test_baseconv.py | 3 ++- tests/utils_tests/test_crypto.py | 4 ++-- tests/utils_tests/test_dateparse.py | 2 +- tests/utils_tests/test_encoding.py | 3 ++- tests/utils_tests/test_functional.py | 3 ++- tests/utils_tests/test_html.py | 2 +- tests/utils_tests/test_http.py | 2 +- tests/utils_tests/test_ipv6.py | 3 ++- tests/utils_tests/test_module_loading.py | 4 ++-- tests/utils_tests/test_os_utils.py | 2 +- tests/utils_tests/test_regex_helper.py | 3 ++- tests/utils_tests/test_simplelazyobject.py | 2 +- tests/utils_tests/test_termcolors.py | 3 ++- tests/utils_tests/test_timezone.py | 3 ++- tests/utils_tests/test_tzinfo.py | 3 ++- tests/validation/test_error_messages.py | 3 ++- tests/validation/test_unique.py | 2 +- tests/validators/tests.py | 4 ++-- tests/version/tests.py | 2 +- tests/view_tests/tests/test_debug.py | 2 +- 123 files changed, 212 insertions(+), 161 deletions(-) diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py index 25ea230b28..82d0bdcb7b 100644 --- a/django/contrib/admin/tests.py +++ b/django/contrib/admin/tests.py @@ -1,8 +1,8 @@ import os +from unittest import SkipTest from django.test import LiveServerTestCase from django.utils.module_loading import import_by_path -from django.utils.unittest import SkipTest from django.utils.translation import ugettext as _ diff --git a/django/contrib/admindocs/tests/test_fields.py b/django/contrib/admindocs/tests/test_fields.py index c048f20287..b505d2deeb 100644 --- a/django/contrib/admindocs/tests/test_fields.py +++ b/django/contrib/admindocs/tests/test_fields.py @@ -1,9 +1,10 @@ from __future__ import absolute_import, unicode_literals +import unittest + from django.contrib.admindocs import views from django.db import models from django.db.models import fields -from django.utils import unittest from django.utils.translation import ugettext as _ diff --git a/django/contrib/auth/tests/test_hashers.py b/django/contrib/auth/tests/test_hashers.py index 9b7811a335..8e8119b741 100644 --- a/django/contrib/auth/tests/test_hashers.py +++ b/django/contrib/auth/tests/test_hashers.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import unittest +from unittest import skipUnless + from django.conf.global_settings import PASSWORD_HASHERS as default_hashers from django.contrib.auth.hashers import (is_password_usable, BasePasswordHasher, check_password, make_password, PBKDF2PasswordHasher, load_hashers, PBKDF2SHA1PasswordHasher, get_hasher, identify_hasher, UNUSABLE_PASSWORD_PREFIX, UNUSABLE_PASSWORD_SUFFIX_LENGTH) from django.utils import six -from django.utils import unittest -from django.utils.unittest import skipUnless try: diff --git a/django/contrib/auth/tests/test_tokens.py b/django/contrib/auth/tests/test_tokens.py index e8aeb46326..7afafc3e94 100644 --- a/django/contrib/auth/tests/test_tokens.py +++ b/django/contrib/auth/tests/test_tokens.py @@ -1,12 +1,12 @@ -import sys from datetime import date, timedelta +import sys +import unittest from django.conf import settings from django.contrib.auth.models import User from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.auth.tests.utils import skipIfCustomUser from django.test import TestCase -from django.utils import unittest @skipIfCustomUser diff --git a/django/contrib/auth/tests/utils.py b/django/contrib/auth/tests/utils.py index 6bb3d9994f..09b34156b5 100644 --- a/django/contrib/auth/tests/utils.py +++ b/django/contrib/auth/tests/utils.py @@ -1,5 +1,6 @@ +from unittest import skipIf + from django.conf import settings -from django.utils.unittest import skipIf def skipIfCustomUser(test_func): diff --git a/django/contrib/formtools/tests/tests.py b/django/contrib/formtools/tests/tests.py index 58c59f4041..e1e6ab860b 100644 --- a/django/contrib/formtools/tests/tests.py +++ b/django/contrib/formtools/tests/tests.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import datetime import os +import unittest import warnings from django import http @@ -10,7 +11,6 @@ from django.contrib.formtools import preview, utils from django.test import TestCase from django.test.utils import override_settings from django.utils._os import upath -from django.utils import unittest from django.contrib.formtools.tests.forms import * diff --git a/django/contrib/gis/gdal/tests/test_driver.py b/django/contrib/gis/gdal/tests/test_driver.py index c27302da72..af0ecf5dc8 100644 --- a/django/contrib/gis/gdal/tests/test_driver.py +++ b/django/contrib/gis/gdal/tests/test_driver.py @@ -1,6 +1,7 @@ +import unittest +from unittest import skipUnless + from django.contrib.gis.gdal import HAS_GDAL -from django.utils import unittest -from django.utils.unittest import skipUnless if HAS_GDAL: from django.contrib.gis.gdal import Driver, OGRException diff --git a/django/contrib/gis/gdal/tests/test_ds.py b/django/contrib/gis/gdal/tests/test_ds.py index 3664f055f2..3ac2370fb7 100644 --- a/django/contrib/gis/gdal/tests/test_ds.py +++ b/django/contrib/gis/gdal/tests/test_ds.py @@ -1,9 +1,9 @@ import os +import unittest +from unittest import skipUnless from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geometry.test_data import get_ds_file, TestDS, TEST_DATA -from django.utils import unittest -from django.utils.unittest import skipUnless if HAS_GDAL: from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError, GDAL_VERSION diff --git a/django/contrib/gis/gdal/tests/test_envelope.py b/django/contrib/gis/gdal/tests/test_envelope.py index 7518dc69aa..14258ff816 100644 --- a/django/contrib/gis/gdal/tests/test_envelope.py +++ b/django/contrib/gis/gdal/tests/test_envelope.py @@ -1,6 +1,7 @@ +import unittest +from unittest import skipUnless + from django.contrib.gis.gdal import HAS_GDAL -from django.utils import unittest -from django.utils.unittest import skipUnless if HAS_GDAL: from django.contrib.gis.gdal import Envelope, OGRException @@ -17,7 +18,7 @@ class EnvelopeTest(unittest.TestCase): def setUp(self): self.e = Envelope(0, 0, 5, 5) - + def test01_init(self): "Testing Envelope initilization." e1 = Envelope((0, 0, 5, 5)) diff --git a/django/contrib/gis/gdal/tests/test_geom.py b/django/contrib/gis/gdal/tests/test_geom.py index c048d2bb82..74b1e894e1 100644 --- a/django/contrib/gis/gdal/tests/test_geom.py +++ b/django/contrib/gis/gdal/tests/test_geom.py @@ -4,12 +4,12 @@ try: from django.utils.six.moves import cPickle as pickle except ImportError: import pickle +import unittest +from unittest import skipUnless from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geometry.test_data import TestDataMixin from django.utils.six.moves import xrange -from django.utils import unittest -from django.utils.unittest import skipUnless if HAS_GDAL: from django.contrib.gis.gdal import (OGRGeometry, OGRGeomType, diff --git a/django/contrib/gis/gdal/tests/test_srs.py b/django/contrib/gis/gdal/tests/test_srs.py index 363b597dae..cacff4be04 100644 --- a/django/contrib/gis/gdal/tests/test_srs.py +++ b/django/contrib/gis/gdal/tests/test_srs.py @@ -1,6 +1,7 @@ +import unittest +from unittest import skipUnless + from django.contrib.gis.gdal import HAS_GDAL -from django.utils import unittest -from django.utils.unittest import skipUnless if HAS_GDAL: from django.contrib.gis.gdal import SpatialReference, CoordTransform, OGRException, SRSException diff --git a/django/contrib/gis/geoip/tests.py b/django/contrib/gis/geoip/tests.py index 3fa64bf6be..c49ffb5bf1 100644 --- a/django/contrib/gis/geoip/tests.py +++ b/django/contrib/gis/geoip/tests.py @@ -2,11 +2,12 @@ from __future__ import unicode_literals import os +import unittest +from unittest import skipUnless + from django.conf import settings from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.geoip import HAS_GEOIP -from django.utils import unittest -from django.utils.unittest import skipUnless from django.utils import six diff --git a/django/contrib/gis/geos/tests/test_geos.py b/django/contrib/gis/geos/tests/test_geos.py index c7fe5b2321..900a0adb40 100644 --- a/django/contrib/gis/geos/tests/test_geos.py +++ b/django/contrib/gis/geos/tests/test_geos.py @@ -3,6 +3,8 @@ from __future__ import unicode_literals import ctypes import json import random +import unittest +from unittest import skipUnless from binascii import a2b_hex, b2a_hex from io import BytesIO @@ -14,8 +16,6 @@ from django.contrib.gis.geometry.test_data import TestDataMixin from django.utils.encoding import force_bytes from django.utils import six from django.utils.six.moves import xrange -from django.utils import unittest -from django.utils.unittest import skipUnless from .. import HAS_GEOS diff --git a/django/contrib/gis/geos/tests/test_geos_mutation.py b/django/contrib/gis/geos/tests/test_geos_mutation.py index 40b708a0ef..337abb4d8b 100644 --- a/django/contrib/gis/geos/tests/test_geos_mutation.py +++ b/django/contrib/gis/geos/tests/test_geos_mutation.py @@ -2,8 +2,8 @@ # Modified from original contribution by Aryeh Leib Taurog, which was # released under the New BSD license. -from django.utils import unittest -from django.utils.unittest import skipUnless +import unittest +from unittest import skipUnless from .. import HAS_GEOS diff --git a/django/contrib/gis/geos/tests/test_io.py b/django/contrib/gis/geos/tests/test_io.py index 34eeaf95b9..2278be8a87 100644 --- a/django/contrib/gis/geos/tests/test_io.py +++ b/django/contrib/gis/geos/tests/test_io.py @@ -2,9 +2,9 @@ from __future__ import unicode_literals import binascii import unittest +from unittest import skipUnless from django.contrib.gis import memoryview -from django.utils.unittest import skipUnless from ..import HAS_GEOS diff --git a/django/contrib/gis/geos/tests/test_mutable_list.py b/django/contrib/gis/geos/tests/test_mutable_list.py index a4a56f2e5f..ae50a5f616 100644 --- a/django/contrib/gis/geos/tests/test_mutable_list.py +++ b/django/contrib/gis/geos/tests/test_mutable_list.py @@ -3,9 +3,11 @@ # # Modified from original contribution by Aryeh Leib Taurog, which was # released under the New BSD license. + +import unittest + from django.contrib.gis.geos.mutable_list import ListMixin from django.utils import six -from django.utils import unittest class UserListA(ListMixin): diff --git a/django/contrib/gis/tests/distapp/tests.py b/django/contrib/gis/tests/distapp/tests.py index 2ed17a03bd..8915f01e50 100644 --- a/django/contrib/gis/tests/distapp/tests.py +++ b/django/contrib/gis/tests/distapp/tests.py @@ -1,5 +1,7 @@ from __future__ import absolute_import +from unittest import skipUnless + from django.db import connection from django.db.models import Q from django.contrib.gis.geos import HAS_GEOS @@ -8,7 +10,6 @@ from django.contrib.gis.tests.utils import ( HAS_SPATIAL_DB, mysql, oracle, postgis, spatialite, no_oracle, no_spatialite ) from django.test import TestCase -from django.utils.unittest import skipUnless if HAS_GEOS and HAS_SPATIAL_DB: from django.contrib.gis.geos import GEOSGeometry, LineString diff --git a/django/contrib/gis/tests/geo3d/tests.py b/django/contrib/gis/tests/geo3d/tests.py index df9f35690b..6c17003982 100644 --- a/django/contrib/gis/tests/geo3d/tests.py +++ b/django/contrib/gis/tests/geo3d/tests.py @@ -2,13 +2,13 @@ from __future__ import absolute_import, unicode_literals import os import re +from unittest import skipUnless from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.tests.utils import postgis from django.test import TestCase from django.utils._os import upath -from django.utils.unittest import skipUnless if HAS_GEOS: from django.contrib.gis.db.models import Union, Extent3D diff --git a/django/contrib/gis/tests/geoadmin/tests.py b/django/contrib/gis/tests/geoadmin/tests.py index 15874758be..c34964a2da 100644 --- a/django/contrib/gis/tests/geoadmin/tests.py +++ b/django/contrib/gis/tests/geoadmin/tests.py @@ -1,9 +1,10 @@ from __future__ import absolute_import +from unittest import skipUnless + from django.test import TestCase from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.tests.utils import HAS_SPATIAL_DB -from django.utils.unittest import skipUnless if HAS_GEOS and HAS_SPATIAL_DB: from django.contrib.gis import admin diff --git a/django/contrib/gis/tests/geoapp/test_feeds.py b/django/contrib/gis/tests/geoapp/test_feeds.py index 778cadc9d4..b2953b4a70 100644 --- a/django/contrib/gis/tests/geoapp/test_feeds.py +++ b/django/contrib/gis/tests/geoapp/test_feeds.py @@ -1,5 +1,6 @@ from __future__ import absolute_import +from unittest import skipUnless from xml.dom import minidom from django.conf import settings @@ -7,7 +8,6 @@ from django.contrib.sites.models import Site from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.tests.utils import HAS_SPATIAL_DB from django.test import TestCase -from django.utils.unittest import skipUnless if HAS_GEOS: from .models import City diff --git a/django/contrib/gis/tests/geoapp/test_regress.py b/django/contrib/gis/tests/geoapp/test_regress.py index 43dbcfd852..2ffbaec9a9 100644 --- a/django/contrib/gis/tests/geoapp/test_regress.py +++ b/django/contrib/gis/tests/geoapp/test_regress.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals from datetime import datetime +from unittest import skipUnless from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.tests.utils import no_mysql, no_spatialite @@ -9,7 +10,6 @@ from django.contrib.gis.shortcuts import render_to_kmz from django.contrib.gis.tests.utils import HAS_SPATIAL_DB from django.db.models import Count, Min from django.test import TestCase -from django.utils.unittest import skipUnless if HAS_GEOS: from .models import City, PennsylvaniaCity, State, Truth diff --git a/django/contrib/gis/tests/geoapp/test_sitemaps.py b/django/contrib/gis/tests/geoapp/test_sitemaps.py index 337b4b75c6..facd94c247 100644 --- a/django/contrib/gis/tests/geoapp/test_sitemaps.py +++ b/django/contrib/gis/tests/geoapp/test_sitemaps.py @@ -1,6 +1,7 @@ from __future__ import absolute_import from io import BytesIO +from unittest import skipUnless from xml.dom import minidom import zipfile @@ -9,7 +10,6 @@ from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.tests.utils import HAS_SPATIAL_DB from django.contrib.sites.models import Site from django.test import TestCase -from django.utils.unittest import skipUnless if HAS_GEOS: from .models import City, Country diff --git a/django/contrib/gis/tests/geoapp/tests.py b/django/contrib/gis/tests/geoapp/tests.py index cf6e316919..89505ae1f2 100644 --- a/django/contrib/gis/tests/geoapp/tests.py +++ b/django/contrib/gis/tests/geoapp/tests.py @@ -1,6 +1,7 @@ from __future__ import absolute_import import re +from unittest import skipUnless from django.db import connection from django.contrib.gis import gdal @@ -10,7 +11,6 @@ from django.contrib.gis.tests.utils import ( mysql, oracle, postgis, spatialite) from django.test import TestCase from django.utils import six, unittest -from django.utils.unittest import skipUnless if HAS_GEOS: from django.contrib.gis.geos import (fromstr, GEOSGeometry, diff --git a/django/contrib/gis/tests/geogapp/tests.py b/django/contrib/gis/tests/geogapp/tests.py index ed54999f90..2a60f623fa 100644 --- a/django/contrib/gis/tests/geogapp/tests.py +++ b/django/contrib/gis/tests/geogapp/tests.py @@ -4,6 +4,7 @@ Tests for geography support in PostGIS 1.5+ from __future__ import absolute_import import os +from unittest import skipUnless from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geos import HAS_GEOS @@ -11,7 +12,6 @@ from django.contrib.gis.measure import D from django.contrib.gis.tests.utils import postgis from django.test import TestCase from django.utils._os import upath -from django.utils.unittest import skipUnless if HAS_GEOS: from .models import City, County, Zipcode diff --git a/django/contrib/gis/tests/inspectapp/tests.py b/django/contrib/gis/tests/inspectapp/tests.py index 668b87ba86..34dab1ab7d 100644 --- a/django/contrib/gis/tests/inspectapp/tests.py +++ b/django/contrib/gis/tests/inspectapp/tests.py @@ -1,13 +1,13 @@ from __future__ import absolute_import import os +from unittest import skipUnless from django.db import connections from django.test import TestCase from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geometry.test_data import TEST_DATA from django.contrib.gis.tests.utils import HAS_SPATIAL_DB -from django.utils.unittest import skipUnless if HAS_GDAL: from django.contrib.gis.gdal import Driver diff --git a/django/contrib/gis/tests/layermap/tests.py b/django/contrib/gis/tests/layermap/tests.py index 8379311a2b..c4c27b353e 100644 --- a/django/contrib/gis/tests/layermap/tests.py +++ b/django/contrib/gis/tests/layermap/tests.py @@ -1,17 +1,17 @@ # coding: utf-8 from __future__ import absolute_import, unicode_literals -import os from copy import copy from decimal import Decimal +import os +import unittest +from unittest import skipUnless from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.tests.utils import HAS_SPATIAL_DB, mysql from django.db import router from django.conf import settings from django.test import TestCase -from django.utils import unittest -from django.utils.unittest import skipUnless from django.utils._os import upath if HAS_GDAL: diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py index d93adbcd4c..653bda8aaf 100644 --- a/django/contrib/gis/tests/relatedapp/tests.py +++ b/django/contrib/gis/tests/relatedapp/tests.py @@ -1,9 +1,10 @@ from __future__ import absolute_import +from unittest import skipUnless + from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.tests.utils import HAS_SPATIAL_DB, mysql, oracle, no_mysql, no_oracle, no_spatialite from django.test import TestCase -from django.utils.unittest import skipUnless if HAS_GEOS: from django.contrib.gis.db.models import Collect, Count, Extent, F, Union diff --git a/django/contrib/gis/tests/test_geoforms.py b/django/contrib/gis/tests/test_geoforms.py index 402d9b944b..b04bdea71d 100644 --- a/django/contrib/gis/tests/test_geoforms.py +++ b/django/contrib/gis/tests/test_geoforms.py @@ -1,15 +1,16 @@ +from unittest import skipUnless + from django.forms import ValidationError from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.tests.utils import HAS_SPATIALREFSYS from django.test import SimpleTestCase from django.utils import six -from django.utils.unittest import skipUnless - if HAS_SPATIALREFSYS: from django.contrib.gis import forms from django.contrib.gis.geos import GEOSGeometry + @skipUnless(HAS_GDAL and HAS_SPATIALREFSYS, "GeometryFieldTest needs gdal support and a spatial database") class GeometryFieldTest(SimpleTestCase): @@ -143,7 +144,7 @@ class SpecializedFieldTest(SimpleTestCase): def assertTextarea(self, geom, rendered): """Makes sure the wkt and a textarea are in the content""" - + self.assertIn('