diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index efa46ef135e..4212bd37461 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -30,15 +30,15 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -import multiprocessing import os + # With OS X, Python 3.8 defaults to use 'spawn' instead of 'fork' in # multiprocessing, and Sage doctesting doesn't work with 'spawn'. See # trac #27754. if os.uname().sysname == 'Darwin': + import multiprocessing multiprocessing.set_start_method('fork', force=True) -Array = multiprocessing.Array # Functions in this module whose name is of the form 'has_xxx' tests if the # software xxx is available to Sage. @@ -428,7 +428,11 @@ def __init__(self): features.update(all_features()) self._features = sorted(features, key=lambda feature: feature.name) self._indices = {feature.name: idx for idx, feature in enumerate(self._features)} - self._seen = Array('i', len(self._features)) # initialized to zeroes + try: + from multiprocessing import Array + self._seen = Array('i', len(self._features)) # initialized to zeroes + except ImportError: # module '_multiprocessing' is removed in Pyodide due to browser limitations + self._seen = [0] * len(self._features) def __contains__(self, item): """ diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index b24ae96bcc3..dfa2172372c 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -52,7 +52,6 @@ import signal import linecache import hashlib -import multiprocessing import warnings import re import errno @@ -80,8 +79,17 @@ # multiprocessing, and Sage doctesting doesn't work with 'spawn'. See # trac #27754. if os.uname().sysname == 'Darwin': + import multiprocessing multiprocessing.set_start_method('fork', force=True) +try: + import multiprocessing + from multiprocessing import Process +except ImportError: + multiprocessing = None + class Process: + pass + def _sorted_dict_pprinter_factory(start, end): """ @@ -1457,7 +1465,7 @@ def report_failure(self, out, test, example, got, globs): except KeyboardInterrupt: # Assume this is a *real* interrupt. We need to # escalate this to the master doctesting process. - if not self.options.serial: + if not self.options.serial and multiprocessing: os.kill(os.getppid(), signal.SIGINT) raise finally: @@ -1594,7 +1602,7 @@ def report_unexpected_exception(self, out, test, example, exc_info): except KeyboardInterrupt: # Assume this is a *real* interrupt. We need to # escalate this to the master doctesting process. - if not self.options.serial: + if not self.options.serial and multiprocessing: os.kill(os.getppid(), signal.SIGINT) raise finally: @@ -2106,13 +2114,13 @@ def dispatch(self): sage -t .../sage/rings/big_oh.py [... tests, ... s] """ - if self.controller.options.serial: + if self.controller.options.serial or not multiprocessing: self.serial_dispatch() else: self.parallel_dispatch() -class DocTestWorker(multiprocessing.Process): +class DocTestWorker(Process): """ The DocTestWorker process runs one :class:`DocTestTask` for a given source. It returns messages about doctest failures (or all tests if @@ -2176,7 +2184,7 @@ def __init__(self, source, options, funclist=[], baseline=None): cumulative wall time: ... seconds Features detected... """ - multiprocessing.Process.__init__(self) + Process.__init__(self) self.source = source self.options = options diff --git a/src/sage/parallel/multiprocessing_sage.py b/src/sage/parallel/multiprocessing_sage.py index 2ccab49ac0f..ea7b2b3a07c 100644 --- a/src/sage/parallel/multiprocessing_sage.py +++ b/src/sage/parallel/multiprocessing_sage.py @@ -11,7 +11,6 @@ # https://www.gnu.org/licenses/ ################################################################################ -from multiprocessing import Pool from functools import partial from sage.misc.fpickle import pickle_function, call_pickled_function from . import ncpus @@ -67,6 +66,8 @@ def parallel_iter(processes, f, inputs): sage: v.sort(); v [(((2,), {}), 4), (((3,), {}), 6)] """ + from multiprocessing import Pool + if processes == 0: processes = ncpus.ncpus() p = Pool(processes)