From 17d23e9f81355875d60944359c67815c6acddaad Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 23 Jun 2015 10:15:48 +0200 Subject: [PATCH] Fix interrupting Singular --- src/sage/interfaces/expect.py | 9 +++- src/sage/interfaces/sagespawn.pyx | 36 ++++++++++++++++ src/sage/interfaces/singular.py | 68 +++++++++++++++++++++---------- 3 files changed, 89 insertions(+), 24 deletions(-) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index 034df830ed3..789da270163 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -822,10 +822,15 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if sage: singular._eval_using_file_cutoff = 4 sage: singular._eval_line('for(int i=1;i<=3;i++){i=1;};', wait_for_prompt=False) '' - sage: singular.interrupt(timeout=3) # sometimes very slow (up to 60s on sage.math, 2012) - False + sage: singular.interrupt() + True sage: singular._eval_using_file_cutoff = cutoff + The interface still works after this interrupt:: + + sage: singular('2+3') + 5 + Last, we demonstrate that by default the execution of a command is tried twice if it fails the first time due to a crashed interface:: diff --git a/src/sage/interfaces/sagespawn.pyx b/src/sage/interfaces/sagespawn.pyx index 911f37b653f..f77569673b2 100644 --- a/src/sage/interfaces/sagespawn.pyx +++ b/src/sage/interfaces/sagespawn.pyx @@ -109,6 +109,42 @@ class SageSpawn(spawn): """ Py_INCREF(self) + def expect_peek(self, *args, **kwds): + """ + Like :meth:`expect` but restore the read buffer such that it + looks like nothing was actually read. The next reading will + continue at the current position. + + EXAMPLES:: + + sage: from sage.interfaces.sagespawn import SageSpawn + sage: E = SageSpawn("sh", ["-c", "echo hello world"]) + sage: _ = E.expect_peek("w") + sage: E.read() + 'hello world\r\n' + """ + ret = self.expect(*args, **kwds) + self.buffer = self.before + self.after + self.buffer + return ret + + def expect_upto(self, *args, **kwds): + """ + Like :meth:`expect` but restore the read buffer starting from + the matched string. The next reading will continue starting + with the matched string. + + EXAMPLES:: + + sage: from sage.interfaces.sagespawn import SageSpawn + sage: E = SageSpawn("sh", ["-c", "echo hello world"]) + sage: _ = E.expect_upto("w") + sage: E.read() + 'world\r\n' + """ + ret = self.expect(*args, **kwds) + self.buffer = self.after + self.buffer + return ret + def close(self): """ Quit the child process: send the quit string, close the diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index c8f82f3d771..6452d71f5f8 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -304,37 +304,22 @@ '' """ - - -#We could also do these calculations without using the singular -#interface (behind the scenes the interface is used by Sage): -# sage: x, y = PolynomialRing(RationalField(), 2, names=['x','y']).gens() -# sage: C = ProjectivePlaneCurve(y**9 - x**2*(x-1)**9) -# sage: C.genus() -# 0 -# sage: C = ProjectivePlaneCurve(y**9 - x**2*(x-1)**9 + x) -# sage: C.genus() -# 40 - #***************************************************************************** # Copyright (C) 2005 David Joyner and William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + import os import re import sys import pexpect +from time import sleep from expect import Expect, ExpectElement, FunctionElement, ExpectFunction @@ -386,7 +371,9 @@ def __init__(self, maxread=1000, script_subdirectory=None, terminal_echo=False, name = 'singular', prompt = prompt, - command = "Singular -t --ticks-per-sec 1000", #no tty and fine grained cputime() + # no tty, fine grained cputime() + # and do not display CTRL-C prompt + command = "Singular -t --ticks-per-sec 1000 --cntrlc=a", maxread = maxread, server = server, server_tmpdir = server_tmpdir, @@ -468,6 +455,43 @@ def _quit_string(self): """ return 'quit' + def _send_interrupt(self): + """ + Send an interrupt to Singular. If needed, additional + semi-colons are sent until we get back at the prompt. + + TESTS: + + The following works without restarting Singular:: + + sage: a = singular(1) + sage: _ = singular._expect.sendline('1+') # unfinished input + sage: try: + ....: alarm(0.5) + ....: singular._expect_expr('>') # interrupt this + ....: except KeyboardInterrupt: + ....: pass + Control-C pressed. Interrupting Singular. Please wait a few seconds... + + We can still access a:: + + sage: 2*a + 2 + """ + # Work around for Singular bug + # http://www.singular.uni-kl.de:8002/trac/ticket/727 + sleep(0.1) + + E = self._expect + E.sendline(chr(3)) + for i in range(5): + try: + E.expect_upto(self._prompt, timeout=1.0) + return + except Exception: + pass + E.sendline(";") + def _read_in_file_command(self, filename): r""" EXAMPLES::