From 544d75a47e85d2b334267a43ba065bb69538ad75 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Sun, 7 Apr 2024 14:49:45 -0400 Subject: Add mypy to Python REPL I tried to add it to Lint.hs but I can't because mypy needs the target's external libraries in its environment to load and check the types for that dependency. So instead, I just added a function to the REPL environment that runs the type checker. I already had mypy in the REPL environment so I must have started down this path before and just didn't add the REPL tooling. The automatic typechecking on load feels like Haskell. A previous version of this patch had just provided a `typecheck()` function to the REPL, but it felt awkward to type all that out just to check my code after loading it. I would like to bind a key like ctrl-r or alt-r to the `reload()` function, but I'm not sure how to do that. I think Python uses GNU readline so there should be some docs, but a first pass search didn't find what I needed. --- Biz/Ide/repl.sh | 1 - Biz/Repl.py | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/Biz/Ide/repl.sh b/Biz/Ide/repl.sh index 8b28dcd..1aca862 100755 --- a/Biz/Ide/repl.sh +++ b/Biz/Ide/repl.sh @@ -69,7 +69,6 @@ fi ;; Py) langdeps+=("mypy") - flags+=(--packages ruff) flags+=(--packages "$BILD.bild.python.pythonWith (p: with p; [${langdeps[*]}])") PYTHONPATH=$CODEROOT:$PYTHONPATH pycommand="python -i $CODEROOT/Biz/Repl.py $module ${targets[*]}" diff --git a/Biz/Repl.py b/Biz/Repl.py index 9cc0c35..cb9dfe7 100644 --- a/Biz/Repl.py +++ b/Biz/Repl.py @@ -2,14 +2,26 @@ Improve the standard Python REPL. This module attempts to emulate the workflow of ghci or lisp repls. It uses -importlib to load a namespace from the given path. It then binds 'r()' to a -function that reloads the same namespace. +importlib to load a namespace from the provided path, typechecks it with mypy, +and provides some tools for improving repl-driven development. + +This module is called in Biz/Ide/repl.sh like so: + + python -i Biz/Repl.py NS PATH + +where NS is the dot-partitioned namespace of the main module, and PATH is the +path to the same file. In the future this could be expanded to be a list of +additional files to load. """ import importlib +import importlib.util import logging +import os import sys +import mypy.api + from Biz import Log @@ -34,20 +46,31 @@ def use(ns: str, path: str) -> None: globals().update({k: getattr(module, k) for k in names}) +def typecheck(path: str) -> None: + """Typecheck this namespace.""" + # this envvar is undocumented, but it works + # https://github.com/python/mypy/issues/13815 + os.environ["MYPY_FORCE_COLOR"] = "1" + logging.info("typechecking %s", path) + stdout, stderr, _ = mypy.api.run([path]) + sys.stdout.write(stdout) + sys.stdout.flush() + sys.stderr.write(stderr) + sys.stderr.flush() + + if __name__ == "__main__": Log.setup() NS = sys.argv[1] PATH = sys.argv[2] use(NS, PATH) + typecheck(PATH) - logging.info("use reload() or _r() after making changes") + logging.info("use reload() after making changes") sys.ps1 = f"{NS}> " sys.ps2 = f"{NS}| " def reload() -> None: """Reload the namespace.""" - return use(NS, PATH) - - def _r() -> None: - """Shorthand: Reload the namespace.""" - return use(NS, PATH) + use(NS, PATH) + typecheck(PATH) -- cgit v1.2.3