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