def test_run_with_check_raise() -> None: """Asure compatibility with subprocess.run when using check (return 1).""" with pytest.raises(subprocess.CalledProcessError) as ours: run("false", check=True) with pytest.raises(subprocess.CalledProcessError) as original: subprocess.run("false", check=True, universal_newlines=True) assert ours.value.returncode == original.value.returncode assert ours.value.cmd == original.value.cmd assert ours.value.output == original.value.output assert ours.value.stdout == original.value.stdout assert ours.value.stderr == original.value.stderr
def run_command(cmd, env=None, debug=False, echo=False) -> CompletedProcess: """ Execute the given command and returns None. :param cmd: : - a string or list of strings (similar to subprocess.run) - a BakedCommand object ( :param debug: An optional bool to toggle debug output. :return: ``sh`` object """ args = [] stdout = None stderr = None if cmd.__class__.__name__ == "Command": raise RuntimeError( "Molecule 3.2.0 dropped use of sh library, update plugin code to use new API. " "See https://github.com/ansible-community/molecule/issues/2678") elif cmd.__class__.__name__ == "BakedCommand": env = cmd.env if not env else cmd.env.copy().update(env) args = cmd.cmd stdout = cmd.stdout stderr = cmd.stderr else: args = cmd if debug: print_environment_vars(env) return run(args, env=env, stdout=stdout, stderr=stderr, echo=echo or debug)
def test_run_shell_undefined() -> None: """Validate run call with multiple shell commands works.""" cmd = "echo a && echo b && false || exit 4" # "python --version" result = run(cmd, echo=True, env={}) assert result.returncode == 4 assert result.stdout == "a\nb\n"
def run_command( cmd: Union[str, List[str]], env: Optional[Dict[str, str]] = None, debug: bool = False, echo: bool = False, quiet: bool = False, check: bool = False, cwd: Union[Optional[str], Optional[Path]] = None, ) -> CompletedProcess: """Run a command Parameters ---------- cmd: Union[str, List[str]] A string or list of strings that will be passed to subprocess.run() env: Optional[Dict[str, str]] A dict of environment variables and their respective values (defaults to None) debug: bool Whether to run in debug mode, which prints environment variables and command output (defaults to False) echo: bool Whether to print the command before running it (defaults to False) quiet: bool Whether to print the output of command while running it (defaults to False) check: bool Whether to check the return code of the command, which implies raising CallecProcessError (defaults to False) cwd: Union[Optional[str], Optional[Path]] In which directory to run the command (defaults to None, which means current working directory) Raises ------ CalledProcessError If check is True and the commands return code is not 0 Returns ------- CompletedProcess The result of the command """ if debug: _print_env(env) result = run( cmd, env=env, stdout=PIPE, stderr=STDOUT, echo=echo or debug, quiet=quiet, cwd=cwd, ) if result.returncode != 0 and check: raise CalledProcessError( returncode=result.returncode, cmd=result.args, output=result.stdout, stderr=result.stderr, ) return result
def test_run_with_check_pass() -> None: """Asure compatibility with subprocess.run when using check (return 0).""" ours = run("true", check=True) original = subprocess.run("true", check=True, universal_newlines=True) assert ours.returncode == original.returncode assert ours.args == original.args assert ours.stdout == original.stdout assert ours.stderr == original.stderr
def run_command(cmd, env=None, debug=False, echo=False, quiet=False, check=False, cwd=None) -> CompletedProcess: """ Execute the given command and returns None. :param cmd: : - a string or list of strings (similar to subprocess.run) - a BakedCommand object ( :param debug: An optional bool to toggle debug output. """ args = [] stdout = None stderr = None if cmd.__class__.__name__ == "Command": raise RuntimeError( "Molecule 3.2.0 dropped use of sh library, update plugin code to use new API. " "See https://github.com/ansible-community/molecule/issues/2678") elif cmd.__class__.__name__ == "BakedCommand": if cmd.env and env: env = dict(cmd.env, **env) else: env = cmd.env or env args = cmd.cmd stdout = cmd.stdout stderr = cmd.stderr else: args = cmd if debug: print_environment_vars(env) result = run( args, env=env, stdout=stdout, stderr=stderr, echo=echo or debug, quiet=quiet, cwd=cwd, ) if result.returncode != 0 and check: raise CalledProcessError( returncode=result.returncode, cmd=result.args, output=result.stdout, stderr=result.stderr, ) return result
def test_run_string() -> None: """Valida run() called with a single string command.""" cmd = "echo 111 && >&2 echo 222" old_result = subprocess.run( cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False, ) result = run(cmd) assert result.returncode == old_result.returncode assert result.stdout == old_result.stdout assert result.stderr == old_result.stderr
def test_rich_console_ex() -> None: """Validate that ConsoleEx can capture output from print() calls.""" console = Console(record=True, redirect=True) console.print("alpha") print("beta") sys.stdout.write("gamma\n") sys.stderr.write("delta\n") # While not supposed to happen we want to be sure that this will not raise # an exception. Some libraries may still sometimes send bytes to the # streams, notable example being click. # sys.stdout.write(b"epsilon\n") # type: ignore proc = run("echo 123") assert proc.stdout == "123\n" text = console.export_text() assert text == "alpha\nbeta\ngamma\ndelta\n123\n"
def test_rich_console_ex_ansi() -> None: """Validate that ANSI sent to sys.stdout does not become garbage in record.""" print() console = Console(force_terminal=True, record=True, redirect=True) console.print("[green]this from Console.print()[/green]", style="red") proc = run(r'echo -e "\033[31mred\033[0m"') assert proc.returncode == 0 assert "red" in proc.stdout # validate that what rich recorded is the same as what the subprocess produced text = console.export_text(clear=False) assert "red" in text # validate that html export also contains at least the "red" text html = console.export_html(clear=False) assert '<span class="r3">red</span>' in html
def test_run_echo(capsys: CaptureFixture[str]) -> None: """Validate run call with echo dumps command.""" cmd = ["python3", "--version"] old_result = subprocess.run( cmd, # shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False, ) result = run(cmd, echo=True) assert result.returncode == old_result.returncode assert result.stdout == old_result.stdout assert result.stderr == old_result.stderr out, err = capsys.readouterr() assert out.startswith("COMMAND:") assert err == ""
def test_run_list() -> None: """Validate run call with a command made of list of strings.""" # NOTICE: subprocess.run() does fail to capture any output when cmd is # a list and you specific shell=True. Still, when not mentioning shell, # it does work. cmd = ["python3", "--version"] old_result = subprocess.run( cmd, # shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False, ) result = run(cmd) assert result.returncode == old_result.returncode assert result.stdout == old_result.stdout assert result.stderr == old_result.stderr
def main(): """ Execute input command and send output to telegram bot. """ config = get_config() # parsing the arguments parser = argparse.ArgumentParser() parser.add_argument('command', type=str, help='command to be executed') input = parser.parse_args() start_time = time.time() print(f'executing command {input.command}') try: output = subprocess_tee.run(input.command, shell=True) output = output.stdout duration = time.time() - start_time message = f'Job {input.command} at {config["other"]["loc_name"]} just finished. ' \ f'It took {duration} seconds or {duration // 60} minutes. \n ' # Telegram limits the message length to 4096 characters if len(output) > 4095 and config.getboolean('other', 'ppb'): import tempfile import os with tempfile.TemporaryDirectory() as tmpdirname: file_path = Path(tmpdirname) / 'tempfile.txt' with open(file_path, mode='w') as f: f.write(output) ppb_output = str( subprocess.check_output(f'ppb {file_path}', shell=True).rstrip()) for substr in ppb_output.split(): if substr.startswith('http'): break message += f'The output can be seen here: {substr}.' else: message += f'The output is: \n\n {output[:3500]}' except: e = sys.exc_info()[0] duration = time.time() - start_time message = f'your job got killed with: \n {e} \n It lived for {duration} seconds. Sorry for your loss.' send_msg(message)
def test_run_with_env(env: Dict[str, str]) -> None: """Validate that passing custom env to run() works.""" env["FOO"] = "BAR" result = run("echo $FOO", env=env, echo=True) assert result.stdout == "BAR\n"
def test_run_cwd() -> None: """Validate that run accepts cwd and respects it.""" cmd = "pwd" result = run(cmd, echo=True, cwd="/") assert result.returncode == 0 assert result.stdout == "/\n"