diff --git a/news/fix_process_traceback.rst b/news/fix_process_traceback.rst new file mode 100644 index 000000000..4a2b8fddf --- /dev/null +++ b/news/fix_process_traceback.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Fixed empty stacktrace for CalledProcessError. + +**Security:** + +* diff --git a/tests/test_integrations.py b/tests/test_integrations.py index 25b6aad66..bdc303313 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -3,6 +3,7 @@ This requires Xonsh installed in venv or otherwise available on PATH """ import os +import re import shutil import subprocess as sp import tempfile @@ -995,3 +996,47 @@ def test_run_fail_not_on_path(): cmd = "hello_world.bat" out, _, _ = run_xonsh(cmd, stdout=sp.PIPE, stderr=sp.PIPE, path=os.environ["PATH"]) assert out != "Hello world" + + +@skip_if_on_windows +@pytest.mark.parametrize("interactive", [True, False]) +def test_raise_subproc_error_with_show_traceback(monkeypatch, interactive): + out, err, ret = run_xonsh( + "$COLOR_RESULTS=False\n$RAISE_SUBPROC_ERROR=False\n$XONSH_SHOW_TRACEBACK=False\nls nofile", + interactive=interactive, + single_command=True, + ) + assert ret != 0 + assert re.match("ls.*No such file or directory\n", out) + + out, err, ret = run_xonsh( + "$COLOR_RESULTS=False\n$RAISE_SUBPROC_ERROR=True\n$XONSH_SHOW_TRACEBACK=False\nls nofile", + interactive=interactive, + single_command=True, + ) + assert ret != 0 + assert re.match( + "ls:.*No such file or directory\nsubprocess.CalledProcessError: Command '\\['ls', 'nofile'\\]' returned non-zero exit status .*", + out, + re.MULTILINE | re.DOTALL, + ) + + out, err, ret = run_xonsh( + "$COLOR_RESULTS=False\n$RAISE_SUBPROC_ERROR=True\n$XONSH_SHOW_TRACEBACK=True\nls nofile", + interactive=interactive, + single_command=True, + ) + assert ret != 0 + assert re.match( + "ls.*No such file or directory.*Traceback .*\nsubprocess.CalledProcessError: Command '\\['ls', 'nofile'\\]' returned non-zero exit status .*", + out, + re.MULTILINE | re.DOTALL, + ) + + out, err, ret = run_xonsh( + "$COLOR_RESULTS=False\n$RAISE_SUBPROC_ERROR=False\n$XONSH_SHOW_TRACEBACK=True\nls nofile", + interactive=interactive, + single_command=True, + ) + assert ret != 0 + assert re.match("ls.*No such file or directory\n", out) diff --git a/xonsh/tools.py b/xonsh/tools.py index 7053a9e02..e38ae0e10 100644 --- a/xonsh/tools.py +++ b/xonsh/tools.py @@ -1119,10 +1119,9 @@ def display_colored_error_message(exc_info, strip_xonsh_error_types=True, limit= no_trace_and_raise_subproc_error and "subprocess.CalledProcessError:" in content[-1] ): - content = content[:-1] + content = [content[-1].rstrip()] traceback_str = "".join([v for v in content]) - traceback_str += "" if traceback_str.endswith("\n") else "\n" # color the traceback if available _, interactive = _get_manual_env_var("XONSH_INTERACTIVE", 0) @@ -1137,7 +1136,7 @@ def display_colored_error_message(exc_info, strip_xonsh_error_types=True, limit= lexer = pygments.lexers.python.PythonTracebackLexer() tokens = list(pygments.lex(traceback_str, lexer=lexer)) # this goes to stdout, but since we are interactive it doesn't matter - print_color(tokens, end="\n", file=sys.stderr) + print_color(tokens, end="", file=sys.stderr) return