ex4nicegui 0.7.1__py3-none-any.whl → 0.8.1__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.
Files changed (50) hide show
  1. ex4nicegui/reactive/EChartsComponent/ECharts.js +2 -3
  2. ex4nicegui/reactive/EChartsComponent/ECharts.py +2 -4
  3. ex4nicegui/reactive/__init__.py +2 -1
  4. ex4nicegui/reactive/base.py +108 -50
  5. ex4nicegui/reactive/mermaid/mermaid.py +2 -5
  6. ex4nicegui/reactive/mixins/backgroundColor.py +4 -8
  7. ex4nicegui/reactive/mixins/disableable.py +4 -8
  8. ex4nicegui/reactive/mixins/flexLayout.py +51 -0
  9. ex4nicegui/reactive/mixins/textColor.py +8 -10
  10. ex4nicegui/reactive/mixins/value_element.py +27 -0
  11. ex4nicegui/reactive/officials/card.py +32 -2
  12. ex4nicegui/reactive/officials/checkbox.py +7 -12
  13. ex4nicegui/reactive/officials/chip.py +11 -1
  14. ex4nicegui/reactive/officials/circular_progress.py +7 -11
  15. ex4nicegui/reactive/officials/color_picker.py +4 -10
  16. ex4nicegui/reactive/officials/column.py +19 -11
  17. ex4nicegui/reactive/officials/date.py +4 -11
  18. ex4nicegui/reactive/officials/dialog.py +4 -11
  19. ex4nicegui/reactive/officials/echarts.py +9 -7
  20. ex4nicegui/reactive/officials/expansion.py +4 -11
  21. ex4nicegui/reactive/officials/input.py +13 -13
  22. ex4nicegui/reactive/officials/knob.py +4 -13
  23. ex4nicegui/reactive/officials/linear_progress.py +6 -11
  24. ex4nicegui/reactive/officials/number.py +7 -11
  25. ex4nicegui/reactive/officials/radio.py +4 -12
  26. ex4nicegui/reactive/officials/row.py +18 -11
  27. ex4nicegui/reactive/officials/slider.py +4 -12
  28. ex4nicegui/reactive/officials/switch.py +5 -12
  29. ex4nicegui/reactive/officials/tab_panels.py +4 -8
  30. ex4nicegui/reactive/officials/tabs.py +4 -9
  31. ex4nicegui/reactive/officials/textarea.py +6 -12
  32. ex4nicegui/reactive/services/reactive_service.py +2 -1
  33. ex4nicegui/reactive/vfor.py +4 -0
  34. ex4nicegui/reactive/view_model.py +147 -6
  35. ex4nicegui/utils/proxy/__init__.py +19 -0
  36. ex4nicegui/utils/proxy/base.py +9 -0
  37. ex4nicegui/utils/proxy/bool.py +58 -0
  38. ex4nicegui/utils/proxy/date.py +88 -0
  39. ex4nicegui/utils/proxy/descriptor.py +126 -0
  40. ex4nicegui/utils/proxy/dict.py +110 -0
  41. ex4nicegui/utils/proxy/float.py +153 -0
  42. ex4nicegui/utils/proxy/int.py +223 -0
  43. ex4nicegui/utils/proxy/list.py +147 -0
  44. ex4nicegui/utils/proxy/string.py +430 -0
  45. ex4nicegui/utils/proxy/utils.py +6 -0
  46. ex4nicegui/utils/signals.py +9 -1
  47. {ex4nicegui-0.7.1.dist-info → ex4nicegui-0.8.1.dist-info}/METADATA +497 -244
  48. {ex4nicegui-0.7.1.dist-info → ex4nicegui-0.8.1.dist-info}/RECORD +50 -37
  49. {ex4nicegui-0.7.1.dist-info → ex4nicegui-0.8.1.dist-info}/LICENSE +0 -0
  50. {ex4nicegui-0.7.1.dist-info → ex4nicegui-0.8.1.dist-info}/WHEEL +0 -0
@@ -10,9 +10,10 @@ from ex4nicegui.utils.signals import (
10
10
  from ex4nicegui.utils.scheduler import next_tick
11
11
  from nicegui import ui, background_tasks, core
12
12
  from .base import BindableUi
13
+ from ex4nicegui.reactive.mixins.value_element import ValueElementMixin
13
14
 
14
15
 
15
- class TabPanelsBindableUi(BindableUi[ui.tab_panels]):
16
+ class TabPanelsBindableUi(BindableUi[ui.tab_panels], ValueElementMixin[bool]):
16
17
  def __init__(
17
18
  self,
18
19
  value: Optional[TMaybeRef[str]] = None,
@@ -50,16 +51,11 @@ class TabPanelsBindableUi(BindableUi[ui.tab_panels]):
50
51
  return self.element.value
51
52
 
52
53
  def bind_prop(self, prop: str, value: TGetterOrReadonlyRef):
53
- if prop == "value":
54
- return self.bind_value(value)
54
+ if ValueElementMixin._bind_specified_props(self, prop, value):
55
+ return self
55
56
 
56
57
  return super().bind_prop(prop, value)
57
58
 
58
- def bind_value(self, value: TGetterOrReadonlyRef):
59
- @self._ui_effect
60
- def _():
61
- self.element.set_value(to_value(value))
62
-
63
59
 
64
60
  class lazy_tab_panel(ui.tab_panel):
65
61
  def __init__(self, name: str) -> None:
@@ -2,14 +2,14 @@ from typing import Any, Callable, Optional
2
2
  from ex4nicegui.reactive.services.reactive_service import ParameterClassifier
3
3
  from ex4nicegui.utils.signals import (
4
4
  TGetterOrReadonlyRef,
5
- to_value,
6
5
  _TMaybeRef as TMaybeRef,
7
6
  )
8
7
  from nicegui import ui
9
8
  from .base import BindableUi
9
+ from ex4nicegui.reactive.mixins.value_element import ValueElementMixin
10
10
 
11
11
 
12
- class TabsBindableUi(BindableUi[ui.tabs]):
12
+ class TabsBindableUi(BindableUi[ui.tabs], ValueElementMixin[str]):
13
13
  def __init__(
14
14
  self,
15
15
  value: Optional[TMaybeRef[str]] = None,
@@ -33,12 +33,7 @@ class TabsBindableUi(BindableUi[ui.tabs]):
33
33
  return self.element.value
34
34
 
35
35
  def bind_prop(self, prop: str, value: TGetterOrReadonlyRef):
36
- if prop == "value":
37
- return self.bind_value(value)
36
+ if ValueElementMixin._bind_specified_props(self, prop, value):
37
+ return self
38
38
 
39
39
  return super().bind_prop(prop, value)
40
-
41
- def bind_value(self, value: TGetterOrReadonlyRef):
42
- @self._ui_effect
43
- def _():
44
- self.element.set_value(to_value(value))
@@ -16,9 +16,10 @@ from ex4nicegui.utils.signals import (
16
16
  from nicegui import ui
17
17
  from nicegui.events import handle_event
18
18
  from .base import BindableUi
19
+ from ex4nicegui.reactive.mixins.value_element import ValueElementMixin
19
20
 
20
21
 
21
- class TextareaBindableUi(BindableUi[ui.textarea]):
22
+ class TextareaBindableUi(BindableUi[ui.textarea], ValueElementMixin[str]):
22
23
  def __init__(
23
24
  self,
24
25
  label: Optional[TMaybeRef[str]] = None,
@@ -26,7 +27,7 @@ class TextareaBindableUi(BindableUi[ui.textarea]):
26
27
  placeholder: Optional[TMaybeRef[str]] = None,
27
28
  value: TMaybeRef[str] = "",
28
29
  on_change: Optional[Callable[..., Any]] = None,
29
- validation: Dict[str, Callable[..., bool]] = {},
30
+ validation: Optional[Dict[str, Callable[..., bool]]] = None,
30
31
  ) -> None:
31
32
  pc = ParameterClassifier(
32
33
  locals(),
@@ -53,18 +54,11 @@ class TextareaBindableUi(BindableUi[ui.textarea]):
53
54
  return self.element.value
54
55
 
55
56
  def bind_prop(self, prop: str, value: TGetterOrReadonlyRef):
56
- if prop == "value":
57
- return self.bind_value(value)
57
+ if ValueElementMixin._bind_specified_props(self, prop, value):
58
+ return self
58
59
 
59
60
  return super().bind_prop(prop, value)
60
61
 
61
- def bind_value(self, value: TGetterOrReadonlyRef[str]):
62
- @self._ui_signal_on(value)
63
- def _():
64
- self.element.set_value(to_value(value))
65
-
66
- return self
67
-
68
62
 
69
63
  class LazyTextareaBindableUi(TextareaBindableUi):
70
64
  def __init__(
@@ -74,7 +68,7 @@ class LazyTextareaBindableUi(TextareaBindableUi):
74
68
  placeholder: Optional[TMaybeRef[str]] = None,
75
69
  value: TMaybeRef[str] = "",
76
70
  on_change: Optional[Callable[..., Any]] = None,
77
- validation: Dict[str, Callable[..., bool]] = {},
71
+ validation: Optional[Dict[str, Callable[..., bool]]] = None,
78
72
  ) -> None:
79
73
  org_value = value
80
74
  is_setter_value = is_setter_ref(value)
@@ -15,6 +15,7 @@ from ex4nicegui.reactive.systems.reactive_system import (
15
15
  convert_kws_ref2value,
16
16
  inject_method,
17
17
  )
18
+ from ex4nicegui.utils.proxy import is_base_type_proxy
18
19
 
19
20
 
20
21
  class ParameterClassifier:
@@ -34,7 +35,7 @@ class ParameterClassifier:
34
35
  exclude.append(extend_kws)
35
36
 
36
37
  self._args: Dict[str, Any] = {
37
- k: v
38
+ k: v._ref if is_base_type_proxy(v) else v
38
39
  for k, v in args.items()
39
40
  if k != "self" and k[0] != "_" and (k not in exclude)
40
41
  }
@@ -28,6 +28,8 @@ from dataclasses import dataclass
28
28
  from signe.core.scope import Scope
29
29
  from ex4nicegui.reactive.systems.object_system import get_attribute
30
30
  from ex4nicegui.reactive.empty import Empty
31
+ from ex4nicegui.utils.proxy import to_ref_if_base_type_proxy
32
+
31
33
 
32
34
  _T = TypeVar("_T")
33
35
  _T_data = Union[List[Any], TGetterOrReadonlyRef[List[Any]], RefWrapper]
@@ -178,6 +180,8 @@ class vfor(Generic[_T]):
178
180
  *,
179
181
  key: Optional[Union[str, Callable[[int, Any], Any]]] = None,
180
182
  ) -> None:
183
+ data = to_ref_if_base_type_proxy(data)
184
+
181
185
  self._data = to_ref_wrapper(lambda: data) if is_reactive(data) else data
182
186
  self._get_key = vfor.index_key
183
187
  self._transition_props = {}
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Callable, Union, Type, TypeVar
2
+ from typing import Any, Callable, List, Optional, Union, Type, TypeVar, overload
3
3
  from ex4nicegui.utils.signals import (
4
4
  deep_ref,
5
5
  is_ref,
@@ -13,8 +13,16 @@ from ex4nicegui.utils.signals import (
13
13
  from ex4nicegui.utils.types import ReadonlyRef
14
14
  from functools import partial
15
15
  from signe.core.reactive import NoProxy
16
+ from ex4nicegui.utils.proxy.descriptor import class_var_setter
17
+ from ex4nicegui.utils.proxy.bool import BoolProxy
18
+ from ex4nicegui.utils.proxy.int import IntProxy
19
+ from ex4nicegui.utils.proxy.float import FloatProxy
20
+ from ex4nicegui.utils.proxy.base import Proxy
21
+ from ex4nicegui.utils.proxy import to_value_if_base_type_proxy
22
+ from ex4nicegui.utils.proxy.descriptor import ProxyDescriptor
16
23
 
17
24
  _CACHED_VARS_FLAG = "__vm_cached__"
25
+ _LIST_VAR_FLAG = "__vm_list_var__"
18
26
 
19
27
  _T = TypeVar("_T")
20
28
 
@@ -31,15 +39,30 @@ class ViewModel(NoProxy):
31
39
  from ex4nicegui import rxui
32
40
 
33
41
  class MyVm(rxui.ViewModel):
34
- count = rxui.var(0)
35
- data = rxui.var(lambda: [1,2,3])
42
+ count = 0
43
+ data = []
44
+ nums = rxui.list_var(lambda: [1, 2, 3])
45
+
46
+ def __init__(self):
47
+ super().__init__()
48
+ self.data = [1, 2, 3]
49
+
50
+ def increment(self):
51
+ self.count += 1
52
+
53
+ def add_data(self):
54
+ self.data.append(4)
55
+
36
56
 
37
57
  vm = MyVm()
38
58
 
39
59
  rxui.label(vm.count)
40
60
  rxui.number(value=vm.count)
41
61
 
42
-
62
+ ui.button("Increment", on_click=vm.increment)
63
+ ui.button("Add Data", on_click=vm.add_data)
64
+ rxui.label(vm.data)
65
+ rxui.label(vm.nums)
43
66
  """
44
67
 
45
68
  def __init__(self):
@@ -49,8 +72,86 @@ class ViewModel(NoProxy):
49
72
  if callable(value) and hasattr(value, _CACHED_VARS_FLAG):
50
73
  setattr(self, name, computed(partial(value, self)))
51
74
 
75
+ def __init_subclass__(cls) -> None:
76
+ need_vars = (
77
+ (name, value)
78
+ for name, value in vars(cls).items()
79
+ if not name.startswith("_")
80
+ )
81
+
82
+ for name, value in need_vars:
83
+ class_var_setter(cls, name, value, _LIST_VAR_FLAG)
84
+
85
+ @overload
86
+ @staticmethod
87
+ def on_refs_changed(vm: ViewModel): ...
88
+
89
+ @overload
90
+ @staticmethod
91
+ def on_refs_changed(
92
+ vm: ViewModel, callback: Optional[Callable[[], Any]] = None
93
+ ): ...
94
+
95
+ @staticmethod
96
+ def on_refs_changed(vm: ViewModel, callback: Optional[Callable[[], Any]] = None):
97
+ if callback is None:
98
+
99
+ def wrapper(fn: Callable[[], Any]):
100
+ return ViewModel.on_refs_changed(vm, fn)
101
+
102
+ return wrapper
103
+
104
+ refs = ViewModel.get_refs(vm)
105
+
106
+ @on(refs, onchanges=True, deep=False)
107
+ def _():
108
+ callback()
109
+
110
+ @staticmethod
111
+ def get_refs(vm: ViewModel):
112
+ """Get all the refs of a ViewModel.
113
+
114
+ Args:
115
+ vm (ViewModel): The ViewModel to get refs from.
116
+
117
+
118
+ """
119
+ var_names = [
120
+ name
121
+ for name, value in vm.__class__.__dict__.items()
122
+ if isinstance(value, ProxyDescriptor)
123
+ ]
124
+
125
+ return [getattr(vm, name) for name in var_names]
126
+
127
+ @staticmethod
128
+ def to_value(value: Union[ViewModel, Proxy, List, dict]):
129
+ """Convert a ViewModel, Proxy, List, or dict to a value.
130
+
131
+ Args:
132
+ value (Union[ViewModel, Proxy, List, dict]): The value to convert.
133
+
134
+ """
135
+ if isinstance(value, list):
136
+ return [ViewModel.to_value(v) for v in value]
137
+
138
+ if isinstance(value, dict):
139
+ return {k: ViewModel.to_value(v) for k, v in value.items()}
140
+
141
+ if isinstance(value, ViewModel):
142
+ return {
143
+ k: ViewModel.to_value(v)
144
+ for k, v in value.__dict__.items()
145
+ if not k.startswith("_")
146
+ }
147
+
148
+ if isinstance(value, Proxy):
149
+ return to_value_if_base_type_proxy(value)
150
+
151
+ return value
152
+
52
153
  @staticmethod
53
- def display(model: Union[ViewModel, Type]):
154
+ def _display(model: Union[ViewModel, Type]):
54
155
  result = to_ref("")
55
156
 
56
157
  watch_refs = _recursive_to_refs(model)
@@ -67,6 +168,18 @@ class ViewModel(NoProxy):
67
168
 
68
169
  return result
69
170
 
171
+ @staticmethod
172
+ def is_bool(value: Any):
173
+ return isinstance(value, bool) or isinstance(value, BoolProxy)
174
+
175
+ @staticmethod
176
+ def is_int(value: Any):
177
+ return isinstance(value, int) or isinstance(value, IntProxy)
178
+
179
+ @staticmethod
180
+ def is_float(value: Any):
181
+ return isinstance(value, float) or isinstance(value, FloatProxy)
182
+
70
183
 
71
184
  def _recursive_to_value(value_or_model):
72
185
  value = to_raw(to_value(value_or_model))
@@ -139,6 +252,34 @@ def var(value: Union[_T_Var_Value, Callable[[], _T_Var_Value]]) -> Ref[_T_Var_Va
139
252
  return deep_ref(value)
140
253
 
141
254
 
255
+ def list_var(factory: Callable[[], List[_T_Var_Value]]) -> List[_T_Var_Value]:
256
+ """Create implicitly proxied reactive variables for lists. Use them just like ordinary lists while maintaining reactivity. Only use within rxui.ViewModel.
257
+
258
+ Args:
259
+ factory (Callable[[], List[_T_Var_Value]]): A factory function that returns a new list.
260
+
261
+ Example:
262
+ .. code-block:: python
263
+ from ex4nicegui import rxui
264
+ class State(rxui.ViewModel):
265
+ data = rxui.list_var(lambda: [1, 2, 3])
266
+
267
+ def append_data(self):
268
+ self.data.append(len(self.data) + 1)
269
+
270
+ def display_data(self):
271
+ return ",".join(map(str, self.data))
272
+
273
+ state = State()
274
+ ui.button("Append", on_click=state.append_data)
275
+ rxui.label(state.display_data)
276
+
277
+ """
278
+ assert callable(factory), "factory must be a callable"
279
+ setattr(factory, _LIST_VAR_FLAG, None)
280
+ return factory # type: ignore
281
+
282
+
142
283
  def cached_var(func: Callable[..., _T]) -> ReadonlyRef[_T]:
143
284
  """A decorator to cache the result of a function. Only use within rxui.ViewModel.
144
285
 
@@ -149,7 +290,7 @@ def cached_var(func: Callable[..., _T]) -> ReadonlyRef[_T]:
149
290
  .. code-block:: python
150
291
  from ex4nicegui import rxui
151
292
  class MyVm(rxui.ViewModel):
152
- name = rxui.var("John")
293
+ name = "John"
153
294
 
154
295
  @rxui.cached_var
155
296
  def uppper_name(self):
@@ -0,0 +1,19 @@
1
+ from .base import Proxy
2
+
3
+
4
+ def is_base_type_proxy(obj):
5
+ return isinstance(obj, Proxy)
6
+
7
+
8
+ def to_ref_if_base_type_proxy(obj):
9
+ if is_base_type_proxy(obj):
10
+ return obj._ref
11
+ else:
12
+ return obj
13
+
14
+
15
+ def to_value_if_base_type_proxy(obj):
16
+ if is_base_type_proxy(obj):
17
+ return obj._ref.value
18
+ else:
19
+ return obj
@@ -0,0 +1,9 @@
1
+ from typing import Any, Protocol
2
+
3
+
4
+ class ProxyProtocol(Protocol):
5
+ _ref: Any
6
+
7
+
8
+ class Proxy(ProxyProtocol):
9
+ pass
@@ -0,0 +1,58 @@
1
+ from typing import Tuple
2
+ from . import utils
3
+ from .base import Proxy
4
+
5
+
6
+ class BoolProxy(Proxy):
7
+ def __init__(self, value: bool):
8
+ from ex4nicegui.utils.signals import to_ref
9
+
10
+ if isinstance(value, (bool, int)):
11
+ self._ref = to_ref(value)
12
+ else:
13
+ raise ValueError("only accepts boolean or integer values")
14
+
15
+ def __bool__(self):
16
+ return self._ref.value
17
+
18
+ def __str__(self):
19
+ return str(self._ref.value)
20
+
21
+ def __eq__(self, other):
22
+ return self._ref.value.__eq__(utils.to_value(other))
23
+
24
+ def __ne__(self, other):
25
+ return self._ref.value.__ne__(utils.to_value(other))
26
+
27
+ def __lt__(self, value) -> bool:
28
+ return self._ref.value.__lt__(bool(utils.to_value(value)))
29
+
30
+ def __le__(self, value) -> bool:
31
+ return self._ref.value.__le__(bool(utils.to_value(value)))
32
+
33
+ def __gt__(self, value) -> bool:
34
+ return self._ref.value.__gt__(bool(utils.to_value(value)))
35
+
36
+ def __ge__(self, value) -> bool:
37
+ return self._ref.value.__ge__(bool(utils.to_value(value)))
38
+
39
+ def __and__(self, other):
40
+ return self._ref.value.__and__(utils.to_value(other))
41
+
42
+ def __or__(self, other):
43
+ return self._ref.value.__or__(utils.to_value(other))
44
+
45
+ def __xor__(self, other):
46
+ return self._ref.value.__xor__(utils.to_value(other))
47
+
48
+ def __rand__(self, value: bool, /) -> bool:
49
+ return self._ref.value.__rand__(value)
50
+
51
+ def __ror__(self, value: bool, /) -> bool:
52
+ return self._ref.value.__ror__(value)
53
+
54
+ def __rxor__(self, value: bool, /) -> bool:
55
+ return self._ref.value.__rxor__(value)
56
+
57
+ def __getnewargs__(self) -> Tuple[int]:
58
+ return (int(self._ref.value),)
@@ -0,0 +1,88 @@
1
+ from . import utils
2
+ import datetime
3
+ from .base import Proxy
4
+
5
+
6
+ class DateProxy(Proxy, datetime.date):
7
+ def __new__(cls, year, month=None, day=None):
8
+ return super().__new__(cls, year, month, day)
9
+
10
+ def __init__(self, year, month, day):
11
+ from ex4nicegui.utils.signals import to_ref
12
+
13
+ self._ref = to_ref(datetime.date(year, month, day))
14
+
15
+ def __str__(self):
16
+ return str(self._ref.value)
17
+
18
+ def ctime(self):
19
+ return self._ref.value.ctime()
20
+
21
+ def strftime(self, fmt):
22
+ return self._ref.value.strftime(fmt)
23
+
24
+ def __format__(self, fmt):
25
+ return self._ref.value.__format__(fmt)
26
+
27
+ def isoformat(self):
28
+ return self._ref.value.isoformat()
29
+
30
+ @property
31
+ def year(self):
32
+ return self._ref.value.year
33
+
34
+ @property
35
+ def month(self):
36
+ return self._ref.value.month
37
+
38
+ @property
39
+ def day(self):
40
+ return self._ref.value.day
41
+
42
+ def timetuple(self):
43
+ return self._ref.value.timetuple()
44
+
45
+ def toordinal(self):
46
+ return self._ref.value.toordinal()
47
+
48
+ def replace(self, year=None, month=None, day=None):
49
+ return self._ref.value.replace(year=year, month=month, day=day)
50
+
51
+ def __eq__(self, other):
52
+ return self._ref.value.__eq__(utils.to_value(other))
53
+
54
+ def __le__(self, other):
55
+ return self._ref.value.__le__(utils.to_value(other))
56
+
57
+ def __lt__(self, other):
58
+ return self._ref.value.__lt__(utils.to_value(other))
59
+
60
+ def __ge__(self, other):
61
+ return self._ref.value.__ge__(utils.to_value(other))
62
+
63
+ def __gt__(self, other):
64
+ return self._ref.value.__gt__(utils.to_value(other))
65
+
66
+ def __hash__(self):
67
+ return self._ref.value.__hash__()
68
+
69
+ def __add__(self, other):
70
+ return self._ref.value.__add__(utils.to_value(other))
71
+
72
+ def __radd__(self, other):
73
+ return self._ref.value.__radd__(utils.to_value(other))
74
+
75
+ def __sub__(self, other):
76
+ return self._ref.value.__sub__(utils.to_value(other))
77
+
78
+ def weekday(self):
79
+ return self._ref.value.weekday()
80
+
81
+ def isoweekday(self):
82
+ return self._ref.value.isoweekday()
83
+
84
+ def isocalendar(self):
85
+ return self._ref.value.isocalendar()
86
+
87
+ def __reduce__(self):
88
+ return self._ref.value.__reduce__()
@@ -0,0 +1,126 @@
1
+ import sys
2
+ from typing import Any, Callable, Dict, List, Optional, Type, TypeVar, Generic
3
+ from .base import ProxyProtocol
4
+ from .int import IntProxy
5
+ from .list import ListProxy
6
+ from .string import StringProxy
7
+ from .float import FloatProxy
8
+ from .bool import BoolProxy
9
+ from .date import DateProxy
10
+ from .dict import DictProxy
11
+ import datetime
12
+ import warnings
13
+ from . import to_value_if_base_type_proxy
14
+
15
+ T = TypeVar("T")
16
+
17
+
18
+ class ProxyDescriptor(Generic[T]):
19
+ def __init__(
20
+ self,
21
+ name: str,
22
+ value: T,
23
+ proxy_builder: Callable[[T], ProxyProtocol],
24
+ proxy_rebuilder: Optional[Callable[[ProxyProtocol], ProxyProtocol]] = None,
25
+ ) -> None:
26
+ self.value = value
27
+ self.name = name
28
+ self._proxy_builder = proxy_builder
29
+ self._proxy_rebuilder = proxy_rebuilder
30
+
31
+ def __get__(self, instance: object, owner: Any):
32
+ if instance is None:
33
+ return self
34
+
35
+ proxy = instance.__dict__.get(self.name, None)
36
+ if proxy is None:
37
+ proxy = self._proxy_builder(self.value)
38
+ instance.__dict__[self.name] = proxy
39
+ else:
40
+ proxy = self._proxy_rebuilder(proxy) if self._proxy_rebuilder else proxy
41
+ proxy._ref.value # type: ignore
42
+ return proxy
43
+
44
+ def __set__(self, instance: object, value: T) -> None:
45
+ value = to_value_if_base_type_proxy(value)
46
+ proxy = instance.__dict__.get(self.name, None)
47
+ if proxy is None:
48
+ proxy = self._proxy_builder(value) # type: ignore
49
+ instance.__dict__[self.name] = proxy
50
+
51
+ proxy._ref.value = value # type: ignore
52
+
53
+
54
+ class IntDescriptor(ProxyDescriptor[int]):
55
+ def __init__(self, name: str, value: int) -> None:
56
+ super().__init__(name, value, IntProxy)
57
+
58
+
59
+ class ListDescriptor(ProxyDescriptor[Callable[[], List]]):
60
+ def __init__(self, name: str, value: Callable[[], List]) -> None:
61
+ super().__init__(name, value, lambda x: ListProxy(x() if callable(x) else x))
62
+
63
+
64
+ class DictDescriptor(ProxyDescriptor[Dict]):
65
+ def __init__(self, name: str, value: Dict) -> None:
66
+ super().__init__(name, value, DictProxy)
67
+
68
+
69
+ class StringDescriptor(ProxyDescriptor[str]):
70
+ def __init__(self, name: str, value: str) -> None:
71
+ def rebuild_proxy(proxy: ProxyProtocol) -> ProxyProtocol:
72
+ sp = StringProxy(proxy._ref.value)
73
+ sp._ref = proxy._ref
74
+ return sp
75
+
76
+ super().__init__(name, value, StringProxy, rebuild_proxy)
77
+
78
+
79
+ class FloatDescriptor(ProxyDescriptor[float]):
80
+ def __init__(self, name: str, value: float) -> None:
81
+ super().__init__(name, value, FloatProxy)
82
+
83
+
84
+ class BoolDescriptor(ProxyDescriptor[bool]):
85
+ def __init__(self, name: str, value: bool) -> None:
86
+ super().__init__(name, value, BoolProxy)
87
+
88
+
89
+ class DateDescriptor(ProxyDescriptor[datetime.date]):
90
+ def __init__(self, name: str, value: datetime.date) -> None:
91
+ super().__init__(name, value, lambda x: DateProxy(x.year, x.month, x.day))
92
+
93
+
94
+ def class_var_setter(cls: Type, name: str, value, list_var_flat: str) -> None:
95
+ if value is None or isinstance(value, str):
96
+ setattr(cls, name, StringDescriptor(name, value))
97
+ elif isinstance(value, bool):
98
+ setattr(cls, name, BoolDescriptor(name, value))
99
+ elif isinstance(value, int):
100
+ setattr(cls, name, IntDescriptor(name, value))
101
+ elif callable(value) and hasattr(value, list_var_flat):
102
+ setattr(cls, name, ListDescriptor(name, value))
103
+
104
+ elif isinstance(value, list):
105
+ if len(value) > 0:
106
+ with warnings.catch_warnings():
107
+ warnings.showwarning = _custom_showwarning
108
+ warnings.warn(
109
+ f"The variable [{cls.__name__}.{name}] will be empty list.you should initialize it in the constructor,or use list_var for definition.\n {name} = rxui.list_var(lambda:[1,2,3])",
110
+ stacklevel=3,
111
+ )
112
+ setattr(cls, name, ListDescriptor(name, lambda: []))
113
+
114
+ elif isinstance(value, dict):
115
+ pass # TODO
116
+ # setattr(cls, name, DictDescriptor(name, value))
117
+ elif isinstance(value, float):
118
+ setattr(cls, name, FloatDescriptor(name, value))
119
+ elif isinstance(value, datetime.date):
120
+ setattr(cls, name, DateDescriptor(name, value))
121
+ else:
122
+ pass
123
+
124
+
125
+ def _custom_showwarning(message, category, filename, lineno, file=None, line=None):
126
+ sys.stderr.write(f"{filename}:{lineno}: {category.__name__}: {message}\n")