mirror of
				https://github.com/django/django.git
				synced 2025-11-04 05:35:37 +00:00 
			
		
		
		
	Fixed #20503 - Moved doctest utilities in with the rest of the deprecated test code.
The ``DocTestRunner`` and ``OutputChecker`` were formerly in ``django.test.testcases``, now they are in ``django.test.simple``. This avoids triggering the ``django.test._doctest`` deprecation message with any import from ``django.test``. Since these utility classes are undocumented internal API, they can be moved without a separate deprecation process. Also removed the deprecation warnings specific to these classes, as they are now covered by the module-level warning in ``django.test.simple``. Thanks Anssi for the report. Refs #17365.
This commit is contained in:
		
							parent
							
								
									0027f13904
								
							
						
					
					
						commit
						cd79f33723
					
				
					 4 changed files with 78 additions and 95 deletions
				
			
		| 
						 | 
					@ -3,14 +3,15 @@ This module is pending deprecation as of Django 1.6 and will be removed in
 | 
				
			||||||
version 1.8.
 | 
					version 1.8.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
import unittest as real_unittest
 | 
					import unittest as real_unittest
 | 
				
			||||||
import warnings
 | 
					import warnings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db.models import get_app, get_apps
 | 
					from django.db.models import get_app, get_apps
 | 
				
			||||||
from django.test import _doctest as doctest
 | 
					from django.test import _doctest as doctest
 | 
				
			||||||
from django.test import runner
 | 
					from django.test import runner
 | 
				
			||||||
from django.test.testcases import OutputChecker, DocTestRunner
 | 
					from django.test.utils import compare_xml, strip_quotes
 | 
				
			||||||
from django.utils import unittest
 | 
					from django.utils import unittest
 | 
				
			||||||
from django.utils.importlib import import_module
 | 
					from django.utils.importlib import import_module
 | 
				
			||||||
from django.utils.module_loading import module_has_submodule
 | 
					from django.utils.module_loading import module_has_submodule
 | 
				
			||||||
| 
						 | 
					@ -25,6 +26,71 @@ warnings.warn(
 | 
				
			||||||
# The module name for tests outside models.py
 | 
					# The module name for tests outside models.py
 | 
				
			||||||
TEST_MODULE = 'tests'
 | 
					TEST_MODULE = 'tests'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
 | 
				
			||||||
 | 
					normalize_decimals = lambda s: re.sub(r"Decimal\('(\d+(\.\d*)?)'\)",
 | 
				
			||||||
 | 
					                                lambda m: "Decimal(\"%s\")" % m.groups()[0], s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OutputChecker(doctest.OutputChecker):
 | 
				
			||||||
 | 
					    def check_output(self, want, got, optionflags):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        The entry method for doctest output checking. Defers to a sequence of
 | 
				
			||||||
 | 
					        child checkers
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        checks = (self.check_output_default,
 | 
				
			||||||
 | 
					                  self.check_output_numeric,
 | 
				
			||||||
 | 
					                  self.check_output_xml,
 | 
				
			||||||
 | 
					                  self.check_output_json)
 | 
				
			||||||
 | 
					        for check in checks:
 | 
				
			||||||
 | 
					            if check(want, got, optionflags):
 | 
				
			||||||
 | 
					                return True
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_output_default(self, want, got, optionflags):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        The default comparator provided by doctest - not perfect, but good for
 | 
				
			||||||
 | 
					        most purposes
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return doctest.OutputChecker.check_output(self, want, got, optionflags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_output_numeric(self, want, got, optionflags):
 | 
				
			||||||
 | 
					        """Doctest does an exact string comparison of output, which means that
 | 
				
			||||||
 | 
					        some numerically equivalent values aren't equal. This check normalizes
 | 
				
			||||||
 | 
					         * long integers (22L) so that they equal normal integers. (22)
 | 
				
			||||||
 | 
					         * Decimals so that they are comparable, regardless of the change
 | 
				
			||||||
 | 
					           made to __repr__ in Python 2.6.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return doctest.OutputChecker.check_output(self,
 | 
				
			||||||
 | 
					            normalize_decimals(normalize_long_ints(want)),
 | 
				
			||||||
 | 
					            normalize_decimals(normalize_long_ints(got)),
 | 
				
			||||||
 | 
					            optionflags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_output_xml(self, want, got, optionsflags):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return compare_xml(want, got)
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_output_json(self, want, got, optionsflags):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Tries to compare want and got as if they were JSON-encoded data
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        want, got = strip_quotes(want, got)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            want_json = json.loads(want)
 | 
				
			||||||
 | 
					            got_json = json.loads(got)
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return want_json == got_json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DocTestRunner(doctest.DocTestRunner):
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        doctest.DocTestRunner.__init__(self, *args, **kwargs)
 | 
				
			||||||
 | 
					        self.optionflags = doctest.ELLIPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
doctestOutputChecker = OutputChecker()
 | 
					doctestOutputChecker = OutputChecker()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,12 +30,11 @@ from django.core.urlresolvers import clear_url_caches, set_urlconf
 | 
				
			||||||
from django.db import connection, connections, DEFAULT_DB_ALIAS, transaction
 | 
					from django.db import connection, connections, DEFAULT_DB_ALIAS, transaction
 | 
				
			||||||
from django.forms.fields import CharField
 | 
					from django.forms.fields import CharField
 | 
				
			||||||
from django.http import QueryDict
 | 
					from django.http import QueryDict
 | 
				
			||||||
from django.test import _doctest as doctest
 | 
					 | 
				
			||||||
from django.test.client import Client
 | 
					from django.test.client import Client
 | 
				
			||||||
from django.test.html import HTMLParseError, parse_html
 | 
					from django.test.html import HTMLParseError, parse_html
 | 
				
			||||||
from django.test.signals import template_rendered
 | 
					from django.test.signals import template_rendered
 | 
				
			||||||
from django.test.utils import (CaptureQueriesContext, ContextList,
 | 
					from django.test.utils import (CaptureQueriesContext, ContextList,
 | 
				
			||||||
    override_settings, compare_xml, strip_quotes)
 | 
					    override_settings, compare_xml)
 | 
				
			||||||
from django.utils import six, unittest as ut2
 | 
					from django.utils import six, unittest as ut2
 | 
				
			||||||
from django.utils.encoding import force_text
 | 
					from django.utils.encoding import force_text
 | 
				
			||||||
from django.utils.unittest import skipIf # Imported here for backward compatibility
 | 
					from django.utils.unittest import skipIf # Imported here for backward compatibility
 | 
				
			||||||
| 
						 | 
					@ -43,15 +42,10 @@ from django.utils.unittest.util import safe_repr
 | 
				
			||||||
from django.views.static import serve
 | 
					from django.views.static import serve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ('DocTestRunner', 'OutputChecker', 'TestCase', 'TransactionTestCase',
 | 
					__all__ = ('TestCase', 'TransactionTestCase',
 | 
				
			||||||
           'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature')
 | 
					           'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
 | 
					 | 
				
			||||||
normalize_decimals = lambda s: re.sub(r"Decimal\('(\d+(\.\d*)?)'\)",
 | 
					 | 
				
			||||||
                                lambda m: "Decimal(\"%s\")" % m.groups()[0], s)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def to_list(value):
 | 
					def to_list(value):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Puts value into a list if it's not already one.
 | 
					    Puts value into a list if it's not already one.
 | 
				
			||||||
| 
						 | 
					@ -96,75 +90,6 @@ def assert_and_parse_html(self, html, user_msg, msg):
 | 
				
			||||||
    return dom
 | 
					    return dom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OutputChecker(doctest.OutputChecker):
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					 | 
				
			||||||
        warnings.warn(
 | 
					 | 
				
			||||||
            "The django.test.testcases.OutputChecker class is deprecated; "
 | 
					 | 
				
			||||||
            "use the doctest module from the Python standard library instead.",
 | 
					 | 
				
			||||||
            PendingDeprecationWarning)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_output(self, want, got, optionflags):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        The entry method for doctest output checking. Defers to a sequence of
 | 
					 | 
				
			||||||
        child checkers
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        checks = (self.check_output_default,
 | 
					 | 
				
			||||||
                  self.check_output_numeric,
 | 
					 | 
				
			||||||
                  self.check_output_xml,
 | 
					 | 
				
			||||||
                  self.check_output_json)
 | 
					 | 
				
			||||||
        for check in checks:
 | 
					 | 
				
			||||||
            if check(want, got, optionflags):
 | 
					 | 
				
			||||||
                return True
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_output_default(self, want, got, optionflags):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        The default comparator provided by doctest - not perfect, but good for
 | 
					 | 
				
			||||||
        most purposes
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        return doctest.OutputChecker.check_output(self, want, got, optionflags)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_output_numeric(self, want, got, optionflags):
 | 
					 | 
				
			||||||
        """Doctest does an exact string comparison of output, which means that
 | 
					 | 
				
			||||||
        some numerically equivalent values aren't equal. This check normalizes
 | 
					 | 
				
			||||||
         * long integers (22L) so that they equal normal integers. (22)
 | 
					 | 
				
			||||||
         * Decimals so that they are comparable, regardless of the change
 | 
					 | 
				
			||||||
           made to __repr__ in Python 2.6.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        return doctest.OutputChecker.check_output(self,
 | 
					 | 
				
			||||||
            normalize_decimals(normalize_long_ints(want)),
 | 
					 | 
				
			||||||
            normalize_decimals(normalize_long_ints(got)),
 | 
					 | 
				
			||||||
            optionflags)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_output_xml(self, want, got, optionsflags):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            return compare_xml(want, got)
 | 
					 | 
				
			||||||
        except Exception:
 | 
					 | 
				
			||||||
            return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_output_json(self, want, got, optionsflags):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Tries to compare want and got as if they were JSON-encoded data
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        want, got = strip_quotes(want, got)
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            want_json = json.loads(want)
 | 
					 | 
				
			||||||
            got_json = json.loads(got)
 | 
					 | 
				
			||||||
        except Exception:
 | 
					 | 
				
			||||||
            return False
 | 
					 | 
				
			||||||
        return want_json == got_json
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class DocTestRunner(doctest.DocTestRunner):
 | 
					 | 
				
			||||||
    def __init__(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        warnings.warn(
 | 
					 | 
				
			||||||
            "The django.test.testcases.DocTestRunner class is deprecated; "
 | 
					 | 
				
			||||||
            "use the doctest module from the Python standard library instead.",
 | 
					 | 
				
			||||||
            PendingDeprecationWarning)
 | 
					 | 
				
			||||||
        doctest.DocTestRunner.__init__(self, *args, **kwargs)
 | 
					 | 
				
			||||||
        self.optionflags = doctest.ELLIPSIS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _AssertNumQueriesContext(CaptureQueriesContext):
 | 
					class _AssertNumQueriesContext(CaptureQueriesContext):
 | 
				
			||||||
    def __init__(self, test_case, num, connection):
 | 
					    def __init__(self, test_case, num, connection):
 | 
				
			||||||
        self.test_case = test_case
 | 
					        self.test_case = test_case
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -386,10 +386,8 @@ these changes.
 | 
				
			||||||
  ``django.test.simple.DjangoTestSuiteRunner`` will be removed. Instead use
 | 
					  ``django.test.simple.DjangoTestSuiteRunner`` will be removed. Instead use
 | 
				
			||||||
  ``django.test.runner.DiscoverRunner``.
 | 
					  ``django.test.runner.DiscoverRunner``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* The module ``django.test._doctest`` and the classes
 | 
					* The module ``django.test._doctest`` will be removed. Instead use the doctest
 | 
				
			||||||
  ``django.test.testcases.DocTestRunner`` and
 | 
					  module from the Python standard library.
 | 
				
			||||||
  ``django.test.testcases.OutputChecker`` will be removed. Instead use the
 | 
					 | 
				
			||||||
  doctest module from the Python standard library.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
* The ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting will be removed.
 | 
					* The ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting will be removed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -345,21 +345,15 @@ support some types of tests that were supported by the previous runner:
 | 
				
			||||||
  your test suite, follow the `recommendations in the Python documentation`_.
 | 
					  your test suite, follow the `recommendations in the Python documentation`_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Django bundles a modified version of the :mod:`doctest` module from the Python
 | 
					Django bundles a modified version of the :mod:`doctest` module from the Python
 | 
				
			||||||
standard library (in ``django.test._doctest``) in order to allow passing in a
 | 
					standard library (in ``django.test._doctest``) and includes some additional
 | 
				
			||||||
custom ``DocTestRunner`` when instantiating a ``DocTestSuite``, and includes
 | 
					doctest utilities. These utilities are deprecated and will be removed in Django
 | 
				
			||||||
some additional doctest utilities (``django.test.testcases.DocTestRunner``
 | 
					1.8; doctest suites should be updated to work with the standard library's
 | 
				
			||||||
turns on the ``ELLIPSIS`` option by default, and
 | 
					doctest module (or converted to unittest-compatible tests).
 | 
				
			||||||
``django.test.testcases.OutputChecker`` provides better matching of XML, JSON,
 | 
					 | 
				
			||||||
and numeric data types).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
These utilities are deprecated and will be removed in Django 1.8; doctest
 | 
					 | 
				
			||||||
suites should be updated to work with the standard library's doctest module (or
 | 
					 | 
				
			||||||
converted to unittest-compatible tests).
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you wish to delay updates to your test suite, you can set your
 | 
					If you wish to delay updates to your test suite, you can set your
 | 
				
			||||||
:setting:`TEST_RUNNER` setting to ``django.test.simple.DjangoTestSuiteRunner``
 | 
					:setting:`TEST_RUNNER` setting to ``django.test.simple.DjangoTestSuiteRunner``
 | 
				
			||||||
to fully restore the old test behavior. ``DjangoTestSuiteRunner`` is
 | 
					to fully restore the old test behavior. ``DjangoTestSuiteRunner`` is deprecated
 | 
				
			||||||
deprecated but will not be removed from Django until version 1.8.
 | 
					but will not be removed from Django until version 1.8.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. _recommendations in the Python documentation: http://docs.python.org/2/library/doctest.html#unittest-api
 | 
					.. _recommendations in the Python documentation: http://docs.python.org/2/library/doctest.html#unittest-api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue