mirror of
https://github.com/clearml/wexpect-venv
synced 2025-04-30 18:52:10 +00:00
[CLN][REM] Remove Linux-only public methods. (use pexpect on Linux)
This commit is contained in:
parent
07eb34c8e2
commit
7dd77eadcb
385
wexpect.py
385
wexpect.py
@ -542,177 +542,7 @@ class spawn_unix (object):
|
||||
s.append('delayafterterminate: ' + str(self.delayafterterminate))
|
||||
return '\n'.join(s)
|
||||
|
||||
|
||||
def _spawn(self,command,args=[]): # pragma: no cover
|
||||
warnings.warn(no_unix_deprecation_warning.format("spawn_unix::_spawn"), DeprecationWarning)
|
||||
|
||||
"""This starts the given command in a child process. This does all the
|
||||
fork/exec type of stuff for a pty. This is called by __init__. If args
|
||||
is empty then command will be parsed (split on spaces) and args will be
|
||||
set to parsed arguments. """
|
||||
|
||||
# The pid and child_fd of this object get set by this method.
|
||||
# Note that it is difficult for this method to fail.
|
||||
# You cannot detect if the child process cannot start.
|
||||
# So the only way you can tell if the child process started
|
||||
# or not is to try to read from the file descriptor. If you get
|
||||
# EOF immediately then it means that the child is already dead.
|
||||
# That may not necessarily be bad because you may haved spawned a child
|
||||
# that performs some task; creates no stdout output; and then dies.
|
||||
|
||||
# If command is an int type then it may represent a file descriptor.
|
||||
if type(command) == type(0):
|
||||
raise ExceptionPexpect ('Command is an int type. If this is a file descriptor then maybe you want to use fdpexpect.fdspawn which takes an existing file descriptor instead of a command string.')
|
||||
|
||||
if type (args) != type([]):
|
||||
raise TypeError ('The argument, args, must be a list.')
|
||||
|
||||
if args == []:
|
||||
self.args = split_command_line(command)
|
||||
self.command = self.args[0]
|
||||
else:
|
||||
self.args = args[:] # work with a copy
|
||||
self.args.insert (0, command)
|
||||
self.command = command
|
||||
|
||||
command_with_path = shutil.which(self.command)
|
||||
if command_with_path is None:
|
||||
raise ExceptionPexpect ('The command was not found or was not executable: %s.' % self.command)
|
||||
self.command = command_with_path
|
||||
self.args[0] = self.command
|
||||
|
||||
self.name = '<' + ' '.join (self.args) + '>'
|
||||
|
||||
assert self.pid is None, 'The pid member should be None.'
|
||||
assert self.command is not None, 'The command member should not be None.'
|
||||
|
||||
if self.use_native_pty_fork:
|
||||
try:
|
||||
self.pid, self.child_fd = pty.fork()
|
||||
except OSError as e:
|
||||
raise ExceptionPexpect('Error! pty.fork() failed: ' + str(e))
|
||||
else: # Use internal __fork_pty
|
||||
self.pid, self.child_fd = self.__fork_pty()
|
||||
|
||||
if self.pid == 0: # Child
|
||||
try:
|
||||
self.child_fd = sys.stdout.fileno() # used by setwinsize()
|
||||
self.setwinsize(24, 80)
|
||||
except:
|
||||
# Some platforms do not like setwinsize (Cygwin).
|
||||
# This will cause problem when running applications that
|
||||
# are very picky about window size.
|
||||
# This is a serious limitation, but not a show stopper.
|
||||
pass
|
||||
# Do not allow child to inherit open file descriptors from parent.
|
||||
max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
|
||||
for i in range (3, max_fd):
|
||||
try:
|
||||
os.close (i)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# I don't know why this works, but ignoring SIGHUP fixes a
|
||||
# problem when trying to start a Java daemon with sudo
|
||||
# (specifically, Tomcat).
|
||||
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
||||
|
||||
if self.cwd is not None:
|
||||
os.chdir(self.cwd)
|
||||
if self.env is None:
|
||||
os.execv(self.command, self.args)
|
||||
else:
|
||||
os.execvpe(self.command, self.args, self.env)
|
||||
|
||||
if self.cwd is not None:
|
||||
# Restore the original working dir
|
||||
os.chdir(self.ocwd)
|
||||
|
||||
# Parent
|
||||
self.terminated = False
|
||||
self.closed = False
|
||||
|
||||
def __fork_pty(self): # pragma: no cover
|
||||
warnings.warn(no_unix_deprecation_warning.format("spawn_unix::__fork_pty"), DeprecationWarning)
|
||||
|
||||
|
||||
"""This implements a substitute for the forkpty system call. This
|
||||
should be more portable than the pty.fork() function. Specifically,
|
||||
this should work on Solaris.
|
||||
|
||||
Modified 10.06.05 by Geoff Marshall: Implemented __fork_pty() method to
|
||||
resolve the issue with Python's pty.fork() not supporting Solaris,
|
||||
particularly ssh. Based on patch to posixmodule.c authored by Noah
|
||||
Spurrier::
|
||||
|
||||
http://mail.python.org/pipermail/python-dev/2003-May/035281.html
|
||||
|
||||
"""
|
||||
|
||||
parent_fd, child_fd = os.openpty()
|
||||
if parent_fd < 0 or child_fd < 0:
|
||||
raise ExceptionPexpect("Error! Could not open pty with os.openpty().")
|
||||
|
||||
pid = os.fork()
|
||||
if pid < 0:
|
||||
raise ExceptionPexpect("Error! Failed os.fork().")
|
||||
elif pid == 0:
|
||||
# Child.
|
||||
os.close(parent_fd)
|
||||
self.__pty_make_controlling_tty(child_fd)
|
||||
|
||||
os.dup2(child_fd, 0)
|
||||
os.dup2(child_fd, 1)
|
||||
os.dup2(child_fd, 2)
|
||||
|
||||
if child_fd > 2:
|
||||
os.close(child_fd)
|
||||
else:
|
||||
# Parent.
|
||||
os.close(child_fd)
|
||||
|
||||
return pid, parent_fd
|
||||
|
||||
def __pty_make_controlling_tty(self, tty_fd): # pragma: no cover
|
||||
warnings.warn(no_unix_deprecation_warning.format("spawn_unix::__pty_make_controlling_tty"), DeprecationWarning)
|
||||
|
||||
"""This makes the pseudo-terminal the controlling tty. This should be
|
||||
more portable than the pty.fork() function. Specifically, this should
|
||||
work on Solaris. """
|
||||
|
||||
child_name = os.ttyname(tty_fd)
|
||||
|
||||
# Disconnect from controlling tty if still connected.
|
||||
fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY);
|
||||
if fd >= 0:
|
||||
os.close(fd)
|
||||
|
||||
os.setsid()
|
||||
|
||||
# Verify we are disconnected from controlling tty
|
||||
try:
|
||||
fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY);
|
||||
if fd >= 0:
|
||||
os.close(fd)
|
||||
raise ExceptionPexpect("Error! We are not disconnected from a controlling tty.")
|
||||
except:
|
||||
# Good! We are disconnected from a controlling tty.
|
||||
pass
|
||||
|
||||
# Verify we can open child pty.
|
||||
fd = os.open(child_name, os.O_RDWR);
|
||||
if fd < 0:
|
||||
raise ExceptionPexpect("Error! Could not open child pty, " + child_name)
|
||||
else:
|
||||
os.close(fd)
|
||||
|
||||
# Verify we now have a controlling tty.
|
||||
fd = os.open("/dev/tty", os.O_WRONLY)
|
||||
if fd < 0:
|
||||
raise ExceptionPexpect("Error! Could not open controlling tty, /dev/tty")
|
||||
else:
|
||||
os.close(fd)
|
||||
|
||||
def fileno (self): # File-like object.
|
||||
|
||||
"""This returns the file descriptor of the pty for the child.
|
||||
@ -739,14 +569,6 @@ class spawn_unix (object):
|
||||
self.closed = True
|
||||
#self.pid = None
|
||||
|
||||
def flush (self): # pragma: no cover # File-like object.
|
||||
|
||||
warnings.warn(no_unix_deprecation_warning.format("spawn_unix::flush"), DeprecationWarning)
|
||||
"""This does nothing. It is here to support the interface for a
|
||||
File-like object. """
|
||||
|
||||
pass
|
||||
|
||||
def isatty (self): # File-like object.
|
||||
|
||||
"""This returns True if the file descriptor is open and connected to a
|
||||
@ -838,85 +660,6 @@ class spawn_unix (object):
|
||||
# and blocked on some platforms. TCSADRAIN is probably ideal if it worked.
|
||||
termios.tcsetattr(self.child_fd, termios.TCSANOW, attr)
|
||||
|
||||
def read_nonblocking (self, size = 1, timeout = -1): # pragma: no cover
|
||||
warnings.warn(no_unix_deprecation_warning.format("spawn_unix::read_nonblocking"), DeprecationWarning)
|
||||
|
||||
"""This reads at most size characters from the child application. It
|
||||
includes a timeout. If the read does not complete within the timeout
|
||||
period then a TIMEOUT exception is raised. If the end of file is read
|
||||
then an EOF exception will be raised. If a log file was set using
|
||||
setlog() then all data will also be written to the log file.
|
||||
|
||||
If timeout is None then the read may block indefinitely. If timeout is -1
|
||||
then the self.timeout value is used. If timeout is 0 then the child is
|
||||
polled and if there was no data immediately ready then this will raise
|
||||
a TIMEOUT exception.
|
||||
|
||||
The timeout refers only to the amount of time to read at least one
|
||||
character. This is not effected by the 'size' parameter, so if you call
|
||||
read_nonblocking(size=100, timeout=30) and only one character is
|
||||
available right away then one character will be returned immediately.
|
||||
It will not wait for 30 seconds for another 99 characters to come in.
|
||||
|
||||
This is a wrapper around os.read(). It uses select.select() to
|
||||
implement the timeout. """
|
||||
|
||||
if self.closed:
|
||||
raise ValueError ('I/O operation on closed file in read_nonblocking().')
|
||||
|
||||
if timeout == -1:
|
||||
timeout = self.timeout
|
||||
|
||||
# Note that some systems such as Solaris do not give an EOF when
|
||||
# the child dies. In fact, you can still try to read
|
||||
# from the child_fd -- it will block forever or until TIMEOUT.
|
||||
# For this case, I test isalive() before doing any reading.
|
||||
# If isalive() is false, then I pretend that this is the same as EOF.
|
||||
if not self.isalive():
|
||||
r,w,e = self.__select([self.child_fd], [], [], 0) # timeout of 0 means "poll"
|
||||
if not r:
|
||||
self.flag_eof = True
|
||||
raise EOF ('End Of File (EOF) in read_nonblocking(). Braindead platform.')
|
||||
elif self.__irix_hack:
|
||||
# This is a hack for Irix. It seems that Irix requires a long delay before checking isalive.
|
||||
# This adds a 2 second delay, but only when the child is terminated.
|
||||
r, w, e = self.__select([self.child_fd], [], [], 2)
|
||||
if not r and not self.isalive():
|
||||
self.flag_eof = True
|
||||
raise EOF ('End Of File (EOF) in read_nonblocking(). Pokey platform.')
|
||||
|
||||
r,w,e = self.__select([self.child_fd], [], [], timeout)
|
||||
|
||||
if not r:
|
||||
if not self.isalive():
|
||||
# Some platforms, such as Irix, will claim that their processes are alive;
|
||||
# then timeout on the select; and then finally admit that they are not alive.
|
||||
self.flag_eof = True
|
||||
raise EOF ('End of File (EOF) in read_nonblocking(). Very pokey platform.')
|
||||
else:
|
||||
raise TIMEOUT ('Timeout exceeded in read_nonblocking().')
|
||||
|
||||
if self.child_fd in r:
|
||||
try:
|
||||
s = os.read(self.child_fd, size)
|
||||
except OSError as e: # Linux does this
|
||||
self.flag_eof = True
|
||||
raise EOF ('End Of File (EOF) in read_nonblocking(). Exception style platform.')
|
||||
if s == '': # BSD style
|
||||
self.flag_eof = True
|
||||
raise EOF ('End Of File (EOF) in read_nonblocking(). Empty string style platform.')
|
||||
|
||||
if self.logfile is not None:
|
||||
self.logfile.write (s)
|
||||
self.logfile.flush()
|
||||
if self.logfile_read is not None:
|
||||
self.logfile_read.write (s)
|
||||
self.logfile_read.flush()
|
||||
|
||||
return s
|
||||
|
||||
raise ExceptionPexpect ('Reached an unexpected state in read_nonblocking().')
|
||||
|
||||
def read (self, size = -1): # File-like object.
|
||||
|
||||
"""This reads at most "size" bytes from the file (less if the read hits
|
||||
@ -1010,24 +753,6 @@ class spawn_unix (object):
|
||||
for s in sequence:
|
||||
self.write (s)
|
||||
|
||||
def send(self, s): # pragma: no cover
|
||||
warnings.warn(no_unix_deprecation_warning.format("spawn_unix::send"), DeprecationWarning)
|
||||
|
||||
|
||||
"""This sends a string to the child process. This returns the number of
|
||||
bytes written. If a log file was set then the data is also written to
|
||||
the log. """
|
||||
|
||||
time.sleep(self.delaybeforesend)
|
||||
if self.logfile is not None:
|
||||
self.logfile.write (s)
|
||||
self.logfile.flush()
|
||||
if self.logfile_send is not None:
|
||||
self.logfile_send.write (s)
|
||||
self.logfile_send.flush()
|
||||
c = os.write(self.child_fd, s)
|
||||
return c
|
||||
|
||||
def sendline(self, s=''):
|
||||
|
||||
"""This is like send(), but it adds a line feed (os.linesep). This
|
||||
@ -1184,70 +909,6 @@ class spawn_unix (object):
|
||||
raise ExceptionPexpect ('Wait was called for a child process that is stopped. This is not supported. Is some other process attempting job control with our child pid?')
|
||||
return self.exitstatus
|
||||
|
||||
def isalive(self): # pragma: no cover
|
||||
warnings.warn(no_unix_deprecation_warning.format("spawn_unix::isalive"), DeprecationWarning)
|
||||
|
||||
"""This tests if the child process is running or not. This is
|
||||
non-blocking. If the child was terminated then this will read the
|
||||
exitstatus or signalstatus of the child. This returns True if the child
|
||||
process appears to be running or False if not. It can take literally
|
||||
SECONDS for Solaris to return the right status. """
|
||||
|
||||
if self.terminated:
|
||||
return False
|
||||
|
||||
if self.flag_eof:
|
||||
# This is for Linux, which requires the blocking form of waitpid to get
|
||||
# status of a defunct process. This is super-lame. The flag_eof would have
|
||||
# been set in read_nonblocking(), so this should be safe.
|
||||
waitpid_options = 0
|
||||
else:
|
||||
waitpid_options = os.WNOHANG
|
||||
|
||||
try:
|
||||
pid, status = os.waitpid(self.pid, waitpid_options)
|
||||
except OSError as e: # No child processes
|
||||
if e[0] == errno.ECHILD:
|
||||
raise ExceptionPexpect ('isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?')
|
||||
else:
|
||||
raise e
|
||||
|
||||
# I have to do this twice for Solaris. I can't even believe that I figured this out...
|
||||
# If waitpid() returns 0 it means that no child process wishes to
|
||||
# report, and the value of status is undefined.
|
||||
if pid == 0:
|
||||
try:
|
||||
pid, status = os.waitpid(self.pid, waitpid_options) ### os.WNOHANG) # Solaris!
|
||||
except OSError as e: # This should never happen...
|
||||
if e[0] == errno.ECHILD:
|
||||
raise ExceptionPexpect ('isalive() encountered condition that should never happen. There was no child process. Did someone else call waitpid() on our process?')
|
||||
else:
|
||||
raise e
|
||||
|
||||
# If pid is still 0 after two calls to waitpid() then
|
||||
# the process really is alive. This seems to work on all platforms, except
|
||||
# for Irix which seems to require a blocking call on waitpid or select, so I let read_nonblocking
|
||||
# take care of this situation (unfortunately, this requires waiting through the timeout).
|
||||
if pid == 0:
|
||||
return True
|
||||
|
||||
if pid == 0:
|
||||
return True
|
||||
|
||||
if os.WIFEXITED (status):
|
||||
self.status = status
|
||||
self.exitstatus = os.WEXITSTATUS(status)
|
||||
self.signalstatus = None
|
||||
self.terminated = True
|
||||
elif os.WIFSIGNALED (status):
|
||||
self.status = status
|
||||
self.exitstatus = None
|
||||
self.signalstatus = os.WTERMSIG(status)
|
||||
self.terminated = True
|
||||
elif os.WIFSTOPPED (status):
|
||||
raise ExceptionPexpect ('isalive() encountered condition where child process is stopped. This is not supported. Is some other process attempting job control with our child pid?')
|
||||
return False
|
||||
|
||||
def kill(self, sig):
|
||||
|
||||
"""This sends the given signal to the child application. In keeping
|
||||
@ -1523,52 +1184,6 @@ class spawn_unix (object):
|
||||
s = struct.pack('HHHH', r, c, 0, 0)
|
||||
fcntl.ioctl(self.fileno(), TIOCSWINSZ, s)
|
||||
|
||||
def interact(self, escape_character = chr(29), input_filter = None, output_filter = None): # pragma: no cover
|
||||
warnings.warn(no_unix_deprecation_warning.format("spawn_unix::interact"), DeprecationWarning)
|
||||
|
||||
"""This gives control of the child process to the interactive user (the
|
||||
human at the keyboard). Keystrokes are sent to the child process, and
|
||||
the stdout and stderr output of the child process is printed. This
|
||||
simply echos the child stdout and child stderr to the real stdout and
|
||||
it echos the real stdin to the child stdin. When the user types the
|
||||
escape_character this method will stop. The default for
|
||||
escape_character is ^]. This should not be confused with ASCII 27 --
|
||||
the ESC character. ASCII 29 was chosen for historical merit because
|
||||
this is the character used by 'telnet' as the escape character. The
|
||||
escape_character will not be sent to the child process.
|
||||
|
||||
You may pass in optional input and output filter functions. These
|
||||
functions should take a string and return a string. The output_filter
|
||||
will be passed all the output from the child process. The input_filter
|
||||
will be passed all the keyboard input from the user. The input_filter
|
||||
is run BEFORE the check for the escape_character.
|
||||
|
||||
Note that if you change the window size of the parent the SIGWINCH
|
||||
signal will not be passed through to the child. If you want the child
|
||||
window size to change when the parent's window size changes then do
|
||||
something like the following example::
|
||||
|
||||
def sigwinch_passthrough (sig, data):
|
||||
s = struct.pack("HHHH", 0, 0, 0, 0)
|
||||
a = struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ , s))
|
||||
global p
|
||||
p.setwinsize(a[0],a[1])
|
||||
p = pexpect.spawn('/bin/bash') # Note this is global and used in sigwinch_passthrough.
|
||||
signal.signal(signal.SIGWINCH, sigwinch_passthrough)
|
||||
p.interact()
|
||||
"""
|
||||
|
||||
# Flush the buffer.
|
||||
self.stdout.write (self.buffer)
|
||||
self.stdout.flush()
|
||||
self.buffer = ''
|
||||
mode = tty.tcgetattr(self.STDIN_FILENO)
|
||||
tty.setraw(self.STDIN_FILENO)
|
||||
try:
|
||||
self.__interact_copy(escape_character, input_filter, output_filter)
|
||||
finally:
|
||||
tty.tcsetattr(self.STDIN_FILENO, tty.TCSAFLUSH, mode)
|
||||
|
||||
def __interact_writen(self, fd, data):
|
||||
|
||||
"""This is used by the interact() method.
|
||||
|
Loading…
Reference in New Issue
Block a user