hmr 0.0.3__tar.gz → 0.0.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.0.3
3
+ Version: 0.0.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",
8
8
  ]
9
- version = "0.0.3"
9
+ version = "0.0.3.2"
10
10
 
11
11
  [project.scripts]
12
12
  hmr = "reactivity.hmr:cli"
@@ -0,0 +1,5 @@
1
+ from .functional import batch, create_effect, create_memo, create_signal, memoized_method, memoized_property
2
+ from .helpers import Reactive
3
+ from .primitives import State
4
+
5
+ __all__ = ["Reactive", "State", "batch", "create_effect", "create_memo", "create_signal", "memoized_method", "memoized_property"]
@@ -3,7 +3,7 @@ from functools import wraps
3
3
  from typing import Protocol, overload
4
4
 
5
5
  from .helpers import Memoized, MemoizedMethod, MemoizedProperty
6
- from .primitives import Batch, Derived, Signal
6
+ from .primitives import Batch, Effect, Signal
7
7
 
8
8
 
9
9
  class Getter[T](Protocol):
@@ -19,19 +19,19 @@ def create_signal[T](initial_value: T = None, check_equality=True) -> tuple[Gett
19
19
  return signal.get, signal.set
20
20
 
21
21
 
22
- def create_effect[T](fn: Callable[[], T], auto_run=True):
23
- return Derived(fn, auto_run)
22
+ def create_effect[T](fn: Callable[[], T], call_immediately=True):
23
+ return Effect(fn, call_immediately)
24
24
 
25
25
 
26
26
  def create_memo[T](fn: Callable[[], T]):
27
27
  return Memoized(fn)
28
28
 
29
29
 
30
- def memoized_property[T, Self](method: Callable[[Self], T]):
30
+ def memoized_property[T, I](method: Callable[[I], T]):
31
31
  return MemoizedProperty(method)
32
32
 
33
33
 
34
- def memoized_method[T, Self](method: Callable[[Self], T]):
34
+ def memoized_method[T, I](method: Callable[[I], T]):
35
35
  return MemoizedMethod(method)
36
36
 
37
37
 
@@ -1,5 +1,7 @@
1
+ from collections import defaultdict
1
2
  from collections.abc import Callable, Mapping, MutableMapping
2
3
  from functools import partial
4
+ from typing import Self, overload
3
5
  from weakref import WeakKeyDictionary
4
6
 
5
7
  from .primitives import BaseComputation, Batch, Signal, Subscribable
@@ -14,53 +16,69 @@ class Memoized[T](Subscribable, BaseComputation[T]):
14
16
  self._recompute = False
15
17
 
16
18
  def trigger(self):
19
+ self.track()
17
20
  if self._recompute:
21
+ self._recompute = False
18
22
  self._before()
19
- self.cached_value = self.fn()
20
- self._after()
21
- self.is_stale = False
22
- elif not self.is_stale:
23
- del self.cached_value
24
- self.is_stale = True
25
- self.track()
23
+ try:
24
+ self.cached_value = self.fn()
25
+ self.is_stale = False
26
+ finally:
27
+ self._after()
28
+ else:
29
+ self.invalidate()
26
30
 
27
31
  def __call__(self):
28
32
  if self.is_stale:
29
33
  self._recompute = True
30
34
  self.trigger()
31
- self._recompute = False
35
+ assert not self._recompute
32
36
  return self.cached_value
33
37
 
34
38
  def invalidate(self):
35
- del self.cached_value
36
- self.is_stale = True
39
+ if not self.is_stale:
40
+ del self.cached_value
41
+ self.is_stale = True
37
42
 
38
43
 
39
- class MemoizedProperty[T, Self]:
40
- def __init__(self, method: Callable[[Self], T]):
44
+ class MemoizedProperty[T, I]:
45
+ def __init__(self, method: Callable[[I], T]):
41
46
  super().__init__()
42
47
  self.method = method
43
- self.map = WeakKeyDictionary[Self, Memoized]()
48
+ self.map = WeakKeyDictionary[I, Memoized[T]]()
49
+
50
+ @overload
51
+ def __get__(self, instance: None, owner: type[I]) -> Self: ...
52
+ @overload
53
+ def __get__(self, instance: I, owner: type[I]) -> T: ...
44
54
 
45
- def __get__(self, instance, owner):
55
+ def __get__(self, instance: I | None, owner):
56
+ if instance is None:
57
+ return self
46
58
  if func := self.map.get(instance):
47
59
  return func()
48
60
  self.map[instance] = func = Memoized(partial(self.method, instance))
49
61
  return func()
50
62
 
51
63
 
52
- class MemoizedMethod[T, Self]:
53
- def __init__(self, method: Callable[[Self], T]):
64
+ class MemoizedMethod[T, I]:
65
+ def __init__(self, method: Callable[[I], T]):
54
66
  super().__init__()
55
67
  self.method = method
56
- self.map = WeakKeyDictionary[Self, Memoized]()
68
+ self.map = WeakKeyDictionary[I, Memoized[T]]()
57
69
 
58
- def __get__(self, instance, owner):
59
- try:
60
- return self.map[instance]
61
- except KeyError:
62
- self.map[instance] = memo = Memoized(partial(self.method, instance))
70
+ @overload
71
+ def __get__(self, instance: None, owner: type[I]) -> Self: ...
72
+ @overload
73
+ def __get__(self, instance: I, owner: type[I]) -> Memoized[T]: ...
74
+
75
+ def __get__(self, instance: I | None, owner):
76
+ if instance is None:
77
+ return self
78
+ if memo := self.map.get(instance):
63
79
  return memo
80
+ self.map[instance] = memo = Memoized(partial(self.method, instance))
81
+ return memo
64
82
 
65
83
 
66
84
  class Reactive[K, V](Subscribable, MutableMapping[K, V]):
@@ -69,24 +87,23 @@ class Reactive[K, V](Subscribable, MutableMapping[K, V]):
69
87
  def __hash__(self):
70
88
  return id(self)
71
89
 
72
- def __init__(self, initial: Mapping | None = None, check_equality=True):
90
+ def _null(self):
91
+ return Signal(self.UNSET, self._check_equality)
92
+
93
+ def __init__(self, initial: Mapping[K, V] | None = None, check_equality=True):
73
94
  super().__init__()
74
- self._signals: dict[K, Signal[V]] = {} if initial is None else {k: Signal(v, check_equality) for k, v in initial.items()}
95
+ self._signals = defaultdict[K, Signal[V]](self._null) if initial is None else defaultdict(self._null, {k: Signal(v, check_equality) for k, v in initial.items()})
75
96
  self._check_equality = check_equality
76
97
 
77
98
  def __getitem__(self, key: K):
78
- value = self._signals.setdefault(key, Signal(self.UNSET, self._check_equality)).get()
99
+ value = self._signals[key].get()
79
100
  if value is self.UNSET:
80
101
  raise KeyError(key)
81
102
  return value
82
103
 
83
104
  def __setitem__(self, key: K, value: V):
84
105
  with Batch():
85
- try:
86
- self._signals[key].set(value)
87
- except KeyError:
88
- self._signals[key] = Signal(value, self._check_equality)
89
- self._signals[key].set(value)
106
+ self._signals[key].set(value)
90
107
  self.notify()
91
108
 
92
109
  def __delitem__(self, key: K):
@@ -95,7 +112,6 @@ class Reactive[K, V](Subscribable, MutableMapping[K, V]):
95
112
  raise KeyError(key)
96
113
  with Batch():
97
114
  state.set(self.UNSET)
98
- state.notify()
99
115
  self.notify()
100
116
 
101
117
  def __iter__(self):
@@ -58,11 +58,14 @@ class ReactiveModule(ModuleType):
58
58
  return self.__file
59
59
  raise AttributeError("file")
60
60
 
61
+ def __load(self):
62
+ code = compile(self.__file.read_text("utf-8"), str(self.__file), "exec", dont_inherit=True)
63
+ exec(code, self.__namespace, self.__namespace_proxy)
64
+
61
65
  @property
62
66
  def load(self):
63
67
  if is_called_in_this_file():
64
- code = compile(self.__file.read_text("utf-8"), str(self.__file), "exec", dont_inherit=True)
65
- return lambda: exec(code, self.__namespace, self.__namespace_proxy)
68
+ return self.__load
66
69
  raise AttributeError("load")
67
70
 
68
71
  def __dir__(self):
@@ -95,7 +98,7 @@ class ReactiveModuleLoader(Loader):
95
98
  namespace = {"__file__": str(self._file), "__spec__": spec, "__loader__": self}
96
99
  if self._is_package:
97
100
  assert self._file.name == "__init__.py"
98
- namespace["__path__"] = [str(self._file.parent.parent)]
101
+ namespace["__path__"] = [str(self._file.parent)]
99
102
  return ReactiveModule(self._file, namespace, spec.name)
100
103
 
101
104
  def exec_module(self, module: ModuleType):
@@ -246,4 +249,4 @@ def cli():
246
249
  SyncReloader(entry, excludes={".venv"}).keep_watching_until_interrupt()
247
250
 
248
251
 
249
- __version__ = "0.0.3"
252
+ __version__ = "0.0.3.2"
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Callable
2
- from typing import Any
2
+ from typing import Any, Self, overload
3
3
  from weakref import WeakKeyDictionary, WeakSet
4
4
 
5
5
 
@@ -79,7 +79,14 @@ class State[T](Signal[T]):
79
79
  self._check_equality = check_equality
80
80
  self.map = WeakKeyDictionary[Any, Signal[T]]()
81
81
 
82
+ @overload
83
+ def __get__(self, instance: None, owner: type) -> Self: ...
84
+ @overload
85
+ def __get__(self, instance: Any, owner: type) -> T: ...
86
+
82
87
  def __get__(self, instance, owner):
88
+ if instance is None:
89
+ return self
83
90
  try:
84
91
  return self.map[instance].get()
85
92
  except KeyError:
@@ -94,13 +101,13 @@ class State[T](Signal[T]):
94
101
  state.set(value)
95
102
 
96
103
 
97
- class Derived[T](BaseComputation[T]):
98
- def __init__(self, fn: Callable[[], T], auto_run=True):
104
+ class Effect[T](BaseComputation[T]):
105
+ def __init__(self, fn: Callable[[], T], call_immediately=True):
99
106
  super().__init__()
100
107
 
101
108
  self._fn = fn
102
109
 
103
- if auto_run:
110
+ if call_immediately:
104
111
  self()
105
112
 
106
113
  def trigger(self):
@@ -1,5 +0,0 @@
1
- from .functional import batch, create_effect, create_memo, create_signal, memoized_method, memoized_property
2
- from .helpers import Reactive
3
- from .primitives import Derived, State
4
-
5
- __all__ = ["Derived", "Reactive", "State", "batch", "create_effect", "create_memo", "create_signal", "memoized_method", "memoized_property"]