mirror of
https://github.com/clearml/wexpect-venv
synced 2025-06-26 18:15:52 +00:00
minimal 16
This commit is contained in:
566
wexpect/host.py
566
wexpect/host.py
@@ -396,321 +396,7 @@ class SpawnBase:
|
||||
self.child_process = self.get_console_process()
|
||||
self.child_pid = self.child_process.pid
|
||||
return self.child_process
|
||||
|
||||
def close(self): # File-like object.
|
||||
""" Closes the child console."""
|
||||
|
||||
self.closed = self.terminate()
|
||||
|
||||
def terminate(self, force=False):
|
||||
"""Terminate the child. Force not used. """
|
||||
|
||||
if not self.isalive():
|
||||
return True
|
||||
|
||||
self.kill()
|
||||
time.sleep(self.delayafterterminate)
|
||||
if not self.isalive():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def isalive(self, trust_console=True):
|
||||
"""True if the child is still alive, false otherwise"""
|
||||
if trust_console:
|
||||
if self.flag_eof:
|
||||
return False
|
||||
|
||||
if self.child_process is None:
|
||||
# Child process has not been started... Not alive
|
||||
return False
|
||||
|
||||
try:
|
||||
self.exitstatus = self.child_process.wait(timeout=0)
|
||||
logger.info(f'exitstatus: {self.exitstatus}')
|
||||
except psutil.TimeoutExpired:
|
||||
return True
|
||||
|
||||
def kill(self, sig=signal.SIGTERM):
|
||||
"""Sig == sigint for ctrl-c otherwise the child is terminated."""
|
||||
try:
|
||||
self.child_process.send_signal(sig)
|
||||
except psutil.NoSuchProcess as e:
|
||||
logger.info('Child has already died. %s', e)
|
||||
|
||||
def wait(self, child=True, console=False):
|
||||
if child:
|
||||
self.exitstatus = self.child_process.wait()
|
||||
logger.info(f'exitstatus: {self.exitstatus}')
|
||||
if console:
|
||||
self.exitstatus = self.console_process.wait()
|
||||
logger.info(f'exitstatus: {self.exitstatus}')
|
||||
return self.exitstatus
|
||||
|
||||
def send(self, s, delaybeforesend=None):
|
||||
"""Virtual definition
|
||||
"""
|
||||
if delaybeforesend is None:
|
||||
delaybeforesend = self.delaybeforesend
|
||||
|
||||
if delaybeforesend:
|
||||
time.sleep(delaybeforesend)
|
||||
|
||||
return self._send_impl(s)
|
||||
|
||||
def _send_impl(self, s):
|
||||
"""Virtual definition
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def connect_to_child(self):
|
||||
"""Virtual definition
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def disconnect_from_child(self):
|
||||
"""Virtual definition
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def compile_pattern_list(self, patterns):
|
||||
"""This compiles a pattern-string or a list of pattern-strings.
|
||||
Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or a list of
|
||||
those. Patterns may also be None which results in an empty list (you
|
||||
might do this if waiting for an EOF or TIMEOUT condition without
|
||||
expecting any pattern).
|
||||
|
||||
This is used by expect() when calling expect_list(). Thus expect() is
|
||||
nothing more than::
|
||||
|
||||
cpl = self.compile_pattern_list(pl)
|
||||
return self.expect_list(cpl, timeout)
|
||||
|
||||
If you are using expect() within a loop it may be more
|
||||
efficient to compile the patterns first and then call expect_list().
|
||||
This avoid calls in a loop to compile_pattern_list()::
|
||||
|
||||
cpl = self.compile_pattern_list(my_pattern)
|
||||
while some_condition:
|
||||
...
|
||||
i = self.expect_list(clp, timeout)
|
||||
...
|
||||
"""
|
||||
|
||||
if patterns is None:
|
||||
return []
|
||||
if type(patterns) is not list:
|
||||
patterns = [patterns]
|
||||
|
||||
compile_flags = re.DOTALL # Allow dot to match \n
|
||||
if self.ignorecase:
|
||||
compile_flags = compile_flags | re.IGNORECASE
|
||||
compiled_pattern_list = []
|
||||
for p in patterns:
|
||||
if type(p) in (str,):
|
||||
compiled_pattern_list.append(re.compile(p, compile_flags))
|
||||
elif p is EOF:
|
||||
compiled_pattern_list.append(EOF)
|
||||
elif p is TIMEOUT:
|
||||
compiled_pattern_list.append(TIMEOUT)
|
||||
elif type(p) is type(re.compile('')):
|
||||
compiled_pattern_list.append(p)
|
||||
else:
|
||||
logger.warning("TypeError ('Argument must be one of StringTypes, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p)))")
|
||||
raise TypeError ('Argument must be one of StringTypes, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p)))
|
||||
|
||||
return compiled_pattern_list
|
||||
|
||||
def expect(self, pattern, timeout = -1, searchwindowsize=None):
|
||||
"""This seeks through the stream until a pattern is matched. The
|
||||
pattern is overloaded and may take several types. The pattern can be a
|
||||
StringType, EOF, a compiled re, or a list of any of those types.
|
||||
Strings will be compiled to re types. This returns the index into the
|
||||
pattern list. If the pattern was not a list this returns index 0 on a
|
||||
successful match. This may raise exceptions for EOF or TIMEOUT. To
|
||||
avoid the EOF or TIMEOUT exceptions add EOF or TIMEOUT to the pattern
|
||||
list. That will cause expect to match an EOF or TIMEOUT condition
|
||||
instead of raising an exception.
|
||||
|
||||
If you pass a list of patterns and more than one matches, the first match
|
||||
in the stream is chosen. If more than one pattern matches at that point,
|
||||
the leftmost in the pattern list is chosen. For example::
|
||||
|
||||
# the input is 'foobar'
|
||||
index = p.expect (['bar', 'foo', 'foobar'])
|
||||
# returns 1 ('foo') even though 'foobar' is a "better" match
|
||||
|
||||
Please note, however, that buffering can affect this behavior, since
|
||||
input arrives in unpredictable chunks. For example::
|
||||
|
||||
# the input is 'foobar'
|
||||
index = p.expect (['foobar', 'foo'])
|
||||
# returns 0 ('foobar') if all input is available at once,
|
||||
# but returs 1 ('foo') if parts of the final 'bar' arrive late
|
||||
|
||||
After a match is found the instance attributes 'before', 'after' and
|
||||
'match' will be set. You can see all the data read before the match in
|
||||
'before'. You can see the data that was matched in 'after'. The
|
||||
re.MatchObject used in the re match will be in 'match'. If an error
|
||||
occurred then 'before' will be set to all the data read so far and
|
||||
'after' and 'match' will be None.
|
||||
|
||||
If timeout is -1 then timeout will be set to the self.timeout value.
|
||||
|
||||
A list entry may be EOF or TIMEOUT instead of a string. This will
|
||||
catch these exceptions and return the index of the list entry instead
|
||||
of raising the exception. The attribute 'after' will be set to the
|
||||
exception type. The attribute 'match' will be None. This allows you to
|
||||
write code like this::
|
||||
|
||||
index = p.expect (['good', 'bad', wexpect.EOF, wexpect.TIMEOUT])
|
||||
if index == 0:
|
||||
do_something()
|
||||
elif index == 1:
|
||||
do_something_else()
|
||||
elif index == 2:
|
||||
do_some_other_thing()
|
||||
elif index == 3:
|
||||
do_something_completely_different()
|
||||
|
||||
instead of code like this::
|
||||
|
||||
try:
|
||||
index = p.expect (['good', 'bad'])
|
||||
if index == 0:
|
||||
do_something()
|
||||
elif index == 1:
|
||||
do_something_else()
|
||||
except EOF:
|
||||
do_some_other_thing()
|
||||
except TIMEOUT:
|
||||
do_something_completely_different()
|
||||
|
||||
These two forms are equivalent. It all depends on what you want. You
|
||||
can also just expect the EOF if you are waiting for all output of a
|
||||
child to finish. For example::
|
||||
|
||||
p = wexpect.spawn('/bin/ls')
|
||||
p.expect (wexpect.EOF)
|
||||
print p.before
|
||||
|
||||
If you are trying to optimize for speed then see expect_list().
|
||||
"""
|
||||
|
||||
compiled_pattern_list = self.compile_pattern_list(pattern)
|
||||
return self.expect_list(compiled_pattern_list, timeout, searchwindowsize)
|
||||
|
||||
def expect_list(self, pattern_list, timeout = -1, searchwindowsize = -1):
|
||||
"""This takes a list of compiled regular expressions and returns the
|
||||
index into the pattern_list that matched the child output. The list may
|
||||
also contain EOF or TIMEOUT (which are not compiled regular
|
||||
expressions). This method is similar to the expect() method except that
|
||||
expect_list() does not recompile the pattern list on every call. This
|
||||
may help if you are trying to optimize for speed, otherwise just use
|
||||
the expect() method. This is called by expect(). If timeout==-1 then
|
||||
the self.timeout value is used. If searchwindowsize==-1 then the
|
||||
self.searchwindowsize value is used. """
|
||||
|
||||
return self.expect_loop(searcher_re(pattern_list), timeout, searchwindowsize)
|
||||
|
||||
def expect_exact(self, pattern_list, timeout = -1, searchwindowsize = -1):
|
||||
"""This is similar to expect(), but uses plain string matching instead
|
||||
of compiled regular expressions in 'pattern_list'. The 'pattern_list'
|
||||
may be a string; a list or other sequence of strings; or TIMEOUT and
|
||||
EOF.
|
||||
|
||||
This call might be faster than expect() for two reasons: string
|
||||
searching is faster than RE matching and it is possible to limit the
|
||||
search to just the end of the input buffer.
|
||||
|
||||
This method is also useful when you don't want to have to worry about
|
||||
escaping regular expression characters that you want to match."""
|
||||
|
||||
if not isinstance(pattern_list, list):
|
||||
pattern_list = [pattern_list]
|
||||
|
||||
for p in pattern_list:
|
||||
if type(p) not in (str,) and p not in (TIMEOUT, EOF):
|
||||
logger.warning('Argument must be one of StringTypes, EOF, TIMEOUT, or a list of those type. %s' % str(type(p)))
|
||||
raise TypeError ('Argument must be one of StringTypes, EOF, TIMEOUT, or a list of those type. %s' % str(type(p)))
|
||||
|
||||
return self.expect_loop(searcher_string(pattern_list), timeout, searchwindowsize)
|
||||
|
||||
def expect_loop(self, searcher, timeout = -1, searchwindowsize = -1):
|
||||
"""This is the common loop used inside expect. The 'searcher' should be
|
||||
an instance of searcher_re or searcher_string, which describes how and what
|
||||
to search for in the input.
|
||||
|
||||
See expect() for other arguments, return value and exceptions. """
|
||||
|
||||
self.searcher = searcher
|
||||
|
||||
if timeout == -1:
|
||||
timeout = self.timeout
|
||||
if timeout is not None:
|
||||
end_time = time.time() + timeout
|
||||
if searchwindowsize == -1:
|
||||
searchwindowsize = self.searchwindowsize
|
||||
|
||||
logger.debug(f'searcher: {searcher}')
|
||||
|
||||
try:
|
||||
incoming = self.buffer
|
||||
freshlen = len(incoming)
|
||||
while True: # Keep reading until exception or return.
|
||||
index = searcher.search(incoming, freshlen, searchwindowsize)
|
||||
if index >= 0:
|
||||
self.buffer = incoming[searcher.end : ]
|
||||
self.before = incoming[ : searcher.start]
|
||||
self.after = incoming[searcher.start : searcher.end]
|
||||
self.match = searcher.match
|
||||
self.match_index = index
|
||||
return self.match_index
|
||||
# No match at this point
|
||||
if timeout is not None and end_time < time.time():
|
||||
logger.info('Timeout exceeded in expect_any().')
|
||||
raise TIMEOUT ('Timeout exceeded in expect_any().')
|
||||
# Still have time left, so read more data
|
||||
self.isalive()
|
||||
c = self.read_nonblocking(self.maxread)
|
||||
freshlen = len(c)
|
||||
time.sleep (0.01)
|
||||
incoming += c
|
||||
except EOF as e:
|
||||
self.buffer = ''
|
||||
self.before = incoming
|
||||
self.after = EOF
|
||||
index = searcher.eof_index
|
||||
if index >= 0:
|
||||
self.match = EOF
|
||||
self.match_index = index
|
||||
return self.match_index
|
||||
else:
|
||||
self.match = None
|
||||
self.match_index = None
|
||||
logger.info(f'EOF: {e}\n{self}')
|
||||
raise EOF(f'{e}\n{self}')
|
||||
except TIMEOUT as e:
|
||||
self.buffer = incoming
|
||||
self.before = incoming
|
||||
self.after = TIMEOUT
|
||||
index = searcher.timeout_index
|
||||
if index >= 0:
|
||||
self.match = TIMEOUT
|
||||
self.match_index = index
|
||||
return self.match_index
|
||||
else:
|
||||
self.match = None
|
||||
self.match_index = None
|
||||
logger.info(f'TIMEOUT: {e}\n{self}')
|
||||
raise TIMEOUT(f'{e}\n{self}')
|
||||
except:
|
||||
self.before = incoming
|
||||
self.after = None
|
||||
self.match = None
|
||||
self.match_index = None
|
||||
raise
|
||||
|
||||
class SpawnPipe(SpawnBase):
|
||||
|
||||
def __init__(self, command, args=[], timeout=30, maxread=60000, searchwindowsize=None,
|
||||
@@ -834,257 +520,11 @@ class SpawnPipe(SpawnBase):
|
||||
|
||||
|
||||
class SpawnSocket(SpawnBase):
|
||||
|
||||
def __init__(self, command, args=[], timeout=30, maxread=60000, searchwindowsize=None,
|
||||
logfile=None, cwd=None, env=None, codepage=None, echo=True, port=4321, host='127.0.0.1', interact=False):
|
||||
self.port = port
|
||||
self.host = host
|
||||
self.sock = None
|
||||
self.console_class_name = 'ConsoleReaderSocket'
|
||||
self.console_class_parameters = {'port': port}
|
||||
|
||||
super().__init__(command=command, args=args, timeout=timeout, maxread=maxread,
|
||||
searchwindowsize=searchwindowsize, cwd=cwd, env=env, codepage=codepage, echo=echo, interact=interact)
|
||||
|
||||
self.delayafterterminate = 1 # Sets delay in terminate() method to allow kernel time to update process status. Time in seconds.
|
||||
|
||||
def _send_impl(self, s):
|
||||
"""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. """
|
||||
if isinstance(s, str):
|
||||
s = str.encode(s)
|
||||
self.sock.sendall(s)
|
||||
return len(s)
|
||||
|
||||
def connect_to_child(self):
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.connect((self.host, self.port))
|
||||
self.sock.settimeout(.2)
|
||||
|
||||
def disconnect_from_child(self):
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
|
||||
def read_nonblocking (self, size = 1):
|
||||
"""This reads at most size characters from the child application. If
|
||||
the end of file is read then an EOF exception will be raised.
|
||||
|
||||
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 Wtty.read(). """
|
||||
|
||||
if self.closed:
|
||||
logger.info('I/O operation on closed file in read_nonblocking().')
|
||||
raise ValueError ('I/O operation on closed file in read_nonblocking().')
|
||||
|
||||
try:
|
||||
s = self.sock.recv(size)
|
||||
|
||||
if s:
|
||||
logger.debug(f'Readed: {s}')
|
||||
else:
|
||||
logger.spam(f'Readed: {s}')
|
||||
|
||||
|
||||
if EOF_CHAR in s:
|
||||
self.flag_eof = True
|
||||
logger.info("EOF: EOF character has been arrived")
|
||||
raise EOF('EOF character has been arrived')
|
||||
|
||||
except ConnectionResetError:
|
||||
self.flag_eof = True
|
||||
logger.info("EOF('ConnectionResetError')")
|
||||
raise EOF('ConnectionResetError')
|
||||
except socket.timeout:
|
||||
return ''
|
||||
|
||||
return s.decode()
|
||||
|
||||
def kill(self, sig=signal.SIGTERM):
|
||||
"""Sig == sigint for ctrl-c otherwise the child is terminated."""
|
||||
try:
|
||||
logger.info(f'Sending kill signal: {sig}')
|
||||
self.send(SIGNAL_CHARS[sig])
|
||||
except EOF as e:
|
||||
logger.info(e)
|
||||
|
||||
pass
|
||||
|
||||
class searcher_re (object):
|
||||
"""This is regular expression string search helper for the
|
||||
spawn.expect_any() method.
|
||||
|
||||
Attributes:
|
||||
|
||||
eof_index - index of EOF, or -1
|
||||
timeout_index - index of TIMEOUT, or -1
|
||||
|
||||
After a successful match by the search() method the following attributes
|
||||
are available:
|
||||
|
||||
start - index into the buffer, first byte of match
|
||||
end - index into the buffer, first byte after match
|
||||
match - the re.match object returned by a succesful re.search
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, patterns):
|
||||
"""This creates an instance that searches for 'patterns' Where
|
||||
'patterns' may be a list or other sequence of compiled regular
|
||||
expressions, or the EOF or TIMEOUT types."""
|
||||
|
||||
self.eof_index = -1
|
||||
self.timeout_index = -1
|
||||
self._searches = []
|
||||
for n, s in zip(list(range(len(patterns))), patterns):
|
||||
if s is EOF:
|
||||
self.eof_index = n
|
||||
continue
|
||||
if s is TIMEOUT:
|
||||
self.timeout_index = n
|
||||
continue
|
||||
self._searches.append((n, s))
|
||||
|
||||
def __str__(self):
|
||||
"""This returns a human-readable string that represents the state of
|
||||
the object."""
|
||||
|
||||
ss = [ (n,' %d: re.compile("%s")' % (n,str(s.pattern))) for n,s in self._searches]
|
||||
ss.append((-1,'searcher_re:'))
|
||||
if self.eof_index >= 0:
|
||||
ss.append ((self.eof_index,' %d: EOF' % self.eof_index))
|
||||
if self.timeout_index >= 0:
|
||||
ss.append ((self.timeout_index,' %d: TIMEOUT' % self.timeout_index))
|
||||
ss.sort()
|
||||
ss = list(zip(*ss))[1]
|
||||
return '\n'.join(ss)
|
||||
|
||||
def search(self, buffer, freshlen, searchwindowsize=None):
|
||||
"""This searches 'buffer' for the first occurence of one of the regular
|
||||
expressions. 'freshlen' must indicate the number of bytes at the end of
|
||||
'buffer' which have not been searched before.
|
||||
|
||||
See class spawn for the 'searchwindowsize' argument.
|
||||
|
||||
If there is a match this returns the index of that string, and sets
|
||||
'start', 'end' and 'match'. Otherwise, returns -1."""
|
||||
|
||||
absurd_match = len(buffer)
|
||||
first_match = absurd_match
|
||||
# 'freshlen' doesn't help here -- we cannot predict the
|
||||
# length of a match, and the re module provides no help.
|
||||
if searchwindowsize is None:
|
||||
searchstart = 0
|
||||
else:
|
||||
searchstart = max(0, len(buffer)-searchwindowsize)
|
||||
for index, s in self._searches:
|
||||
match = s.search(buffer, searchstart)
|
||||
if match is None:
|
||||
continue
|
||||
n = match.start()
|
||||
if n < first_match:
|
||||
first_match = n
|
||||
the_match = match
|
||||
best_index = index
|
||||
if first_match == absurd_match:
|
||||
return -1
|
||||
self.start = first_match
|
||||
self.match = the_match
|
||||
self.end = self.match.end()
|
||||
return best_index
|
||||
pass
|
||||
|
||||
|
||||
class searcher_string (object):
|
||||
"""This is a plain string search helper for the spawn.expect_any() method.
|
||||
|
||||
Attributes:
|
||||
|
||||
eof_index - index of EOF, or -1
|
||||
timeout_index - index of TIMEOUT, or -1
|
||||
|
||||
After a successful match by the search() method the following attributes
|
||||
are available:
|
||||
|
||||
start - index into the buffer, first byte of match
|
||||
end - index into the buffer, first byte after match
|
||||
match - the matching string itself
|
||||
"""
|
||||
|
||||
def __init__(self, strings):
|
||||
"""This creates an instance of searcher_string. This argument 'strings'
|
||||
may be a list; a sequence of strings; or the EOF or TIMEOUT types. """
|
||||
|
||||
self.eof_index = -1
|
||||
self.timeout_index = -1
|
||||
self._strings = []
|
||||
for n, s in zip(list(range(len(strings))), strings):
|
||||
if s is EOF:
|
||||
self.eof_index = n
|
||||
continue
|
||||
if s is TIMEOUT:
|
||||
self.timeout_index = n
|
||||
continue
|
||||
self._strings.append((n, s))
|
||||
|
||||
def __str__(self):
|
||||
"""This returns a human-readable string that represents the state of
|
||||
the object."""
|
||||
|
||||
ss = [ (ns[0],' %d: "%s"' % ns) for ns in self._strings ]
|
||||
ss.append((-1,'searcher_string:'))
|
||||
if self.eof_index >= 0:
|
||||
ss.append ((self.eof_index,' %d: EOF' % self.eof_index))
|
||||
if self.timeout_index >= 0:
|
||||
ss.append ((self.timeout_index,' %d: TIMEOUT' % self.timeout_index))
|
||||
ss.sort()
|
||||
ss = list(zip(*ss))[1]
|
||||
return '\n'.join(ss)
|
||||
|
||||
def search(self, buffer, freshlen, searchwindowsize=None):
|
||||
"""This searches 'buffer' for the first occurence of one of the search
|
||||
strings. 'freshlen' must indicate the number of bytes at the end of
|
||||
'buffer' which have not been searched before. It helps to avoid
|
||||
searching the same, possibly big, buffer over and over again.
|
||||
|
||||
See class spawn for the 'searchwindowsize' argument.
|
||||
|
||||
If there is a match this returns the index of that string, and sets
|
||||
'start', 'end' and 'match'. Otherwise, this returns -1. """
|
||||
|
||||
absurd_match = len(buffer)
|
||||
first_match = absurd_match
|
||||
|
||||
# 'freshlen' helps a lot here. Further optimizations could
|
||||
# possibly include:
|
||||
#
|
||||
# using something like the Boyer-Moore Fast String Searching
|
||||
# Algorithm; pre-compiling the search through a list of
|
||||
# strings into something that can scan the input once to
|
||||
# search for all N strings; realize that if we search for
|
||||
# ['bar', 'baz'] and the input is '...foo' we need not bother
|
||||
# rescanning until we've read three more bytes.
|
||||
#
|
||||
# Sadly, I don't know enough about this interesting topic. /grahn
|
||||
|
||||
for index, s in self._strings:
|
||||
if searchwindowsize is None:
|
||||
# the match, if any, can only be in the fresh data,
|
||||
# or at the very end of the old data
|
||||
offset = -(freshlen+len(s))
|
||||
else:
|
||||
# better obey searchwindowsize
|
||||
offset = -searchwindowsize
|
||||
n = buffer.find(s, offset)
|
||||
if n >= 0 and n < first_match:
|
||||
first_match = n
|
||||
best_index, best_match = index, s
|
||||
if first_match == absurd_match:
|
||||
return -1
|
||||
self.match = best_match
|
||||
self.start = first_match
|
||||
self.end = self.start + len(self.match)
|
||||
return best_index
|
||||
pass
|
||||
@@ -524,575 +524,6 @@ class spawn_windows ():
|
||||
self.pid = self.wtty.pid
|
||||
|
||||
|
||||
def fileno (self): # File-like object.
|
||||
"""There is no child fd."""
|
||||
|
||||
return 0
|
||||
|
||||
def close(self, force=True): # File-like object.
|
||||
""" Closes the child console."""
|
||||
|
||||
self.closed = self.terminate(force)
|
||||
if not self.closed:
|
||||
logger.info('ExceptionPexpect: close() could not terminate the child using terminate()')
|
||||
raise ExceptionPexpect ('close() could not terminate the child using terminate()')
|
||||
self.closed = True
|
||||
|
||||
def isatty(self): # File-like object.
|
||||
"""The child is always created with a console."""
|
||||
|
||||
return True
|
||||
|
||||
def waitnoecho (self, timeout=-1):
|
||||
"""This waits until the terminal ECHO flag is set False. This returns
|
||||
True if the echo mode is off. This returns False if the ECHO flag was
|
||||
not set False before the timeout. This can be used to detect when the
|
||||
child is waiting for a password. Usually a child application will turn
|
||||
off echo mode when it is waiting for the user to enter a password. For
|
||||
example, instead of expecting the "password:" prompt you can wait for
|
||||
the child to set ECHO off::
|
||||
|
||||
p = wexpect.spawn ('ssh user@example.com')
|
||||
p.waitnoecho()
|
||||
p.sendline(mypassword)
|
||||
|
||||
If timeout is None then this method to block forever until ECHO flag is
|
||||
False.
|
||||
|
||||
"""
|
||||
|
||||
if timeout == -1:
|
||||
timeout = self.timeout
|
||||
if timeout is not None:
|
||||
end_time = time.time() + timeout
|
||||
while True:
|
||||
if not self.getecho():
|
||||
return True
|
||||
if timeout < 0 and timeout is not None:
|
||||
return False
|
||||
if timeout is not None:
|
||||
timeout = end_time - time.time()
|
||||
time.sleep(0.1)
|
||||
|
||||
def getecho (self):
|
||||
"""This returns the terminal echo mode. This returns True if echo is
|
||||
on or False if echo is off. Child applications that are expecting you
|
||||
to enter a password often set ECHO False. See waitnoecho()."""
|
||||
|
||||
return self.wtty.getecho()
|
||||
|
||||
def setecho (self, state):
|
||||
"""This sets the terminal echo mode on or off."""
|
||||
|
||||
self.wtty.setecho(state)
|
||||
|
||||
def read (self, size = -1): # File-like object.
|
||||
|
||||
"""This reads at most "size" bytes from the file (less if the read hits
|
||||
EOF before obtaining size bytes). If the size argument is negative or
|
||||
omitted, read all data until EOF is reached. The bytes are returned as
|
||||
a string object. An empty string is returned when EOF is encountered
|
||||
immediately. """
|
||||
|
||||
if size == 0:
|
||||
return ''
|
||||
if size < 0:
|
||||
self.expect (self.delimiter) # delimiter default is EOF
|
||||
return self.before
|
||||
|
||||
# I could have done this more directly by not using expect(), but
|
||||
# I deliberately decided to couple read() to expect() so that
|
||||
# I would catch any bugs early and ensure consistant behavior.
|
||||
# It's a little less efficient, but there is less for me to
|
||||
# worry about if I have to later modify read() or expect().
|
||||
# Note, it's OK if size==-1 in the regex. That just means it
|
||||
# will never match anything in which case we stop only on EOF.
|
||||
cre = re.compile('.{%d}' % size, re.DOTALL)
|
||||
index = self.expect ([cre, self.delimiter]) # delimiter default is EOF
|
||||
if index == 0:
|
||||
return self.after ### self.before should be ''. Should I assert this?
|
||||
return self.before
|
||||
|
||||
def readline (self, size = -1): # File-like object.
|
||||
|
||||
"""This reads and returns one entire line. A trailing newline is kept
|
||||
in the string, but may be absent when a file ends with an incomplete
|
||||
line. Note: This readline() looks for a \\r\\n pair even on UNIX
|
||||
because this is what the pseudo tty device returns. So contrary to what
|
||||
you may expect you will receive the newline as \\r\\n. An empty string
|
||||
is returned when EOF is hit immediately. Currently, the size argument is
|
||||
mostly ignored, so this behavior is not standard for a file-like
|
||||
object. If size is 0 then an empty string is returned. """
|
||||
|
||||
if size == 0:
|
||||
return ''
|
||||
index = self.expect (['\r\n', self.delimiter]) # delimiter default is EOF
|
||||
if index == 0:
|
||||
return self.before + '\r\n'
|
||||
else:
|
||||
return self.before
|
||||
|
||||
def __iter__ (self): # File-like object.
|
||||
|
||||
"""This is to support iterators over a file-like object.
|
||||
"""
|
||||
|
||||
return self
|
||||
|
||||
def __next__ (self): # File-like object.
|
||||
|
||||
"""This is to support iterators over a file-like object.
|
||||
"""
|
||||
|
||||
result = self.readline()
|
||||
if self.after == self.delimiter:
|
||||
raise StopIteration
|
||||
return result
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.terminate()
|
||||
|
||||
def readlines (self, sizehint = -1): # File-like object.
|
||||
|
||||
"""This reads until EOF using readline() and returns a list containing
|
||||
the lines thus read. The optional "sizehint" argument is ignored. """
|
||||
|
||||
lines = []
|
||||
while True:
|
||||
line = self.readline()
|
||||
if not line:
|
||||
break
|
||||
lines.append(line)
|
||||
return lines
|
||||
|
||||
def read_nonblocking (self, size = 1):
|
||||
"""This reads at most size characters from the child application. If
|
||||
the end of file is read then an EOF exception will be raised.
|
||||
|
||||
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 Wtty.read(). """
|
||||
|
||||
if self.closed:
|
||||
logger.info('ValueError: I/O operation on closed file in read_nonblocking().')
|
||||
raise ValueError ('I/O operation on closed file in read_nonblocking().')
|
||||
|
||||
try:
|
||||
# The real child and it's console are two different process. The console dies 0.1 sec
|
||||
# later to be able to read the child's last output (before EOF). So here we check
|
||||
# isalive() (which checks the real child.) and try a last read on the console. To catch
|
||||
# the last output.
|
||||
# The flag_child_finished flag shows that this is the second trial, where we raise the EOF.
|
||||
if self.flag_child_finished:
|
||||
logger.info('EOF: self.flag_child_finished')
|
||||
raise EOF('self.flag_child_finished')
|
||||
if not self.isalive():
|
||||
self.flag_child_finished = True
|
||||
s = self.wtty.read_nonblocking(size)
|
||||
except EOF:
|
||||
self.flag_eof = True
|
||||
raise
|
||||
|
||||
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
|
||||
|
||||
def write(self, s): # File-like object.
|
||||
|
||||
"""This is similar to send() except that there is no return value.
|
||||
"""
|
||||
|
||||
self.send (s)
|
||||
|
||||
def writelines (self, sequence): # File-like object.
|
||||
|
||||
"""This calls write() for each element in the sequence. The sequence
|
||||
can be any iterable object producing strings, typically a list of
|
||||
strings. This does not add line separators There is no return value.
|
||||
"""
|
||||
|
||||
for s in sequence:
|
||||
self.write (s)
|
||||
|
||||
def sendline(self, s=''):
|
||||
|
||||
"""This is like send(), but it adds a line feed (os.linesep). This
|
||||
returns the number of bytes written. """
|
||||
|
||||
n = self.send(s)
|
||||
n = n + self.send (os.linesep)
|
||||
return n
|
||||
|
||||
def sendeof(self):
|
||||
|
||||
"""This sends an EOF to the child. This sends a character which causes
|
||||
the pending parent output buffer to be sent to the waiting child
|
||||
program without waiting for end-of-line. If it is the first character
|
||||
of the line, the read() in the user program returns 0, which signifies
|
||||
end-of-file. This means to work as expected a sendeof() has to be
|
||||
called at the beginning of a line. This method does not send a newline.
|
||||
It is the responsibility of the caller to ensure the eof is sent at the
|
||||
beginning of a line. """
|
||||
|
||||
# platform does not define VEOF so assume CTRL-D
|
||||
char = chr(4)
|
||||
self.send(char)
|
||||
|
||||
def send(self, s):
|
||||
"""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. """
|
||||
|
||||
(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 = self.wtty.write(s)
|
||||
return c
|
||||
|
||||
def sendintr(self):
|
||||
"""This sends a SIGINT to the child. It does not require
|
||||
the SIGINT to be the first character on a line. """
|
||||
|
||||
self.wtty.sendintr()
|
||||
|
||||
def eof (self):
|
||||
|
||||
"""This returns True if the EOF exception was ever raised.
|
||||
"""
|
||||
|
||||
return self.flag_eof
|
||||
|
||||
def terminate(self, force=False):
|
||||
"""Terminate the child. Force not used. """
|
||||
|
||||
if not self.isalive():
|
||||
return True
|
||||
|
||||
self.wtty.terminate_child()
|
||||
time.sleep(self.delayafterterminate)
|
||||
if not self.isalive():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def kill(self, sig=signal.SIGTERM):
|
||||
"""Sig == sigint for ctrl-c otherwise the child is terminated."""
|
||||
|
||||
if not self.isalive():
|
||||
return
|
||||
|
||||
if sig == signal.SIGINT:
|
||||
self.wtty.sendintr()
|
||||
else:
|
||||
self.wtty.terminate_child()
|
||||
|
||||
def wait(self):
|
||||
"""This waits until the child exits. This is a blocking call. This will
|
||||
not read any data from the child, so this will block forever if the
|
||||
child has unread output and has terminated. In other words, the child
|
||||
may have printed output then called exit(); but, technically, the child
|
||||
is still alive until its output is read."""
|
||||
|
||||
# We can't use os.waitpid under Windows because of 'permission denied'
|
||||
# exception? Perhaps if not running as admin (or UAC enabled under
|
||||
# Vista/7). Simply loop and wait for child to exit.
|
||||
while self.isalive():
|
||||
time.sleep(.05) # Keep CPU utilization down
|
||||
|
||||
return self.exitstatus
|
||||
|
||||
def isalive(self):
|
||||
"""Determines if the child is still alive."""
|
||||
|
||||
if self.terminated:
|
||||
logger.debug('self.terminated is true')
|
||||
return False
|
||||
|
||||
if self.wtty.isalive():
|
||||
return True
|
||||
else:
|
||||
logger.debug('self.wtty.isalive() is false')
|
||||
self.exitstatus = win32process.GetExitCodeProcess(self.wtty.getchild())
|
||||
self.status = (self.pid, self.exitstatus << 8) # left-shift exit status by 8 bits like os.waitpid
|
||||
self.terminated = True
|
||||
return False
|
||||
|
||||
def compile_pattern_list(self, patterns):
|
||||
|
||||
"""This compiles a pattern-string or a list of pattern-strings.
|
||||
Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or a list of
|
||||
those. Patterns may also be None which results in an empty list (you
|
||||
might do this if waiting for an EOF or TIMEOUT condition without
|
||||
expecting any pattern).
|
||||
|
||||
This is used by expect() when calling expect_list(). Thus expect() is
|
||||
nothing more than::
|
||||
|
||||
cpl = self.compile_pattern_list(pl)
|
||||
return self.expect_list(cpl, timeout)
|
||||
|
||||
If you are using expect() within a loop it may be more
|
||||
efficient to compile the patterns first and then call expect_list().
|
||||
This avoid calls in a loop to compile_pattern_list()::
|
||||
|
||||
cpl = self.compile_pattern_list(my_pattern)
|
||||
while some_condition:
|
||||
...
|
||||
i = self.expect_list(clp, timeout)
|
||||
...
|
||||
"""
|
||||
|
||||
if patterns is None:
|
||||
return []
|
||||
if type(patterns) is not list:
|
||||
patterns = [patterns]
|
||||
|
||||
compile_flags = re.DOTALL # Allow dot to match \n
|
||||
if self.ignorecase:
|
||||
compile_flags = compile_flags | re.IGNORECASE
|
||||
compiled_pattern_list = []
|
||||
for p in patterns:
|
||||
if type(p) in (str,):
|
||||
compiled_pattern_list.append(re.compile(p, compile_flags))
|
||||
elif p is EOF:
|
||||
compiled_pattern_list.append(EOF)
|
||||
elif p is TIMEOUT:
|
||||
compiled_pattern_list.append(TIMEOUT)
|
||||
elif type(p) is type(re.compile('')):
|
||||
compiled_pattern_list.append(p)
|
||||
else:
|
||||
logger.info('TypeError: Argument must be one of StringTypes, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p)))
|
||||
raise TypeError ('Argument must be one of StringTypes, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p)))
|
||||
|
||||
return compiled_pattern_list
|
||||
|
||||
def expect(self, pattern, timeout = -1, searchwindowsize=None):
|
||||
|
||||
"""This seeks through the stream until a pattern is matched. The
|
||||
pattern is overloaded and may take several types. The pattern can be a
|
||||
StringType, EOF, a compiled re, or a list of any of those types.
|
||||
Strings will be compiled to re types. This returns the index into the
|
||||
pattern list. If the pattern was not a list this returns index 0 on a
|
||||
successful match. This may raise exceptions for EOF or TIMEOUT. To
|
||||
avoid the EOF or TIMEOUT exceptions add EOF or TIMEOUT to the pattern
|
||||
list. That will cause expect to match an EOF or TIMEOUT condition
|
||||
instead of raising an exception.
|
||||
|
||||
If you pass a list of patterns and more than one matches, the first match
|
||||
in the stream is chosen. If more than one pattern matches at that point,
|
||||
the leftmost in the pattern list is chosen. For example::
|
||||
|
||||
# the input is 'foobar'
|
||||
index = p.expect (['bar', 'foo', 'foobar'])
|
||||
# returns 1 ('foo') even though 'foobar' is a "better" match
|
||||
|
||||
Please note, however, that buffering can affect this behavior, since
|
||||
input arrives in unpredictable chunks. For example::
|
||||
|
||||
# the input is 'foobar'
|
||||
index = p.expect (['foobar', 'foo'])
|
||||
# returns 0 ('foobar') if all input is available at once,
|
||||
# but returs 1 ('foo') if parts of the final 'bar' arrive late
|
||||
|
||||
After a match is found the instance attributes 'before', 'after' and
|
||||
'match' will be set. You can see all the data read before the match in
|
||||
'before'. You can see the data that was matched in 'after'. The
|
||||
re.MatchObject used in the re match will be in 'match'. If an error
|
||||
occurred then 'before' will be set to all the data read so far and
|
||||
'after' and 'match' will be None.
|
||||
|
||||
If timeout is -1 then timeout will be set to the self.timeout value.
|
||||
|
||||
A list entry may be EOF or TIMEOUT instead of a string. This will
|
||||
catch these exceptions and return the index of the list entry instead
|
||||
of raising the exception. The attribute 'after' will be set to the
|
||||
exception type. The attribute 'match' will be None. This allows you to
|
||||
write code like this::
|
||||
|
||||
index = p.expect (['good', 'bad', wexpect.EOF, wexpect.TIMEOUT])
|
||||
if index == 0:
|
||||
do_something()
|
||||
elif index == 1:
|
||||
do_something_else()
|
||||
elif index == 2:
|
||||
do_some_other_thing()
|
||||
elif index == 3:
|
||||
do_something_completely_different()
|
||||
|
||||
instead of code like this::
|
||||
|
||||
try:
|
||||
index = p.expect (['good', 'bad'])
|
||||
if index == 0:
|
||||
do_something()
|
||||
elif index == 1:
|
||||
do_something_else()
|
||||
except EOF:
|
||||
do_some_other_thing()
|
||||
except TIMEOUT:
|
||||
do_something_completely_different()
|
||||
|
||||
These two forms are equivalent. It all depends on what you want. You
|
||||
can also just expect the EOF if you are waiting for all output of a
|
||||
child to finish. For example::
|
||||
|
||||
p = wexpect.spawn('/bin/ls')
|
||||
p.expect (wexpect.EOF)
|
||||
print p.before
|
||||
|
||||
If you are trying to optimize for speed then see expect_list().
|
||||
"""
|
||||
|
||||
compiled_pattern_list = self.compile_pattern_list(pattern)
|
||||
return self.expect_list(compiled_pattern_list, timeout, searchwindowsize)
|
||||
|
||||
def expect_list(self, pattern_list, timeout = -1, searchwindowsize = -1):
|
||||
|
||||
"""This takes a list of compiled regular expressions and returns the
|
||||
index into the pattern_list that matched the child output. The list may
|
||||
also contain EOF or TIMEOUT (which are not compiled regular
|
||||
expressions). This method is similar to the expect() method except that
|
||||
expect_list() does not recompile the pattern list on every call. This
|
||||
may help if you are trying to optimize for speed, otherwise just use
|
||||
the expect() method. This is called by expect(). If timeout==-1 then
|
||||
the self.timeout value is used. If searchwindowsize==-1 then the
|
||||
self.searchwindowsize value is used. """
|
||||
|
||||
return self.expect_loop(searcher_re(pattern_list), timeout, searchwindowsize)
|
||||
|
||||
def expect_exact(self, pattern_list, timeout = -1, searchwindowsize = -1):
|
||||
|
||||
"""This is similar to expect(), but uses plain string matching instead
|
||||
of compiled regular expressions in 'pattern_list'. The 'pattern_list'
|
||||
may be a string; a list or other sequence of strings; or TIMEOUT and
|
||||
EOF.
|
||||
|
||||
This call might be faster than expect() for two reasons: string
|
||||
searching is faster than RE matching and it is possible to limit the
|
||||
search to just the end of the input buffer.
|
||||
|
||||
This method is also useful when you don't want to have to worry about
|
||||
escaping regular expression characters that you want to match."""
|
||||
|
||||
if not isinstance(pattern_list, list):
|
||||
pattern_list = [pattern_list]
|
||||
|
||||
for p in pattern_list:
|
||||
if type(p) not in (str,) and p not in (TIMEOUT, EOF):
|
||||
logger.info('TypeError: Argument must be one of StringTypes, EOF, TIMEOUT, or a list of those type. %s' % str(type(p)))
|
||||
raise TypeError ('Argument must be one of StringTypes, EOF, TIMEOUT, or a list of those type. %s' % str(type(p)))
|
||||
|
||||
return self.expect_loop(searcher_string(pattern_list), timeout, searchwindowsize)
|
||||
|
||||
def expect_loop(self, searcher, timeout = -1, searchwindowsize = -1):
|
||||
|
||||
"""This is the common loop used inside expect. The 'searcher' should be
|
||||
an instance of searcher_re or searcher_string, which describes how and what
|
||||
to search for in the input.
|
||||
|
||||
See expect() for other arguments, return value and exceptions. """
|
||||
|
||||
self.searcher = searcher
|
||||
|
||||
if timeout == -1:
|
||||
timeout = self.timeout
|
||||
if timeout is not None:
|
||||
end_time = time.time() + timeout
|
||||
if searchwindowsize == -1:
|
||||
searchwindowsize = self.searchwindowsize
|
||||
|
||||
try:
|
||||
incoming = self.buffer
|
||||
freshlen = len(incoming)
|
||||
while True: # Keep reading until exception or return.
|
||||
index = searcher.search(incoming, freshlen, searchwindowsize)
|
||||
if index >= 0:
|
||||
self.buffer = incoming[searcher.end : ]
|
||||
self.before = incoming[ : searcher.start]
|
||||
self.after = incoming[searcher.start : searcher.end]
|
||||
self.match = searcher.match
|
||||
self.match_index = index
|
||||
return self.match_index
|
||||
# No match at this point
|
||||
if timeout is not None and end_time < time.time():
|
||||
logger.info('TIMEOUT: Timeout exceeded in expect_any().')
|
||||
raise TIMEOUT ('Timeout exceeded in expect_any().')
|
||||
# Still have time left, so read more data
|
||||
c = self.read_nonblocking(self.maxread)
|
||||
freshlen = len(c)
|
||||
time.sleep (0.01)
|
||||
incoming += c
|
||||
except EOF as e:
|
||||
self.buffer = ''
|
||||
self.before = incoming
|
||||
self.after = EOF
|
||||
index = searcher.eof_index
|
||||
if index >= 0:
|
||||
self.match = EOF
|
||||
self.match_index = index
|
||||
return self.match_index
|
||||
else:
|
||||
self.match = None
|
||||
self.match_index = None
|
||||
logger.info(str(e) + '\n' + str(self))
|
||||
raise EOF (str(e) + '\n' + str(self))
|
||||
except TIMEOUT as e:
|
||||
self.buffer = incoming
|
||||
self.before = incoming
|
||||
self.after = TIMEOUT
|
||||
index = searcher.timeout_index
|
||||
if index >= 0:
|
||||
self.match = TIMEOUT
|
||||
self.match_index = index
|
||||
return self.match_index
|
||||
else:
|
||||
self.match = None
|
||||
self.match_index = None
|
||||
logger.info(str(e) + '\n' + str(self))
|
||||
raise TIMEOUT (str(e) + '\n' + str(self))
|
||||
except:
|
||||
self.before = incoming
|
||||
self.after = None
|
||||
self.match = None
|
||||
self.match_index = None
|
||||
raise
|
||||
|
||||
def getwinsize(self):
|
||||
"""This returns the terminal window size of the child tty. The return
|
||||
value is a tuple of (rows, cols). """
|
||||
|
||||
return self.wtty.getwinsize()
|
||||
|
||||
def setwinsize(self, r, c):
|
||||
"""Set the size of the child screen buffer. """
|
||||
|
||||
self.wtty.setwinsize(r, c)
|
||||
|
||||
def interact(self):
|
||||
"""Makes the child console visible for interaction"""
|
||||
|
||||
self.wtty.interact()
|
||||
|
||||
def stop_interact(self):
|
||||
"""Hides the child console from the user."""
|
||||
|
||||
self.wtty.stop_interact()
|
||||
|
||||
##############################################################################
|
||||
# End of spawn_windows class
|
||||
##############################################################################
|
||||
|
||||
Reference in New Issue
Block a user