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
|
@ -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…
Reference in New Issue