diff options
author | Ben Sima <ben@bsima.me> | 2024-04-07 14:49:45 -0400 |
---|---|---|
committer | Ben Sima <ben@bsima.me> | 2024-04-10 19:56:46 -0400 |
commit | 544d75a47e85d2b334267a43ba065bb69538ad75 (patch) | |
tree | c2ffd7f305be45e3f0484199a5e5ef7b5fd4ee36 | |
parent | 2c09c7f73e2fc770f42b5dd2588aa9634b4e7c6e (diff) |
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.
-rwxr-xr-x | Biz/Ide/repl.sh | 1 | ||||
-rw-r--r-- | 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) |