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..117e005 --- /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__).parent / "assets" + +with (ASSETS / "haiku.txt").open() 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..1100c39 --- /dev/null +++ b/src/systems-swamp/breakpointhook.py @@ -0,0 +1,84 @@ +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): + """ + Adds locals + extra formatting to default Pdb debugger at `breakpoint()` + """ + + def _short_path(self, filename): + try: + return Path(filename).resolve().relative_to(Path.cwd()) + except ValueError: + return Path(filename).resolve() + + 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, lineno): + if lineno is not None: + 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): + 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__" + } + 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=": "): + _, lineno = frame_lineno + lines = [self._header(lineno)] + if line := self._code_line(lineno): + lines.append(line) + lines.append(self._locals()) + return "\n\n".join(lines) + "\n" + + +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 debug_with_locals(): + sys.breakpointhook = set_trace + yield + sys.breakpointhook = sys.__breakpointhook__ + + +# Example +def foo(x, y): + z = x + y + return z + + +def main(): + breakpoint() + result = foo(1, 1) + print(result) + + +if __name__ == "__main__": + with debug_with_locals(): + main() diff --git a/src/systems-swamp/displayhook.py b/src/systems-swamp/displayhook.py new file mode 100644 index 0000000..c402acc --- /dev/null +++ b/src/systems-swamp/displayhook.py @@ -0,0 +1,24 @@ +""" +Run in interactive mode (-i) or import this in an interactive session to +pretty print the result of all expressions +""" + +import builtins +import sys +from pprint import pprint + + +def activate_pretty(): + def hook(value): + if value is not None: + pprint(value) + builtins._ = value + + sys.displayhook = hook + + +def deactivate_pretty(): + sys.displayhook = sys.__displayhook__ + + +activate_pretty() diff --git a/src/systems-swamp/excepthook.py b/src/systems-swamp/excepthook.py new file mode 100644 index 0000000..87e44d6 --- /dev/null +++ b/src/systems-swamp/excepthook.py @@ -0,0 +1,27 @@ +import sys +from contextlib import redirect_stderr +from io import StringIO +from types import TracebackType + + +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( + exc_type: type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, +) -> None: + """Makes error messages sArCaStIc""" + with redirect_stderr(StringIO()) as f: + sys.__excepthook__(exc_type, exc_val, exc_tb) + message = memeify(f.getvalue()) + print(message, file=sys.stderr) + + +sys.excepthook = sarcastic_excepthook + +1 / 0 diff --git a/src/systems-swamp/exceptions.py b/src/systems-swamp/exceptions.py new file mode 100644 index 0000000..0f3341b --- /dev/null +++ b/src/systems-swamp/exceptions.py @@ -0,0 +1,115 @@ +from types import TracebackType + +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) -> 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) -> None: + print(" " * (pad - 2) + CAUSE_HEADER) + print_error(exception.__cause__, pad=pad + 4) + + +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) + if exception.__cause__ is not None: + print_cause(exception, pad=pad + 4) + + +class ExplainErrors: + """Explains and suppresses any exceptions raised""" + + def __enter__(self) -> None: + ... + + 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 + + +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("Another Exception with context:") +with ExplainErrors(): + try: + result = 1 / 0 + except ZeroDivisionError: + result = "a" + 3 + +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/interactivehook.py b/src/systems-swamp/interactivehook.py new file mode 100644 index 0000000..814d00b --- /dev/null +++ b/src/systems-swamp/interactivehook.py @@ -0,0 +1,32 @@ +""" +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 = """ +******************************************************************************** +*********************** Remember to like and subscribe ************************* +******************************************************************************** +""" + + +def displayhook(value): + if value is not None: + pprint(value) + builtins._ = value + + +def interactivehook(): + print(BANNER) + sys.displayhook = displayhook + + +sys.__interactivehook__ = interactivehook diff --git a/src/systems-swamp/stdio.py b/src/systems-swamp/stdio.py new file mode 100644 index 0000000..89599b5 --- /dev/null +++ b/src/systems-swamp/stdio.py @@ -0,0 +1,61 @@ +import builtins +import contextlib +import sys +from collections.abc import Iterator +from io import StringIO +from textwrap import indent +from typing import TextIO, TypeVar + +T = TypeVar("T", bound=TextIO) + +SAVED_INPUT = input + + +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 +) -> Iterator[None]: + 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) -> Iterator[T]: + """Example only, use contextlib.redirect_stdout instead""" + sys.stdout = target + yield target + sys.stdout = sys.__stdout__ + + +@contextlib.contextmanager +def redirect_stderr(target: T) -> Iterator[T]: + """Example only, use contextlib.redirect_stderr instead""" + 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("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 new file mode 100644 index 0000000..30f30a0 --- /dev/null +++ b/src/systems-swamp/unraisablehook.py @@ -0,0 +1,28 @@ +"""A context manager to ignore unraiseable errors""" +import contextlib +import sys + + +@contextlib.contextmanager +def silence_unraiseables(): + sys.unraisablehook = lambda *args, **kwargs: None + 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(file=sys.stderr) + +print("Making Foo with regular hook:", file=sys.stderr) +Foo() +print("Done", file=sys.stderr)