minimal 19

This commit is contained in:
Benedek Racz 2020-02-04 10:45:11 +01:00
parent 2b13b60920
commit 03e20dc08c

View File

@ -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