diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8c3ce23..5ebb8c8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,10 +1,13 @@ xscope fileio change log ======================== -0.3.3 +0.4.0 ----- * CHANGED: Python code to use subprocess instead of sh + * REMOVED: Contextlib support for capturing stout/stderr + * ADDED: run_on_target() method returns stdout/err as list of lines + * ADDED: optional verbose kwarg in run_on_target()to reduce verbosity 0.3.2 ----- diff --git a/xscope_fileio/__init__.py b/xscope_fileio/__init__.py index b147499..0d5a6b0 100644 --- a/xscope_fileio/__init__.py +++ b/xscope_fileio/__init__.py @@ -76,17 +76,17 @@ def xcore_done(self, cmd, success, exit_code): # sys.stderr.write(dump.stdout.decode()) self.host_process.terminate() -def popenAndCall(onExit, *popenArgs, **popenKWArgs): +def popenAndCall(onExit, *popenArgs): """ Runs a subprocess.Popen, and then calls the function onExit when the subprocess completes. Use it exactly the way you'd normally use subprocess.Popen, except include a callable to execute as the first argument. onExit is a callable object, and - *popenArgs and **popenKWArgs are simply passed up to subprocess.Popen. + *popenArgs are simply passed up to subprocess.Popen. """ - def runInThread(onExit, popenArgs, popenKWArgs, q): - proc = subprocess.Popen(*popenArgs, **popenKWArgs) + def runInThread(onExit, popenArgs, q): + proc = subprocess.Popen(*popenArgs) q.put(proc) proc.wait() onExit(proc.args, proc.returncode == 0, proc.returncode) @@ -95,13 +95,13 @@ def runInThread(onExit, popenArgs, popenKWArgs, q): q = queue.Queue() thread = threading.Thread(target=runInThread, - args=(onExit, popenArgs, popenKWArgs, q)) + args=(onExit, popenArgs, q)) thread.start() return q.get() # returns immediately after the thread starts -def run_on_target(adapter_id, firmware_xe, use_xsim=False, **kwargs): +def run_on_target(adapter_id, firmware_xe, use_xsim=False, verbose=True): port = _get_open_port() xrun_cmd = ( f"--xscope-port localhost:{port} --adapter-id {adapter_id} {firmware_xe}" @@ -117,28 +117,41 @@ def run_on_target(adapter_id, firmware_xe, use_xsim=False, **kwargs): xrun_proc = subprocess.Popen(['xsim'] + xsim_cmd) else: print(xrun_cmd) - xrun_proc = popenAndCall(exit_handler.xcore_done, ["xrun"] + xrun_cmd.split(), **kwargs) + xrun_proc = popenAndCall(exit_handler.xcore_done, ["xrun"] + xrun_cmd.split()) - print("Waiting for xrun", end="") + if verbose: + print("Waiting for xrun", end="") timeout = time.time() + XRUN_TIMEOUT while _test_port_is_open(port): - print(".", end="", flush=True) + if verbose: + print(".", end="", flush=True) time.sleep(0.1) if time.time() > timeout: xrun_proc.kill() assert 0, f"xrun timed out - took more than {XRUN_TIMEOUT} seconds to start" - print() - - print("Starting host app...", end="\n") + if verbose: + print() + print("Starting host app...", end="\n") host_exe = _get_host_exe() host_args = f"{port}" - host_proc = subprocess.Popen([host_exe] + host_args.split(), **kwargs) - exit_handler.set_host_process(host_proc) + + stdout = [] + with subprocess.Popen([host_exe] + host_args.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, \ + close_fds=True) as host_proc: + exit_handler.set_host_process(host_proc) + + for line in host_proc.stdout: # b'\n'-separated lines + if verbose: + sys.stdout.buffer.write(line) # pass bytes as is to console + sys.stdout.flush() + stdout.append(line.decode('utf-8').strip()) + host_proc.wait() assert host_proc.returncode == 0, f'\nERROR: host app exited with error code {host_proc.returncode}\n' - print("Running on target finished") + if verbose: + print("Running on target finished") - return host_proc.returncode + return stdout