diff --git a/src/core/runner.py b/src/core/runner.py index 2c6b36a..b800ea7 100644 --- a/src/core/runner.py +++ b/src/core/runner.py @@ -1,22 +1,28 @@ import pickle import time -from django.test.runner import DiscoverRunner, ParallelTestSuite, RemoteTestRunner +from django.test.runner import DiscoverRunner, ParallelTestSuite, RemoteTestResult, RemoteTestRunner -class _Py313SafeRemoteTestRunner(RemoteTestRunner): +class _Py313SafeRemoteTestResult(RemoteTestResult): """Prevents Python 3.13 'cannot pickle traceback' crash in parallel workers. In Python 3.13, traceback objects are not picklable. Django's parallel - runner tries to pickle test errors to pass them back to the main process. - When a Selenium test crashes the browser, the resulting exception traceback - fails to pickle, aborting the entire parallel run with a confusing TypeError. + runner serialises test errors via multiprocessing (pickle) to send them + from worker subprocesses back to the main process. When a Selenium test + crashes the browser, the resulting exception traceback fails to pickle, + aborting the entire parallel run with a confusing TypeError. Fix: strip the traceback (replace with None) before it reaches pickle. Error type and message are preserved; only the traceback is lost in transit. + + Note: the fix must be on RemoteTestResult (not RemoteTestRunner) because + addError/addFailure — where check_picklable is called — live on the result + object, not the runner. RemoteTestRunner.run() instantiates resultclass() + and passes it to the test suite; overriding it on the runner has no effect. """ @staticmethod - def _strip_tb_if_needed(err): + def _safe_err(err): exc_type, exc_value, exc_tb = err try: pickle.dumps((exc_type, exc_value, exc_tb)) @@ -25,10 +31,14 @@ class _Py313SafeRemoteTestRunner(RemoteTestRunner): return err def addError(self, test, err): - super().addError(test, self._strip_tb_if_needed(err)) + super().addError(test, self._safe_err(err)) def addFailure(self, test, err): - super().addFailure(test, self._strip_tb_if_needed(err)) + super().addFailure(test, self._safe_err(err)) + + +class _Py313SafeRemoteTestRunner(RemoteTestRunner): + resultclass = _Py313SafeRemoteTestResult class _SafeParallelTestSuite(ParallelTestSuite):