violit 0.0.5__tar.gz → 0.0.6__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.
- {violit-0.0.5/src/violit.egg-info → violit-0.0.6}/PKG-INFO +1 -1
- {violit-0.0.5 → violit-0.0.6}/pyproject.toml +1 -1
- {violit-0.0.5 → violit-0.0.6}/src/violit/component.py +38 -38
- violit-0.0.6/src/violit/state.py +310 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/chart_widgets.py +253 -253
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/data_widgets.py +27 -9
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/input_widgets.py +745 -745
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/status_widgets.py +308 -255
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/text_widgets.py +154 -109
- {violit-0.0.5 → violit-0.0.6/src/violit.egg-info}/PKG-INFO +1 -1
- violit-0.0.5/src/violit/state.py +0 -109
- {violit-0.0.5 → violit-0.0.6}/LICENSE +0 -0
- {violit-0.0.5 → violit-0.0.6}/README.md +0 -0
- {violit-0.0.5 → violit-0.0.6}/setup.cfg +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/__init__.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/app.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/broadcast.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/broadcast_primitives.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/context.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/engine.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/theme.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/__init__.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/card_widgets.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/chat_widgets.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/form_widgets.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/layout_widgets.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/list_widgets.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit/widgets/media_widgets.py +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit.egg-info/SOURCES.txt +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit.egg-info/dependency_links.txt +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit.egg-info/requires.txt +0 -0
- {violit-0.0.5 → violit-0.0.6}/src/violit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: violit
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Violit: Faster than Light, Beautiful as Violet. The High-Performance Python Web Framework (Streamlit Alternative with Zero Rerun).
|
|
5
5
|
Author-email: Violit Team <violit.company@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "violit"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.6"
|
|
8
8
|
description = "Violit: Faster than Light, Beautiful as Violet. The High-Performance Python Web Framework (Streamlit Alternative with Zero Rerun)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
from typing import Any, Optional
|
|
2
|
-
import html
|
|
3
|
-
|
|
4
|
-
class Component:
|
|
5
|
-
def __init__(self, tag: Optional[str], id: str, escape_content: bool = False, **props):
|
|
6
|
-
self.tag = tag
|
|
7
|
-
self.id = id
|
|
8
|
-
self.escape_content = escape_content # XSS protection
|
|
9
|
-
self.props = props
|
|
10
|
-
|
|
11
|
-
def render(self) -> str:
|
|
12
|
-
if self.tag is None:
|
|
13
|
-
content = str(self.props.get('content', ''))
|
|
14
|
-
# Escape content if enabled
|
|
15
|
-
return html.escape(content) if self.escape_content else content
|
|
16
|
-
|
|
17
|
-
attrs = []
|
|
18
|
-
for k, v in self.props.items():
|
|
19
|
-
if k == 'content': continue
|
|
20
|
-
clean_k = k.replace('_', '-') if not k.startswith('on') else k
|
|
21
|
-
if clean_k.startswith('on'):
|
|
22
|
-
attrs.append(f'{clean_k}="{v}"')
|
|
23
|
-
else:
|
|
24
|
-
if v is True: attrs.append(clean_k)
|
|
25
|
-
elif v is False or v is None: continue
|
|
26
|
-
else:
|
|
27
|
-
# Escape attribute values for XSS protection
|
|
28
|
-
escaped_v = html.escape(str(v), quote=True)
|
|
29
|
-
attrs.append(f'{clean_k}="{escaped_v}"')
|
|
30
|
-
|
|
31
|
-
props_str = " ".join(attrs)
|
|
32
|
-
content = self.props.get('content', '')
|
|
33
|
-
|
|
34
|
-
# Escape content if enabled
|
|
35
|
-
if self.escape_content:
|
|
36
|
-
content = html.escape(str(content))
|
|
37
|
-
|
|
38
|
-
return f"<{self.tag} id=\"{self.id}\" {props_str}>{content}</{self.tag}>"
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
import html
|
|
3
|
+
|
|
4
|
+
class Component:
|
|
5
|
+
def __init__(self, tag: Optional[str], id: str, escape_content: bool = False, **props):
|
|
6
|
+
self.tag = tag
|
|
7
|
+
self.id = id
|
|
8
|
+
self.escape_content = escape_content # XSS protection
|
|
9
|
+
self.props = props
|
|
10
|
+
|
|
11
|
+
def render(self) -> str:
|
|
12
|
+
if self.tag is None:
|
|
13
|
+
content = str(self.props.get('content', ''))
|
|
14
|
+
# Escape content if enabled
|
|
15
|
+
return html.escape(content) if self.escape_content else content
|
|
16
|
+
|
|
17
|
+
attrs = []
|
|
18
|
+
for k, v in self.props.items():
|
|
19
|
+
if k == 'content': continue
|
|
20
|
+
clean_k = k.replace('_', '-') if not k.startswith('on') else k
|
|
21
|
+
if clean_k.startswith('on'):
|
|
22
|
+
attrs.append(f'{clean_k}="{v}"')
|
|
23
|
+
else:
|
|
24
|
+
if v is True: attrs.append(clean_k)
|
|
25
|
+
elif v is False or v is None: continue
|
|
26
|
+
else:
|
|
27
|
+
# Escape attribute values for XSS protection
|
|
28
|
+
escaped_v = html.escape(str(v), quote=True)
|
|
29
|
+
attrs.append(f'{clean_k}="{escaped_v}"')
|
|
30
|
+
|
|
31
|
+
props_str = " ".join(attrs)
|
|
32
|
+
content = self.props.get('content', '')
|
|
33
|
+
|
|
34
|
+
# Escape content if enabled
|
|
35
|
+
if self.escape_content:
|
|
36
|
+
content = html.escape(str(content))
|
|
37
|
+
|
|
38
|
+
return f"<{self.tag} id=\"{self.id}\" {props_str}>{content}</{self.tag}>"
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
from typing import Any, Dict, Set
|
|
2
|
+
from cachetools import TTLCache
|
|
3
|
+
from .context import session_ctx, rendering_ctx, app_instance_ref
|
|
4
|
+
from .theme import Theme
|
|
5
|
+
|
|
6
|
+
class DependencyTracker:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.subscribers: Dict[str, Set[str]] = {}
|
|
9
|
+
|
|
10
|
+
def register_dependency(self, state_name: str, component_id: str):
|
|
11
|
+
if state_name not in self.subscribers:
|
|
12
|
+
self.subscribers[state_name] = set()
|
|
13
|
+
self.subscribers[state_name].add(component_id)
|
|
14
|
+
|
|
15
|
+
def get_dirty_components(self, state_name: str) -> Set[str]:
|
|
16
|
+
return self.subscribers.get(state_name, set())
|
|
17
|
+
|
|
18
|
+
GLOBAL_STORE = TTLCache(maxsize=1000, ttl=1800)
|
|
19
|
+
|
|
20
|
+
def get_session_store():
|
|
21
|
+
sid = session_ctx.get()
|
|
22
|
+
if sid not in GLOBAL_STORE:
|
|
23
|
+
initial_theme = 'light'
|
|
24
|
+
if app_instance_ref[0]:
|
|
25
|
+
initial_theme = app_instance_ref[0].theme_manager.preset_name
|
|
26
|
+
|
|
27
|
+
GLOBAL_STORE[sid] = {
|
|
28
|
+
'states': {},
|
|
29
|
+
'tracker': DependencyTracker(),
|
|
30
|
+
'builders': {},
|
|
31
|
+
'actions': {},
|
|
32
|
+
'component_count': 0,
|
|
33
|
+
'fragment_components': {},
|
|
34
|
+
'order': [],
|
|
35
|
+
'sidebar_order': [],
|
|
36
|
+
'theme': Theme(initial_theme)
|
|
37
|
+
}
|
|
38
|
+
return GLOBAL_STORE[sid]
|
|
39
|
+
|
|
40
|
+
class State:
|
|
41
|
+
def __init__(self, name: str, default_value: Any):
|
|
42
|
+
object.__setattr__(self, 'name', name)
|
|
43
|
+
object.__setattr__(self, 'default_value', default_value)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def value(self):
|
|
47
|
+
store = get_session_store()
|
|
48
|
+
current_comp_id = rendering_ctx.get()
|
|
49
|
+
if current_comp_id:
|
|
50
|
+
store['tracker'].register_dependency(self.name, current_comp_id)
|
|
51
|
+
return store['states'].get(self.name, self.default_value)
|
|
52
|
+
|
|
53
|
+
@value.setter
|
|
54
|
+
def value(self, new_value: Any):
|
|
55
|
+
self.set(new_value)
|
|
56
|
+
|
|
57
|
+
def set(self, new_value: Any):
|
|
58
|
+
store = get_session_store()
|
|
59
|
+
store['states'][self.name] = new_value
|
|
60
|
+
if 'dirty_states' not in store: store['dirty_states'] = set()
|
|
61
|
+
store['dirty_states'].add(self.name)
|
|
62
|
+
|
|
63
|
+
def __setattr__(self, attr: str, val: Any):
|
|
64
|
+
if attr == 'value':
|
|
65
|
+
self.set(val)
|
|
66
|
+
else:
|
|
67
|
+
object.__setattr__(self, attr, val)
|
|
68
|
+
|
|
69
|
+
def __str__(self):
|
|
70
|
+
return str(self.value)
|
|
71
|
+
|
|
72
|
+
def __call__(self):
|
|
73
|
+
return self.value
|
|
74
|
+
|
|
75
|
+
def __repr__(self):
|
|
76
|
+
return f"State({self.name}, {self.value})"
|
|
77
|
+
|
|
78
|
+
# Reactive Comparison Operators
|
|
79
|
+
def __eq__(self, other): return ComputedState(lambda: self.value == other)
|
|
80
|
+
def __ne__(self, other): return ComputedState(lambda: self.value != other)
|
|
81
|
+
def __lt__(self, other): return ComputedState(lambda: self.value < other)
|
|
82
|
+
def __le__(self, other): return ComputedState(lambda: self.value <= other)
|
|
83
|
+
def __gt__(self, other): return ComputedState(lambda: self.value > other)
|
|
84
|
+
def __ge__(self, other): return ComputedState(lambda: self.value >= other)
|
|
85
|
+
|
|
86
|
+
# Reactive Arithmetic Operators
|
|
87
|
+
def __add__(self, other):
|
|
88
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
89
|
+
def compute():
|
|
90
|
+
self_val = self.value
|
|
91
|
+
# If either is a string, concatenate as strings
|
|
92
|
+
if isinstance(self_val, str) or isinstance(other_val, str):
|
|
93
|
+
return str(self_val) + str(other_val)
|
|
94
|
+
else:
|
|
95
|
+
return self_val + other_val
|
|
96
|
+
return ComputedState(compute)
|
|
97
|
+
|
|
98
|
+
def __radd__(self, other):
|
|
99
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
100
|
+
def compute():
|
|
101
|
+
self_val = self.value
|
|
102
|
+
# If either is a string, concatenate as strings
|
|
103
|
+
if isinstance(self_val, str) or isinstance(other_val, str):
|
|
104
|
+
return str(other_val) + str(self_val)
|
|
105
|
+
else:
|
|
106
|
+
return other_val + self_val
|
|
107
|
+
return ComputedState(compute)
|
|
108
|
+
|
|
109
|
+
def __sub__(self, other):
|
|
110
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
111
|
+
return ComputedState(lambda: self.value - other_val)
|
|
112
|
+
|
|
113
|
+
def __rsub__(self, other):
|
|
114
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
115
|
+
return ComputedState(lambda: other_val - self.value)
|
|
116
|
+
|
|
117
|
+
def __mul__(self, other):
|
|
118
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
119
|
+
def compute():
|
|
120
|
+
self_val = self.value
|
|
121
|
+
# String repetition: "ab" * 3 or 3 * "ab"
|
|
122
|
+
if isinstance(self_val, str) or isinstance(other_val, str):
|
|
123
|
+
# One must be int for string repetition to work
|
|
124
|
+
if isinstance(self_val, str) and isinstance(other_val, int):
|
|
125
|
+
return self_val * other_val
|
|
126
|
+
elif isinstance(other_val, str) and isinstance(self_val, int):
|
|
127
|
+
return self_val * other_val
|
|
128
|
+
else:
|
|
129
|
+
# Both strings? Convert to numeric or error
|
|
130
|
+
return self_val * other_val
|
|
131
|
+
else:
|
|
132
|
+
return self_val * other_val
|
|
133
|
+
return ComputedState(compute)
|
|
134
|
+
|
|
135
|
+
def __rmul__(self, other):
|
|
136
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
137
|
+
def compute():
|
|
138
|
+
self_val = self.value
|
|
139
|
+
# String repetition: "ab" * 3 or 3 * "ab"
|
|
140
|
+
if isinstance(self_val, str) or isinstance(other_val, str):
|
|
141
|
+
if isinstance(other_val, str) and isinstance(self_val, int):
|
|
142
|
+
return other_val * self_val
|
|
143
|
+
elif isinstance(self_val, str) and isinstance(other_val, int):
|
|
144
|
+
return other_val * self_val
|
|
145
|
+
else:
|
|
146
|
+
return other_val * self_val
|
|
147
|
+
else:
|
|
148
|
+
return other_val * self_val
|
|
149
|
+
return ComputedState(compute)
|
|
150
|
+
|
|
151
|
+
def __truediv__(self, other):
|
|
152
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
153
|
+
return ComputedState(lambda: self.value / other_val)
|
|
154
|
+
|
|
155
|
+
def __rtruediv__(self, other):
|
|
156
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
157
|
+
return ComputedState(lambda: other_val / self.value)
|
|
158
|
+
|
|
159
|
+
def __floordiv__(self, other):
|
|
160
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
161
|
+
return ComputedState(lambda: self.value // other_val)
|
|
162
|
+
|
|
163
|
+
def __rfloordiv__(self, other):
|
|
164
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
165
|
+
return ComputedState(lambda: other_val // self.value)
|
|
166
|
+
|
|
167
|
+
def __mod__(self, other):
|
|
168
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
169
|
+
return ComputedState(lambda: self.value % other_val)
|
|
170
|
+
|
|
171
|
+
def __rmod__(self, other):
|
|
172
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
173
|
+
return ComputedState(lambda: other_val % self.value)
|
|
174
|
+
|
|
175
|
+
def __pow__(self, other):
|
|
176
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
177
|
+
return ComputedState(lambda: self.value ** other_val)
|
|
178
|
+
|
|
179
|
+
def __rpow__(self, other):
|
|
180
|
+
other_val = other.value if isinstance(other, (State, ComputedState)) else other
|
|
181
|
+
return ComputedState(lambda: other_val ** self.value)
|
|
182
|
+
|
|
183
|
+
# String formatting support
|
|
184
|
+
def __format__(self, format_spec):
|
|
185
|
+
"""Support for f-string formatting: f'{state:03d}'"""
|
|
186
|
+
return format(self.value, format_spec)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class ComputedState:
|
|
190
|
+
"""A state derived from other states (e.g. expressions)"""
|
|
191
|
+
def __init__(self, func):
|
|
192
|
+
self.func = func
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def value(self):
|
|
196
|
+
return self.func()
|
|
197
|
+
|
|
198
|
+
def __bool__(self):
|
|
199
|
+
return bool(self.value)
|
|
200
|
+
|
|
201
|
+
def __call__(self):
|
|
202
|
+
return self.value
|
|
203
|
+
|
|
204
|
+
# Logical operators for chaining
|
|
205
|
+
def __and__(self, other):
|
|
206
|
+
val_other = other.value if hasattr(other, 'value') else other
|
|
207
|
+
return ComputedState(lambda: self.value and val_other)
|
|
208
|
+
|
|
209
|
+
def __or__(self, other):
|
|
210
|
+
val_other = other.value if hasattr(other, 'value') else other
|
|
211
|
+
return ComputedState(lambda: self.value or val_other)
|
|
212
|
+
|
|
213
|
+
# Reactive Arithmetic Operators (Mirroring State)
|
|
214
|
+
def __add__(self, other):
|
|
215
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
216
|
+
def compute():
|
|
217
|
+
self_val = self.value
|
|
218
|
+
# If either is a string, concatenate as strings
|
|
219
|
+
if isinstance(self_val, str) or isinstance(other_val, str):
|
|
220
|
+
return str(self_val) + str(other_val)
|
|
221
|
+
else:
|
|
222
|
+
return self_val + other_val
|
|
223
|
+
return ComputedState(compute)
|
|
224
|
+
|
|
225
|
+
def __radd__(self, other):
|
|
226
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
227
|
+
def compute():
|
|
228
|
+
self_val = self.value
|
|
229
|
+
# If either is a string, concatenate as strings
|
|
230
|
+
if isinstance(self_val, str) or isinstance(other_val, str):
|
|
231
|
+
return str(other_val) + str(self_val)
|
|
232
|
+
else:
|
|
233
|
+
return other_val + self_val
|
|
234
|
+
return ComputedState(compute)
|
|
235
|
+
|
|
236
|
+
def __sub__(self, other):
|
|
237
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
238
|
+
return ComputedState(lambda: self.value - other_val)
|
|
239
|
+
|
|
240
|
+
def __rsub__(self, other):
|
|
241
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
242
|
+
return ComputedState(lambda: other_val - self.value)
|
|
243
|
+
|
|
244
|
+
def __mul__(self, other):
|
|
245
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
246
|
+
def compute():
|
|
247
|
+
self_val = self.value
|
|
248
|
+
# String repetition: "ab" * 3 or 3 * "ab"
|
|
249
|
+
if isinstance(self_val, str) or isinstance(other_val, str):
|
|
250
|
+
if isinstance(self_val, str) and isinstance(other_val, int):
|
|
251
|
+
return self_val * other_val
|
|
252
|
+
elif isinstance(other_val, str) and isinstance(self_val, int):
|
|
253
|
+
return self_val * other_val
|
|
254
|
+
else:
|
|
255
|
+
return self_val * other_val
|
|
256
|
+
else:
|
|
257
|
+
return self_val * other_val
|
|
258
|
+
return ComputedState(compute)
|
|
259
|
+
|
|
260
|
+
def __rmul__(self, other):
|
|
261
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
262
|
+
def compute():
|
|
263
|
+
self_val = self.value
|
|
264
|
+
# String repetition: "ab" * 3 or 3 * "ab"
|
|
265
|
+
if isinstance(self_val, str) or isinstance(other_val, str):
|
|
266
|
+
if isinstance(other_val, str) and isinstance(self_val, int):
|
|
267
|
+
return other_val * self_val
|
|
268
|
+
elif isinstance(self_val, str) and isinstance(other_val, int):
|
|
269
|
+
return other_val * self_val
|
|
270
|
+
else:
|
|
271
|
+
return other_val * self_val
|
|
272
|
+
else:
|
|
273
|
+
return other_val * self_val
|
|
274
|
+
return ComputedState(compute)
|
|
275
|
+
|
|
276
|
+
def __truediv__(self, other):
|
|
277
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
278
|
+
return ComputedState(lambda: self.value / other_val)
|
|
279
|
+
|
|
280
|
+
def __rtruediv__(self, other):
|
|
281
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
282
|
+
return ComputedState(lambda: other_val / self.value)
|
|
283
|
+
|
|
284
|
+
def __floordiv__(self, other):
|
|
285
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
286
|
+
return ComputedState(lambda: self.value // other_val)
|
|
287
|
+
|
|
288
|
+
def __rfloordiv__(self, other):
|
|
289
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
290
|
+
return ComputedState(lambda: other_val // self.value)
|
|
291
|
+
|
|
292
|
+
def __mod__(self, other):
|
|
293
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
294
|
+
return ComputedState(lambda: self.value % other_val)
|
|
295
|
+
|
|
296
|
+
def __rmod__(self, other):
|
|
297
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
298
|
+
return ComputedState(lambda: other_val % self.value)
|
|
299
|
+
|
|
300
|
+
def __pow__(self, other):
|
|
301
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
302
|
+
return ComputedState(lambda: self.value ** other_val)
|
|
303
|
+
|
|
304
|
+
def __rpow__(self, other):
|
|
305
|
+
other_val = other.value if hasattr(other, 'value') else other
|
|
306
|
+
return ComputedState(lambda: other_val ** self.value)
|
|
307
|
+
|
|
308
|
+
# String formatting support
|
|
309
|
+
def __format__(self, format_spec):
|
|
310
|
+
return format(self.value, format_spec)
|