Commit 073ea9e8 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Acknoweldeged a limitation of the parallel test runner.

Notably it will fail to report a Model.DoesNotExist exceptions because
the class itself isn't pickleable. (Django has specific code to make its
instances pickleable.)
parent b799a50c
Loading
Loading
Loading
Loading
+54 −0
Original line number Diff line number Diff line
@@ -4,6 +4,8 @@ import itertools
import logging
import multiprocessing
import os
import pickle
import textwrap
import unittest
from importlib import import_module

@@ -82,6 +84,55 @@ class RemoteTestResult(object):
    def test_index(self):
        return self.testsRun - 1

    def check_pickleable(self, test, err):
        # Ensure that sys.exc_info() tuples are picklable. This displays a
        # clear multiprocessing.pool.RemoteTraceback generated in the child
        # process instead of a multiprocessing.pool.MaybeEncodingError, making
        # the root cause easier to figure out for users who aren't familiar
        # with the multiprocessing module. Since we're in a forked process,
        # our best chance to communicate with them is to print to stdout.
        try:
            pickle.dumps(err)
        except Exception as exc:
            original_exc_txt = repr(err[1])
            original_exc_txt = textwrap.fill(original_exc_txt, 75)
            original_exc_txt = textwrap.indent(original_exc_txt, '    ')
            pickle_exc_txt = repr(exc)
            pickle_exc_txt = textwrap.fill(pickle_exc_txt, 75)
            pickle_exc_txt = textwrap.indent(pickle_exc_txt, '    ')
            if tblib is None:
                print("""

{} failed:

{}

Unfortunately, tracebacks cannot be pickled, making it impossible for the
parallel test runner to handle this exception cleanly.

In order to see the traceback, you should install tblib:

    pip install tblib
""".format(test, original_exc_txt))
            else:
                print("""

{} failed:

{}

Unfortunately, the exception it raised cannot be pickled, making it impossible
for the parallel test runner to handle it cleanly.

Here's the error encountered while trying to pickle the exception:

{}

You should re-run this test without the --parallel option to reproduce the
failure and get a correct traceback.
""".format(test, original_exc_txt, pickle_exc_txt))
            raise

    def stop_if_failfast(self):
        if self.failfast:
            self.stop()
@@ -103,10 +154,12 @@ class RemoteTestResult(object):
        self.events.append(('stopTest', self.test_index))

    def addError(self, test, err):
        self.check_pickleable(test, err)
        self.events.append(('addError', self.test_index, err))
        self.stop_if_failfast()

    def addFailure(self, test, err):
        self.check_pickleable(test, err)
        self.events.append(('addFailure', self.test_index, err))
        self.stop_if_failfast()

@@ -120,6 +173,7 @@ class RemoteTestResult(object):
        self.events.append(('addSkip', self.test_index, reason))

    def addExpectedFailure(self, test, err):
        self.check_pickleable(test, err)
        self.events.append(('addExpectedFailure', self.test_index, err))

    def addUnexpectedSuccess(self, test):
+11 −0
Original line number Diff line number Diff line
@@ -1285,6 +1285,17 @@ correctly:

    $ pip install tblib

.. warning::

    When test parallelization is enabled and a test fails, Django may be
    unable to display the exception traceback. This can make debugging
    difficult. If you encounter this problem, run the affected test without
    parallelization to see the traceback of the failure.

    This is a known limitation. It arises from the need to serialize objects
    in order to exchange them between processes. See
    :ref:`python:pickle-picklable` for details.

testserver <fixture fixture ...>
--------------------------------