From ba20b3f0d8727a2ad75e0a4d22c331fde6e9eae2 Mon Sep 17 00:00:00 2001 From: Benedek Racz Date: Mon, 20 Jan 2020 13:27:40 +0100 Subject: [PATCH] [ADD] run function --- wexpect/__init__.py | 3 +- wexpect/spawn.py | 123 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 119 insertions(+), 7 deletions(-) diff --git a/wexpect/__init__.py b/wexpect/__init__.py index 02d6f64..f7b46ea 100644 --- a/wexpect/__init__.py +++ b/wexpect/__init__.py @@ -11,6 +11,7 @@ from .console_reader import ConsoleReaderPipe from .spawn import Spawn from .spawn import Spawn as spawn +from .spawn import run __all__ = ['split_command_line', 'join_args', 'ExceptionPexpect', 'EOF', 'TIMEOUT', - 'ConsoleReaderSocket', 'ConsoleReaderPipe', 'spawn', 'Spawn'] + 'ConsoleReaderSocket', 'ConsoleReaderPipe', 'spawn', 'Spawn', 'run'] diff --git a/wexpect/spawn.py b/wexpect/spawn.py index 6e96e34..a1db127 100644 --- a/wexpect/spawn.py +++ b/wexpect/spawn.py @@ -70,6 +70,7 @@ import os import shutil import re import traceback +import types import pywintypes import win32process @@ -80,10 +81,117 @@ import winerror import win32pipe import socket -from wexpect_util import ExceptionPexpect -from wexpect_util import EOF -from wexpect_util import TIMEOUT -from wexpect_util import split_command_line +from .wexpect_util import ExceptionPexpect +from .wexpect_util import EOF +from .wexpect_util import TIMEOUT +from .wexpect_util import split_command_line + + +def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None): + + """ + 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. """ + + if timeout == -1: + child = Spawn(command, maxread=2000, logfile=logfile, cwd=cwd, env=env) + else: + child = Spawn(command, timeout=timeout, maxread=2000, logfile=logfile, cwd=cwd, env=env) + 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: + 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.close() + return (child_result, child.exitstatus) + else: + return child_result class Spawn: @@ -171,6 +279,7 @@ class Spawn: try: self.terminate() + self.disconnect_from_child() except: pass @@ -393,6 +502,10 @@ class Spawn: def connect_to_child(self, host, port): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) + + def disconnect_from_child(self): + if self.sock: + self.sock.close() def startChild(self, args, env): @@ -433,8 +546,6 @@ class Spawn: "import time;" f"wexpect.ConsoleReaderSocket(wexpect.join_args({args}), {pid}, port=4321);" ) - - print(commandLine) self.conproc, _, conpid, __otid = win32process.CreateProcess(None, commandLine, None, None, False, win32process.CREATE_NEW_CONSOLE, None, None, si)