hmr 0.3.3__tar.gz → 0.3.3.2__tar.gz

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.3.3
3
+ Version: 0.3.3.2
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
@@ -6,7 +6,7 @@ description = "Hot Module Reload for Python"
6
6
  dependencies = [
7
7
  "watchfiles>=0.21,<2 ; sys_platform != 'emscripten'",
8
8
  ]
9
- version = "0.3.3"
9
+ version = "0.3.3.2"
10
10
 
11
11
  [project.scripts]
12
12
  hmr = "reactivity.hmr:cli"
@@ -1,4 +1,11 @@
1
- from .core import AsyncReloader, SyncReloader
1
+ from .core import AsyncReloader, BaseReloader, SyncReloader
2
+
3
+
4
+ def _clean_up(r: BaseReloader):
5
+ r.run_entry_file.dispose()
6
+ r.run_entry_file.invalidate()
7
+ r.entry_module.load.dispose()
8
+ r.entry_module.load.invalidate()
2
9
 
3
10
 
4
11
  class SyncReloaderAPI(SyncReloader):
@@ -13,21 +20,19 @@ class SyncReloaderAPI(SyncReloader):
13
20
  def __exit__(self, *_):
14
21
  self.stop_watching()
15
22
  self.thread.join()
16
- self.run_entry_file.dispose()
17
- self.run_entry_file.invalidate()
23
+ _clean_up(self)
18
24
 
19
25
  async def __aenter__(self):
20
26
  from asyncio import ensure_future, to_thread
21
27
 
22
- self.run_entry_file()
28
+ await to_thread(self.run_entry_file)
23
29
  self.future = ensure_future(to_thread(self.start_watching))
24
30
  return super()
25
31
 
26
32
  async def __aexit__(self, *_):
27
33
  self.stop_watching()
28
34
  await self.future
29
- self.run_entry_file.dispose()
30
- self.run_entry_file.invalidate()
35
+ _clean_up(self)
31
36
 
32
37
 
33
38
  class AsyncReloaderAPI(AsyncReloader):
@@ -43,8 +48,7 @@ class AsyncReloaderAPI(AsyncReloader):
43
48
  def __exit__(self, *_):
44
49
  self.stop_watching()
45
50
  self.thread.join()
46
- self.run_entry_file.dispose()
47
- self.run_entry_file.invalidate()
51
+ _clean_up(self)
48
52
 
49
53
  async def __aenter__(self):
50
54
  from asyncio import ensure_future, to_thread
@@ -56,5 +60,4 @@ class AsyncReloaderAPI(AsyncReloader):
56
60
  async def __aexit__(self, *_):
57
61
  self.stop_watching()
58
62
  await self.future
59
- self.run_entry_file.dispose()
60
- self.run_entry_file.invalidate()
63
+ _clean_up(self)
@@ -9,7 +9,8 @@ from inspect import currentframe
9
9
  from pathlib import Path
10
10
  from site import getsitepackages
11
11
  from types import ModuleType, TracebackType
12
- from typing import Any
12
+ from typing import Any, Self
13
+ from weakref import WeakValueDictionary
13
14
 
14
15
  from .. import Reactive, batch, memoized_method
15
16
  from .hooks import call_post_reload_hooks, call_pre_reload_hooks
@@ -43,6 +44,8 @@ class NamespaceProxy(Reactive[str, Any]):
43
44
 
44
45
 
45
46
  class ReactiveModule(ModuleType):
47
+ instances: WeakValueDictionary[Path, Self] = WeakValueDictionary()
48
+
46
49
  def __init__(self, file: Path, namespace: dict, name: str, doc: str | None = None):
47
50
  super().__init__(name, doc)
48
51
  self.__is_initialized = False
@@ -53,6 +56,8 @@ class ReactiveModule(ModuleType):
53
56
  self.__namespace_proxy = NamespaceProxy(namespace)
54
57
  self.__file = file
55
58
 
59
+ self.instances[file.resolve()] = self
60
+
56
61
  @property
57
62
  def file(self):
58
63
  if is_called_in_this_file():
@@ -155,7 +160,7 @@ def patch_meta_path(includes: Iterable[str] = (".",), excludes: Iterable[str] =
155
160
 
156
161
 
157
162
  def get_path_module_map():
158
- return {module.file.resolve(): module for module in sys.modules.values() if isinstance(module, ReactiveModule)}
163
+ return {**ReactiveModule.instances}
159
164
 
160
165
 
161
166
  class ErrorFilter:
@@ -190,15 +195,17 @@ class BaseReloader:
190
195
  patch_meta_path(includes, excludes)
191
196
  self.error_filter = ErrorFilter(*(str(i) for i in Path(__file__, "../..").resolve().glob("**/*.py")))
192
197
 
198
+ @cached_property
199
+ def entry_module(self):
200
+ namespace = {"__file__": self.entry, "__name__": "__main__"}
201
+ return ReactiveModule(Path(self.entry), namespace, "__main__")
202
+
193
203
  @memoized_method
194
204
  def run_entry_file(self):
195
205
  call_pre_reload_hooks()
196
206
 
197
- if not isinstance(module := sys.modules["__main__"], ReactiveModule):
198
- namespace = {"__file__": self.entry, "__name__": "__main__"}
199
- module = sys.modules["__main__"] = ReactiveModule(Path(self.entry), namespace, "__main__")
200
- module.load.invalidate()
201
- module.load()
207
+ self.entry_module.load.invalidate()
208
+ self.entry_module.load()
202
209
 
203
210
  call_post_reload_hooks()
204
211
 
@@ -305,4 +312,4 @@ def cli():
305
312
  SyncReloader(entry).keep_watching_until_interrupt()
306
313
 
307
314
 
308
- __version__ = "0.3.3"
315
+ __version__ = "0.3.3.2"
@@ -1,8 +1,8 @@
1
- import sys
2
1
  from collections import UserDict
3
2
  from collections.abc import Callable
4
3
  from functools import wraps
5
- from inspect import getsource
4
+ from inspect import getsource, getsourcefile
5
+ from pathlib import Path
6
6
  from types import FunctionType
7
7
 
8
8
  from .. import create_memo
@@ -28,7 +28,9 @@ def clear_memos():
28
28
 
29
29
 
30
30
  def cache_across_reloads[T](func: Callable[[], T]) -> Callable[[], T]:
31
- module = sys.modules[func.__module__]
31
+ file = getsourcefile(func)
32
+ assert file is not None
33
+ module = ReactiveModule.instances[Path(file).resolve()]
32
34
 
33
35
  if not isinstance(module, ReactiveModule):
34
36
  from functools import cache
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes