[elbe-devel] [PATCH 3/5] shellhelper: Add doctests
Torben Hohn
torben.hohn at linutronix.de
Mon May 25 15:12:10 CEST 2020
On Mon, May 18, 2020 at 10:09:58AM -0400, Olivier Dion wrote:
> Tests were made in order to execute all code paths and to keep stdout
> clean. Thus, nothing is ever printed to the terminal.
>
> The tests for do(), chroot() and get_command_out() require to
> manipulate the loggers of the Python's logging module.
>
> Fixed an eror in the second 'Popen' of get_command_out(). For some
> reason, stdout was redirected to elbe's logging when stdin is not
> None.
>
> Signed-off-by: Olivier Dion <dion at linutronix.de>
see inline...
> ---
> elbepack/shellhelper.py | 182 +++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 179 insertions(+), 3 deletions(-)
>
> diff --git a/elbepack/shellhelper.py b/elbepack/shellhelper.py
> index 203b5f4c..69481926 100644
> --- a/elbepack/shellhelper.py
> +++ b/elbepack/shellhelper.py
> @@ -17,7 +17,6 @@ from io import TextIOWrapper, BytesIO
> log = logging.getLogger("log")
> soap = logging.getLogger("soap")
>
> -
> class CommandError(Exception):
> def __init__(self, cmd, returncode):
> self.returncode = returncode
> @@ -28,6 +27,22 @@ class CommandError(Exception):
> self.returncode, self.cmd)
>
> def system(cmd, allow_fail=False, env_add=None):
> + """system() - Execute cmd in a shell.
> +
> + Throws a CommandError if cmd returns none-zero and allow_fail=False
> +
> + --
> +
> + >>> system("true")
> +
> + >>> system("false", allow_fail=True)
> +
> + >>> system("$FALSE", env_add={"FALSE":"false"}) # doctest: +ELLIPSIS
> + Traceback (most recent call last):
> + ...
> + elbepack.shellhelper.CommandError: ...
> +
> + """
> new_env = os.environ.copy()
> if env_add:
> new_env.update(env_add)
> @@ -40,6 +55,28 @@ def system(cmd, allow_fail=False, env_add=None):
>
>
> def command_out(cmd, stdin=None, output=PIPE, env_add=None):
> + """command_out() - Execute cmd in a shell.
> +
> + Returns a tuple with the exitcode and the output of cmd.
> +
> + --
> +
> + >>> command_out("true")
> + (0, '')
> +
> + >>> command_out("$TRUE && true", env_add={"TRUE":"true"})
> + (0, '')
> +
> + >>> command_out("cat -", stdin=b"ELBE")
> + (0, 'ELBE')
> +
> + >>> command_out("2>&1 cat -", stdin=b"ELBE")
> + (0, 'ELBE')
> +
> + >>> command_out("false")
> + (1, '')
> +
> + """
> new_env = os.environ.copy()
> if env_add:
> new_env.update(env_add)
> @@ -59,6 +96,22 @@ def command_out(cmd, stdin=None, output=PIPE, env_add=None):
>
>
> def system_out(cmd, stdin=None, allow_fail=False, env_add=None):
> + """system_out() - Wrapper around command_out().
> +
> + On failure, raises an exception if allow_fail=False, on success,
> + returns the output of cmd.
> +
> + --
> +
> + >>> system_out("false") # doctest: +ELLIPSIS
> + Traceback (most recent call last):
> + ...
> + elbepack.shellhelper.CommandError: ...
> +
> + >>> system_out("false", allow_fail=True)
> + ''
> +
> + """
> code, out = command_out(cmd, stdin=stdin, env_add=env_add)
>
> if code != 0:
> @@ -69,6 +122,22 @@ def system_out(cmd, stdin=None, allow_fail=False, env_add=None):
>
>
> def command_out_stderr(cmd, stdin=None, env_add=None):
> + """command_out_stderr() - Execute cmd in a shell.
> +
> + Returns a tuple of the exitcode, stdout and stderr of cmd.
> +
> + --
> +
> + >>> command_out_stderr("$TRUE && cat -", stdin=b"ELBE", env_add={"TRUE":"true"})
> + (0, 'ELBE', '')
> +
> + >>> command_out_stderr("1>&2 cat - && false", stdin=b"ELBE")
> + (1, '', 'ELBE')
> +
> + >>> command_out_stderr("true")
> + (0, '', '')
> +
> + """
> new_env = os.environ.copy()
> if env_add:
> new_env.update(env_add)
> @@ -89,6 +158,24 @@ def command_out_stderr(cmd, stdin=None, env_add=None):
>
>
> def system_out_stderr(cmd, stdin=None, allow_fail=False, env_add=None):
> + """system_out_stderr() - Wrapper around command_out_stderr()
> +
> + Throws CommandError if cmd failed and allow_fail=False. Otherwise,
> + returns the stdout and stderr of cmd.
> +
> + --
> +
> + >>> system_out_stderr("false") # doctest: +ELLIPSIS
> + Traceback (most recent call last):
> + ...
> + elbepack.shellhelper.CommandError: ...
> +
> + >>> system_out_stderr("cat - && false", allow_fail=True, stdin=b"ELBE")
> + ('ELBE', '')
> +
> + >>> system_out_stderr("1>&2 cat -", allow_fail=True, stdin=b"ELBE")
> + ('', 'ELBE')
> + """
> code, out, err = command_out_stderr(cmd, stdin, env_add)
>
> if code != 0:
> @@ -99,6 +186,43 @@ def system_out_stderr(cmd, stdin=None, allow_fail=False, env_add=None):
>
>
> def do(cmd, allow_fail=False, stdin=None, env_add=None):
> + """do() - Execute cmd in a shell and redirect outputs to logging.
> +
> + Throws a CommandError if cmd failed with allow_Fail=False.
> +
> + --
> +
> + Let's redirect the loggers to current stdout
> + >>> import logging
> + >>> import sys
> + >>> from elbepack.log import context_fmt
> + >>> root = logging.getLogger()
> + >>> for hdlr in root.handlers:
> + ... root.removeHandler(hdlr)
> + >>> hdlr = logging.StreamHandler(sys.stdout)
> + >>> hdlr.setFormatter(context_fmt)
> + >>> root.addHandler(hdlr)
this is too complicated for a doc test.
Please move this code into the logging module.
and just call logging.log_to_stdout_for_test()
> +
> + >>> do("true")
> + [CMD] true
> +
> + >>> do("false", allow_fail=True)
> + [CMD] false
> +
> + >>> do("cat -", stdin=b"ELBE")
> + [CMD] cat -
> +
> + >>> do("cat - && false", stdin=b"ELBE") # doctest: +ELLIPSIS
> + Traceback (most recent call last):
> + ...
> + elbepack.shellhelper.CommandError: ...
> +
> + >>> do("false") # doctest: +ELLIPSIS
> + Traceback (most recent call last):
> + ...
> + elbepack.shellhelper.CommandError: ...
> + """
> +
> new_env = os.environ.copy()
> if env_add:
> new_env.update(env_add)
> @@ -121,6 +245,27 @@ def do(cmd, allow_fail=False, stdin=None, env_add=None):
>
>
> def chroot(directory, cmd, env_add=None, **kwargs):
> + """chroot() - Wrapper around do().
> +
> + --
> +
> + Let's redirect the loggers to current stdout
> + >>> import logging
> + >>> import sys
> + >>> from elbepack.log import context_fmt
> + >>> root = logging.getLogger()
> + >>> for hdlr in root.handlers:
> + ... root.removeHandler(hdlr)
> + >>> hdlr = logging.StreamHandler(sys.stdout)
> + >>> hdlr.setFormatter(context_fmt)
> + >>> root.addHandler(hdlr)
yeah... we really want: logging.log_to_stdout_for_test()
> +
> + >>> chroot("/", "true") # doctest: +ELLIPSIS
> + Traceback (most recent call last):
> + ...
> + elbepack.shellhelper.CommandError: ...
> + """
> +
> new_env = {"LANG":"C",
> "LANGUAGE":"C",
> "LC_ALL":"C"}
> @@ -130,6 +275,37 @@ def chroot(directory, cmd, env_add=None, **kwargs):
> do(chcmd, env_add=new_env, **kwargs)
>
> def get_command_out(cmd, stdin=None, allow_fail=False, env_add={}):
> + """get_command_out() - Like do() but returns stdout.
> +
> + --
> +
> + Let's quiet the loggers
> + >>> import logging
> + >>> import sys
> + >>> from elbepack.log import context_fmt
> + >>> log = logging.getLogger("log")
> + >>> for hdlr in log.handlers:
> + ... log.removeHandler(hdlr)
> + >>> hdlr = logging.StreamHandler(sys.stdout)
> + >>> hdlr.setFormatter(context_fmt)
> + >>> log.addHandler(hdlr)
yeah... we really want: logging.log_to_stdout_for_test()
> +
> + >>> get_command_out("echo ELBE")
> + b'ELBE\\n'
> +
> + >>> get_command_out("false") # doctest: +ELLIPSIS
> + Traceback (most recent call last):
> + ...
> + elbepack.shellhelper.CommandError: ...
> +
> + >>> get_command_out("false", allow_fail=True)
> + b''
> +
> + >>> get_command_out("cat -", stdin=b"ELBE", env_add={"TRUE":"true"})
> + b'ELBE'
> +
> + """
> +
> new_env = os.environ.copy()
> new_env.update(env_add)
>
> @@ -140,10 +316,10 @@ def get_command_out(cmd, stdin=None, allow_fail=False, env_add={}):
> if stdin is None:
> p = Popen(cmd, shell=True, stdout=PIPE, stderr=w, env=new_env)
> else:
> - p = Popen(cmd, shell=True, stdin=PIPE, stdout=w, stderr=STDOUT, env=new_env)
> + p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=w, env=new_env)
dont mix fixes to tests with the tests.
please isolate this into another commit.
>
> async_logging(r, w, soap, log)
> - stdout, stderr = p.communicate(input=stdin)
> + stdout, _ = p.communicate(input=stdin)
>
> if p.returncode and not allow_fail:
> raise CommandError(cmd, p.returncode)
anyways, this looks very good and useful.
> --
> 2.26.2
>
>
> _______________________________________________
> elbe-devel mailing list
> elbe-devel at linutronix.de
> https://lists.linutronix.de/mailman/listinfo/elbe-devel
--
Torben Hohn
Linutronix GmbH | Bahnhofstrasse 3 | D-88690 Uhldingen-Mühlhofen
Phone: +49 7556 25 999 18; Fax.: +49 7556 25 999 99
Hinweise zum Datenschutz finden Sie hier (Informations on data privacy
can be found here): https://linutronix.de/kontakt/Datenschutz.php
Linutronix GmbH | Firmensitz (Registered Office): Uhldingen-Mühlhofen |
Registergericht (Registration Court): Amtsgericht Freiburg i.Br., HRB700
806 | Geschäftsführer (Managing Directors): Heinz Egger, Thomas Gleixner
More information about the elbe-devel
mailing list