diff --git a/coding_assistant/__init__.py b/coding_assistant/__init__.py index 2be4d5c..9bbc4f2 100644 --- a/coding_assistant/__init__.py +++ b/coding_assistant/__init__.py @@ -1,9 +1,15 @@ # type: ignore import sys +import traceback + + +class AssistantException(Exception): + ... + _old_hook = sys.excepthook -# I don't want to be sued my MS so I'll use a single 'p' +# I don't want to be sued by MS so I'll use a single 'p' clipy = """ _-_ | / /_ \\ |/ @@ -14,11 +20,26 @@ ¯--¯""" +def pathname(obj): + """Create qualified pathname for objs. eg. module.sub.obj, module.sub.inner.obj""" + + module = obj.__module__ + + # TB doesn't include builtin path + if module is None or module == "builtins": + return obj.__name__ + return module + '.' + obj.__name__ + + def assistant_print(type_, value, tb): _old_hook(type_, value, tb) - width = len(f"{type_.__qualname__}{f': {value}' if value else ''}")-2 - print(f"\\{'_'*width}/" + clipy) - + # Traceback is inconsistent about which path to use, `format_exception_only` displays relative paths, + # dropping magic roots, eg. "__main__" but still formatting necessary message information + # When the exception hook uses them, it drops absolute paths, eg. "__main__.Super.Sub" becomes "__main__.Sub" + message = traceback.format_exception_only(value).pop().strip().split(": ", 1) + message[0] = pathname(type_) + width = len(": ".join(message)) - 2 + print(f"\\{'_' * width}/" + clipy, file=sys.stderr) sys.excepthook = assistant_print diff --git a/coding_assistant/__main__.py b/coding_assistant/__main__.py new file mode 100644 index 0000000..9b3f154 --- /dev/null +++ b/coding_assistant/__main__.py @@ -0,0 +1,44 @@ +import sys +from pathlib import Path +from subprocess import Popen, PIPE + +import coding_assistant + + +# Hardcoded values, helps refactoring for multiple assistants down the line +PATH_IDX = 1 +ARGS_IDX = 2 +ASSISTANT = coding_assistant.clipy + + +def cli(): + try: + path = Path(sys.argv[PATH_IDX]).resolve() + except IndexError: + raise coding_assistant.AssistantException("Path to file is missing!") from None + args = sys.argv[ARGS_IDX:] + + # Path to Python Interpreter + python = sys.executable + p = Popen([python, path, *args], text=True, stdin=sys.stdin, stdout=sys.stdout, stderr=PIPE) + _, err = p.communicate() + if err: + # Normal encoding of the ASCII art was causing errors, this is Windows-1252, a legacy encoding scheme + err = err + + # Break into lines + split_assistant = ASSISTANT.splitlines() + split_err = err.splitlines() + + # Prevent double assistant by comparing lines that should be equivalent + # Skip first line due to funky spacing of decoded characters + if split_assistant[1:] != split_err[-len(split_assistant)+1:]: + width = len(split_err[-1]) - 2 + err += f"\\{'_' * width}/" + ASSISTANT + + print(err, file=sys.stderr) + return p.returncode + + +if __name__ == '__main__': + sys.exit(cli()) diff --git a/setup.py b/setup.py index 7db5e68..1fa1900 100644 --- a/setup.py +++ b/setup.py @@ -25,4 +25,8 @@ "Operating System :: OS Independent", ], python_requires='>=3.9', + entry_points={'console_scripts': [ + 'assistant = coding_assistant.__main__:cli', + ], + } )