mirror of
https://github.com/clearml/wexpect-venv
synced 2025-06-26 18:15:52 +00:00
minimal 19
This commit is contained in:
parent
2b13b60920
commit
03e20dc08c
244
wexpect/host.py
244
wexpect/host.py
@ -96,114 +96,6 @@ logger = logging.getLogger('wexpect')
|
|||||||
init_logger(logger)
|
init_logger(logger)
|
||||||
|
|
||||||
|
|
||||||
def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None,
|
|
||||||
cwd=None, env=None, **kwargs):
|
|
||||||
"""
|
|
||||||
This function runs the given command; waits for it to finish; then
|
|
||||||
returns all output as a string. STDERR is included in output. If the full
|
|
||||||
path to the command is not given then the path is searched.
|
|
||||||
|
|
||||||
Note that lines are terminated by CR/LF (\\r\\n) combination even on
|
|
||||||
UNIX-like systems because this is the standard for pseudo ttys. If you set
|
|
||||||
'withexitstatus' to true, then run will return a tuple of (command_output,
|
|
||||||
exitstatus). If 'withexitstatus' is false then this returns just
|
|
||||||
command_output.
|
|
||||||
|
|
||||||
The run() function can often be used instead of creating a spawn instance.
|
|
||||||
For example, the following code uses spawn::
|
|
||||||
|
|
||||||
child = spawn('scp foo myname@host.example.com:.')
|
|
||||||
child.expect ('(?i)password')
|
|
||||||
child.sendline (mypassword)
|
|
||||||
|
|
||||||
The previous code can be replace with the following::
|
|
||||||
|
|
||||||
Examples
|
|
||||||
========
|
|
||||||
|
|
||||||
Start the apache daemon on the local machine::
|
|
||||||
|
|
||||||
run ("/usr/local/apache/bin/apachectl start")
|
|
||||||
|
|
||||||
Check in a file using SVN::
|
|
||||||
|
|
||||||
run ("svn ci -m 'automatic commit' my_file.py")
|
|
||||||
|
|
||||||
Run a command and capture exit status::
|
|
||||||
|
|
||||||
(command_output, exitstatus) = run ('ls -l /bin', withexitstatus=1)
|
|
||||||
|
|
||||||
Tricky Examples
|
|
||||||
===============
|
|
||||||
|
|
||||||
The following will run SSH and execute 'ls -l' on the remote machine. The
|
|
||||||
password 'secret' will be sent if the '(?i)password' pattern is ever seen::
|
|
||||||
|
|
||||||
run ("ssh username@machine.example.com 'ls -l'", events={'(?i)password':'secret\\n'})
|
|
||||||
|
|
||||||
This will start mencoder to rip a video from DVD. This will also display
|
|
||||||
progress ticks every 5 seconds as it runs. For example::
|
|
||||||
|
|
||||||
The 'events' argument should be a dictionary of patterns and responses.
|
|
||||||
Whenever one of the patterns is seen in the command out run() will send the
|
|
||||||
associated response string. Note that you should put newlines in your
|
|
||||||
string if Enter is necessary. The responses may also contain callback
|
|
||||||
functions. Any callback is function that takes a dictionary as an argument.
|
|
||||||
The dictionary contains all the locals from the run() function, so you can
|
|
||||||
access the child spawn object or any other variable defined in run()
|
|
||||||
(event_count, child, and extra_args are the most useful). A callback may
|
|
||||||
return True to stop the current run process otherwise run() continues until
|
|
||||||
the next event. A callback may also return a string which will be sent to
|
|
||||||
the child. 'extra_args' is not used by directly run(). It provides a way to
|
|
||||||
pass data to a callback function through run() through the locals
|
|
||||||
dictionary passed to a callback. """
|
|
||||||
|
|
||||||
from .__init__ import spawn
|
|
||||||
if timeout == -1:
|
|
||||||
child = spawn(command, maxread=2000, logfile=logfile, cwd=cwd, env=env, **kwargs)
|
|
||||||
else:
|
|
||||||
child = spawn(command, timeout=timeout, maxread=2000, logfile=logfile, cwd=cwd, env=env, **kwargs)
|
|
||||||
if events is not None:
|
|
||||||
patterns = list(events.keys())
|
|
||||||
responses = list(events.values())
|
|
||||||
else:
|
|
||||||
patterns=None # We assume that EOF or TIMEOUT will save us.
|
|
||||||
responses=None
|
|
||||||
child_result_list = []
|
|
||||||
event_count = 0
|
|
||||||
while 1:
|
|
||||||
try:
|
|
||||||
index = child.expect (patterns)
|
|
||||||
if type(child.after) in (str,):
|
|
||||||
child_result_list.append(child.before + child.after)
|
|
||||||
else: # child.after may have been a TIMEOUT or EOF, so don't cat those.
|
|
||||||
child_result_list.append(child.before)
|
|
||||||
if type(responses[index]) in (str,):
|
|
||||||
child.send(responses[index])
|
|
||||||
elif type(responses[index]) is types.FunctionType:
|
|
||||||
callback_result = responses[index](locals())
|
|
||||||
sys.stdout.flush()
|
|
||||||
if type(callback_result) in (str,):
|
|
||||||
child.send(callback_result)
|
|
||||||
elif callback_result:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
logger.warning("TypeError ('The callback must be a string or function type.')")
|
|
||||||
raise TypeError ('The callback must be a string or function type.')
|
|
||||||
event_count = event_count + 1
|
|
||||||
except TIMEOUT:
|
|
||||||
child_result_list.append(child.before)
|
|
||||||
break
|
|
||||||
except EOF:
|
|
||||||
child_result_list.append(child.before)
|
|
||||||
break
|
|
||||||
child_result = ''.join(child_result_list)
|
|
||||||
if withexitstatus:
|
|
||||||
child.wait()
|
|
||||||
return (child_result, child.exitstatus)
|
|
||||||
else:
|
|
||||||
return child_result
|
|
||||||
|
|
||||||
|
|
||||||
class SpawnBase:
|
class SpawnBase:
|
||||||
def __init__(self, command, args=[], timeout=30, maxread=60000, searchwindowsize=None,
|
def __init__(self, command, args=[], timeout=30, maxread=60000, searchwindowsize=None,
|
||||||
@ -303,34 +195,6 @@ class SpawnBase:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
logger.warning(traceback.format_exc())
|
logger.warning(traceback.format_exc())
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
"""This returns a human-readable string that represents the state of
|
|
||||||
the object. """
|
|
||||||
|
|
||||||
s = []
|
|
||||||
s.append(repr(self))
|
|
||||||
s.append('command: ' + str(self.command))
|
|
||||||
s.append('args: ' + str(self.args))
|
|
||||||
s.append('searcher: ' + str(self.searcher))
|
|
||||||
s.append('buffer (last 100 chars): ' + str(self.buffer)[-100:])
|
|
||||||
s.append('before (last 100 chars): ' + str(self.before)[-100:])
|
|
||||||
s.append('after: ' + str(self.after))
|
|
||||||
s.append('match: ' + str(self.match))
|
|
||||||
s.append('match_index: ' + str(self.match_index))
|
|
||||||
s.append('exitstatus: ' + str(self.exitstatus))
|
|
||||||
s.append('flag_eof: ' + str(self.flag_eof))
|
|
||||||
s.append('host_pid: ' + str(self.host_pid))
|
|
||||||
s.append('child_fd: ' + str(self.child_fd))
|
|
||||||
s.append('closed: ' + str(self.closed))
|
|
||||||
s.append('timeout: ' + str(self.timeout))
|
|
||||||
s.append('delimiter: ' + str(self.delimiter))
|
|
||||||
s.append('maxread: ' + str(self.maxread))
|
|
||||||
s.append('ignorecase: ' + str(self.ignorecase))
|
|
||||||
s.append('searchwindowsize: ' + str(self.searchwindowsize))
|
|
||||||
s.append('delaybeforesend: ' + str(self.delaybeforesend))
|
|
||||||
s.append('delayafterterminate: ' + str(self.delayafterterminate))
|
|
||||||
return '\n'.join(s)
|
|
||||||
|
|
||||||
def startChild(self, args, env):
|
def startChild(self, args, env):
|
||||||
si = win32process.GetStartupInfo()
|
si = win32process.GetStartupInfo()
|
||||||
si.dwFlags = win32process.STARTF_USESHOWWINDOW
|
si.dwFlags = win32process.STARTF_USESHOWWINDOW
|
||||||
@ -411,114 +275,6 @@ class SpawnPipe(SpawnBase):
|
|||||||
|
|
||||||
self.delayafterterminate = 1 # Sets delay in terminate() method to allow kernel time to update process status. Time in seconds.
|
self.delayafterterminate = 1 # Sets delay in terminate() method to allow kernel time to update process status. Time in seconds.
|
||||||
|
|
||||||
def connect_to_child(self):
|
|
||||||
pipe_name = 'wexpect_{}'.format(self.console_pid)
|
|
||||||
pipe_full_path = r'\\.\pipe\{}'.format(pipe_name)
|
|
||||||
logger.debug(f'Trying to connect to pipe: {pipe_full_path}')
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
self.pipe = win32file.CreateFile(
|
|
||||||
pipe_full_path,
|
|
||||||
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
|
|
||||||
0,
|
|
||||||
None,
|
|
||||||
win32file.OPEN_EXISTING,
|
|
||||||
0,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
logger.debug('Pipe found')
|
|
||||||
res = win32pipe.SetNamedPipeHandleState(self.pipe, win32pipe.PIPE_READMODE_MESSAGE, None, None)
|
|
||||||
if res == 0:
|
|
||||||
logger.debug(f"SetNamedPipeHandleState return code: {res}")
|
|
||||||
return
|
|
||||||
except pywintypes.error as e:
|
|
||||||
if e.args[0] == winerror.ERROR_FILE_NOT_FOUND: #2
|
|
||||||
logger.debug("no pipe, trying again in a bit later")
|
|
||||||
time.sleep(0.2)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def disconnect_from_child(self):
|
|
||||||
if self.pipe:
|
|
||||||
win32file.CloseHandle(self.pipe)
|
|
||||||
|
|
||||||
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.warning('I/O operation on closed file in read_nonblocking().')
|
|
||||||
raise ValueError ('I/O operation on closed file in read_nonblocking().')
|
|
||||||
|
|
||||||
try:
|
|
||||||
s = win32file.ReadFile(self.pipe, size)[1]
|
|
||||||
|
|
||||||
if s:
|
|
||||||
logger.debug(f'Readed: {s}')
|
|
||||||
else:
|
|
||||||
logger.spam(f'Readed: {s}')
|
|
||||||
|
|
||||||
if b'\x04' in s:
|
|
||||||
self.flag_eof = True
|
|
||||||
logger.info("EOF: EOF character has been arrived")
|
|
||||||
raise EOF('EOF character has been arrived')
|
|
||||||
|
|
||||||
return s.decode()
|
|
||||||
except pywintypes.error as e:
|
|
||||||
if e.args[0] == winerror.ERROR_BROKEN_PIPE: #109
|
|
||||||
self.flag_eof = True
|
|
||||||
logger.info("EOF('broken pipe, bye bye')")
|
|
||||||
raise EOF('broken pipe, bye bye')
|
|
||||||
elif e.args[0] == winerror.ERROR_NO_DATA:
|
|
||||||
'''232 (0xE8): The pipe is being closed.
|
|
||||||
'''
|
|
||||||
self.flag_eof = True
|
|
||||||
logger.info("EOF('The pipe is being closed.')")
|
|
||||||
raise EOF('The pipe is being closed.')
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
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)
|
|
||||||
try:
|
|
||||||
if s:
|
|
||||||
logger.debug(f"Writing: {s}")
|
|
||||||
win32file.WriteFile(self.pipe, s)
|
|
||||||
logger.spam(f"WriteFile finished.")
|
|
||||||
except pywintypes.error as e:
|
|
||||||
if e.args[0] == winerror.ERROR_BROKEN_PIPE: #109
|
|
||||||
logger.info("EOF: broken pipe, bye bye")
|
|
||||||
raise EOF("broken pipe, bye bye")
|
|
||||||
elif e.args[0] == winerror.ERROR_NO_DATA:
|
|
||||||
'''232 (0xE8)
|
|
||||||
The pipe is being closed.
|
|
||||||
'''
|
|
||||||
logger.info("The pipe is being closed.")
|
|
||||||
raise EOF("The pipe is being closed.")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
return len(s)
|
|
||||||
|
|
||||||
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])
|
|
||||||
self.terminated = True
|
|
||||||
except EOF as e:
|
|
||||||
logger.info(e)
|
|
||||||
|
|
||||||
|
|
||||||
class SpawnSocket(SpawnBase):
|
class SpawnSocket(SpawnBase):
|
||||||
pass
|
pass
|
||||||
|
Loading…
Reference in New Issue
Block a user