hmr 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hmr
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Hot Module Reload for Python
5
5
  Project-URL: repository, https://github.com/promplate/pyth-on-line/tree/reactivity
6
6
  Requires-Python: >=3.12
@@ -0,0 +1,12 @@
1
+ hmr-0.3.0.dist-info/METADATA,sha256=PcpVod1MtZSUbVP91KWKD9ioN7qTULhzxHR9vK20ZvA,258
2
+ hmr-0.3.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ hmr-0.3.0.dist-info/entry_points.txt,sha256=g_T0uJ43WgsdG14kkkdaBQuIL0HO-m1qvtjXMP6d060,59
4
+ reactivity/__init__.py,sha256=pX-RUzkezCC1x4eOWGxNhXbwrbvBLP_3pQuZr9eZz1Y,300
5
+ reactivity/functional.py,sha256=U06vshcVhZ0sb218gcmHtEhfgTNAGtQ7zyvPz2w5qKM,1292
6
+ reactivity/helpers.py,sha256=7gwsIKKrjEahSz9G9oR4s1LdYXQTCIMO0k4UGXGla9Y,3714
7
+ reactivity/hmr/__init__.py,sha256=S5ZIHqCRpevdzWuhS0aCua_S8F0LkK0YNg6IgeTScFQ,177
8
+ reactivity/hmr/core.py,sha256=NfzgqTdFD7wrO_8ogZTSqMmVIRw4Z_hhk5-ONRlunSI,9993
9
+ reactivity/hmr/hooks.py,sha256=-yLr5ktiyqPb1nDbHsgv6-c_ZkziBjNqCU-0PCfXGYU,592
10
+ reactivity/hmr/utils.py,sha256=zgKjz3RhcUDYLoIqZFRVBcPtPWUJA1YphJycyrQx3tk,1464
11
+ reactivity/primitives.py,sha256=DR2waJbzhVKOioHXMliE4FIsxQUq7DZA0umPrlvchA4,4217
12
+ hmr-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ from .core import cli
2
+ from .hooks import post_reload, pre_reload
3
+ from .utils import cache_across_reloads
4
+
5
+ __all__ = ("cache_across_reloads", "cli", "post_reload", "pre_reload")
@@ -7,11 +7,12 @@ from importlib.machinery import ModuleSpec
7
7
  from importlib.util import spec_from_loader
8
8
  from inspect import currentframe
9
9
  from pathlib import Path
10
- from runpy import run_path
10
+ from site import getsitepackages
11
11
  from types import ModuleType, TracebackType
12
12
  from typing import Any
13
13
 
14
- from . import Reactive, batch, memoized_method
14
+ from .. import Reactive, batch, memoized_method
15
+ from .hooks import call_post_reload_hooks, call_pre_reload_hooks
15
16
 
16
17
 
17
18
  def is_called_in_this_file() -> bool:
@@ -115,7 +116,7 @@ class ReactiveModuleFinder(MetaPathFinder):
115
116
  def __init__(self, includes: Iterable[str] = ".", excludes: Iterable[str] = ()):
116
117
  super().__init__()
117
118
  self.includes = [Path(i).resolve() for i in includes]
118
- self.excludes = [Path(e).resolve() for e in excludes]
119
+ self.excludes = [Path(e).resolve() for e in (*excludes, *getsitepackages())]
119
120
 
120
121
  def _accept(self, path: Path):
121
122
  return path.is_file() and not is_relative_to_any(path, self.excludes) and is_relative_to_any(path, self.includes)
@@ -149,7 +150,7 @@ def patch_module(name_or_module: str | ModuleType):
149
150
  return m
150
151
 
151
152
 
152
- def patch_meta_path(includes: Iterable[str] = (".",), excludes: Iterable[str] = (".venv",)):
153
+ def patch_meta_path(includes: Iterable[str] = (".",), excludes: Iterable[str] = ()):
153
154
  sys.meta_path.insert(0, ReactiveModuleFinder(includes, excludes))
154
155
 
155
156
 
@@ -187,13 +188,20 @@ class BaseReloader:
187
188
  self.includes = includes
188
189
  self.excludes = excludes
189
190
  patch_meta_path(includes, excludes)
190
- self.last_globals = {}
191
- self.error_filter = ErrorFilter(__file__, "<frozen runpy>")
191
+ self.error_filter = ErrorFilter(*(str(i) for i in Path(__file__, "../..").resolve().glob("**/*.py")))
192
192
 
193
193
  @memoized_method
194
194
  def run_entry_file(self):
195
+ call_pre_reload_hooks()
196
+
195
197
  with self.error_filter:
196
- self.last_globals = run_path(self.entry, self.last_globals, "__main__")
198
+ if not isinstance(module := sys.modules["__main__"], ReactiveModule):
199
+ namespace = {"__file__": self.entry, "__name__": "__main__"}
200
+ module = sys.modules["__main__"] = ReactiveModule(Path(self.entry), namespace, "__main__")
201
+ module.load.invalidate()
202
+ module.load()
203
+
204
+ call_post_reload_hooks()
197
205
 
198
206
  @property
199
207
  def watch_filter(self):
@@ -281,7 +289,7 @@ def cli():
281
289
  if not (path := Path(entry)).is_file():
282
290
  raise FileNotFoundError(path.resolve())
283
291
  sys.path.insert(0, ".")
284
- SyncReloader(entry, excludes={".venv"}).keep_watching_until_interrupt()
292
+ SyncReloader(entry).keep_watching_until_interrupt()
285
293
 
286
294
 
287
- __version__ = "0.2.0"
295
+ __version__ = "0.3.0"
@@ -0,0 +1,25 @@
1
+ from collections.abc import Callable
2
+ from typing import Any
3
+
4
+ pre_reload_hooks: dict[str, Callable[[], Any]] = {}
5
+ post_reload_hooks: dict[str, Callable[[], Any]] = {}
6
+
7
+
8
+ def pre_reload[T](func: Callable[[], T]) -> Callable[[], T]:
9
+ pre_reload_hooks[func.__name__] = func
10
+ return func
11
+
12
+
13
+ def post_reload[T](func: Callable[[], T]) -> Callable[[], T]:
14
+ post_reload_hooks[func.__name__] = func
15
+ return func
16
+
17
+
18
+ def call_pre_reload_hooks():
19
+ for func in pre_reload_hooks.values():
20
+ func()
21
+
22
+
23
+ def call_post_reload_hooks():
24
+ for func in post_reload_hooks.values():
25
+ func()
@@ -0,0 +1,61 @@
1
+ import sys
2
+ from collections import UserDict
3
+ from collections.abc import Callable
4
+ from functools import wraps
5
+ from inspect import getsource
6
+ from types import FunctionType
7
+
8
+ from .. import create_memo
9
+ from .core import NamespaceProxy, ReactiveModule
10
+ from .hooks import post_reload, pre_reload
11
+
12
+ memos: dict[str, Callable] = {}
13
+ functions: dict[str, Callable] = {}
14
+ functions_last: set[str] = set()
15
+
16
+
17
+ @pre_reload
18
+ def swap():
19
+ functions_last.update(functions)
20
+ functions.clear()
21
+
22
+
23
+ @post_reload
24
+ def clear_memos():
25
+ for func in functions_last:
26
+ if func not in functions and func in memos:
27
+ del memos[func]
28
+
29
+
30
+ def cache_across_reloads[T](func: Callable[[], T]) -> Callable[[], T]:
31
+ module = sys.modules[func.__module__]
32
+
33
+ if not isinstance(module, ReactiveModule):
34
+ from functools import cache
35
+
36
+ return cache(func) # type: ignore
37
+
38
+ source = getsource(func)
39
+
40
+ proxy: NamespaceProxy = module._ReactiveModule__namespace_proxy # type: ignore # noqa: SLF001
41
+
42
+ func = FunctionType(func.__code__, DictProxy(proxy), func.__name__, func.__defaults__, func.__closure__)
43
+
44
+ functions[source] = func
45
+
46
+ if source in memos and source in functions_last:
47
+ return memos[source]
48
+
49
+ @wraps(func)
50
+ @create_memo
51
+ def wrapper():
52
+ return functions[source]()
53
+
54
+ memos[source] = wrapper
55
+
56
+ return wrapper
57
+
58
+
59
+ class DictProxy(UserDict, dict): # type: ignore
60
+ def __init__(self, data):
61
+ self.data = data
@@ -1,9 +0,0 @@
1
- hmr-0.2.0.dist-info/METADATA,sha256=EOVgm-SYoDvhCdf7X3stdnfoOwt4JiJz4dU-MH5qp4A,258
2
- hmr-0.2.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- hmr-0.2.0.dist-info/entry_points.txt,sha256=g_T0uJ43WgsdG14kkkdaBQuIL0HO-m1qvtjXMP6d060,59
4
- reactivity/__init__.py,sha256=pX-RUzkezCC1x4eOWGxNhXbwrbvBLP_3pQuZr9eZz1Y,300
5
- reactivity/functional.py,sha256=U06vshcVhZ0sb218gcmHtEhfgTNAGtQ7zyvPz2w5qKM,1292
6
- reactivity/helpers.py,sha256=7gwsIKKrjEahSz9G9oR4s1LdYXQTCIMO0k4UGXGla9Y,3714
7
- reactivity/hmr.py,sha256=CeJSXPeDA-wc_tE_JC0gXfFgeaGsCEvmVbbsCYJtGok,9603
8
- reactivity/primitives.py,sha256=DR2waJbzhVKOioHXMliE4FIsxQUq7DZA0umPrlvchA4,4217
9
- hmr-0.2.0.dist-info/RECORD,,
File without changes