From 67c70801c4efbfaf951a3a31fd770365bd473898 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Sun, 18 Jun 2023 17:34:12 +1200 Subject: [PATCH 01/17] Added systems hooks, file, and suppress_context --- src/module-mesa/file/assets/haiku.txt | 3 ++ src/module-mesa/file/file.py | 7 ++++ src/systems-swamp/breakpointhook.py | 19 +++++++++++ src/systems-swamp/displayhook.py | 22 ++++++++++++ src/systems-swamp/excepthook.py | 22 ++++++++++++ src/systems-swamp/interactivehook.py | 33 ++++++++++++++++++ src/systems-swamp/stderr.py | 26 ++++++++++++++ src/systems-swamp/stdin.py | 49 +++++++++++++++++++++++++++ src/systems-swamp/stdout.py | 25 ++++++++++++++ src/systems-swamp/suppress_context.py | 17 ++++++++++ src/systems-swamp/unraisablehook.py | 27 +++++++++++++++ 11 files changed, 250 insertions(+) create mode 100644 src/module-mesa/file/assets/haiku.txt create mode 100644 src/module-mesa/file/file.py create mode 100644 src/systems-swamp/breakpointhook.py create mode 100644 src/systems-swamp/displayhook.py create mode 100644 src/systems-swamp/excepthook.py create mode 100644 src/systems-swamp/interactivehook.py create mode 100644 src/systems-swamp/stderr.py create mode 100644 src/systems-swamp/stdin.py create mode 100644 src/systems-swamp/stdout.py create mode 100644 src/systems-swamp/suppress_context.py create mode 100644 src/systems-swamp/unraisablehook.py diff --git a/src/module-mesa/file/assets/haiku.txt b/src/module-mesa/file/assets/haiku.txt new file mode 100644 index 0000000..a196a33 --- /dev/null +++ b/src/module-mesa/file/assets/haiku.txt @@ -0,0 +1,3 @@ +I am a haiku +Inside of a small text file +Waiting to be read diff --git a/src/module-mesa/file/file.py b/src/module-mesa/file/file.py new file mode 100644 index 0000000..16f0ae1 --- /dev/null +++ b/src/module-mesa/file/file.py @@ -0,0 +1,7 @@ +"""Sets a directory relative to _this_ file""" +from pathlib import Path + +ASSETS = (Path(__file__) / ".." / "assets").resolve() + +with open(ASSETS / "haiku.txt", "r") as f: + print(f.read()) diff --git a/src/systems-swamp/breakpointhook.py b/src/systems-swamp/breakpointhook.py new file mode 100644 index 0000000..3d16404 --- /dev/null +++ b/src/systems-swamp/breakpointhook.py @@ -0,0 +1,19 @@ +import contextlib +import sys + + +@contextlib.contextmanager +def no_breakpoint(): + sys.breakpointhook = lambda *args, **kwargs: print( + "You didn't think I'd let you debug so easily, did you?", + file=sys.stderr, + ) + yield + sys.breakpointhook = sys.__breakpointhook__ + + +with no_breakpoint(): + breakpoint() + x = 1 + y = x * 2 + print(y) diff --git a/src/systems-swamp/displayhook.py b/src/systems-swamp/displayhook.py new file mode 100644 index 0000000..2fba0e1 --- /dev/null +++ b/src/systems-swamp/displayhook.py @@ -0,0 +1,22 @@ +""" +When in an interactive session – import these functions and call +activate_pretty to pretty print the result of all expressions +""" + +import builtins +import sys +from pprint import pprint + + +def activate_pretty(): + def hook(value): + builtins._ = value + if value is None: + return + pprint(value) + + sys.displayhook = hook + + +def deactivate_pretty(): + sys.displayhook = sys.__displayhook__ diff --git a/src/systems-swamp/excepthook.py b/src/systems-swamp/excepthook.py new file mode 100644 index 0000000..03e66dd --- /dev/null +++ b/src/systems-swamp/excepthook.py @@ -0,0 +1,22 @@ +"""Makes error messages sArCaStIc""" +import sys +from contextlib import redirect_stderr +from io import StringIO + + +def memeify(value: str) -> str: + return "".join( + c.upper() if i % 2 == 0 else c.lower() for i, c in enumerate(value) + ) + + +def sarcastic_excepthook(type, value, traceback): + with redirect_stderr(StringIO()) as f: + sys.__excepthook__(type, value, traceback) + message = memeify(f.getvalue()) + print(message, file=sys.stderr) + + +sys.excepthook = sarcastic_excepthook + +1 / 0 diff --git a/src/systems-swamp/interactivehook.py b/src/systems-swamp/interactivehook.py new file mode 100644 index 0000000..7bcf445 --- /dev/null +++ b/src/systems-swamp/interactivehook.py @@ -0,0 +1,33 @@ +""" +This file needs to be set as a PYTHONSTARTUP environment variable: + + export PYTHONSTARTUP=src/systems-swamp/interactivehook.py + +It will then run when an interactive session is started. + +Sets all outputs to be pretty printed + adds an introductory message +""" +import builtins +import sys +from pprint import pprint + +BANNER = """ +******************************************************************************** +******************** We in the interactive session, babey! ********************* +******************************************************************************** +""" + + +def displayhook(value): + builtins._ = value + if value is None: + return + pprint(value) + + +def _startup(): + print(BANNER) + sys.displayhook = displayhook + + +sys.__interactivehook__ = _startup diff --git a/src/systems-swamp/stderr.py b/src/systems-swamp/stderr.py new file mode 100644 index 0000000..e3fa9a3 --- /dev/null +++ b/src/systems-swamp/stderr.py @@ -0,0 +1,26 @@ +"""For real projects, use contextlib.redirect_stderr instead""" +import contextlib +import sys +from io import StringIO +from textwrap import indent +from typing import TextIO, TypeVar + +T = TypeVar("T", bound=TextIO) + + +@contextlib.contextmanager +def redirect_stderr(target: T) -> T: + sys.stderr = target + yield target + sys.stderr = sys.__stderr__ + + +with redirect_stderr(StringIO()) as f: + try: + 1 / 0 + except ZeroDivisionError as e: + sys.excepthook(ZeroDivisionError, e, e.__traceback__) + + +print("And the error is:") +print(indent(f.getvalue(), "> ")) diff --git a/src/systems-swamp/stdin.py b/src/systems-swamp/stdin.py new file mode 100644 index 0000000..73b2f8f --- /dev/null +++ b/src/systems-swamp/stdin.py @@ -0,0 +1,49 @@ +"""No contextlib option!""" +import builtins +import contextlib +import sys +from io import StringIO +from typing import TextIO + +SAVED_INPUT = input + + +def _input(prompt): + result = SAVED_INPUT(prompt) + print(result) + return result + + +@contextlib.contextmanager +def redirect_stdin(target: TextIO): + sys.stdin = target + builtins.input = _input + yield + sys.stdout = sys.__stdin__ + builtins.input = SAVED_INPUT + + +menu = """ +1) egg and spam +2) egg bacon and spam +3) egg bacon sausage and spam +4) spam bacon sausage and spam +5) spam egg spam spam bacon and spam +6) spam sausage spam spam bacon spam tomato and spam +""".strip() + +stdin = StringIO(""" +Have you got anything without spam? +1 +""".lstrip()) + +with redirect_stdin(stdin): + print("We've got:") + print(menu) + choice = input("> ") + while not choice.isdigit() or int(choice) not in range(1, 7): + print("No, we've got:") + print(menu) + choice = input("> ") + print("Excellent choice!") + print("Spam spam spam spam. Lovely spam! Wonderful spam!") diff --git a/src/systems-swamp/stdout.py b/src/systems-swamp/stdout.py new file mode 100644 index 0000000..2e343f5 --- /dev/null +++ b/src/systems-swamp/stdout.py @@ -0,0 +1,25 @@ +"""For real projects, use contextlib.redirect_stdout instead""" +import contextlib +import sys +from io import StringIO +from textwrap import indent +from typing import TextIO, TypeVar + +T = TypeVar("T", bound=TextIO) + + +@contextlib.contextmanager +def redirect_stdout(target: T) -> T: + sys.stdout = target + yield target + sys.stdout = sys.__stdout__ + + +with redirect_stdout(StringIO()) as f: + print("How much foo could a foo bar bar if a foo bar could bar foo?") + print("A foo bar could bar as much foo as a foo bar could bar") + print("if a foo bar could bar foo") + + +print("And the stdout was:") +print(indent(f.getvalue(), "> ")) diff --git a/src/systems-swamp/suppress_context.py b/src/systems-swamp/suppress_context.py new file mode 100644 index 0000000..a74d342 --- /dev/null +++ b/src/systems-swamp/suppress_context.py @@ -0,0 +1,17 @@ +"""Forces __suppress_context__ to be false when an exception is raised""" + + +class ForceContext: + def __enter__(self): + ... + + def __exit__(self, exc_type, exc_val, exc_tb): + exc_val.__suppress_context__ = False + return False + + +with ForceContext(): + try: + 1 / 0 + except ZeroDivisionError: + raise ValueError("Something went wrong!") from None diff --git a/src/systems-swamp/unraisablehook.py b/src/systems-swamp/unraisablehook.py new file mode 100644 index 0000000..35c9032 --- /dev/null +++ b/src/systems-swamp/unraisablehook.py @@ -0,0 +1,27 @@ +"""A context manager to ignore unraiseable errors""" +import contextlib +import sys + + +@contextlib.contextmanager +def silence_unraiseables(): + sys.unraisablehook = lambda *args, **kwargs: ... + yield + sys.unraisablehook = sys.__unraisablehook__ + + +class Foo: + def __del__(self): + raise Exception("Whoops!") + + +with silence_unraiseables(): + # stderr to keep error messages in order + print("Making Foo with silenced unraisables:", file=sys.stderr) + Foo() + print("Done", file=sys.stderr) + + +print("Making Foo with regular hook:", file=sys.stderr) +Foo() +print("Done", file=sys.stderr) From e3e794bfb6e23ddacd26aebbbb047ec0dd98796f Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Sun, 18 Jun 2023 20:50:36 +1200 Subject: [PATCH 02/17] Update breakpointhook.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit girlies – I am struggling with this one --- src/systems-swamp/breakpointhook.py | 73 +++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/src/systems-swamp/breakpointhook.py b/src/systems-swamp/breakpointhook.py index 3d16404..bacda42 100644 --- a/src/systems-swamp/breakpointhook.py +++ b/src/systems-swamp/breakpointhook.py @@ -1,19 +1,72 @@ +"""Adds locals to breaks?""" import contextlib +import linecache +import reprlib import sys +from pathlib import Path +from pdb import Pdb +from pprint import pformat +from textwrap import indent + + +class PdbLocals(Pdb): + @staticmethod + def _short_path(filename) -> Path: + """ + Returns a relative path to the frame file if possible or an absolute path + """ + try: + return Path(filename).resolve().relative_to(Path.cwd()) + except ValueError: + return Path(filename).resolve() + + def format_stack_entry(self, frame_lineno, lprefix=': '): + frame, lineno = frame_lineno + name = frame.f_code.co_name if frame.f_code.co_name else "" + filename = PdbLocals._short_path(frame.f_code.co_filename) + lines = [f"File \"{filename}\" line {lineno} in {name}", ""] + + if lineno is not None: + path = self.canonic(frame.f_code.co_filename) + if line := linecache.getline(path, lineno, frame.f_globals): + lines += [indent(line.strip(), " " * 4), ""] + + lines += [f"Locals: {pformat(self.curframe_locals)}"] + + if '__return__' in frame.f_locals: + rv = frame.f_locals['__return__'] + lines += ["Returning -> " + reprlib.repr(rv)] + lines += [""] + + return "\n".join(lines) + + +def set_trace(*, header=None): + pdb = PdbLocals() + if header is not None: + pdb.message(header) + pdb.set_trace(sys._getframe().f_back) @contextlib.contextmanager -def no_breakpoint(): - sys.breakpointhook = lambda *args, **kwargs: print( - "You didn't think I'd let you debug so easily, did you?", - file=sys.stderr, - ) +def locals_debugger(): + sys.breakpointhook = set_trace yield sys.breakpointhook = sys.__breakpointhook__ -with no_breakpoint(): - breakpoint() - x = 1 - y = x * 2 - print(y) +# Example +def foo(x, y): + z = x + y + return z + + +def main(): + with locals_debugger(): + breakpoint() + result = foo(1, 1) + print(result) + + +if __name__ == "__main__": + main() From bf6180dc376a858d4123dff87b21428b43067b5a Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Sun, 18 Jun 2023 21:05:21 +1200 Subject: [PATCH 03/17] Update breakpointhook.py --- src/systems-swamp/breakpointhook.py | 37 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/systems-swamp/breakpointhook.py b/src/systems-swamp/breakpointhook.py index bacda42..9288fc4 100644 --- a/src/systems-swamp/breakpointhook.py +++ b/src/systems-swamp/breakpointhook.py @@ -1,5 +1,4 @@ """Adds locals to breaks?""" -import contextlib import linecache import reprlib import sys @@ -9,21 +8,20 @@ from textwrap import indent -class PdbLocals(Pdb): - @staticmethod - def _short_path(filename) -> Path: - """ - Returns a relative path to the frame file if possible or an absolute path - """ - try: - return Path(filename).resolve().relative_to(Path.cwd()) - except ValueError: - return Path(filename).resolve() +def _short_path(filename) -> Path: + try: + return Path(filename).resolve().relative_to(Path.cwd()) + except ValueError: + return Path(filename).resolve() + +class PdbLocals(Pdb): + """Pdb... with locals (and some minor formatting)""" def format_stack_entry(self, frame_lineno, lprefix=': '): frame, lineno = frame_lineno + name = frame.f_code.co_name if frame.f_code.co_name else "" - filename = PdbLocals._short_path(frame.f_code.co_filename) + filename = _short_path(frame.f_code.co_filename) lines = [f"File \"{filename}\" line {lineno} in {name}", ""] if lineno is not None: @@ -48,10 +46,11 @@ def set_trace(*, header=None): pdb.set_trace(sys._getframe().f_back) -@contextlib.contextmanager -def locals_debugger(): +def debug_with_locals(): sys.breakpointhook = set_trace - yield + + +def reset_debug(): sys.breakpointhook = sys.__breakpointhook__ @@ -62,11 +61,11 @@ def foo(x, y): def main(): - with locals_debugger(): - breakpoint() - result = foo(1, 1) - print(result) + breakpoint() + result = foo(1, 1) + print(result) if __name__ == "__main__": + debug_with_locals() main() From 9c02de47d11b6e3e52e4ec2a0260591bc5e77960 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Sun, 18 Jun 2023 21:17:15 +1200 Subject: [PATCH 04/17] reformat --- src/systems-swamp/breakpointhook.py | 11 ++++++----- src/systems-swamp/stdin.py | 5 +---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/systems-swamp/breakpointhook.py b/src/systems-swamp/breakpointhook.py index 9288fc4..8e6ea9d 100644 --- a/src/systems-swamp/breakpointhook.py +++ b/src/systems-swamp/breakpointhook.py @@ -17,12 +17,13 @@ def _short_path(filename) -> Path: class PdbLocals(Pdb): """Pdb... with locals (and some minor formatting)""" - def format_stack_entry(self, frame_lineno, lprefix=': '): + + def format_stack_entry(self, frame_lineno, lprefix=": "): frame, lineno = frame_lineno - + name = frame.f_code.co_name if frame.f_code.co_name else "" filename = _short_path(frame.f_code.co_filename) - lines = [f"File \"{filename}\" line {lineno} in {name}", ""] + lines = [f'File "{filename}" line {lineno} in {name}', ""] if lineno is not None: path = self.canonic(frame.f_code.co_filename) @@ -31,8 +32,8 @@ def format_stack_entry(self, frame_lineno, lprefix=': '): lines += [f"Locals: {pformat(self.curframe_locals)}"] - if '__return__' in frame.f_locals: - rv = frame.f_locals['__return__'] + if "__return__" in frame.f_locals: + rv = frame.f_locals["__return__"] lines += ["Returning -> " + reprlib.repr(rv)] lines += [""] diff --git a/src/systems-swamp/stdin.py b/src/systems-swamp/stdin.py index 73b2f8f..645448c 100644 --- a/src/systems-swamp/stdin.py +++ b/src/systems-swamp/stdin.py @@ -32,10 +32,7 @@ def redirect_stdin(target: TextIO): 6) spam sausage spam spam bacon spam tomato and spam """.strip() -stdin = StringIO(""" -Have you got anything without spam? -1 -""".lstrip()) +stdin = StringIO("Have you got anything without spam?\n1\n") with redirect_stdin(stdin): print("We've got:") From a9c07dfc4482f5345d443d7383a5ee1d6e61b6a7 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Mon, 19 Jun 2023 11:07:13 +1200 Subject: [PATCH 05/17] Update breakpointhook.py --- src/systems-swamp/breakpointhook.py | 54 ++++++++++++++++++----------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/systems-swamp/breakpointhook.py b/src/systems-swamp/breakpointhook.py index 8e6ea9d..c5865df 100644 --- a/src/systems-swamp/breakpointhook.py +++ b/src/systems-swamp/breakpointhook.py @@ -1,4 +1,5 @@ -"""Adds locals to breaks?""" +"""Adds locals to default Pdb debugger at `breakpoint()`""" +import contextlib import linecache import reprlib import sys @@ -8,35 +9,45 @@ from textwrap import indent -def _short_path(filename) -> Path: - try: - return Path(filename).resolve().relative_to(Path.cwd()) - except ValueError: - return Path(filename).resolve() - - class PdbLocals(Pdb): """Pdb... with locals (and some minor formatting)""" - def format_stack_entry(self, frame_lineno, lprefix=": "): - frame, lineno = frame_lineno + def _short_path(self, filename) -> Path: + try: + return Path(filename).resolve().relative_to(Path.cwd()) + except ValueError: + return Path(filename).resolve() + def _header(self, frame, lineno): name = frame.f_code.co_name if frame.f_code.co_name else "" - filename = _short_path(frame.f_code.co_filename) - lines = [f'File "{filename}" line {lineno} in {name}', ""] + filename = self._short_path(frame.f_code.co_filename) + return f'File "{filename}" line {lineno} in {name}' + def _code_line(self, frame, lineno): if lineno is not None: path = self.canonic(frame.f_code.co_filename) if line := linecache.getline(path, lineno, frame.f_globals): - lines += [indent(line.strip(), " " * 4), ""] - - lines += [f"Locals: {pformat(self.curframe_locals)}"] + return indent(line.strip(), " " * 4) + def _return_value(self, frame): if "__return__" in frame.f_locals: rv = frame.f_locals["__return__"] - lines += ["Returning -> " + reprlib.repr(rv)] - lines += [""] + return "Returning -> " + reprlib.repr(rv) + def _locals(self): + return { + k: v for k, v in self.curframe_locals.items() if k != "__return__" + } + + def format_stack_entry(self, frame_lineno, lprefix=": "): + frame, lineno = frame_lineno + lines = [self._header(frame, lineno) + "\n"] + if line := self._code_line(frame, lineno): + lines.append(line + "\n") + lines.append(f"Locals: {pformat(self._locals())}") + if return_value := self._return_value(frame): + lines.append(return_value) + lines.append("") return "\n".join(lines) @@ -47,11 +58,10 @@ def set_trace(*, header=None): pdb.set_trace(sys._getframe().f_back) +@contextlib.contextmanager def debug_with_locals(): sys.breakpointhook = set_trace - - -def reset_debug(): + yield sys.breakpointhook = sys.__breakpointhook__ @@ -68,5 +78,7 @@ def main(): if __name__ == "__main__": - debug_with_locals() + # enter "continue" before main finishes the first time + with debug_with_locals(): + main() main() From e11a3de345181f3f2b0359af6babb8edad492782 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Mon, 19 Jun 2023 12:29:19 +1200 Subject: [PATCH 06/17] =?UTF-8?q?cleanup=20=E2=80=93=20merged=20std=20io?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/systems-swamp/breakpointhook.py | 7 ++-- src/systems-swamp/displayhook.py | 5 +-- src/systems-swamp/interactivehook.py | 7 ++-- src/systems-swamp/stderr.py | 26 ------------- src/systems-swamp/stdin.py | 46 ----------------------- src/systems-swamp/stdio.py | 55 ++++++++++++++++++++++++++++ src/systems-swamp/stdout.py | 25 ------------- src/systems-swamp/unraisablehook.py | 2 +- 8 files changed, 65 insertions(+), 108 deletions(-) delete mode 100644 src/systems-swamp/stderr.py delete mode 100644 src/systems-swamp/stdin.py create mode 100644 src/systems-swamp/stdio.py delete mode 100644 src/systems-swamp/stdout.py diff --git a/src/systems-swamp/breakpointhook.py b/src/systems-swamp/breakpointhook.py index c5865df..fc30a62 100644 --- a/src/systems-swamp/breakpointhook.py +++ b/src/systems-swamp/breakpointhook.py @@ -12,7 +12,7 @@ class PdbLocals(Pdb): """Pdb... with locals (and some minor formatting)""" - def _short_path(self, filename) -> Path: + def _short_path(self, filename): try: return Path(filename).resolve().relative_to(Path.cwd()) except ValueError: @@ -35,16 +35,17 @@ def _return_value(self, frame): return "Returning -> " + reprlib.repr(rv) def _locals(self): - return { + local_values = { k: v for k, v in self.curframe_locals.items() if k != "__return__" } + return f"Locals: {pformat(local_values)}" def format_stack_entry(self, frame_lineno, lprefix=": "): frame, lineno = frame_lineno lines = [self._header(frame, lineno) + "\n"] if line := self._code_line(frame, lineno): lines.append(line + "\n") - lines.append(f"Locals: {pformat(self._locals())}") + lines.append(self._locals()) if return_value := self._return_value(frame): lines.append(return_value) lines.append("") diff --git a/src/systems-swamp/displayhook.py b/src/systems-swamp/displayhook.py index 2fba0e1..45aa6cc 100644 --- a/src/systems-swamp/displayhook.py +++ b/src/systems-swamp/displayhook.py @@ -10,10 +10,9 @@ def activate_pretty(): def hook(value): + if value is not None: + pprint(value) builtins._ = value - if value is None: - return - pprint(value) sys.displayhook = hook diff --git a/src/systems-swamp/interactivehook.py b/src/systems-swamp/interactivehook.py index 7bcf445..e866474 100644 --- a/src/systems-swamp/interactivehook.py +++ b/src/systems-swamp/interactivehook.py @@ -13,16 +13,15 @@ BANNER = """ ******************************************************************************** -******************** We in the interactive session, babey! ********************* +*********************** Remember to like and subscribe ************************* ******************************************************************************** """ def displayhook(value): + if value is not None: + pprint(value) builtins._ = value - if value is None: - return - pprint(value) def _startup(): diff --git a/src/systems-swamp/stderr.py b/src/systems-swamp/stderr.py deleted file mode 100644 index e3fa9a3..0000000 --- a/src/systems-swamp/stderr.py +++ /dev/null @@ -1,26 +0,0 @@ -"""For real projects, use contextlib.redirect_stderr instead""" -import contextlib -import sys -from io import StringIO -from textwrap import indent -from typing import TextIO, TypeVar - -T = TypeVar("T", bound=TextIO) - - -@contextlib.contextmanager -def redirect_stderr(target: T) -> T: - sys.stderr = target - yield target - sys.stderr = sys.__stderr__ - - -with redirect_stderr(StringIO()) as f: - try: - 1 / 0 - except ZeroDivisionError as e: - sys.excepthook(ZeroDivisionError, e, e.__traceback__) - - -print("And the error is:") -print(indent(f.getvalue(), "> ")) diff --git a/src/systems-swamp/stdin.py b/src/systems-swamp/stdin.py deleted file mode 100644 index 645448c..0000000 --- a/src/systems-swamp/stdin.py +++ /dev/null @@ -1,46 +0,0 @@ -"""No contextlib option!""" -import builtins -import contextlib -import sys -from io import StringIO -from typing import TextIO - -SAVED_INPUT = input - - -def _input(prompt): - result = SAVED_INPUT(prompt) - print(result) - return result - - -@contextlib.contextmanager -def redirect_stdin(target: TextIO): - sys.stdin = target - builtins.input = _input - yield - sys.stdout = sys.__stdin__ - builtins.input = SAVED_INPUT - - -menu = """ -1) egg and spam -2) egg bacon and spam -3) egg bacon sausage and spam -4) spam bacon sausage and spam -5) spam egg spam spam bacon and spam -6) spam sausage spam spam bacon spam tomato and spam -""".strip() - -stdin = StringIO("Have you got anything without spam?\n1\n") - -with redirect_stdin(stdin): - print("We've got:") - print(menu) - choice = input("> ") - while not choice.isdigit() or int(choice) not in range(1, 7): - print("No, we've got:") - print(menu) - choice = input("> ") - print("Excellent choice!") - print("Spam spam spam spam. Lovely spam! Wonderful spam!") diff --git a/src/systems-swamp/stdio.py b/src/systems-swamp/stdio.py new file mode 100644 index 0000000..45d7d8c --- /dev/null +++ b/src/systems-swamp/stdio.py @@ -0,0 +1,55 @@ +"""Note: for stdout and stderr use contextlib.redirect_{stderr|stdout}""" +import builtins +import contextlib +import sys +from io import StringIO +from textwrap import indent +from typing import Any, TextIO, TypeVar + +T = TypeVar("T", bound=TextIO) + +SAVED_INPUT = input + + +def _input(prompt: Any) -> str: + result = SAVED_INPUT(prompt) + print(result) + return result + + +@contextlib.contextmanager +def redirect_stdin(target: TextIO, write_to_stdout: bool = False): + sys.stdin = target + if write_to_stdout: + builtins.input = _input + yield + sys.stdin = sys.__stdin__ + if write_to_stdout: + builtins.input = SAVED_INPUT + + +@contextlib.contextmanager +def redirect_stdout(target: T) -> T: + sys.stdout = target + yield target + sys.stdout = sys.__stdout__ + + +@contextlib.contextmanager +def redirect_stderr(target: T) -> T: + sys.stderr = target + yield target + sys.stderr = sys.__stderr__ + + +with ( + redirect_stdin(StringIO("Some redirected stdin\n")), + redirect_stdout(StringIO()) as stdout, + redirect_stderr(StringIO()) as stderr, +): + value = input("Input: ") + print(f"Some stdout, with the input value of `{value}`") + print("Some stderr", file=sys.stderr) + +print("The stdout:", indent(stdout.getvalue(), "> "), sep="\n") +print("The stderr:", indent(stderr.getvalue(), "> "), sep="\n") diff --git a/src/systems-swamp/stdout.py b/src/systems-swamp/stdout.py deleted file mode 100644 index 2e343f5..0000000 --- a/src/systems-swamp/stdout.py +++ /dev/null @@ -1,25 +0,0 @@ -"""For real projects, use contextlib.redirect_stdout instead""" -import contextlib -import sys -from io import StringIO -from textwrap import indent -from typing import TextIO, TypeVar - -T = TypeVar("T", bound=TextIO) - - -@contextlib.contextmanager -def redirect_stdout(target: T) -> T: - sys.stdout = target - yield target - sys.stdout = sys.__stdout__ - - -with redirect_stdout(StringIO()) as f: - print("How much foo could a foo bar bar if a foo bar could bar foo?") - print("A foo bar could bar as much foo as a foo bar could bar") - print("if a foo bar could bar foo") - - -print("And the stdout was:") -print(indent(f.getvalue(), "> ")) diff --git a/src/systems-swamp/unraisablehook.py b/src/systems-swamp/unraisablehook.py index 35c9032..f8c10c4 100644 --- a/src/systems-swamp/unraisablehook.py +++ b/src/systems-swamp/unraisablehook.py @@ -5,7 +5,7 @@ @contextlib.contextmanager def silence_unraiseables(): - sys.unraisablehook = lambda *args, **kwargs: ... + sys.unraisablehook = lambda *args, **kwargs: None yield sys.unraisablehook = sys.__unraisablehook__ From 6b2686f0803c393f1b2985f8c7ff33343d50d5f1 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Mon, 19 Jun 2023 14:48:05 +1200 Subject: [PATCH 07/17] added exceptions and stdio --- src/systems-swamp/exceptions.py | 98 +++++++++++++++++++++++++++ src/systems-swamp/stdio.py | 3 +- src/systems-swamp/suppress_context.py | 17 ----- 3 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 src/systems-swamp/exceptions.py delete mode 100644 src/systems-swamp/suppress_context.py diff --git a/src/systems-swamp/exceptions.py b/src/systems-swamp/exceptions.py new file mode 100644 index 0000000..9d2a252 --- /dev/null +++ b/src/systems-swamp/exceptions.py @@ -0,0 +1,98 @@ +SUPPRESSED_HEADER = "(But the context was suppressed (__suppress_context__))" +CONTEXT_HEADER = ( + "- The above error happened while handling this error (__context__):" +) +CAUSE_HEADER = ( + "- The above error was caused by the following error (__cause__):" +) + + +def print_context(exception: BaseException, pad: int = 0): + print(" " * (pad - 2) + CONTEXT_HEADER) + if exception.__suppress_context__: + print(" " * pad + SUPPRESSED_HEADER) + print_error(exception.__context__, pad=pad + 4) + + +def print_cause(exception: BaseException, pad: int = 0): + print(" " * (pad - 2) + CAUSE_HEADER) + print_error(exception.__cause__, pad=pad + 4) + + +def print_error(exception: BaseException, pad: int = 0): + print(f"{' ' * pad}{type(exception).__name__}: {exception}") + if exception.__context__ is not None: + print_context(exception, pad=pad + 4) + if exception.__cause__ is not None: + print_cause(exception, pad=pad + 4) + + +class ExplainErrors: + def __enter__(self): + ... + + def __exit__(self, exc_type, exc_val, exc_tb): + print_error(exc_val, pad=4) + print() + return True + + +print("Regular Exception:") +with ExplainErrors(): + result = 1 / 0 + +print("Exception with context:") +with ExplainErrors(): + try: + result = 1 / 0 + except ZeroDivisionError: + raise ValueError("Can't use 0") + +print("Exception with suppressed context:") +with ExplainErrors(): + try: + result = 1 / 0 + except ZeroDivisionError: + raise ValueError("Can't use 0") from None + +print("Exception with cause (and suppressed context)") +with ExplainErrors(): + try: + result = 1 / 0 + except ZeroDivisionError as e: + raise ValueError("Can't use 0") from e + +print("Exception with cause (and different suppressed context):") +with ExplainErrors(): + try: + result = 1 / 0 + except ZeroDivisionError: + raise ValueError("Can't use 0") from Exception("Another exception") + +print("Exception with cause and context (unsuppressed):") +with ExplainErrors(): + try: + result = 1 / 0 + except ZeroDivisionError: + err = ValueError("Can't use 0") + err.__cause__ = Exception("Another exception") + err.__suppress_context__ = False + raise err + +print("Manually creating an error with cause and context:") +with ExplainErrors(): + err = Exception("The exception") + err.__context__ = Exception("The context") + err.__cause__ = Exception("The cause") + err.__suppress_context__ = False + raise err + +print("Nested Errors Example:") +with ExplainErrors(): + try: + try: + raise Exception("The initial Exception") + except Exception as e: + raise Exception("Inside the inner try") from e + except Exception: + raise Exception("Inside the outer try") from None diff --git a/src/systems-swamp/stdio.py b/src/systems-swamp/stdio.py index 45d7d8c..955d5ce 100644 --- a/src/systems-swamp/stdio.py +++ b/src/systems-swamp/stdio.py @@ -1,4 +1,3 @@ -"""Note: for stdout and stderr use contextlib.redirect_{stderr|stdout}""" import builtins import contextlib import sys @@ -30,6 +29,7 @@ def redirect_stdin(target: TextIO, write_to_stdout: bool = False): @contextlib.contextmanager def redirect_stdout(target: T) -> T: + """Example only, use contextlib.redirect_stdout instead""" sys.stdout = target yield target sys.stdout = sys.__stdout__ @@ -37,6 +37,7 @@ def redirect_stdout(target: T) -> T: @contextlib.contextmanager def redirect_stderr(target: T) -> T: + """Example only, use contextlib.redirect_stderr instead""" sys.stderr = target yield target sys.stderr = sys.__stderr__ diff --git a/src/systems-swamp/suppress_context.py b/src/systems-swamp/suppress_context.py deleted file mode 100644 index a74d342..0000000 --- a/src/systems-swamp/suppress_context.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Forces __suppress_context__ to be false when an exception is raised""" - - -class ForceContext: - def __enter__(self): - ... - - def __exit__(self, exc_type, exc_val, exc_tb): - exc_val.__suppress_context__ = False - return False - - -with ForceContext(): - try: - 1 / 0 - except ZeroDivisionError: - raise ValueError("Something went wrong!") from None From 91d8c23ac2ba76cff4a3b69e641d516fae5dc05b Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:06:55 +1200 Subject: [PATCH 08/17] reformatted examples --- src/systems-swamp/breakpointhook.py | 43 ++++++++++++++-------------- src/systems-swamp/interactivehook.py | 4 +-- src/systems-swamp/stdio.py | 2 ++ src/systems-swamp/unraisablehook.py | 1 + 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/systems-swamp/breakpointhook.py b/src/systems-swamp/breakpointhook.py index fc30a62..1100c39 100644 --- a/src/systems-swamp/breakpointhook.py +++ b/src/systems-swamp/breakpointhook.py @@ -1,4 +1,3 @@ -"""Adds locals to default Pdb debugger at `breakpoint()`""" import contextlib import linecache import reprlib @@ -10,7 +9,9 @@ class PdbLocals(Pdb): - """Pdb... with locals (and some minor formatting)""" + """ + Adds locals + extra formatting to default Pdb debugger at `breakpoint()` + """ def _short_path(self, filename): try: @@ -18,38 +19,38 @@ def _short_path(self, filename): except ValueError: return Path(filename).resolve() - def _header(self, frame, lineno): - name = frame.f_code.co_name if frame.f_code.co_name else "" - filename = self._short_path(frame.f_code.co_filename) + def _header(self, lineno): + name = self.curframe.f_code.co_name or "" + filename = self._short_path(self.curframe.f_code.co_filename) return f'File "{filename}" line {lineno} in {name}' - def _code_line(self, frame, lineno): + def _code_line(self, lineno): if lineno is not None: - path = self.canonic(frame.f_code.co_filename) - if line := linecache.getline(path, lineno, frame.f_globals): + path = self.canonic(self.curframe.f_code.co_filename) + if line := linecache.getline(path, lineno, self.curframe.f_globals): return indent(line.strip(), " " * 4) - def _return_value(self, frame): - if "__return__" in frame.f_locals: - rv = frame.f_locals["__return__"] + def _return_value(self): + if "__return__" in self.curframe.f_locals: + rv = self.curframe.f_locals["__return__"] return "Returning -> " + reprlib.repr(rv) def _locals(self): local_values = { k: v for k, v in self.curframe_locals.items() if k != "__return__" } - return f"Locals: {pformat(local_values)}" + result = f"Locals: {pformat(local_values)}" + if return_value := self._return_value(): + result += "\n" + return_value + return result def format_stack_entry(self, frame_lineno, lprefix=": "): - frame, lineno = frame_lineno - lines = [self._header(frame, lineno) + "\n"] - if line := self._code_line(frame, lineno): - lines.append(line + "\n") + _, lineno = frame_lineno + lines = [self._header(lineno)] + if line := self._code_line(lineno): + lines.append(line) lines.append(self._locals()) - if return_value := self._return_value(frame): - lines.append(return_value) - lines.append("") - return "\n".join(lines) + return "\n\n".join(lines) + "\n" def set_trace(*, header=None): @@ -79,7 +80,5 @@ def main(): if __name__ == "__main__": - # enter "continue" before main finishes the first time with debug_with_locals(): main() - main() diff --git a/src/systems-swamp/interactivehook.py b/src/systems-swamp/interactivehook.py index e866474..814d00b 100644 --- a/src/systems-swamp/interactivehook.py +++ b/src/systems-swamp/interactivehook.py @@ -24,9 +24,9 @@ def displayhook(value): builtins._ = value -def _startup(): +def interactivehook(): print(BANNER) sys.displayhook = displayhook -sys.__interactivehook__ = _startup +sys.__interactivehook__ = interactivehook diff --git a/src/systems-swamp/stdio.py b/src/systems-swamp/stdio.py index 955d5ce..7b3ce2d 100644 --- a/src/systems-swamp/stdio.py +++ b/src/systems-swamp/stdio.py @@ -51,6 +51,8 @@ def redirect_stderr(target: T) -> T: value = input("Input: ") print(f"Some stdout, with the input value of `{value}`") print("Some stderr", file=sys.stderr) + print(f"Some more stdout") + print("Some more stderr", file=sys.stderr) print("The stdout:", indent(stdout.getvalue(), "> "), sep="\n") print("The stderr:", indent(stderr.getvalue(), "> "), sep="\n") diff --git a/src/systems-swamp/unraisablehook.py b/src/systems-swamp/unraisablehook.py index f8c10c4..30f30a0 100644 --- a/src/systems-swamp/unraisablehook.py +++ b/src/systems-swamp/unraisablehook.py @@ -21,6 +21,7 @@ def __del__(self): Foo() print("Done", file=sys.stderr) +print(file=sys.stderr) print("Making Foo with regular hook:", file=sys.stderr) Foo() From 7f38e7649c9dd581e1972627d68b84022738be23 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:17:39 +1200 Subject: [PATCH 09/17] added some docstrings --- src/systems-swamp/excepthook.py | 2 +- src/systems-swamp/exceptions.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/systems-swamp/excepthook.py b/src/systems-swamp/excepthook.py index 03e66dd..e904bd3 100644 --- a/src/systems-swamp/excepthook.py +++ b/src/systems-swamp/excepthook.py @@ -1,4 +1,3 @@ -"""Makes error messages sArCaStIc""" import sys from contextlib import redirect_stderr from io import StringIO @@ -11,6 +10,7 @@ def memeify(value: str) -> str: def sarcastic_excepthook(type, value, traceback): + """Makes error messages sArCaStIc""" with redirect_stderr(StringIO()) as f: sys.__excepthook__(type, value, traceback) message = memeify(f.getvalue()) diff --git a/src/systems-swamp/exceptions.py b/src/systems-swamp/exceptions.py index 9d2a252..9c33e67 100644 --- a/src/systems-swamp/exceptions.py +++ b/src/systems-swamp/exceptions.py @@ -28,6 +28,7 @@ def print_error(exception: BaseException, pad: int = 0): class ExplainErrors: + """Explains and suppresses any exceptions raised""" def __enter__(self): ... From 04f9b41b402749a9fd5b0ac4890ef3f53f360343 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:31:19 +1200 Subject: [PATCH 10/17] Update exceptions.py --- src/systems-swamp/exceptions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/systems-swamp/exceptions.py b/src/systems-swamp/exceptions.py index 9c33e67..287d8d3 100644 --- a/src/systems-swamp/exceptions.py +++ b/src/systems-swamp/exceptions.py @@ -29,6 +29,7 @@ def print_error(exception: BaseException, pad: int = 0): class ExplainErrors: """Explains and suppresses any exceptions raised""" + def __enter__(self): ... From 89dccc37101e498f42005ac28984f8b4044d29a0 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:34:39 +1200 Subject: [PATCH 11/17] Update stdio.py --- src/systems-swamp/stdio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems-swamp/stdio.py b/src/systems-swamp/stdio.py index 7b3ce2d..f4f0ef8 100644 --- a/src/systems-swamp/stdio.py +++ b/src/systems-swamp/stdio.py @@ -51,7 +51,7 @@ def redirect_stderr(target: T) -> T: value = input("Input: ") print(f"Some stdout, with the input value of `{value}`") print("Some stderr", file=sys.stderr) - print(f"Some more stdout") + print("Some more stdout") print("Some more stderr", file=sys.stderr) print("The stdout:", indent(stdout.getvalue(), "> "), sep="\n") From efaffa7a73335aa19f6bc736b32f9f3d326a75ff Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:57:25 +1200 Subject: [PATCH 12/17] Update displayhook.py --- src/systems-swamp/displayhook.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/systems-swamp/displayhook.py b/src/systems-swamp/displayhook.py index 45aa6cc..c402acc 100644 --- a/src/systems-swamp/displayhook.py +++ b/src/systems-swamp/displayhook.py @@ -1,6 +1,6 @@ """ -When in an interactive session – import these functions and call -activate_pretty to pretty print the result of all expressions +Run in interactive mode (-i) or import this in an interactive session to +pretty print the result of all expressions """ import builtins @@ -19,3 +19,6 @@ def hook(value): def deactivate_pretty(): sys.displayhook = sys.__displayhook__ + + +activate_pretty() From b1e516de268fa2689e7fbb5e24b55a9a27d8b915 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Tue, 20 Jun 2023 13:09:04 +1200 Subject: [PATCH 13/17] Update stdio.py --- src/systems-swamp/stdio.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/systems-swamp/stdio.py b/src/systems-swamp/stdio.py index f4f0ef8..89599b5 100644 --- a/src/systems-swamp/stdio.py +++ b/src/systems-swamp/stdio.py @@ -1,23 +1,26 @@ import builtins import contextlib import sys +from collections.abc import Iterator from io import StringIO from textwrap import indent -from typing import Any, TextIO, TypeVar +from typing import TextIO, TypeVar T = TypeVar("T", bound=TextIO) SAVED_INPUT = input -def _input(prompt: Any) -> str: +def _input(prompt: object) -> str: result = SAVED_INPUT(prompt) print(result) return result @contextlib.contextmanager -def redirect_stdin(target: TextIO, write_to_stdout: bool = False): +def redirect_stdin( + target: TextIO, *, write_to_stdout: bool = False +) -> Iterator[None]: sys.stdin = target if write_to_stdout: builtins.input = _input @@ -28,7 +31,7 @@ def redirect_stdin(target: TextIO, write_to_stdout: bool = False): @contextlib.contextmanager -def redirect_stdout(target: T) -> T: +def redirect_stdout(target: T) -> Iterator[T]: """Example only, use contextlib.redirect_stdout instead""" sys.stdout = target yield target @@ -36,7 +39,7 @@ def redirect_stdout(target: T) -> T: @contextlib.contextmanager -def redirect_stderr(target: T) -> T: +def redirect_stderr(target: T) -> Iterator[T]: """Example only, use contextlib.redirect_stderr instead""" sys.stderr = target yield target From 6f35091a007c9d551288b8b40d58a4635dab19c9 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Tue, 20 Jun 2023 13:09:52 +1200 Subject: [PATCH 14/17] Apply suggestions from code review Co-authored-by: trag1c --- src/module-mesa/file/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module-mesa/file/file.py b/src/module-mesa/file/file.py index 16f0ae1..e055ac4 100644 --- a/src/module-mesa/file/file.py +++ b/src/module-mesa/file/file.py @@ -3,5 +3,5 @@ ASSETS = (Path(__file__) / ".." / "assets").resolve() -with open(ASSETS / "haiku.txt", "r") as f: +with (ASSETS / "haiku.txt").open() as f: print(f.read()) From 4abee02473c47b9a266573127b5a355537123e4f Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Tue, 20 Jun 2023 23:46:47 +1200 Subject: [PATCH 15/17] Update src/module-mesa/file/file.py Co-authored-by: trag1c --- src/module-mesa/file/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module-mesa/file/file.py b/src/module-mesa/file/file.py index e055ac4..117e005 100644 --- a/src/module-mesa/file/file.py +++ b/src/module-mesa/file/file.py @@ -1,7 +1,7 @@ """Sets a directory relative to _this_ file""" from pathlib import Path -ASSETS = (Path(__file__) / ".." / "assets").resolve() +ASSETS = Path(__file__).parent / "assets" with (ASSETS / "haiku.txt").open() as f: print(f.read()) From 8b0758698df919330d1f984fe18c2aa46c186e20 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Thu, 22 Jun 2023 10:49:33 +1200 Subject: [PATCH 16/17] update exc types --- src/systems-swamp/excepthook.py | 8 +++++++- src/systems-swamp/exceptions.py | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/systems-swamp/excepthook.py b/src/systems-swamp/excepthook.py index e904bd3..9210db6 100644 --- a/src/systems-swamp/excepthook.py +++ b/src/systems-swamp/excepthook.py @@ -1,6 +1,8 @@ import sys from contextlib import redirect_stderr from io import StringIO +from types import TracebackType +from typing import Type def memeify(value: str) -> str: @@ -9,7 +11,11 @@ def memeify(value: str) -> str: ) -def sarcastic_excepthook(type, value, traceback): +def sarcastic_excepthook( + type: Type[BaseException], + value: BaseException, + traceback: TracebackType, +): """Makes error messages sArCaStIc""" with redirect_stderr(StringIO()) as f: sys.__excepthook__(type, value, traceback) diff --git a/src/systems-swamp/exceptions.py b/src/systems-swamp/exceptions.py index 287d8d3..c734361 100644 --- a/src/systems-swamp/exceptions.py +++ b/src/systems-swamp/exceptions.py @@ -1,3 +1,6 @@ +from types import TracebackType +from typing import Type + SUPPRESSED_HEADER = "(But the context was suppressed (__suppress_context__))" CONTEXT_HEADER = ( "- The above error happened while handling this error (__context__):" @@ -33,9 +36,15 @@ class ExplainErrors: def __enter__(self): ... - def __exit__(self, exc_type, exc_val, exc_tb): - print_error(exc_val, pad=4) - print() + def __exit__( + self, + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + if exc_val is not None: + print_error(exc_val, pad=4) + print() return True @@ -50,6 +59,13 @@ def __exit__(self, exc_type, exc_val, exc_tb): except ZeroDivisionError: raise ValueError("Can't use 0") +print("Another Exception with context:") +with ExplainErrors(): + try: + result = 1 / 0 + except ZeroDivisionError: + result = "a" + 3 + print("Exception with suppressed context:") with ExplainErrors(): try: From 6dd4531838f580b8d55c7c7a21876444f567b446 Mon Sep 17 00:00:00 2001 From: James Ansley <31982662+James-Ansley@users.noreply.github.com> Date: Sat, 24 Jun 2023 09:52:55 +1200 Subject: [PATCH 17/17] update types --- src/systems-swamp/excepthook.py | 11 +++++------ src/systems-swamp/exceptions.py | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/systems-swamp/excepthook.py b/src/systems-swamp/excepthook.py index 9210db6..87e44d6 100644 --- a/src/systems-swamp/excepthook.py +++ b/src/systems-swamp/excepthook.py @@ -2,7 +2,6 @@ from contextlib import redirect_stderr from io import StringIO from types import TracebackType -from typing import Type def memeify(value: str) -> str: @@ -12,13 +11,13 @@ def memeify(value: str) -> str: def sarcastic_excepthook( - type: Type[BaseException], - value: BaseException, - traceback: TracebackType, -): + exc_type: type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, +) -> None: """Makes error messages sArCaStIc""" with redirect_stderr(StringIO()) as f: - sys.__excepthook__(type, value, traceback) + sys.__excepthook__(exc_type, exc_val, exc_tb) message = memeify(f.getvalue()) print(message, file=sys.stderr) diff --git a/src/systems-swamp/exceptions.py b/src/systems-swamp/exceptions.py index c734361..0f3341b 100644 --- a/src/systems-swamp/exceptions.py +++ b/src/systems-swamp/exceptions.py @@ -1,5 +1,4 @@ from types import TracebackType -from typing import Type SUPPRESSED_HEADER = "(But the context was suppressed (__suppress_context__))" CONTEXT_HEADER = ( @@ -10,19 +9,19 @@ ) -def print_context(exception: BaseException, pad: int = 0): +def print_context(exception: BaseException, pad: int = 0) -> None: print(" " * (pad - 2) + CONTEXT_HEADER) if exception.__suppress_context__: print(" " * pad + SUPPRESSED_HEADER) print_error(exception.__context__, pad=pad + 4) -def print_cause(exception: BaseException, pad: int = 0): +def print_cause(exception: BaseException, pad: int = 0) -> None: print(" " * (pad - 2) + CAUSE_HEADER) print_error(exception.__cause__, pad=pad + 4) -def print_error(exception: BaseException, pad: int = 0): +def print_error(exception: BaseException, pad: int = 0) -> None: print(f"{' ' * pad}{type(exception).__name__}: {exception}") if exception.__context__ is not None: print_context(exception, pad=pad + 4) @@ -33,12 +32,12 @@ def print_error(exception: BaseException, pad: int = 0): class ExplainErrors: """Explains and suppresses any exceptions raised""" - def __enter__(self): + def __enter__(self) -> None: ... def __exit__( self, - exc_type: Type[BaseException] | None, + exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, ) -> bool: