From e672348fd002e349b7257cff77229fbf58ceb75f Mon Sep 17 00:00:00 2001 From: gilch Date: Fri, 28 Jul 2023 21:42:51 -0600 Subject: [PATCH] Cache compiled modules This is required for defonce. `transpile` will also set `__file__`. --- docs/lissp_whirlwind_tour.rst | 16 ++++++++-------- src/hissp/compiler.py | 15 ++++++++------- src/hissp/reader.py | 10 +++++++--- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/docs/lissp_whirlwind_tour.rst b/docs/lissp_whirlwind_tour.rst index 6b6ea041c..791342dae 100644 --- a/docs/lissp_whirlwind_tour.rst +++ b/docs/lissp_whirlwind_tour.rst @@ -1643,7 +1643,7 @@ Lissp Whirlwind Tour ... ('(print "Hello from spam!")\n(.update (globals) : x 42)')) 53 - #> (hissp.reader..transpile __package__ 'spam 'eggs) ; Side effects on compilation + #> (hissp.reader..transpile __package__ 'spam 'eggs) ;Side effects on compilation. >>> __import__('hissp.reader',fromlist='?').transpile( ... __package__, ... 'spam', @@ -1652,20 +1652,20 @@ Lissp Whirlwind Tour Hello World! - #> spam..x ; and import! - >>> __import__('spam').x - Hello from spam! - 42 - - #> spam..x ;Python caches imports. + #> spam..x ;Python caches compiler's imports, >>> __import__('spam').x 42 #> eggs. >>> __import__('eggs') - Hello World! + #> (importlib..reload spam.) ; but side effects happen on reload. + >>> __import__('importlib').reload( + ... __import__('spam')) + Hello from spam! + + #> (any (map (lambda f (os..remove f)) ;Cleanup. #.. '(eggs.lissp spam.lissp spam.py eggs.py))) diff --git a/src/hissp/compiler.py b/src/hissp/compiler.py index 5d4a55e4f..669e29f3f 100644 --- a/src/hissp/compiler.py +++ b/src/hissp/compiler.py @@ -14,11 +14,11 @@ from contextlib import contextmanager, suppress from contextvars import ContextVar from functools import wraps -from itertools import chain, takewhile, starmap +from itertools import chain, starmap, takewhile from pprint import pformat from traceback import format_exc from types import ModuleType -from typing import Iterable, List, NewType, Tuple, TypeVar, Union +from typing import Any, Dict, Iterable, List, NewType, Tuple, TypeVar, Union from warnings import warn PAIR_WORDS = {":*": "*", ":**": "**", ":?": ""} @@ -95,16 +95,17 @@ def __init__(self, qualname="__main__", ns=None, evaluate=True): self.abort = None @staticmethod - def new_ns(name, doc=None, package=None): - """Creates and initializes a dict namespace like a module, - with the given `__name__`, ``__doc__``, and `__package__`; - ``__builtins__``; an empty ``__annotations__``; and whatever - else Python currently adds to new module objects. + def new_ns(name, doc=None, package=None) -> Dict[str, Any]: + """Imports the named module, creating it if necessary. + + Returns the module's ``__dict__``. """ mod = ModuleType(name, doc) mod.__annotations__ = {} mod.__package__ = package mod.__builtins__ = builtins + if name != "__main__": + mod = sys.modules.setdefault(name, mod) return vars(mod) def compile(self, forms: Iterable) -> str: diff --git a/src/hissp/reader.py b/src/hissp/reader.py index 45ec0817a..2359a814c 100644 --- a/src/hissp/reader.py +++ b/src/hissp/reader.py @@ -24,7 +24,7 @@ from pathlib import Path, PurePath from pprint import pformat from threading import Lock -from typing import Any, Iterable, Iterator, NewType, Optional, Tuple, Union, List +from typing import Any, Dict, Iterable, Iterator, List, NewType, Optional, Tuple, Union import hissp.compiler as C from hissp.compiler import Compiler, readerless @@ -218,7 +218,7 @@ def reinit(self): self._p = 0 @property - def ns(self): + def ns(self) -> Dict[str, Any]: """The wrapped `Compiler`'s ``ns``.""" return self.compiler.ns @@ -603,9 +603,13 @@ def transpile_file(path: Union[Path, str], package: Optional[str] = None): because macro definitions can alter the compilation of subsequent top-level forms. A packaged Lissp file must know its package at compile time to handle templates and macros correctly. + + After the .py file is written, `__file__` will be set to it, if it + doesn't exist already. """ path = Path(path).resolve(strict=True) qualname = f"{package or ''}{'.' if package else ''}{PurePath(path.name).stem}" L = Lissp(qualname=qualname, evaluate=True, filename=str(path)) python = L.compile(re.sub(r"^#!.*\n", "", path.read_text("utf8"))) - path.with_suffix(".py").write_text(python, "utf8") + (py := path.with_suffix(".py")).write_text(python, "utf8") + L.ns.setdefault("__file__", str(py))