summaryrefslogtreecommitdiff
path: root/Biz
diff options
context:
space:
mode:
authorBen Sima <ben@bsima.me>2024-04-07 14:49:45 -0400
committerBen Sima <ben@bsima.me>2024-04-10 19:56:46 -0400
commit544d75a47e85d2b334267a43ba065bb69538ad75 (patch)
treec2ffd7f305be45e3f0484199a5e5ef7b5fd4ee36 /Biz
parent2c09c7f73e2fc770f42b5dd2588aa9634b4e7c6e (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.
Diffstat (limited to 'Biz')
-rwxr-xr-xBiz/Ide/repl.sh1
-rw-r--r--Biz/Repl.py39
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)