diff options
| author | Christian Breunig <christian@breunig.cc> | 2026-02-02 21:22:31 +0100 |
|---|---|---|
| committer | Christian Breunig <christian@breunig.cc> | 2026-02-02 21:29:04 +0100 |
| commit | dfa9816e01caa1d0abca8273ce305a53188a5577 (patch) | |
| tree | 993272e331b9e177a7e05426edd62608110fd8ef /python | |
| parent | e31c302a024720da060889ea98eaf8d4069613fd (diff) | |
| download | vyos-1x-dfa9816e01caa1d0abca8273ce305a53188a5577.tar.gz vyos-1x-dfa9816e01caa1d0abca8273ce305a53188a5577.zip | |
utils: T8018: add unbuffered output support to popen()
Instead of executing the command passed to popen() and waiting for it to
finish before printing its output, add a function argument to disable
buffered mode.
When buffered mode (the default) is turned off, each line emitted by the
process is flushed immediately to the calling TTY.
Diffstat (limited to 'python')
| -rw-r--r-- | python/vyos/utils/process.py | 73 |
1 files changed, 47 insertions, 26 deletions
diff --git a/python/vyos/utils/process.py b/python/vyos/utils/process.py index 86a2747af..158371024 100644 --- a/python/vyos/utils/process.py +++ b/python/vyos/utils/process.py @@ -32,7 +32,8 @@ def get_wrapper(vrf, netns): def popen(command, flag='', shell=None, input=None, timeout=None, env=None, - stdout=PIPE, stderr=PIPE, decode='utf-8', vrf=None, netns=None): + stdout=PIPE, stderr=PIPE, decode='utf-8', vrf=None, netns=None, + buffered=True): """ popen is a wrapper helper around subprocess.Popen with it default setting it will return a tuple (out, err) @@ -58,6 +59,9 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None, the default is explicitely utf-8 which is python's own default vrf: run command in a VRF context netns: run command in the named network namespace + buffered: define how process output shall be presented to stdout + - true: buffer output and return once after command finished + - false: immediately output strings on stdout - give live feedback usage: get both stdout and stderr: popen('command', stdout=PIPE, stderr=STDOUT) @@ -104,38 +108,54 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None, stdin = PIPE input = input.encode() if type(input) is str else input + text = None + bufsize = -1 # default: means the system default of io.DEFAULT_BUFFER_SIZE will be used + if not buffered: + text = True # Treat output as strings (not bytes) + bufsize = 1 # Enable line buffering + p = Popen(command, stdin=stdin, stdout=stdout, stderr=stderr, - env=env, shell=use_shell) + env=env, shell=use_shell, text=text, bufsize=bufsize) - pipe = p.communicate(input, timeout) + if buffered: + pipe = p.communicate(input, timeout) + rc = p.returncode - pipe_out = b'' - if stdout == PIPE: - pipe_out = pipe[0] + pipe_out = b'' + if stdout == PIPE: + pipe_out = pipe[0] - pipe_err = b'' - if stderr == PIPE: - pipe_err = pipe[1] + pipe_err = b'' + if stderr == PIPE: + pipe_err = pipe[1] - str_out = pipe_out.decode(decode).replace('\r\n', '\n').strip() - str_err = pipe_err.decode(decode).replace('\r\n', '\n').strip() + str_out = pipe_out.decode(decode).replace('\r\n', '\n').strip() + str_err = pipe_err.decode(decode).replace('\r\n', '\n').strip() - out_msg = f"returned (out):\n{str_out}" - if str_out: - debug.message(out_msg, flag) + out_msg = f"returned (out):\n{str_out}" + if str_out: + debug.message(out_msg, flag) - if str_err: - from sys import stderr - err_msg = f"returned (err):\n{str_err}" - # this message will also be send to syslog via airbag - debug.message(err_msg, flag, destination=stderr) + if str_err: + from sys import stderr + err_msg = f"returned (err):\n{str_err}" + # this message will also be send to syslog via airbag + debug.message(err_msg, flag, destination=stderr) - # should something go wrong, report this too via airbag - airbag.noteworthy(cmd_msg) - airbag.noteworthy(out_msg) - airbag.noteworthy(err_msg) + # should something go wrong, report this too via airbag + airbag.noteworthy(cmd_msg) + airbag.noteworthy(out_msg) + airbag.noteworthy(err_msg) + else: + output_lines = [] + for line in p.stdout: + print(line, end='', flush=True) # print each line as it arrives + output_lines.append(line) + p.stdout.close() + rc = p.wait() + str_out = ''.join(output_lines) - return str_out, p.returncode + return str_out, rc def run(command, flag='', shell=None, input=None, timeout=None, env=None, @@ -191,9 +211,9 @@ def cmd(command, flag='', shell=None, input=None, timeout=None, env=None, raise raising(feedback) return decoded - def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None, - stdout=PIPE, stderr=STDOUT, decode='utf-8', vrf=None, netns=None): + stdout=PIPE, stderr=STDOUT, decode='utf-8', vrf=None, netns=None, + buffered=True): """ A wrapper around popen, which returns the return code of a command and stdout @@ -211,6 +231,7 @@ def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None, decode=decode, vrf=vrf, netns=netns, + buffered=buffered, ) return code, out |
