actually bubbles up original error w.o pickling TypeErrors wrapping it
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user