diff options
Diffstat (limited to 'tests/test_traceback.py')
-rw-r--r-- | tests/test_traceback.py | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/tests/test_traceback.py b/tests/test_traceback.py new file mode 100644 index 0000000..6941a2e --- /dev/null +++ b/tests/test_traceback.py @@ -0,0 +1,211 @@ +import io +import sys + +import pytest + +from rich.console import Console +from rich.traceback import install, Traceback + +# from .render import render + +try: + from ._exception_render import expected +except ImportError: + expected = None + + +CAPTURED_EXCEPTION = 'Traceback (most recent call last):\n╭──────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ File "/Users/willmcgugan/projects/rich/tests/test_traceback.py", line 26, in test_handler │\n│ 23 try: │\n│ 24 old_handler = install(console=console, line_numbers=False) │\n│ 25 try: │\n│ ❱ 26 1 / 0 │\n│ 27 except Exception: │\n│ 28 exc_type, exc_value, traceback = sys.exc_info() │\n│ 29 sys.excepthook(exc_type, exc_value, traceback) │\n╰──────────────────────────────────────────────────────────────────────────────────────────────────╯\nZeroDivisionError: division by zero\n' + + +def test_handler(): + console = Console(file=io.StringIO(), width=100, color_system=None) + expected_old_handler = sys.excepthook + try: + old_handler = install(console=console) + try: + 1 / 0 + except Exception: + exc_type, exc_value, traceback = sys.exc_info() + sys.excepthook(exc_type, exc_value, traceback) + rendered_exception = console.file.getvalue() + print(repr(rendered_exception)) + assert "Traceback" in rendered_exception + assert "ZeroDivisionError" in rendered_exception + finally: + sys.excepthook = old_handler + assert old_handler == expected_old_handler + + +def text_exception_render(): + exc_render = render(get_exception()) + assert exc_render == expected + + +def test_capture(): + try: + 1 / 0 + except Exception: + tb = Traceback() + assert tb.trace.stacks[0].exc_type == "ZeroDivisionError" + + +def test_no_exception(): + with pytest.raises(ValueError): + tb = Traceback() + + +def get_exception() -> Traceback: + def bar(a): + print(1 / a) + + def foo(a): + bar(a) + + try: + try: + foo(0) + except: + foobarbaz + except: + tb = Traceback() + return tb + + +def test_print_exception(): + console = Console(width=100, file=io.StringIO()) + try: + 1 / 0 + except Exception: + console.print_exception() + exception_text = console.file.getvalue() + assert "ZeroDivisionError" in exception_text + + +def test_print_exception_locals(): + console = Console(width=100, file=io.StringIO()) + try: + 1 / 0 + except Exception: + console.print_exception(show_locals=True) + exception_text = console.file.getvalue() + print(exception_text) + assert "ZeroDivisionError" in exception_text + assert "locals" in exception_text + assert "console = <console width=100 None>" in exception_text + + +def test_syntax_error(): + console = Console(width=100, file=io.StringIO()) + try: + # raises SyntaxError: unexpected EOF while parsing + eval("(2 + 2") + except Exception: + console.print_exception() + exception_text = console.file.getvalue() + assert "SyntaxError" in exception_text + + +def test_nested_exception(): + console = Console(width=100, file=io.StringIO()) + value_error_message = "ValueError because of ZeroDivisionError" + + try: + try: + 1 / 0 + except ZeroDivisionError: + raise ValueError(value_error_message) + except Exception: + console.print_exception() + exception_text = console.file.getvalue() + + text_should_contain = [ + value_error_message, + "ZeroDivisionError", + "ValueError", + "During handling of the above exception", + ] + + for msg in text_should_contain: + assert msg in exception_text + + # ZeroDivisionError should come before ValueError + assert exception_text.find("ZeroDivisionError") < exception_text.find("ValueError") + + +def test_caused_exception(): + console = Console(width=100, file=io.StringIO()) + value_error_message = "ValueError caused by ZeroDivisionError" + + try: + try: + 1 / 0 + except ZeroDivisionError as e: + raise ValueError(value_error_message) from e + except Exception: + console.print_exception() + exception_text = console.file.getvalue() + + text_should_contain = [ + value_error_message, + "ZeroDivisionError", + "ValueError", + "The above exception was the direct cause", + ] + + for msg in text_should_contain: + assert msg in exception_text + + # ZeroDivisionError should come before ValueError + assert exception_text.find("ZeroDivisionError") < exception_text.find("ValueError") + + +def test_filename_with_bracket(): + console = Console(width=100, file=io.StringIO()) + try: + exec(compile("1/0", filename="<string>", mode="exec")) + except Exception: + console.print_exception() + exception_text = console.file.getvalue() + assert "<string>" in exception_text + + +def test_filename_not_a_file(): + console = Console(width=100, file=io.StringIO()) + try: + exec(compile("1/0", filename="string", mode="exec")) + except Exception: + console.print_exception() + exception_text = console.file.getvalue() + assert "string" in exception_text + + +def test_broken_str(): + class BrokenStr(Exception): + def __str__(self): + 1 / 0 + + console = Console(width=100, file=io.StringIO()) + try: + raise BrokenStr() + except Exception: + console.print_exception() + result = console.file.getvalue() + print(result) + assert "<exception str() failed>" in result + + +def test_guess_lexer(): + assert Traceback._guess_lexer("foo.py", "code") == "python" + code_python = "#! usr/bin/env python\nimport this" + assert Traceback._guess_lexer("foo", code_python) == "python" + assert Traceback._guess_lexer("foo", "foo\nbnar") == "text" + + +if __name__ == "__main__": # pragma: no cover + + expected = render(get_exception()) + + with open("_exception_render.py", "wt") as fh: + exc_render = render(get_exception()) + print(exc_render) + fh.write(f"expected={exc_render!r}") |