Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/sage/doctest/external.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,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.
Expand Down Expand Up @@ -431,8 +431,13 @@ 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
self._hidden = Array('i', len(self._features)) # initialized to zeroes
try:
from multiprocessing import Array
self._seen = Array('i', len(self._features)) # initialized to zeroes
self._hidden = 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)
self._hidden = [0] * len(self._features)

def __contains__(self, item):
"""
Expand Down
21 changes: 14 additions & 7 deletions src/sage/doctest/forker.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
import signal
import linecache
import hashlib
import multiprocessing
import warnings
import re
import errno
Expand Down Expand Up @@ -81,8 +80,16 @@
# 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)

from multiprocessing import Process

try:
import _multiprocessing
except ImportError:
_multiprocessing = None


def _sorted_dict_pprinter_factory(start, end):
"""
Expand Down Expand Up @@ -236,7 +243,7 @@ def init_sage(controller=None):
pass

try:
import sympy
import sympy.printing
except ImportError:
# Do not require sympy for running doctests (Issue #25106).
pass
Expand Down Expand Up @@ -1482,7 +1489,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:
Expand Down Expand Up @@ -1626,7 +1633,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:
Expand Down Expand Up @@ -2131,13 +2138,13 @@ def dispatch(self):
sage -t .../sage/rings/big_oh.py
[... tests, ...s wall]
"""
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
Expand Down Expand Up @@ -2200,7 +2207,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
Expand Down
7 changes: 5 additions & 2 deletions src/sage/features/threejs.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ def required_version(self):

filename = Path(SAGE_EXTCODE) / 'threejs' / 'threejs-version.txt'

with open(filename) as f:
return f.read().strip()
try:
with open(filename) as f:
return f.read().strip()
except FileNotFoundError:
return "unknown"


def all_features():
Expand Down
7 changes: 6 additions & 1 deletion src/sage/misc/timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
# ****************************************************************************


import resource
import time


Expand Down Expand Up @@ -73,6 +72,12 @@ def cputime(t=0, subprocesses=False):
CPU time is reported correctly because subprocesses can be
started and terminated at any given time.
"""
try:
import resource
except ImportError:
# The module 'resource' is removed in Pyodide to browser limitations.
return walltime(t)

if isinstance(t, GlobalCputime):
subprocesses = True

Expand Down
3 changes: 2 additions & 1 deletion src/sage/parallel/multiprocessing_sage.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -63,6 +62,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)
Expand Down
Loading