def _install_spec(self): """ Install the Sage IPython kernel It is safe to call this method multiple times, only one Sage kernel spec is ever installed for any given Sage version. However, it resets the IPython kernel spec directory so additional resources symlinked there are lost. See :meth:`symlink_resources`. EXAMPLES:: sage: from sage.repl.ipython_kernel.install import SageKernelSpec sage: spec = SageKernelSpec() sage: spec._install_spec() # not tested """ import json temp = tmp_dir() kernel_spec = os.path.join(temp, "kernel.json") with open(kernel_spec, "w") as f: json.dump(self.kernel_spec(), f) identifier = self.identifier() install_kernel_spec(temp, identifier, user=True, replace=True) self._spec = get_kernel_spec(identifier)
def launch_script_filename(self): """ Return a launch script suitable to display the scene. This method saves the scene to disk and creates a launch script. The latter contains an absolute path to the scene file. The launch script is often necessary to make jmol render the 3d scene. OUTPUT: String. The file name of a suitable launch script. EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputSceneJmol sage: rich_output = OutputSceneJmol.example(); rich_output OutputSceneJmol container sage: filename = rich_output.launch_script_filename(); filename '/.../scene.spt' sage: with open(filename) as fobj: ....: print(fobj.read()) set defaultdirectory "/.../scene.spt.zip" script SCRIPT """ from sage.misc.temporary_file import tmp_dir basedir = tmp_dir() scene_filename = os.path.join(basedir, 'scene.spt.zip') script_filename = os.path.join(basedir, 'scene.spt') self.scene_zip.save_as(scene_filename) with open(script_filename, 'w') as f: f.write('set defaultdirectory "{0}"\n'.format(scene_filename)) f.write('script SCRIPT\n') return script_filename
def launch_script_filename(self): """ Return a launch script suitable to display the scene. This method saves the scene to disk and creates a launch script. The latter contains an absolute path to the scene file. The launch script is often necessary to make jmol render the 3d scene. OUTPUT: String. The file name of a suitable launch script. EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputSceneJmol sage: rich_output = OutputSceneJmol.example(); rich_output OutputSceneJmol container sage: filename = rich_output.launch_script_filename(); filename '/.../scene.spt' sage: print(open(filename).read()) set defaultdirectory "/.../scene.spt.zip" script SCRIPT """ from sage.misc.temporary_file import tmp_dir basedir = tmp_dir() scene_filename = os.path.join(basedir, 'scene.spt.zip') script_filename = os.path.join(basedir, 'scene.spt') self.scene_zip.save_as(scene_filename) with open(script_filename, 'w') as f: f.write('set defaultdirectory "{0}"\n'.format(scene_filename)) f.write('script SCRIPT\n') return script_filename
def __init__(self, n, time): self._n = n if time: cmd = 'time QuadraticSieve' else: cmd = 'QuadraticSieve' env = os.environ.copy() env['TMPDIR'] = tmp_dir('qsieve') self._p = SageSpawn(cmd, env=env) cleaner.cleaner(self._p.pid, 'QuadraticSieve') self._p.sendline(str(self._n) + '\n\n\n') self._done = False self._out = '' self._time = '' self._do_time = time
def obj_filename(self): """ Return the file name of the ``.obj`` file This method saves the object and texture to separate files in a temporary directory and returns the object file name. This is often used to launch a 3d viewer. OUTPUT: String. The file name (absolute path) of the saved obj file. EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputSceneWavefront sage: rich_output = OutputSceneWavefront.example(); rich_output OutputSceneWavefront container sage: obj = rich_output.obj_filename(); obj '/.../scene.obj' sage: with open(obj) as fobj: ....: print(fobj.read()) mtllib scene.mtl g obj_1 ... f 3 2 6 8 sage: path = os.path.dirname(obj) sage: mtl = os.path.join(path, 'scene.mtl'); mtl '/.../scene.mtl' sage: os.path.exists(mtl) True sage: os.path.dirname(obj) == os.path.dirname(mtl) True sage: with open(mtl) as fobj: ....: print(fobj.read()) newmtl texture177 Ka 0.2 0.2 0.5 ... d 1 """ from sage.misc.temporary_file import tmp_dir basedir = tmp_dir() obj_filename = os.path.join(basedir, 'scene.obj') mtl_filename = os.path.join(basedir, self.mtllib()) self.obj.save_as(obj_filename) self.mtl.save_as(mtl_filename) return os.path.abspath(obj_filename)
def directory(self): r""" Return the directory where the input files for 4ti2 are written by Sage and where 4ti2 is run. EXAMPLES:: sage: from sage.interfaces.four_ti_2 import FourTi2 sage: f = FourTi2("/tmp/") sage: f.directory() '/tmp/' """ from sage.misc.temporary_file import tmp_dir if self._directory is None: # we have to put this here rather than in the __init__ # method since apparently importing sage.misc.misc does not # work until Sage is done starting up. self._directory = tmp_dir() return self._directory
def png(self, dir=None): r""" Render PNG images of the frames in this animation, saving them in ``dir``. Return the absolute path to that directory. If the frames have been previously rendered and ``dir`` is ``None``, just return the directory in which they are stored. When ``dir`` is other than ``None``, force re-rendering of frames. INPUT: - ``dir`` -- Directory in which to store frames. Default ``None``; in this case, a temporary directory will be created for storing the frames. EXAMPLES:: sage: a = animate([plot(x^2 + n) for n in range(4)]) sage: d = a.png() sage: v = os.listdir(d); v.sort(); v ['00000000.png', '00000001.png', '00000002.png', '00000003.png'] """ if dir is None: try: return self._png_dir except AttributeError: pass d = tmp_dir() else: d = dir G = self._frames for i, frame in enumerate(self._frames): filename = '%s/%s'%(d,sage.misc.misc.pad_zeros(i,8)) try: frame.save_image(filename + '.png', **self._kwds) except AttributeError: self.make_image(frame, filename + '.png', **self._kwds) self._png_dir = d return d
def png(self, dir=None): """ Return the absolute path to a temp directory that contains the rendered PNG's of all the images in this animation. EXAMPLES:: sage: a = animate([plot(x^2 + n) for n in range(4)]) sage: d = a.png() sage: v = os.listdir(d); v.sort(); v ['00000000.png', '00000001.png', '00000002.png', '00000003.png'] """ try: return self._png_dir except AttributeError: pass d = tmp_dir() G = self._frames for i, frame in enumerate(self._frames): filename = '%s/%s'%(d,sage.misc.misc.pad_zeros(i,8)) frame.save(filename + '.png', **self._kwds) self._png_dir = d return d
def png(self, dir=None): """ Return the absolute path to a temp directory that contains the rendered PNG's of all the images in this animation. EXAMPLES:: sage: a = animate([plot(x^2 + n) for n in range(4)]) sage: d = a.png() sage: v = os.listdir(d); v.sort(); v ['00000000.png', '00000001.png', '00000002.png', '00000003.png'] """ try: return self._png_dir except AttributeError: pass d = tmp_dir() G = self._frames for i, frame in enumerate(self._frames): filename = '%s/%s' % (d, sage.misc.misc.pad_zeros(i, 8)) frame.save(filename + '.png', **self._kwds) self._png_dir = d return d
def qsieve_block(n, time, verbose=False): """ Compute the factorization of n using Hart's quadratic Sieve blocking until complete. """ cmd = ['QuadraticSieve'] if time: cmd = ['time'] + cmd env = os.environ.copy() env['TMPDIR'] = tmp_dir('qsieve') p = sp.Popen(cmd, env=env, stdout=sp.PIPE, stderr=sp.STDOUT, stdin=sp.PIPE, encoding='latin1') out, err = p.communicate(str(n)) z = data_to_list(out, n, time=time) if verbose: print(z[-1]) return z[:2]
def _install_spec(self): """ Install the Sage IPython kernel It is safe to call this method multiple times, only one Sage kernel spec is ever installed for any given Sage version. However, it resets the IPython kernel spec directory so additional resources symlinked there are lost. See :meth:`symlink_resources`. EXAMPLES:: sage: from sage.repl.ipython_kernel.install import SageKernelSpec sage: spec = SageKernelSpec() sage: spec._install_spec() # not tested """ import json temp = tmp_dir() kernel_spec = os.path.join(temp, 'kernel.json') with open(kernel_spec, 'w') as f: json.dump(self.kernel_spec(), f) identifier = self.identifier() install_kernel_spec(temp, identifier, user=True, replace=True) self._spec = get_kernel_spec(identifier)
def eval(self, x, globals=None, locals=None): """ EXAMPLES:: sage: from sage.misc.inline_fortran import InlineFortran, _example sage: fortran = InlineFortran(globals()) sage: fortran(_example) sage: import numpy sage: n = numpy.array(range(10),dtype=float) sage: fib(n,int(10)) sage: n array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.]) TESTS:: sage: os.chdir(SAGE_ROOT) sage: fortran.eval("SYNTAX ERROR !@#$") Traceback (most recent call last): ... RuntimeError: failed to compile Fortran code:... sage: os.getcwd() == SAGE_ROOT True """ if len(x.splitlines()) == 1 and os.path.exists(x): filename = x x = open(x).read() if filename.lower().endswith('.f90'): x = '!f90\n' + x from numpy import f2py # Create everything in a temporary directory mytmpdir = tmp_dir() try: old_cwd = os.getcwd() os.chdir(mytmpdir) old_import_path = os.sys.path os.sys.path.append(mytmpdir) name = "fortran_module" # Python module name # if the first line has !f90 as a comment, gfortran will # treat it as Fortran 90 code if x.startswith('!f90'): fortran_file = name + '.f90' else: fortran_file = name + '.f' s_lib_path = "" s_lib = "" for s in self.library_paths: s_lib_path = s_lib_path + "-L%s " for s in self.libraries: s_lib = s_lib + "-l%s "%s log = name + ".log" extra_args = '--quiet --f77exec=sage-inline-fortran --f90exec=sage-inline-fortran %s %s >"%s" 2>&1'%( s_lib_path, s_lib, log) f2py.compile(x, name, extra_args = extra_args, source_fn=fortran_file) log_string = open(log).read() # f2py.compile() doesn't raise any exception if it fails. # So we manually check whether the compiled file exists. # NOTE: the .so extension is used, even on OS X where .dylib # might be expected. soname = name + '.so' if not os.path.isfile(soname): raise RuntimeError("failed to compile Fortran code:\n" + log_string) if self.verbose: print log_string m = __builtin__.__import__(name) finally: os.sys.path = old_import_path os.chdir(old_cwd) try: import shutil shutil.rmtree(mytmpdir) except OSError: # This can fail for example over NFS pass for k, x in m.__dict__.iteritems(): if k[0] != '_': self.globals[k] = x
def eval(self, x, globals=None, locals=None): """ Compile fortran code ``x`` and adds the functions in it to ``globals``. INPUT: - ``x`` -- Fortran code - ``globals`` -- a dict to which to add the functions from the fortran module - ``locals`` -- ignored EXAMPLES:: sage: code = ''' ....: C FILE: FIB1.F ....: SUBROUTINE FIB(A,N) ....: C ....: C CALCULATE FIRST N FIBONACCI NUMBERS ....: C ....: INTEGER N ....: REAL*8 A(N) ....: DO I=1,N ....: IF (I.EQ.1) THEN ....: A(I) = 0.0D0 ....: ELSEIF (I.EQ.2) THEN ....: A(I) = 1.0D0 ....: ELSE ....: A(I) = A(I-1) + A(I-2) ....: ENDIF ....: ENDDO ....: END ....: C END FILE FIB1.F ....: ''' sage: fortran(code, globals()) sage: import numpy sage: a = numpy.array(range(10), dtype=float) sage: fib(a, 10) sage: a array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.]) TESTS:: sage: os.chdir(SAGE_ROOT) sage: fortran.eval("SYNTAX ERROR !@#$") Traceback (most recent call last): ... RuntimeError: failed to compile Fortran code:... sage: os.getcwd() == SAGE_ROOT True """ if len(x.splitlines()) == 1 and os.path.exists(x): from sage.misc.superseded import deprecation deprecation( 2891, "Calling fortran() with a filename is deprecated, use fortran(open(f).read) instead" ) filename = x x = open(x).read() if filename.lower().endswith('.f90'): x = '!f90\n' + x if globals is None: globals = self.globs from numpy import f2py # Create everything in a temporary directory mytmpdir = tmp_dir() try: old_cwd = os.getcwd() os.chdir(mytmpdir) name = "fortran_module" # Python module name # if the first line has !f90 as a comment, gfortran will # treat it as Fortran 90 code if x.startswith('!f90'): fortran_file = name + '.f90' else: fortran_file = name + '.f' s_lib_path = "" s_lib = "" for s in self.library_paths: s_lib_path = s_lib_path + "-L%s " for s in self.libraries: s_lib = s_lib + "-l%s " % s log = name + ".log" extra_args = '--quiet --f77exec=sage-inline-fortran --f90exec=sage-inline-fortran %s %s >"%s" 2>&1' % ( s_lib_path, s_lib, log) f2py.compile(x, name, extra_args=extra_args, source_fn=fortran_file) log_string = open(log).read() # Note that f2py() doesn't raise an exception if it fails. # In that case, the import below will fail. try: file, pathname, description = imp.find_module(name, [mytmpdir]) except ImportError: raise RuntimeError("failed to compile Fortran code:\n" + log_string) try: m = imp.load_module(name, file, pathname, description) finally: file.close() if self.verbose: print(log_string) finally: os.chdir(old_cwd) try: shutil.rmtree(mytmpdir) except OSError: # This can fail for example over NFS pass for k, x in m.__dict__.iteritems(): if k[0] != '_': globals[k] = x
def eval(self, x, globals=None, locals=None): """ Compile fortran code ``x`` and adds the functions in it to ``globals``. INPUT: - ``x`` -- Fortran code - ``globals`` -- a dict to which to add the functions from the fortran module - ``locals`` -- ignored EXAMPLES:: sage: code = ''' ....: C FILE: FIB1.F ....: SUBROUTINE FIB(A,N) ....: C ....: C CALCULATE FIRST N FIBONACCI NUMBERS ....: C ....: INTEGER N ....: REAL*8 A(N) ....: DO I=1,N ....: IF (I.EQ.1) THEN ....: A(I) = 0.0D0 ....: ELSEIF (I.EQ.2) THEN ....: A(I) = 1.0D0 ....: ELSE ....: A(I) = A(I-1) + A(I-2) ....: ENDIF ....: ENDDO ....: END ....: C END FILE FIB1.F ....: ''' sage: fortran(code, globals()) sage: import numpy sage: a = numpy.array(range(10), dtype=float) sage: fib(a, 10) sage: a array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.]) TESTS:: sage: os.chdir(SAGE_ROOT) sage: fortran.eval("SYNTAX ERROR !@#$") Traceback (most recent call last): ... RuntimeError: failed to compile Fortran code:... sage: os.getcwd() == SAGE_ROOT True """ if len(x.splitlines()) == 1 and os.path.exists(x): from sage.misc.superseded import deprecation deprecation(2891, "Calling fortran() with a filename is deprecated, use fortran(open(f).read) instead") filename = x x = open(x).read() if filename.lower().endswith('.f90'): x = '!f90\n' + x if globals is None: globals = self.globs from numpy import f2py # Create everything in a temporary directory mytmpdir = tmp_dir() try: old_cwd = os.getcwd() os.chdir(mytmpdir) name = "fortran_module" # Python module name # if the first line has !f90 as a comment, gfortran will # treat it as Fortran 90 code if x.startswith('!f90'): fortran_file = name + '.f90' else: fortran_file = name + '.f' s_lib_path = "" s_lib = "" for s in self.library_paths: s_lib_path = s_lib_path + "-L%s " for s in self.libraries: s_lib = s_lib + "-l%s "%s log = name + ".log" extra_args = '--quiet --f77exec=sage-inline-fortran --f90exec=sage-inline-fortran %s %s >"%s" 2>&1'%( s_lib_path, s_lib, log) f2py.compile(x, name, extra_args = extra_args, source_fn=fortran_file) log_string = open(log).read() # Note that f2py() doesn't raise an exception if it fails. # In that case, the import below will fail. try: file, pathname, description = imp.find_module(name, [mytmpdir]) except ImportError: raise RuntimeError("failed to compile Fortran code:\n" + log_string) try: m = imp.load_module(name, file, pathname, description) finally: file.close() if self.verbose: print(log_string) finally: os.chdir(old_cwd) try: shutil.rmtree(mytmpdir) except OSError: # This can fail for example over NFS pass for k, x in m.__dict__.iteritems(): if k[0] != '_': globals[k] = x
def run_val_gdb(self, testing=False): """ Spawns a subprocess to run tests under the control of gdb or valgrind. INPUT: - ``testing`` -- boolean; if True then the command to be run will be printed rather than a subprocess started. EXAMPLES: Note that the command lines include unexpanded environment variables. It is safer to let the shell expand them than to expand them here and risk insufficient quoting. :: sage: from sage.doctest.control import DocTestDefaults, DocTestController sage: DD = DocTestDefaults(gdb=True) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) exec gdb -x "...sage-gdb-commands" --args sage-runtests --serial --timeout=0 hello_world.py :: sage: DD = DocTestDefaults(valgrind=True, optional="all", timeout=172800) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) exec valgrind --tool=memcheck --leak-resolution=high --leak-check=full --num-callers=25 --suppressions="...valgrind/pyalloc.supp" --suppressions="...valgrind/sage.supp" --suppressions="...valgrind/sage-additional.supp" --log-file=".../valgrind/sage-memcheck.%p" sage-runtests --serial --timeout=172800 --optional=all hello_world.py """ try: sage_cmd = self._assemble_cmd() except ValueError: self.log(sys.exc_info()[1]) return 2 opt = self.options if opt.gdb: cmd = '''exec gdb -x "%s" --args ''' % (os.path.join( SAGE_VENV, "bin", "sage-gdb-commands")) flags = "" if opt.logfile: sage_cmd += " --logfile %s" % (opt.logfile) else: if opt.logfile is None: default_log = os.path.join(DOT_SAGE, "valgrind") if os.path.exists(default_log): if not os.path.isdir(default_log): self.log("%s must be a directory" % default_log) return 2 else: os.makedirs(default_log) logfile = os.path.join(default_log, "sage-%s") else: logfile = opt.logfile if opt.valgrind: toolname = "memcheck" flags = os.getenv("SAGE_MEMCHECK_FLAGS") if flags is None: flags = "--leak-resolution=high --leak-check=full --num-callers=25 " flags += '''--suppressions="%s" ''' % (os.path.join( SAGE_EXTCODE, "valgrind", "pyalloc.supp")) flags += '''--suppressions="%s" ''' % (os.path.join( SAGE_EXTCODE, "valgrind", "sage.supp")) flags += '''--suppressions="%s" ''' % (os.path.join( SAGE_EXTCODE, "valgrind", "sage-additional.supp")) elif opt.massif: toolname = "massif" flags = os.getenv("SAGE_MASSIF_FLAGS", "--depth=6 ") elif opt.cachegrind: toolname = "cachegrind" flags = os.getenv("SAGE_CACHEGRIND_FLAGS", "") elif opt.omega: toolname = "exp-omega" flags = os.getenv("SAGE_OMEGA_FLAGS", "") cmd = "exec valgrind --tool=%s " % (toolname) flags += ''' --log-file="%s" ''' % logfile if opt.omega: toolname = "omega" if "%s" in flags: flags %= toolname + ".%p" # replace %s with toolname cmd += flags + sage_cmd sys.stdout.flush() sys.stderr.flush() self.log(cmd) if testing: return # Setup signal handlers. # Save crash logs in temporary directory. os.putenv('CYSIGNALS_CRASH_LOGS', tmp_dir("crash_logs_")) init_cysignals() import signal import subprocess p = subprocess.Popen(cmd, shell=True) if opt.timeout > 0: signal.alarm(opt.timeout) try: return p.wait() except AlarmInterrupt: self.log(" Timed out") return 4 except KeyboardInterrupt: self.log(" Interrupted") return 128 finally: signal.alarm(0) if p.returncode is None: p.terminate()
def eval(self, x, globals=None, locals=None): """ Compile fortran code ``x`` and adds the functions in it to ``globals``. INPUT: - ``x`` -- Fortran code - ``globals`` -- a dict to which to add the functions from the fortran module - ``locals`` -- ignored EXAMPLES:: sage: code = ''' ....: C FILE: FIB1.F ....: SUBROUTINE FIB(A,N) ....: C ....: C CALCULATE FIRST N FIBONACCI NUMBERS ....: C ....: INTEGER N ....: REAL*8 A(N) ....: DO I=1,N ....: IF (I.EQ.1) THEN ....: A(I) = 0.0D0 ....: ELSEIF (I.EQ.2) THEN ....: A(I) = 1.0D0 ....: ELSE ....: A(I) = A(I-1) + A(I-2) ....: ENDIF ....: ENDDO ....: END ....: C END FILE FIB1.F ....: ''' sage: fortran(code, globals()) sage: import numpy sage: a = numpy.array(range(10), dtype=float) sage: fib(a, 10) sage: a array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.]) TESTS:: sage: os.chdir(SAGE_ROOT) sage: fortran.eval("SYNTAX ERROR !@#$") Traceback (most recent call last): ... RuntimeError: failed to compile Fortran code:... sage: os.getcwd() == SAGE_ROOT True """ if globals is None: globals = self.globs if globals is None: from sage.repl.user_globals import get_globals globals = get_globals() from numpy import f2py # Create everything in a temporary directory mytmpdir = tmp_dir() try: old_cwd = os.getcwd() os.chdir(mytmpdir) name = "fortran_module" # Python module name # if the first line has !f90 as a comment, gfortran will # treat it as Fortran 90 code if x.startswith('!f90'): fortran_file = name + '.f90' else: fortran_file = name + '.f' s_lib_path = "" s_lib = "" for s in self.library_paths: s_lib_path = s_lib_path + "-L%s " for s in self.libraries: s_lib = s_lib + "-l%s "%s log = name + ".log" extra_args = ('--quiet --f77exec=sage-inline-fortran ' '--f90exec=sage-inline-fortran {lib_path} {lib} ' '> {log} 2>&1'.format(lib_path=s_lib_path, lib=s_lib, log=log)) f2py.compile(x, name, extra_args=extra_args, source_fn=fortran_file) with open(log) as fobj: log_string = fobj.read() # Note that f2py() doesn't raise an exception if it fails. # In that case, the import below will fail. try: mod = _import_module_from_path(name, [mytmpdir]) except ImportError: raise RuntimeError("failed to compile Fortran code:\n" + log_string) if self.verbose: print(log_string) finally: os.chdir(old_cwd) if sys.platform != 'cygwin': # Do not delete temporary DLLs on Cygwin; this will cause # future forks of this process to fail. Instead temporary DLLs # will be cleaned up upon process exit try: shutil.rmtree(mytmpdir) except OSError: # This can fail for example over NFS pass for k, x in iteritems(mod.__dict__): if k[0] != '_': globals[k] = x
import csv import os from IPython.display import display, Image from sage.matrix.constructor import Matrix from sage.misc.temporary_file import tmp_dir, tmp_filename dirname = tmp_dir() def nb_show(graphics_object, ext='.png'): name = os.path.join(dirname, tmp_filename(ext=ext)) graphics_object.save(name) display(Image(name)) def matrix_of_csv(fname): with open(fname, 'rb') as csvfile: mreader = csv.reader(csvfile, delimiter=',', quotechar="'") return Matrix([[int(x) for x in row] for row in mreader])
def __call__(self, f, inputs): """ Parallel iterator using ``fork()``. INPUT: - ``f`` -- a function (or more general, any callable) - ``inputs`` -- a list of pairs ``(args, kwds)`` to be used as arguments to ``f``, where ``args`` is a tuple and ``kwds`` is a dictionary. OUTPUT: EXAMPLES:: sage: F = sage.parallel.use_fork.p_iter_fork(2,3) sage: sorted(list( F( (lambda x: x^2), [([10],{}), ([20],{})]))) [(([10], {}), 100), (([20], {}), 400)] sage: sorted(list( F( (lambda x, y: x^2+y), [([10],{'y':1}), ([20],{'y':2})]))) [(([10], {'y': 1}), 101), (([20], {'y': 2}), 402)] TESTS: The output of functions decorated with :func:`parallel` is read as a pickle by the parent process. We intentionally break the unpickling and demonstrate that this failure is handled gracefully (the exception is put in the list instead of the answer):: sage: Polygen = parallel(polygen) sage: list(Polygen([QQ])) [(((Rational Field,), {}), x)] sage: from sage.misc.persist import unpickle_override, register_unpickle_override sage: register_unpickle_override('sage.rings.polynomial.polynomial_rational_flint', 'Polynomial_rational_flint', Integer) sage: L = list(Polygen([QQ])) sage: L [(((Rational Field,), {}), 'INVALID DATA __init__() takes at most 2 positional arguments (4 given)')] Fix the unpickling:: sage: del unpickle_override[('sage.rings.polynomial.polynomial_rational_flint', 'Polynomial_rational_flint')] sage: list(Polygen([QQ,QQ])) [(((Rational Field,), {}), x), (((Rational Field,), {}), x)] """ n = self.ncpus v = list(inputs) import os import sys import signal from sage.misc.persist import loads from sage.misc.temporary_file import tmp_dir dir = tmp_dir() timeout = self.timeout workers = {} try: while v or workers: # Spawn up to n subprocesses while v and len(workers) < n: v0 = v.pop(0) # Input value for the next subprocess with ContainChildren(): pid = os.fork() # The way fork works is that pid returns the # nonzero pid of the subprocess for the master # process and returns 0 for the subprocess. if not pid: # This is the subprocess. self._subprocess(f, dir, *v0) workers[pid] = WorkerData(v0) if len(workers) > 0: # Now wait for one subprocess to finish and report the result. # However, wait at most the time since the oldest process started. T = walltime() if timeout: oldest = min(W.starttime for W in workers.values()) alarm(max(timeout - (T - oldest), 0.1)) try: pid = os.wait()[0] cancel_alarm() W = workers.pop(pid) except AlarmInterrupt: # Kill workers that are too old for pid, W in workers.items(): if T - W.starttime > timeout: if self.verbose: print( "Killing subprocess %s with input %s which took too long" % (pid, W.input)) os.kill(pid, signal.SIGKILL) W.failure = " (timed out)" except KeyError: # Some other process exited, not our problem... pass else: # collect data from process that successfully terminated sobj = os.path.join(dir, '%s.sobj' % pid) try: with open(sobj, "rb") as file: data = file.read() except IOError: answer = "NO DATA" + W.failure else: os.unlink(sobj) try: answer = loads(data, compress=False) except Exception as E: answer = "INVALID DATA {}".format(E) out = os.path.join(dir, '%s.out' % pid) try: with open(out) as file: sys.stdout.write(file.read()) os.unlink(out) except IOError: pass yield (W.input, answer) finally: # Send SIGKILL signal to workers that are left. if workers: if self.verbose: print("Killing any remaining workers...") sys.stdout.flush() for pid in workers: try: os.kill(pid, signal.SIGKILL) except OSError: # If kill() failed, it is most likely because # the process already exited. pass else: try: os.waitpid(pid, 0) except OSError as msg: if self.verbose: print(msg) # Clean up all temporary files. rmtree(dir)
def eval(self, x, globals=None, locals=None): """ EXAMPLES:: sage: from sage.misc.inline_fortran import InlineFortran, _example sage: fortran = InlineFortran(globals()) sage: fortran(_example) sage: import numpy sage: n = numpy.array(range(10),dtype=float) sage: fib(n,int(10)) sage: n array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.]) TESTS:: sage: os.chdir(SAGE_ROOT) sage: fortran.eval("SYNTAX ERROR !@#$") Traceback (most recent call last): ... RuntimeError: failed to compile Fortran code:... sage: os.getcwd() == SAGE_ROOT True """ if len(x.splitlines()) == 1 and os.path.exists(x): filename = x x = open(x).read() if filename.lower().endswith('.f90'): x = '!f90\n' + x from numpy import f2py # Create everything in a temporary directory mytmpdir = tmp_dir() try: old_cwd = os.getcwd() os.chdir(mytmpdir) old_import_path = os.sys.path os.sys.path.append(mytmpdir) name = "fortran_module" # Python module name # if the first line has !f90 as a comment, gfortran will # treat it as Fortran 90 code if x.startswith('!f90'): fortran_file = name + '.f90' else: fortran_file = name + '.f' s_lib_path = "" s_lib = "" for s in self.library_paths: s_lib_path = s_lib_path + "-L%s " for s in self.libraries: s_lib = s_lib + "-l%s " % s log = name + ".log" extra_args = '--quiet --f77exec=sage-inline-fortran --f90exec=sage-inline-fortran %s %s >"%s" 2>&1' % ( s_lib_path, s_lib, log) f2py.compile(x, name, extra_args=extra_args, source_fn=fortran_file) log_string = open(log).read() # f2py.compile() doesn't raise any exception if it fails. # So we manually check whether the compiled file exists. # NOTE: the .so extension is used expect on Cygwin, # that is even on OS X where .dylib might be expected. soname = name uname = os.uname()[0].lower() if uname[:6] == "cygwin": soname += '.dll' else: soname += '.so' if not os.path.isfile(soname): raise RuntimeError("failed to compile Fortran code:\n" + log_string) if self.verbose: print(log_string) m = __builtin__.__import__(name) finally: os.sys.path = old_import_path os.chdir(old_cwd) try: import shutil shutil.rmtree(mytmpdir) except OSError: # This can fail for example over NFS pass for k, x in m.__dict__.iteritems(): if k[0] != '_': self.globals[k] = x
def eval(self, x, globals=None, locals=None): """ Compile fortran code ``x`` and adds the functions in it to ``globals``. INPUT: - ``x`` -- Fortran code - ``globals`` -- a dict to which to add the functions from the fortran module - ``locals`` -- ignored EXAMPLES:: sage: code = ''' ....: C FILE: FIB1.F ....: SUBROUTINE FIB(A,N) ....: C ....: C CALCULATE FIRST N FIBONACCI NUMBERS ....: C ....: INTEGER N ....: REAL*8 A(N) ....: DO I=1,N ....: IF (I.EQ.1) THEN ....: A(I) = 0.0D0 ....: ELSEIF (I.EQ.2) THEN ....: A(I) = 1.0D0 ....: ELSE ....: A(I) = A(I-1) + A(I-2) ....: ENDIF ....: ENDDO ....: END ....: C END FILE FIB1.F ....: ''' sage: fortran(code, globals()) sage: import numpy sage: a = numpy.array(range(10), dtype=float) sage: fib(a, 10) sage: a array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.]) TESTS:: sage: os.chdir(DOT_SAGE) sage: fortran.eval("SYNTAX ERROR !@#$") Traceback (most recent call last): ... RuntimeError: failed to compile Fortran code:... sage: os.getcwd() == os.path.realpath(DOT_SAGE) True """ if globals is None: globals = self.globs if globals is None: from sage.repl.user_globals import get_globals globals = get_globals() # Create everything in a temporary directory mytmpdir = tmp_dir() try: old_cwd = os.getcwd() os.chdir(mytmpdir) name = "fortran_module" # Python module name # if the first line has !f90 as a comment, gfortran will # treat it as Fortran 90 code if x.startswith('!f90'): fortran_file = name + '.f90' else: fortran_file = name + '.f' s_lib_path = ['-L' + p for p in self.library_paths] s_lib = ['-l' + l for l in self.libraries] with open(fortran_file, 'w') as fobj: fobj.write(x) # This is basically the same as what f2py.compile() does, but we # do it manually here in order to customize running the subprocess # a bit more (in particular to capture stderr) cmd = [ sys.executable, '-c', 'import numpy.f2py; numpy.f2py.main()' ] # What follows are the arguments to f2py itself (appended later # just for logical separation) cmd += [ '-c', '-m', name, fortran_file, '--quiet', '--f77exec=sage-inline-fortran', '--f90exec=sage-inline-fortran' ] + s_lib_path + s_lib try: out = subprocess.check_output(cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exc: raise RuntimeError( "failed to compile Fortran code:\n{}".format(exc.output)) # Note that f2py() doesn't raise an exception if it fails. # In that case, the import below will fail. try: mod = _import_module_from_path(name, [mytmpdir]) except ImportError as exc: # Failed to import the module; include any output from building # the module (even though it was ostensibly successful) in case # it might help msg = "failed to load compiled Fortran code: {}".format(exc) if out: msg += '\n' + out raise RuntimeError(msg) if self.verbose: print(out) finally: os.chdir(old_cwd) if sys.platform != 'cygwin': # Do not delete temporary DLLs on Cygwin; this will cause # future forks of this process to fail. Instead temporary DLLs # will be cleaned up upon process exit try: shutil.rmtree(mytmpdir) except OSError: # This can fail for example over NFS pass for k, x in mod.__dict__.items(): if k[0] != '_': globals[k] = x
def run_val_gdb(self, testing=False): """ Spawns a subprocess to run tests under the control of gdb or valgrind. INPUT: - ``testing`` -- boolean; if True then the command to be run will be printed rather than a subprocess started. EXAMPLES: Note that the command lines include unexpanded environment variables. It is safer to let the shell expand them than to expand them here and risk insufficient quoting. :: sage: from sage.doctest.control import DocTestDefaults, DocTestController sage: DD = DocTestDefaults(gdb=True) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) exec gdb -x "$SAGE_LOCAL/bin/sage-gdb-commands" --args python "$SAGE_LOCAL/bin/sage-runtests" --serial --timeout=0 hello_world.py :: sage: DD = DocTestDefaults(valgrind=True, optional="all", timeout=172800) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) exec valgrind --tool=memcheck --leak-resolution=high --leak-check=full --num-callers=25 --suppressions="$SAGE_EXTCODE/valgrind/pyalloc.supp" --suppressions="$SAGE_EXTCODE/valgrind/sage.supp" --suppressions="$SAGE_EXTCODE/valgrind/sage-additional.supp" --log-file=".../valgrind/sage-memcheck.%p" python "$SAGE_LOCAL/bin/sage-runtests" --serial --timeout=172800 --optional=all hello_world.py """ try: sage_cmd = self._assemble_cmd() except ValueError: self.log(sys.exc_info()[1]) return 2 opt = self.options if opt.gdb: cmd = '''exec gdb -x "$SAGE_LOCAL/bin/sage-gdb-commands" --args ''' flags = "" if opt.logfile: sage_cmd += " --logfile %s"%(opt.logfile) else: if opt.logfile is None: default_log = os.path.join(DOT_SAGE, "valgrind") if os.path.exists(default_log): if not os.path.isdir(default_log): self.log("%s must be a directory"%default_log) return 2 else: os.makedirs(default_log) logfile = os.path.join(default_log, "sage-%s") else: logfile = opt.logfile if opt.valgrind: toolname = "memcheck" flags = os.getenv("SAGE_MEMCHECK_FLAGS") if flags is None: flags = "--leak-resolution=high --leak-check=full --num-callers=25 " flags += '''--suppressions="%s" '''%(os.path.join("$SAGE_EXTCODE","valgrind","pyalloc.supp")) flags += '''--suppressions="%s" '''%(os.path.join("$SAGE_EXTCODE","valgrind","sage.supp")) flags += '''--suppressions="%s" '''%(os.path.join("$SAGE_EXTCODE","valgrind","sage-additional.supp")) elif opt.massif: toolname = "massif" flags = os.getenv("SAGE_MASSIF_FLAGS", "--depth=6 ") elif opt.cachegrind: toolname = "cachegrind" flags = os.getenv("SAGE_CACHEGRIND_FLAGS", "") elif opt.omega: toolname = "exp-omega" flags = os.getenv("SAGE_OMEGA_FLAGS", "") cmd = "exec valgrind --tool=%s "%(toolname) flags += ''' --log-file="%s" ''' % logfile if opt.omega: toolname = "omega" if "%s" in flags: flags %= toolname + ".%p" # replace %s with toolname cmd += flags + sage_cmd sys.stdout.flush() sys.stderr.flush() self.log(cmd) if testing: return # Setup signal handlers. # Save crash logs in temporary directory. os.putenv('CYSIGNALS_CRASH_LOGS', tmp_dir("crash_logs_")) init_cysignals() import signal, subprocess p = subprocess.Popen(cmd, shell=True) if opt.timeout > 0: signal.alarm(opt.timeout) try: return p.wait() except AlarmInterrupt: self.log(" Timed out") return 4 except KeyboardInterrupt: self.log(" Interrupted") return 128 finally: signal.alarm(0) if p.returncode is None: p.terminate()
def __call__(self, f, inputs): """ Parallel iterator using ``fork()``. INPUT: - ``f`` -- a function (or more general, any callable) - ``inputs`` -- a list of pairs ``(args, kwds)`` to be used as arguments to ``f``, where ``args`` is a tuple and ``kwds`` is a dictionary. OUTPUT: EXAMPLES:: sage: F = sage.parallel.use_fork.p_iter_fork(2,3) sage: sorted(list( F( (lambda x: x^2), [([10],{}), ([20],{})]))) [(([10], {}), 100), (([20], {}), 400)] sage: sorted(list( F( (lambda x, y: x^2+y), [([10],{'y':1}), ([20],{'y':2})]))) [(([10], {'y': 1}), 101), (([20], {'y': 2}), 402)] TESTS: The output of functions decorated with :func:`parallel` is read as a pickle by the parent process. We intentionally break the unpickling and demonstrate that this failure is handled gracefully (the exception is put in the list instead of the answer):: sage: Polygen = parallel(polygen) sage: list(Polygen([QQ])) [(((Rational Field,), {}), x)] sage: from sage.structure.sage_object import unpickle_override, register_unpickle_override sage: register_unpickle_override('sage.rings.polynomial.polynomial_rational_flint', 'Polynomial_rational_flint', Integer) sage: L = list(Polygen([QQ])) sage: L [(((Rational Field,), {}), 'INVALID DATA __init__() takes at most 2 positional arguments (4 given)')] Fix the unpickling:: sage: del unpickle_override[('sage.rings.polynomial.polynomial_rational_flint', 'Polynomial_rational_flint')] sage: list(Polygen([QQ,QQ])) [(((Rational Field,), {}), x), (((Rational Field,), {}), x)] """ n = self.ncpus v = list(inputs) import os, sys, signal from sage.structure.sage_object import loads from sage.misc.temporary_file import tmp_dir dir = tmp_dir() timeout = self.timeout workers = {} try: while len(v) > 0 or len(workers) > 0: # Spawn up to n subprocesses while len(v) > 0 and len(workers) < n: v0 = v.pop(0) # Input value for the next subprocess with ContainChildren(): pid = os.fork() # The way fork works is that pid returns the # nonzero pid of the subprocess for the master # process and returns 0 for the subprocess. if not pid: # This is the subprocess. self._subprocess(f, dir, *v0) workers[pid] = WorkerData(v0) if len(workers) > 0: # Now wait for one subprocess to finish and report the result. # However, wait at most the time since the oldest process started. T = walltime() if timeout: oldest = min(W.starttime for W in workers.values()) alarm(max(timeout - (T - oldest), 0.1)) try: pid = os.wait()[0] cancel_alarm() W = workers.pop(pid) except AlarmInterrupt: # Kill workers that are too old for pid, W in workers.items(): if T - W.starttime > timeout: if self.verbose: print( "Killing subprocess %s with input %s which took too long" % (pid, W.input) ) os.kill(pid, signal.SIGKILL) W.failure = " (timed out)" except KeyError: # Some other process exited, not our problem... pass else: # collect data from process that successfully terminated sobj = os.path.join(dir, '%s.sobj'%pid) try: with open(sobj) as file: data = file.read() except IOError: answer = "NO DATA" + W.failure else: os.unlink(sobj) try: answer = loads(data, compress=False) except Exception as E: answer = "INVALID DATA {}".format(E) out = os.path.join(dir, '%s.out'%pid) try: with open(out) as file: sys.stdout.write(file.read()) os.unlink(out) except IOError: pass yield (W.input, answer) finally: # Send SIGKILL signal to workers that are left. if workers: if self.verbose: print("Killing any remaining workers...") sys.stdout.flush() for pid in workers: try: os.kill(pid, signal.SIGKILL) except OSError: # If kill() failed, it is most likely because # the process already exited. pass else: try: os.waitpid(pid, 0) except OSError as msg: if self.verbose: print(msg) # Clean up all temporary files. rmtree(dir)