Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/interfaces/expect.py
8814 views
1
"""
2
Common Interface Functionality through Pexpect
3
4
See the examples in the other sections for how to use specific
5
interfaces. The interface classes all derive from the generic
6
interface that is described in this section.
7
8
AUTHORS:
9
10
- William Stein (2005): initial version
11
12
- William Stein (2006-03-01): got rid of infinite loop on startup if
13
client system missing
14
15
- Felix Lawrence (2009-08-21): edited ._sage_() to support lists and float exponents in foreign notation.
16
17
- Simon King (2010-09-25): Expect._local_tmpfile() depends on
18
Expect.pid() and is cached; Expect.quit() clears that cache,
19
which is important for forking.
20
21
- Jean-Pierre Flori (2010,2011): Split non Pexpect stuff into a parent class.
22
23
- Simon King (2010-11-23): Ensure that the interface is started again
24
after a crash, when a command is executed in _eval_line. Allow
25
synchronisation of the GAP interface.
26
27
"""
28
29
#*****************************************************************************
30
# Copyright (C) 2005 William Stein <[email protected]>
31
#
32
# Distributed under the terms of the GNU General Public License (GPL)
33
#
34
# This code is distributed in the hope that it will be useful,
35
# but WITHOUT ANY WARRANTY; without even the implied warranty of
36
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
37
# General Public License for more details.
38
#
39
# The full text of the GPL is available at:
40
#
41
# http://www.gnu.org/licenses/
42
#*****************************************************************************
43
44
import os
45
import sys
46
import weakref
47
import time
48
import gc
49
import operator
50
import quit
51
import cleaner
52
from random import randrange
53
54
########################################################
55
# Important note: We use Pexpect 2.0 *not* Pexpect 2.1.
56
# For reasons I don't understand, pexpect2.1 is much
57
# worse than pexpect 2.0 for everything Sage does.
58
########################################################
59
import pexpect
60
from pexpect import ExceptionPexpect
61
from sage.interfaces.interface import Interface, InterfaceElement, InterfaceFunction, InterfaceFunctionElement, AsciiArtString
62
63
from sage.structure.sage_object import SageObject
64
from sage.structure.parent_base import ParentWithBase
65
from sage.structure.element import RingElement
66
67
import sage.misc.sage_eval
68
from sage.misc.misc import SAGE_EXTCODE, verbose, SAGE_TMP_INTERFACE, LOCAL_IDENTIFIER
69
from sage.misc.object_multiplexer import Multiplex
70
71
BAD_SESSION = -2
72
73
failed_to_start = []
74
75
# The subprocess is a shared resource. In a multi-threaded
76
# environment, there would have to be a lock to control access to the
77
# subprocess. Fortunately, Sage does not use Python threads.
78
# Unfortunately, the combination of the garbage collector and __del__
79
# methods gives rise to the same issues. So, in places where we need
80
# to do a sequence of operations on the subprocess and make sure
81
# nothing else intervenes (for example, when we write a command and
82
# then read back the result) we need to disable the garbage collector.
83
# See TRAC #955 for a more detailed description of this problem.
84
85
# To turn off the garbage collector for a particular region of code,
86
# do:
87
# with gc_disabled():
88
# ... your code goes here ...
89
# The garbage collector will be returned to its original state
90
# whenever the code exits by any means (falling off the end, executing
91
# "return", "break", or "continue", raising an exception, ...)
92
93
class gc_disabled(object):
94
"""
95
This is a "with" statement context manager. Garbage collection is
96
disabled within its scope. Nested usage is properly handled.
97
98
EXAMPLES::
99
100
sage: import gc
101
sage: from sage.interfaces.expect import gc_disabled
102
sage: gc.isenabled()
103
True
104
sage: with gc_disabled():
105
... print gc.isenabled()
106
... with gc_disabled():
107
... print gc.isenabled()
108
... print gc.isenabled()
109
False
110
False
111
False
112
sage: gc.isenabled()
113
True
114
"""
115
def __enter__(self):
116
self._enabled = gc.isenabled()
117
gc.disable()
118
119
def __exit__(self, ty, val, tb):
120
if self._enabled:
121
gc.enable()
122
return False
123
124
class Expect(Interface):
125
"""
126
Expect interface object.
127
"""
128
def __init__(self, name, prompt, command=None, server=None, server_tmpdir=None,
129
ulimit = None, maxread=100000,
130
script_subdirectory="", restart_on_ctrlc=False,
131
verbose_start=False, init_code=[], max_startup_time=None,
132
logfile = None, eval_using_file_cutoff=0,
133
do_cleaner = True, remote_cleaner = False, path=None):
134
135
Interface.__init__(self, name)
136
self.__is_remote = False
137
self.__remote_cleaner = remote_cleaner
138
if command == None:
139
command = name
140
if not server is None:
141
if ulimit:
142
command = 'sage-native-execute ssh -t %s "ulimit %s; %s"'%(server, ulimit, command)
143
else:
144
command = "sage-native-execute ssh -t %s %s"%(server, command)
145
self.__is_remote = True
146
# eval_using_file_cutoff = 0 # don't allow this!
147
if verbose_start:
148
print "Using remote server"
149
print command
150
self._server = server
151
if server_tmpdir is None:
152
# TO DO: Why default to /tmp/? Might be better to use the expect process itself to get a tmp folder
153
print "No remote temporary directory (option server_tmpdir) specified, using /tmp/ on "+server
154
self.__remote_tmpdir = "/tmp/"
155
else:
156
self.__remote_tmpdir = server_tmpdir
157
else:
158
self._server = None
159
self.__do_cleaner = do_cleaner
160
self.__maxread = maxread
161
self._eval_using_file_cutoff = eval_using_file_cutoff
162
self.__script_subdirectory = script_subdirectory
163
self.__command = command
164
self._prompt = prompt
165
self._restart_on_ctrlc = restart_on_ctrlc
166
self.__verbose_start = verbose_start
167
if not path is None:
168
self.__path = path
169
elif script_subdirectory is None:
170
self.__path = '.'
171
else:
172
self.__path = os.path.join(SAGE_EXTCODE,name,self.__script_subdirectory)
173
self.__initialized = False
174
self.__seq = -1
175
self._expect = None
176
self._session_number = 0
177
self.__init_code = init_code
178
self.__max_startup_time = max_startup_time
179
180
#Handle the log file
181
if isinstance(logfile, basestring):
182
logfile = open(logfile,'w')
183
self.__logfile = logfile
184
185
quit.expect_objects.append(weakref.ref(self))
186
self._available_vars = []
187
188
def _get(self, wait=0.1, alternate_prompt=None):
189
if self._expect is None:
190
self._start()
191
E = self._expect
192
wait=float(wait)
193
try:
194
if alternate_prompt is None:
195
E.expect(self._prompt, timeout=wait)
196
else:
197
E.expect(alternate_prompt, timeout=wait)
198
except pexpect.TIMEOUT, msg:
199
return False, E.before
200
except pexpect.EOF, msg:
201
return True, E.before
202
except Exception, msg: # weird major problem!
203
return True, E.before
204
return True, E.before
205
206
def _send(self, cmd):
207
if self._expect is None:
208
self._start()
209
E = self._expect
210
self.__so_far = ''
211
E.sendline(cmd)
212
213
def is_running(self):
214
"""
215
Return True if self is currently running.
216
"""
217
if self._expect is None:
218
return False
219
try:
220
os.kill(self._expect.pid,0)
221
except OSError:
222
# This means the process is not running
223
return False
224
return True
225
226
def _so_far(self, wait=0.1, alternate_prompt=None):
227
"""
228
Return whether done and output so far and new output since last
229
time called.
230
"""
231
done, new = self._get(wait=wait, alternate_prompt=alternate_prompt)
232
try:
233
if done:
234
#if not new is None:
235
X = self.__so_far + new
236
del self.__so_far
237
return True, X, new
238
#new = self._expect.before
239
try:
240
self.__so_far += new
241
except (AttributeError, TypeError):
242
self.__so_far = new
243
return False, self.__so_far, new
244
except AttributeError, msg: # no __so_far
245
raise RuntimeError(msg)
246
247
def is_remote(self):
248
return self.__is_remote
249
250
def is_local(self):
251
return not self.__is_remote
252
253
def user_dir(self):
254
return self.__path
255
256
def _change_prompt(self, prompt):
257
self._prompt = prompt
258
259
# (pdehaye 20070819: this was used by some interfaces but does not work well remotely)
260
# def _temp_file(self, x):
261
# T = self.__path + "/tmp/"
262
# if not os.path.exists(T):
263
# os.makedirs(T)
264
# return T + str(x)
265
266
def path(self):
267
return self.__path
268
269
def expect(self):
270
if self._expect is None:
271
self._start()
272
return self._expect
273
274
def pid(self):
275
"""
276
Return the PID of the underlying sub-process.
277
278
REMARK:
279
280
If the interface terminates unexpectedly, the original
281
PID will still be used. But if it was terminated using
282
:meth:`quit`, a new sub-process with a new PID is
283
automatically started.
284
285
EXAMPLE::
286
287
sage: pid = gap.pid()
288
sage: gap.eval('quit;')
289
''
290
sage: pid == gap.pid()
291
True
292
sage: gap.quit()
293
sage: pid == gap.pid()
294
False
295
296
"""
297
if self._expect is None:
298
self._start()
299
return self._expect.pid
300
301
def _install_hints(self):
302
r"""
303
Hints for installing needed slave program on your computer.
304
305
There are no hints by default.
306
"""
307
return ''
308
309
def _install_hints_ssh(self):
310
r"""
311
Hints for installing passwordless authentication on your
312
computer...
313
"""
314
# Written by Paul-Olivier Dehaye 2007/08/23
315
return """
316
In order for Sage (on "local") to launch a "slave" process on "remote", the following command needs to work from local's console, without the need to enter any password:
317
318
"ssh -t remote slave",
319
320
where "slave" could be "math" (for text-mode Mathematica), "gap", "magma", "sage", "maple", etc.
321
322
This thus requires passwordless authentication to be setup, which can be done with commands like these:
323
cd; ssh-keygen -t rsa; scp .ssh/id_rsa.pub remote:.ssh/authorized_keys2\n
324
(WARNING: this would overwrite your current list of authorized keys on "remote")
325
326
In many cases, the server that can actually run "slave" is not accessible from the internet directly, but you have to hop through an intermediate trusted server, say "gate".
327
If that is your case, get help with _install_hints_ssh_through_gate().
328
329
"""
330
331
def _install_hints_ssh_through_gate(self):
332
r"""
333
Hints for installing passwordless authentication through a gate
334
"""
335
# Written by Paul-Olivier Dehaye 2007/08/23
336
return """
337
338
We assume you would like to run a "slave" process on a machine called "remote" from a machine running Sage called "local". We also assume "remote" can only be accessed from "local" by ssh'ing first to "gate" (this is a fairly common setup). Sometimes, "gate" and "remote" have a shared filesystem, and this helps a bit.
339
340
Note: You cannot just create shell scripts on "local" and "gate" that would use two successive SSH connections to "remote" in order to simulate running "slave" locally. This is because Sage will sometimes use files (and scp) to communicate with "remote", which shell scripts would not take care of.
341
342
You need to setup:
343
* passwordless authentication to "gate" from "local"
344
* add passwordless authentication to "remote" from "local",
345
for instance by appending the file local:~/.ssh/id_rsa.pub to remote:~/.ssh/authorized_keys2 and logging in once
346
(this is only needed if "remote" and "gate" don\'t share filesystem)
347
* add a few lines to your local:~/.ssh/ssh_config. Mine look like
348
349
Host remote_for_sage
350
ProxyCommand ssh gate nc -w 1 remote 22
351
352
That's it, normally.
353
354
The last step tells ssh that whenever an ssh connection is required to
355
the host "remote_for_sage", it should tunnel it through "gate". Any
356
attempt to scp-connect to "remote_for_sage" will use ssh and thus
357
this configuration file, and properly channel those file transfers
358
through the tunnel.
359
360
A good test is to attempt an scp connection from the command-line
361
of "local" to "remote_for_sage" as if no tunnel through "gate" was
362
required. No password should be asked for the second time around.
363
364
Finally, we created the new name "remote_for_sage" for "remote",
365
but this name only exists locally. this is to avoid interfering
366
with any other program that might already ssh to "remote" in
367
their own way.
368
369
If this all works, you can then make calls like:
370
math = Mathematica(server="remote_for_sage")
371
372
"""
373
374
375
def _do_cleaner(self):
376
try:
377
return self.__do_cleaner
378
except AttributeError:
379
return False
380
381
def _start(self, alt_message=None, block_during_init=True):
382
from sage.misc.misc import sage_makedirs
383
self.quit() # in case one is already running
384
global failed_to_start
385
386
self._session_number += 1
387
current_path = os.path.abspath('.')
388
dir = self.__path
389
sage_makedirs(dir)
390
os.chdir(dir)
391
392
#If the 'SAGE_PEXPECT_LOG' environment variable is set and
393
#the current logfile is None, then set the logfile to be one
394
#in .sage/pexpect_logs/
395
if self.__logfile is None and 'SAGE_PEXPECT_LOG' in os.environ:
396
from sage.env import DOT_SAGE
397
logs = '%s/pexpect_logs'%DOT_SAGE
398
sage_makedirs(logs)
399
400
filename = '%s/%s-%s-%s-%s.log'%(logs, self.name(), os.getpid(), id(self), self._session_number)
401
self.__logfile = open(filename, 'w')
402
403
cmd = self.__command
404
405
if self.__verbose_start:
406
print cmd
407
print "Starting %s"%cmd.split()[0]
408
409
try:
410
if self.__remote_cleaner and self._server:
411
c = 'sage-native-execute ssh %s "nohup sage -cleaner" &'%self._server
412
os.system(c)
413
414
# Unset some environment variables for the children to
415
# reduce the chances they do something complicated breaking
416
# the terminal interface.
417
# See Trac #12221 and #13859.
418
pexpect_env = dict(os.environ)
419
pexpect_del_vars = ['TERM', 'COLUMNS']
420
for i in pexpect_del_vars:
421
try:
422
del pexpect_env[i]
423
except KeyError:
424
pass
425
self._expect = pexpect.spawn(cmd, logfile=self.__logfile, env=pexpect_env)
426
if self._do_cleaner():
427
cleaner.cleaner(self._expect.pid, cmd)
428
429
except (ExceptionPexpect, pexpect.EOF, IndexError):
430
self._expect = None
431
self._session_number = BAD_SESSION
432
failed_to_start.append(self.name())
433
raise RuntimeError, "Unable to start %s because the command '%s' failed.\n%s"%(
434
self.name(), cmd, self._install_hints())
435
436
os.chdir(current_path)
437
self._expect.timeout = self.__max_startup_time
438
439
#self._expect.setmaxread(self.__maxread)
440
self._expect.maxread = self.__maxread
441
self._expect.delaybeforesend = 0
442
try:
443
self._expect.expect(self._prompt)
444
except (pexpect.TIMEOUT, pexpect.EOF), msg:
445
self._expect = None
446
self._session_number = BAD_SESSION
447
failed_to_start.append(self.name())
448
raise RuntimeError, "Unable to start %s"%self.name()
449
self._expect.timeout = None
450
with gc_disabled():
451
if block_during_init:
452
for X in self.__init_code:
453
self.eval(X)
454
else:
455
for X in self.__init_code:
456
self._send(X)
457
458
459
def clear_prompts(self):
460
while True:
461
try:
462
self._expect.expect(self._prompt, timeout=0.1)
463
except pexpect.TIMEOUT:
464
return
465
466
def __del__(self):
467
try:
468
if self._expect is None:
469
return
470
try:
471
self.quit()
472
except (TypeError, AttributeError):
473
pass
474
475
# The following programs around a bug in pexpect.
476
def dummy(): pass
477
try:
478
self._expect.close = dummy
479
except Exception, msg:
480
pass
481
except Exception, msg:
482
pass
483
484
def quit(self, verbose=False, timeout=0.25):
485
"""
486
EXAMPLES::
487
488
sage: a = maxima('y')
489
sage: maxima.quit()
490
sage: a._check_valid()
491
Traceback (most recent call last):
492
...
493
ValueError: The maxima session in which this object was defined is no longer running.
494
"""
495
self._session_number += 1
496
try:
497
del self.__local_tmpfile
498
except AttributeError:
499
pass
500
if self._expect is None:
501
return
502
# Send a kill -9 to the process *group*.
503
# this is *very useful* when external binaries are started up
504
# by shell scripts, and killing the shell script doesn't
505
# kill the binary.
506
E = self._expect
507
if verbose:
508
if self.is_remote():
509
print "Exiting spawned %s process (local pid=%s, running on %s)"%(self,E.pid,self._server)
510
else:
511
print "Exiting spawned %s process."%self
512
try:
513
# TO DO: This should be implemented or the remote tmp will get crowded
514
# self._remove_remote_tmpfile()
515
E.sendline(self._quit_string())
516
self._so_far(wait=timeout)
517
# In case of is_remote(), killing the local "ssh -t" also kills the remote process it initiated
518
os.killpg(E.pid, 9)
519
os.kill(E.pid, 9)
520
except (RuntimeError, OSError), msg:
521
pass
522
self._expect = None
523
return
524
525
def _quit_string(self):
526
return 'quit'
527
528
def _local_tmpfile(self):
529
"""
530
Return a filename that is used to buffer long command lines for this interface
531
532
INPUT:
533
534
``e`` -- an expect interface instance
535
536
OUTPUT:
537
538
A string that provides a temporary filename and is unique for the
539
given interface.
540
541
TEST:
542
543
The filename is cached::
544
545
sage: gap._local_tmpfile() is gap._local_tmpfile()
546
True
547
548
The following two problems were fixed in #10004.
549
550
1. Different interfaces have different temp-files::
551
552
sage: gap._local_tmpfile() != singular._local_tmpfile()
553
True
554
555
2. Interface instances in different branches of a parallelised
556
function have different temp-files::
557
558
sage: @parallel
559
... def f(n):
560
... return gap._local_tmpfile()
561
...
562
sage: L = [t[1] for t in f(range(5))]
563
sage: len(set(L))
564
5
565
566
The following used to fail::
567
568
sage: s = gap._local_tmpfile()
569
sage: L = [t[1] for t in f(range(5))]
570
sage: len(set(L))
571
5
572
573
AUTHOR:
574
575
- Simon King (2010-09): Making the tmp-file unique for the interface instance
576
577
"""
578
try:
579
return self.__local_tmpfile
580
except AttributeError:
581
self.__local_tmpfile = os.path.join(SAGE_TMP_INTERFACE, 'tmp' + str(self.pid()))
582
return self.__local_tmpfile
583
584
def _remote_tmpdir(self):
585
return self.__remote_tmpdir
586
587
def _remote_tmpfile(self):
588
try:
589
return self.__remote_tmpfile
590
except AttributeError:
591
self.__remote_tmpfile = self._remote_tmpdir()+"/interface_%s:%s"%(LOCAL_IDENTIFIER,self.pid())
592
return self.__remote_tmpfile
593
594
def _send_tmpfile_to_server(self, local_file=None, remote_file=None):
595
if local_file is None:
596
local_file = self._local_tmpfile()
597
if remote_file is None:
598
remote_file = self._remote_tmpfile()
599
cmd = 'scp "%s" %s:"%s" 1>&2 2>/dev/null'%(local_file, self._server, remote_file)
600
# print cmd
601
os.system(cmd)
602
603
def _get_tmpfile_from_server(self, local_file=None,remote_file=None):
604
if local_file is None:
605
local_file = self._local_tmpfile()
606
if remote_file is None:
607
remote_file = self._remote_tmpfile()
608
cmd = 'scp %s:"%s" "%s" 1>&2 2>/dev/null'%( self._server, remote_file, local_file)
609
# print cmd
610
os.system(cmd)
611
612
def _remove_tmpfile_from_server(self):
613
if not (self.__remote_tmpfile is None):
614
raise NotImplementedError
615
616
def read(self, filename):
617
r"""
618
EXAMPLES::
619
620
sage: filename = tmp_filename()
621
sage: f = open(filename, 'w')
622
sage: f.write('x = 2\n')
623
sage: f.close()
624
sage: octave.read(filename) # optional - octave
625
sage: octave.get('x') #optional
626
' 2'
627
sage: import os
628
sage: os.unlink(filename)
629
"""
630
self.eval(self._read_in_file_command(filename))
631
632
def _read_in_file_command(self, filename):
633
raise NotImplementedError
634
635
def _eval_line_using_file(self, line, restart_if_needed=True):
636
"""
637
Evaluate a line of commands, using a temporary file.
638
639
REMARK:
640
641
By default, this is called when a long command is
642
evaluated in :meth:`eval`.
643
644
If the command can not be evaluated since the interface
645
has crashed, it is automatically restarted and tried
646
again *once*.
647
648
INPUT:
649
650
- ``line`` -- (string) a command.
651
- ``restart_if_needed`` - (optional bool, default ``True``) --
652
If it is ``True``, the command evaluation is evaluated
653
a second time after restarting the interface, if an
654
``EOFError`` occured.
655
656
TESTS::
657
658
sage: singular._eval_line_using_file('def a=3;')
659
'< "...";'
660
sage: singular('a')
661
3
662
sage: singular.eval('quit;')
663
''
664
sage: singular._eval_line_using_file('def a=3;')
665
Singular crashed -- automatically restarting.
666
'< "...";'
667
sage: singular('a')
668
3
669
sage: singular.eval('quit;')
670
''
671
sage: singular._eval_line_using_file('def a=3;', restart_if_needed=False)
672
Traceback (most recent call last):
673
...
674
RuntimeError: Singular terminated unexpectedly while reading in a large line...
675
676
We end by triggering a re-start of Singular, since otherwise
677
the doc test of another method would fail by a side effect.
678
::
679
680
sage: singular(3)
681
Singular crashed -- automatically restarting.
682
3
683
684
"""
685
F = open(self._local_tmpfile(), 'w')
686
F.write(line+'\n')
687
F.close()
688
tmp_to_use = self._local_tmpfile()
689
if self.is_remote():
690
self._send_tmpfile_to_server()
691
tmp_to_use = self._remote_tmpfile()
692
try:
693
s = self._eval_line(self._read_in_file_command(tmp_to_use), allow_use_file=False, restart_if_needed=False)
694
except pexpect.EOF, msg:
695
if self._quit_string() in line:
696
# we expect to get an EOF if we're quitting.
697
return ''
698
elif restart_if_needed==True: # the subprocess might have crashed
699
try:
700
self._synchronize()
701
return self._post_process_from_file(self._eval_line_using_file(line, restart_if_needed=False))
702
except RuntimeError, msg:
703
raise RuntimeError, '%s terminated unexpectedly while reading in a large line:\n%s'%(self,msg[0])
704
except TypeError:
705
pass
706
raise RuntimeError, '%s terminated unexpectedly while reading in a large line'%self
707
except RuntimeError,msg:
708
if self._quit_string() in line:
709
if self._expect is None or not self._expect.isalive():
710
return ''
711
raise
712
if restart_if_needed==True and (self._expect is None or not self._expect.isalive()):
713
try:
714
self._synchronize()
715
return self._post_process_from_file(self._eval_line_using_file(line, restart_if_needed=False))
716
except TypeError:
717
pass
718
except RuntimeError, msg:
719
raise RuntimeError, '%s terminated unexpectedly while reading in a large line'%self
720
if "Input/output error" in msg[0]: # This occurs on non-linux machines
721
raise RuntimeError, '%s terminated unexpectedly while reading in a large line'%self
722
raise RuntimeError, '%s terminated unexpectedly while reading in a large line:\n%s'%(self,msg[0])
723
return self._post_process_from_file(s)
724
725
def _post_process_from_file(self, s):
726
return s
727
728
def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if_needed=True):
729
"""
730
Evaluate a line of commands.
731
732
REMARK:
733
734
By default, a long command (length exceeding ``self._eval_using_file_cutoff``)
735
is evaluated using :meth:`_eval_line_using_file`.
736
737
If the command can not be evaluated since the interface
738
has crashed, it is automatically restarted and tried
739
again *once*.
740
741
If the optional ``wait_for_prompt`` is ``False`` then even a very
742
long line will not be evaluated by :meth:`_eval_line_using_file`,
743
since this does not support the ``wait_for_prompt`` option.
744
745
INPUT:
746
747
- ``line`` -- (string) a command.
748
- ``allow_use_file`` (optional bool, default ``True``) --
749
allow to evaluate long commands using :meth:`_eval_line_using_file`.
750
- ``wait_for_prompt`` (optional bool, default ``True``) --
751
wait until the prompt appears in the sub-process' output.
752
- ``restart_if_needed`` (optional bool, default ``True``) --
753
If it is ``True``, the command evaluation is evaluated
754
a second time after restarting the interface, if an
755
``EOFError`` occured.
756
757
TESTS::
758
759
sage: singular._eval_line('def a=3;')
760
'def a=3;'
761
sage: singular('a')
762
3
763
sage: singular.eval('quit;')
764
''
765
sage: singular._eval_line('def a=3;')
766
Singular crashed -- automatically restarting.
767
'def a=3;'
768
sage: singular('a')
769
3
770
sage: singular.eval('kill a')
771
'kill a;'
772
773
We are now sending a command that would run forever. But since
774
we declare that we are not waiting for a prompt, we can interrupt
775
it without a KeyboardInterrupt. At the same time, we test that
776
the line is not forwarded to :meth:`_eval_line_using_file`, since
777
that method would not support the ``wait_for_prompt`` option.
778
For reasons which are currently not understood, the ``interrupt``
779
test usually returns immediately, but sometimes it takes a very
780
long time on the same system. ::
781
782
sage: cutoff = singular._eval_using_file_cutoff
783
sage: singular._eval_using_file_cutoff = 4
784
sage: singular._eval_line('for(int i=1;i<=3;i++){i=1;};', wait_for_prompt=False)
785
''
786
sage: singular.interrupt(timeout=3) # sometimes very slow (up to 60s on sage.math, 2012)
787
False
788
sage: singular._eval_using_file_cutoff = cutoff
789
790
Last, we demonstrate that by default the execution of a command
791
is tried twice if it fails the first time due to a crashed
792
interface::
793
794
sage: singular.eval('quit;')
795
''
796
sage: singular._eval_line_using_file('def a=3;', restart_if_needed=False)
797
Traceback (most recent call last):
798
...
799
RuntimeError: Singular terminated unexpectedly while reading in a large line...
800
801
Since the test of the next method would fail, we re-start
802
Singular now. ::
803
804
sage: singular(2+3)
805
Singular crashed -- automatically restarting.
806
5
807
808
"""
809
if allow_use_file and wait_for_prompt and self._eval_using_file_cutoff and len(line) > self._eval_using_file_cutoff:
810
return self._eval_line_using_file(line)
811
try:
812
if self._expect is None:
813
self._start()
814
E = self._expect
815
try:
816
if len(line) >= 4096:
817
raise RuntimeError, "Sending more than 4096 characters with %s on a line may cause a hang and you're sending %s characters"%(self, len(line))
818
E.sendline(line)
819
if wait_for_prompt == False:
820
return ''
821
822
except OSError, msg:
823
if restart_if_needed:
824
# The subprocess most likely crashed.
825
# If it's really still alive, we fall through
826
# and raise RuntimeError.
827
if sys.platform.startswith('sunos'):
828
# On (Open)Solaris, we might need to wait a
829
# while because the process might not die
830
# immediately. See Trac #14371.
831
for t in [0.5, 1.0, 2.0]:
832
if E.isalive():
833
time.sleep(t)
834
else:
835
break
836
if not E.isalive():
837
try:
838
self._synchronize()
839
except (TypeError, RuntimeError):
840
pass
841
return self._eval_line(line,allow_use_file=allow_use_file, wait_for_prompt=wait_for_prompt, restart_if_needed=False)
842
raise RuntimeError, "%s\nError evaluating %s in %s"%(msg, line, self), sys.exc_info()[2]
843
844
if len(line)>0:
845
try:
846
if isinstance(wait_for_prompt, basestring):
847
E.expect(wait_for_prompt)
848
else:
849
E.expect(self._prompt)
850
except pexpect.EOF, msg:
851
try:
852
if self.is_local():
853
tmp_to_use = self._local_tmpfile()
854
else:
855
tmp_to_use = self._remote_tmpfile()
856
if self._read_in_file_command(tmp_to_use) in line:
857
raise pexpect.EOF, msg
858
except NotImplementedError:
859
pass
860
if self._quit_string() in line:
861
# we expect to get an EOF if we're quitting.
862
return ''
863
elif restart_if_needed==True: # the subprocess might have crashed
864
try:
865
self._synchronize()
866
return self._eval_line(line,allow_use_file=allow_use_file, wait_for_prompt=wait_for_prompt, restart_if_needed=False)
867
except (TypeError, RuntimeError):
868
pass
869
raise RuntimeError, "%s\n%s crashed executing %s"%(msg,self, line)
870
out = E.before
871
else:
872
out = '\n\r'
873
except KeyboardInterrupt:
874
self._keyboard_interrupt()
875
raise KeyboardInterrupt, "Ctrl-c pressed while running %s"%self
876
i = out.find("\n")
877
j = out.rfind("\r")
878
return out[i+1:j].replace('\r\n','\n')
879
880
def _keyboard_interrupt(self):
881
print "Interrupting %s..."%self
882
if self._restart_on_ctrlc:
883
try:
884
self._expect.close(force=1)
885
except pexpect.ExceptionPexpect, msg:
886
raise pexpect.ExceptionPexpect( "THIS IS A BUG -- PLEASE REPORT. This should never happen.\n" + msg)
887
self._start()
888
raise KeyboardInterrupt, "Restarting %s (WARNING: all variables defined in previous session are now invalid)"%self
889
else:
890
self._expect.sendline(chr(3)) # send ctrl-c
891
self._expect.expect(self._prompt)
892
self._expect.expect(self._prompt)
893
raise KeyboardInterrupt, "Ctrl-c pressed while running %s"%self
894
895
def interrupt(self, tries=20, timeout=0.3, quit_on_fail=True):
896
E = self._expect
897
if E is None:
898
return True
899
success = False
900
try:
901
for i in range(tries):
902
E.sendline(self._quit_string())
903
E.sendline(chr(3))
904
try:
905
E.expect(self._prompt, timeout=timeout)
906
success= True
907
break
908
except (pexpect.TIMEOUT, pexpect.EOF), msg:
909
#print msg
910
pass
911
except Exception, msg:
912
pass
913
if success:
914
pass
915
elif quit_on_fail:
916
self.quit()
917
return success
918
919
###########################################################################
920
# BEGIN Synchronization code.
921
###########################################################################
922
923
def _before(self):
924
r"""
925
Return the previous string that was sent through the interface.
926
927
EXAMPLES::
928
929
sage: singular(2+3)
930
5
931
sage: singular._before()
932
'print(sage...);\r\n5\r'
933
"""
934
return self._expect.before
935
936
def _interrupt(self):
937
for i in range(15):
938
try:
939
self._sendstr('quit;\n'+chr(3))
940
self._expect_expr(timeout=2)
941
except pexpect.TIMEOUT:
942
pass
943
except pexpect.EOF:
944
self._crash_msg()
945
self.quit()
946
else:
947
return
948
949
def _expect_expr(self, expr=None, timeout=None):
950
r"""
951
Wait for a given expression expr (which could be a regular
952
expression or list of regular expressions) to appear in the output
953
for at most timeout seconds.
954
955
Use ``r._expect.before`` to see what was put in the
956
output stream before the expression.
957
958
INPUT:
959
960
961
- ``expr`` - None or a string or list of strings
962
(default: None)
963
964
- ``timeout`` - None or a number (default: None)
965
966
967
EXAMPLES:
968
969
We test all of this using the R interface. First we put
970
10 + 15 in the input stream::
971
972
sage: r._sendstr('abc <- 10 +15;\n')
973
974
Here an exception is raised because 25 hasn't appeared yet in the
975
output stream. The key thing is that this doesn't lock, but instead
976
quickly raises an exception.
977
978
::
979
980
sage: t = walltime()
981
sage: try:
982
....: r._expect_expr('25', timeout=0.5)
983
....: except Exception:
984
....: print 'Did not get expression'
985
Did not get expression
986
987
A quick consistency check on the time that the above took::
988
989
sage: w = walltime(t); w > 0.4 and w < 10
990
True
991
992
We tell R to print abc, which equals 25.
993
994
::
995
996
sage: r._sendstr('abc;\n')
997
998
Now 25 is in the output stream, so we can wait for it.
999
1000
::
1001
1002
sage: r._expect_expr('25')
1003
1004
This gives us everything before the 25.
1005
1006
::
1007
1008
sage: r._expect.before
1009
'abc;\r\n[1] '
1010
1011
We test interrupting ``_expect_expr`` using the GP interface,
1012
see #6661. Unfortunately, this test doesn't work reliably using
1013
Singular, see #9163 and the follow-up #10476.
1014
The ``gp.eval('0')`` in this test makes sure that ``gp`` is
1015
running, so a timeout of 1 second should be sufficient. ::
1016
1017
sage: print sage0.eval("dummy=gp.eval('0'); alarm(1); gp._expect_expr('1')") # long time
1018
Control-C pressed. Interrupting PARI/GP interpreter. Please wait a few seconds...
1019
...
1020
AlarmInterrupt:
1021
"""
1022
if expr is None:
1023
# the following works around gap._prompt_wait not being defined
1024
expr = (hasattr(self,'_prompt_wait') and self._prompt_wait) or self._prompt
1025
if self._expect is None:
1026
self._start()
1027
try:
1028
if timeout:
1029
i = self._expect.expect(expr,timeout=timeout)
1030
else:
1031
i = self._expect.expect(expr)
1032
if i > 0:
1033
v = self._expect.before
1034
self.quit()
1035
raise ValueError, "%s\nComputation failed due to a bug in %s -- NOTE: Had to restart."%(v, self)
1036
except KeyboardInterrupt, msg:
1037
i = 0
1038
while True:
1039
try:
1040
print "Control-C pressed. Interrupting %s. Please wait a few seconds..."%self
1041
self._sendstr('quit;\n'+chr(3))
1042
self._sendstr('quit;\n'+chr(3))
1043
self.interrupt()
1044
self.interrupt()
1045
except KeyboardInterrupt:
1046
i += 1
1047
if i > 10:
1048
break
1049
pass
1050
else:
1051
break
1052
raise KeyboardInterrupt, msg
1053
1054
def _sendstr(self, str):
1055
r"""
1056
Send a string to the pexpect interface, autorestarting the expect
1057
interface if anything goes wrong.
1058
1059
INPUT:
1060
1061
1062
- ``str`` - a string
1063
1064
1065
EXAMPLES: We illustrate this function using the R interface::
1066
1067
sage: r._sendstr('a <- 10;\n')
1068
sage: r.eval('a')
1069
'[1] 10'
1070
1071
We illustrate using the singular interface::
1072
1073
sage: singular._sendstr('int i = 5;')
1074
sage: singular('i')
1075
5
1076
"""
1077
if self._expect is None:
1078
self._start()
1079
try:
1080
os.write(self._expect.child_fd, str)
1081
except OSError:
1082
self._crash_msg()
1083
self.quit()
1084
self._sendstr(str)
1085
1086
def _crash_msg(self):
1087
r"""
1088
Show a message if the interface crashed.
1089
1090
EXAMPLE::
1091
1092
sage: singular._crash_msg()
1093
Singular crashed -- automatically restarting.
1094
1095
::
1096
1097
sage: singular('2+3')
1098
5
1099
sage: singular._sendstr('quit;\n') # make it so that singular appears to die.
1100
sage: singular('2+3')
1101
Singular crashed -- automatically restarting.
1102
5
1103
"""
1104
print "%s crashed -- automatically restarting."%self
1105
1106
def _synchronize(self, cmd='1+%s;\n'):
1107
"""
1108
Synchronize pexpect interface.
1109
1110
This put a random integer (plus one!) into the output stream, then
1111
waits for it, thus resynchronizing the stream. If the random
1112
integer doesn't appear within 1 second, the interface is sent
1113
interrupt signals.
1114
1115
This way, even if you somehow left the interface in a busy state
1116
computing, calling _synchronize gets everything fixed.
1117
1118
EXAMPLES: We observe nothing, just as it should be::
1119
1120
sage: r._synchronize()
1121
1122
TESTS: This illustrates a synchronization bug being fixed (thanks
1123
to Simon King and David Joyner for tracking this down)::
1124
1125
sage: R.<x> = QQ[]; f = x^3 + x + 1; g = x^3 - x - 1; r = f.resultant(g); gap(ZZ); singular(R)
1126
Integers
1127
// characteristic : 0
1128
// number of vars : 1
1129
// block 1 : ordering lp
1130
// : names x
1131
// block 2 : ordering C
1132
"""
1133
if self._expect is None:
1134
return
1135
rnd = randrange(2147483647)
1136
s = str(rnd+1)
1137
cmd = cmd%rnd
1138
self._sendstr(cmd)
1139
try:
1140
self._expect_expr(timeout=0.5)
1141
if not s in self._expect.before:
1142
self._expect_expr(s,timeout=0.5)
1143
self._expect_expr(timeout=0.5)
1144
except pexpect.TIMEOUT:
1145
self._interrupt()
1146
except pexpect.EOF:
1147
self._crash_msg()
1148
self.quit()
1149
1150
###########################################################################
1151
# END Synchronization code.
1152
###########################################################################
1153
1154
def eval(self, code, strip=True, synchronize=False, locals=None, allow_use_file=True,
1155
split_lines="nofile", **kwds):
1156
"""
1157
INPUT:
1158
1159
1160
- ``code`` -- text to evaluate
1161
1162
- ``strip`` -- bool; whether to strip output prompts,
1163
etc. (ignored in the base class).
1164
1165
- ``locals`` -- None (ignored); this is used for compatibility
1166
with the Sage notebook's generic system interface.
1167
1168
- ``allow_use_file`` -- bool (default: True); if True and ``code`` exceeds an
1169
interface-specific threshold then ``code`` will be communicated
1170
via a temporary file rather that the character-based interface.
1171
If False then the code will be communicated via the character interface.
1172
1173
- ``split_lines`` -- Tri-state (default: "nofile"); if "nofile" then ``code`` is sent line by line
1174
unless it gets communicated via a temporary file.
1175
If True then ``code`` is sent line by line, but some lines individually
1176
might be sent via temporary file. Depending on the interface, this may transform
1177
grammatical ``code`` into ungrammatical input.
1178
If False, then the whole block of code is evaluated all at once.
1179
1180
- ``**kwds`` -- All other arguments are passed onto the _eval_line
1181
method. An often useful example is reformat=False.
1182
"""
1183
if synchronize:
1184
try:
1185
self._synchronize()
1186
except AttributeError:
1187
pass
1188
1189
if strip:
1190
try:
1191
code = self._strip_prompt(code)
1192
except AttributeError:
1193
pass
1194
1195
if not isinstance(code, basestring):
1196
raise TypeError, 'input code must be a string.'
1197
1198
#Remove extra whitespace
1199
code = code.strip()
1200
1201
try:
1202
with gc_disabled():
1203
if (split_lines is "nofile" and allow_use_file and
1204
self._eval_using_file_cutoff and len(code) > self._eval_using_file_cutoff):
1205
return self._eval_line_using_file(code)
1206
elif split_lines:
1207
return '\n'.join([self._eval_line(L, allow_use_file=allow_use_file, **kwds)
1208
for L in code.split('\n') if L != ''])
1209
else:
1210
return self._eval_line(code, allow_use_file=allow_use_file, **kwds)
1211
# DO NOT CATCH KeyboardInterrupt, as it is being caught
1212
# by _eval_line
1213
# In particular, do NOT call self._keyboard_interrupt()
1214
except TypeError, s:
1215
raise TypeError, 'error evaluating "%s":\n%s'%(code,s)
1216
1217
############################################################
1218
# Functions for working with variables.
1219
# The first three must be overloaded by derived classes,
1220
# and the definition depends a lot on the class. But
1221
# the functionality one gets from this is very nice.
1222
############################################################
1223
1224
def _object_class(self):
1225
"""
1226
EXAMPLES::
1227
1228
sage: from sage.interfaces.expect import Expect
1229
sage: Expect._object_class(maxima)
1230
<class 'sage.interfaces.expect.ExpectElement'>
1231
"""
1232
return ExpectElement
1233
1234
def _function_class(self):
1235
"""
1236
EXAMPLES::
1237
1238
sage: from sage.interfaces.expect import Expect
1239
sage: Expect._function_class(maxima)
1240
<class 'sage.interfaces.expect.ExpectFunction'>
1241
"""
1242
return ExpectFunction
1243
1244
def _function_element_class(self):
1245
"""
1246
EXAMPLES::
1247
1248
sage: from sage.interfaces.expect import Expect
1249
sage: Expect._function_element_class(maxima)
1250
<class 'sage.interfaces.expect.FunctionElement'>
1251
"""
1252
return FunctionElement
1253
1254
1255
class ExpectFunction(InterfaceFunction):
1256
"""
1257
Expect function.
1258
"""
1259
pass
1260
1261
1262
class FunctionElement(InterfaceFunctionElement):
1263
"""
1264
Expect function element.
1265
"""
1266
pass
1267
1268
1269
def is_ExpectElement(x):
1270
return isinstance(x, ExpectElement)
1271
1272
class ExpectElement(InterfaceElement):
1273
"""
1274
Expect element.
1275
"""
1276
def __init__(self, parent, value, is_name=False, name=None):
1277
RingElement.__init__(self, parent)
1278
self._create = value
1279
if parent is None: return # means "invalid element"
1280
# idea: Joe Wetherell -- try to find out if the output
1281
# is too long and if so get it using file, otherwise
1282
# don't.
1283
if isinstance(value, basestring) and parent._eval_using_file_cutoff and \
1284
parent._eval_using_file_cutoff < len(value):
1285
self._get_using_file = True
1286
1287
if is_name:
1288
self._name = value
1289
else:
1290
try:
1291
self._name = parent._create(value, name=name)
1292
# Convert ValueError and RuntimeError to TypeError for
1293
# coercion to work properly.
1294
except (RuntimeError, ValueError), x:
1295
self._session_number = -1
1296
raise TypeError, x, sys.exc_info()[2]
1297
except BaseException:
1298
self._session_number = -1
1299
raise
1300
self._session_number = parent._session_number
1301
1302
def __hash__(self):
1303
"""
1304
Returns the hash of self. This is a default implementation of hash
1305
which just takes the hash of the string of self.
1306
"""
1307
return hash('%s%s'%(self, self._session_number))
1308
1309
1310
def _check_valid(self):
1311
"""
1312
Check that this object is valid, i.e., the session in which this
1313
object is defined is still running. This is relevant for
1314
interpreters that can't be interrupted via ctrl-C, hence get
1315
restarted.
1316
"""
1317
try:
1318
P = self.parent()
1319
if P is None or P._session_number == BAD_SESSION or self._session_number == -1 or \
1320
P._session_number != self._session_number:
1321
raise ValueError, "The %s session in which this object was defined is no longer running."%P.name()
1322
except AttributeError:
1323
raise ValueError, "The session in which this object was defined is no longer running."
1324
return P
1325
1326
def __del__(self):
1327
try:
1328
self._check_valid()
1329
except ValueError:
1330
return
1331
try:
1332
if hasattr(self,'_name'):
1333
P = self.parent()
1334
if not (P is None):
1335
P.clear(self._name)
1336
1337
except (RuntimeError, ExceptionPexpect), msg: # needed to avoid infinite loops in some rare cases
1338
#print msg
1339
pass
1340
1341
# def _sage_repr(self):
1342
#TO DO: this could use file transfers when self.is_remote()
1343
1344
1345
class StdOutContext:
1346
"""
1347
A context in which all communation between Sage and a subprocess
1348
interfaced via pexpect is printed to stdout.
1349
"""
1350
def __init__(self, interface, silent=False, stdout=None):
1351
"""
1352
Construct a new context in which all communation between Sage
1353
and a subprocess interfaced via pexpect is printed to stdout.
1354
1355
INPUT:
1356
1357
- ``interface`` - the interface whose communcation shall be dumped.
1358
1359
- ``silent`` - if ``True`` this context does nothing
1360
1361
- ``stdout`` - optional parameter for alternative stdout device (default: ``None``)
1362
1363
EXAMPLE::
1364
1365
sage: from sage.interfaces.expect import StdOutContext
1366
sage: with StdOutContext(gp):
1367
... gp('1+1')
1368
...
1369
sage=...
1370
"""
1371
self.interface = interface
1372
self.silent = silent
1373
self.stdout = stdout if stdout else sys.stdout
1374
1375
def __enter__(self):
1376
"""
1377
EXAMPLE::
1378
1379
sage: from sage.interfaces.expect import StdOutContext
1380
sage: with StdOutContext(singular):
1381
... singular.eval('1+1')
1382
...
1383
1+1;
1384
...
1385
"""
1386
if self.silent:
1387
return
1388
if self.interface._expect is None:
1389
self.interface._start()
1390
self._logfile_backup = self.interface._expect.logfile
1391
if self.interface._expect.logfile:
1392
self.interface._expect.logfile = Multiplex(self.interface._expect.logfile, self.stdout)
1393
else:
1394
self.interface._expect.logfile = Multiplex(self.stdout)
1395
1396
def __exit__(self, typ, value, tb):
1397
"""
1398
EXAMPLE::
1399
1400
sage: from sage.interfaces.expect import StdOutContext
1401
sage: with StdOutContext(gap):
1402
... gap('1+1')
1403
...
1404
$sage...
1405
"""
1406
if self.silent:
1407
return
1408
self.interface._expect.logfile.flush()
1409
self.stdout.write("\n")
1410
self.interface._expect.logfile = self._logfile_backup
1411
1412
import os
1413
def console(cmd):
1414
os.system(cmd)
1415
1416
1417
1418
1419