""" 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 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 def use(ns: str, path: str) -> None: """ Load or reload the module named 'ns' from 'path'. Like `use` in the Guile Scheme repl. """ logging.info("loading %s from %s", ns, path) spec = importlib.util.spec_from_file_location(ns, path) module = importlib.util.module_from_spec(spec) # delete module and its imported names if its already loaded if ns in sys.modules: del sys.modules[ns] for name in module.__dict__: if name in globals(): del globals()[name] sys.modules[ns] = module spec.loader.exec_module(module) names = list(module.__dict__) 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() after making changes") sys.ps1 = f"{NS}> " sys.ps2 = f"{NS}| " def reload() -> None: """Reload the namespace.""" use(NS, PATH) typecheck(PATH)