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)
|
||||
|
||||
|
||||
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:
|
||||
def __init__(self, command, args=[], timeout=30, maxread=60000, searchwindowsize=None,
|
||||
@ -303,34 +195,6 @@ class SpawnBase:
|
||||
traceback.print_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):
|
||||
si = win32process.GetStartupInfo()
|
||||
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.
|
||||
|
||||
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):
|
||||
pass
|
||||
|
Loading…
Reference in New Issue
Block a user