nova-mvvm 0.13.1__py3-none-any.whl → 0.14.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.
- nova/mvvm/_internal/pydantic_utils.py +1 -1
- nova/mvvm/_internal/pyqt_communicator.py +4 -2
- nova/mvvm/_internal/utils.py +8 -0
- nova/mvvm/panel_binding/binding.py +3 -1
- nova/mvvm/trame_binding/binding.py +16 -1
- {nova_mvvm-0.13.1.dist-info → nova_mvvm-0.14.0.dist-info}/METADATA +2 -2
- {nova_mvvm-0.13.1.dist-info → nova_mvvm-0.14.0.dist-info}/RECORD +9 -9
- {nova_mvvm-0.13.1.dist-info → nova_mvvm-0.14.0.dist-info}/WHEEL +0 -0
- {nova_mvvm-0.13.1.dist-info → nova_mvvm-0.14.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -83,6 +83,6 @@ def get_nested_pydantic_field(model: BaseModel, field_path: str) -> FieldInfo:
|
|
|
83
83
|
if issubclass(type(getattr(current_model, field)), BaseModel):
|
|
84
84
|
current_model = getattr(current_model, field)
|
|
85
85
|
else:
|
|
86
|
-
return current_model.model_fields[field]
|
|
86
|
+
return current_model.__class__.model_fields[field]
|
|
87
87
|
|
|
88
88
|
raise Exception(f"Cannot find field {field_path}")
|
|
@@ -7,7 +7,7 @@ from pydantic import BaseModel, ValidationError
|
|
|
7
7
|
from typing_extensions import override
|
|
8
8
|
|
|
9
9
|
from .._internal.pydantic_utils import get_errored_fields_from_validation_error, get_updated_fields
|
|
10
|
-
from .._internal.utils import check_binding, rsetattr
|
|
10
|
+
from .._internal.utils import check_binding, check_model_type, rsetattr
|
|
11
11
|
from ..bindings_map import bindings_map
|
|
12
12
|
from ..interface import Communicator, ConnectCallbackType
|
|
13
13
|
|
|
@@ -84,6 +84,8 @@ class PyQtCommunicator(Communicator):
|
|
|
84
84
|
return None
|
|
85
85
|
|
|
86
86
|
@override
|
|
87
|
-
def update_in_view(self, value: Any) ->
|
|
87
|
+
def update_in_view(self, value: Any) -> None:
|
|
88
88
|
"""Update a View (GUI) when called by a ViewModel."""
|
|
89
|
+
if issubclass(type(value), BaseModel):
|
|
90
|
+
check_model_type(self.viewmodel_linked_object, value)
|
|
89
91
|
return self.pyqtobject.signal.emit(value)
|
nova/mvvm/_internal/utils.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Internal common functions tp be used within the package."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
+
from types import NoneType
|
|
4
5
|
from typing import Any, Dict
|
|
5
6
|
|
|
6
7
|
from nova.mvvm import bindings_map
|
|
@@ -98,3 +99,10 @@ def check_binding(linked_object: LinkedObjectType, name: str) -> None:
|
|
|
98
99
|
for communicator in bindings_map.values():
|
|
99
100
|
if communicator.viewmodel_linked_object and communicator.viewmodel_linked_object is linked_object:
|
|
100
101
|
raise ValueError(f"cannot connect to binding {name}: object already connected")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def check_model_type(old_value: Any, new_value: Any) -> None:
|
|
105
|
+
old_type = type(old_value)
|
|
106
|
+
new_type = type(new_value)
|
|
107
|
+
if old_type is not NoneType and old_type is not new_type:
|
|
108
|
+
raise TypeError(f"update_in_view expected a value of type '{old_type}' but received '{new_type}'.")
|
|
@@ -9,7 +9,7 @@ from typing_extensions import override
|
|
|
9
9
|
|
|
10
10
|
from .. import bindings_map
|
|
11
11
|
from .._internal.pydantic_utils import get_errored_fields_from_validation_error, get_updated_fields
|
|
12
|
-
from .._internal.utils import check_binding, rgetattr, rsetattr
|
|
12
|
+
from .._internal.utils import check_binding, check_model_type, rgetattr, rsetattr
|
|
13
13
|
from ..interface import BindingInterface, ConnectCallbackType, Worker
|
|
14
14
|
|
|
15
15
|
|
|
@@ -114,6 +114,8 @@ class Communicator:
|
|
|
114
114
|
|
|
115
115
|
# Update the view based on the provided value
|
|
116
116
|
def update_in_view(self, value: Any) -> None:
|
|
117
|
+
if issubclass(type(value), BaseModel):
|
|
118
|
+
check_model_type(self.viewmodel_linked_object, value)
|
|
117
119
|
if is_callable(self.connector):
|
|
118
120
|
cast(Callable, self.connector)(value)
|
|
119
121
|
elif self.viewmodel_linked_object:
|
|
@@ -10,7 +10,14 @@ from trame_server.state import State
|
|
|
10
10
|
from typing_extensions import override
|
|
11
11
|
|
|
12
12
|
from .._internal.pydantic_utils import get_errored_fields_from_validation_error, get_updated_fields
|
|
13
|
-
from .._internal.utils import
|
|
13
|
+
from .._internal.utils import (
|
|
14
|
+
check_binding,
|
|
15
|
+
check_model_type,
|
|
16
|
+
normalize_field_name,
|
|
17
|
+
rget_list_of_fields,
|
|
18
|
+
rgetattr,
|
|
19
|
+
rsetattr,
|
|
20
|
+
)
|
|
14
21
|
from ..bindings_map import bindings_map
|
|
15
22
|
from ..interface import (
|
|
16
23
|
BindingInterface,
|
|
@@ -132,6 +139,8 @@ class CallBackConnection:
|
|
|
132
139
|
self.viewmodel_callback_after_update({"updated": updates, "errored": errors, "error": None})
|
|
133
140
|
|
|
134
141
|
def update_in_view(self, value: Any) -> None:
|
|
142
|
+
if issubclass(type(value), BaseModel):
|
|
143
|
+
check_model_type(self.viewmodel_linked_object, value)
|
|
135
144
|
self.callback(value)
|
|
136
145
|
|
|
137
146
|
def get_callback(self) -> ConnectCallbackType:
|
|
@@ -142,6 +151,7 @@ class StateConnection:
|
|
|
142
151
|
"""Connection that uses a state variable."""
|
|
143
152
|
|
|
144
153
|
def __init__(self, communicator: TrameCommunicator, state_variable_name: Optional[str]) -> None:
|
|
154
|
+
self.has_errors = False
|
|
145
155
|
self.state_variable_name = state_variable_name
|
|
146
156
|
self.communicator = communicator
|
|
147
157
|
self.state = communicator.state
|
|
@@ -226,6 +236,7 @@ class StateConnection:
|
|
|
226
236
|
errors = get_errored_fields_from_validation_error(e)
|
|
227
237
|
error = e
|
|
228
238
|
updated = True
|
|
239
|
+
self.has_errors = True
|
|
229
240
|
elif isinstance(self.viewmodel_linked_object, dict):
|
|
230
241
|
self.viewmodel_linked_object.update(kwargs[state_variable_name])
|
|
231
242
|
updates.append(state_variable_name)
|
|
@@ -234,11 +245,15 @@ class StateConnection:
|
|
|
234
245
|
updates.append(state_variable_name)
|
|
235
246
|
else:
|
|
236
247
|
raise Exception("cannot update", self.viewmodel_linked_object)
|
|
248
|
+
if self.has_errors and not errors:
|
|
249
|
+
updated = True
|
|
250
|
+
self.has_errors = False
|
|
237
251
|
if updated:
|
|
238
252
|
await self._handle_callback({"updated": updates, "errored": errors, "error": error})
|
|
239
253
|
|
|
240
254
|
def update_in_view(self, value: Any) -> None:
|
|
241
255
|
if issubclass(type(value), BaseModel):
|
|
256
|
+
check_model_type(self.viewmodel_linked_object, value)
|
|
242
257
|
value = value.model_dump()
|
|
243
258
|
if self.linked_object_attributes:
|
|
244
259
|
for attribute_name in self.linked_object_attributes:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nova-mvvm
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: A Python Package for Model-View-ViewModel pattern
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Sergey Yakubov <yakubovs@ornl.gov>, John Duggan <dugganjw@ornl.gov>, Greg Watson <watsongr@ornl.gov>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Keywords: MVVM,python
|
|
@@ -3,11 +3,11 @@ nova/mvvm/__init__.py,sha256=fo1bT27jjUYzVoFJXuwokXcyFtOYj8eQ_BHkPgtI4Rg,149
|
|
|
3
3
|
nova/mvvm/bindings_map.py,sha256=SIVNPGHxDf91P9UxcPmuAHx1GNxZk66JUc-fYPcp-kc,417
|
|
4
4
|
nova/mvvm/interface.py,sha256=l4l339BiB46s6KX-2RH1Q3Hxc8pKYdCvCOkhse4zOkw,5812
|
|
5
5
|
nova/mvvm/pydantic_utils.py,sha256=kQ17-Thp_4FMQca6nJ_UNWbctssqHGY3FtZolUIZGQ0,3191
|
|
6
|
-
nova/mvvm/_internal/pydantic_utils.py,sha256=
|
|
7
|
-
nova/mvvm/_internal/pyqt_communicator.py,sha256=
|
|
8
|
-
nova/mvvm/_internal/utils.py,sha256=
|
|
6
|
+
nova/mvvm/_internal/pydantic_utils.py,sha256=anYYyB1Bkg8GgWB3Jn-6iRit0PYR-_2w3paKsD9z4sY,2947
|
|
7
|
+
nova/mvvm/_internal/pyqt_communicator.py,sha256=G_Srttxn7EEwz9EageFxnOgg_P-gfPPhNpoR55btHd0,3929
|
|
8
|
+
nova/mvvm/_internal/utils.py,sha256=LpZ3LALcB9BzG1yIh0Qt0kBXGdctacDMVmSm8H8Py1c,3626
|
|
9
9
|
nova/mvvm/panel_binding/__init__.py,sha256=HHDlUyP-LuRePcHS1s8tq3niuG3f3n8gdSRb7Rwtc4o,100
|
|
10
|
-
nova/mvvm/panel_binding/binding.py,sha256=
|
|
10
|
+
nova/mvvm/panel_binding/binding.py,sha256=rLkxnqjLpZvvRHJVx8PLfJpLSEg3Q0FSRPov81o8e6Y,6483
|
|
11
11
|
nova/mvvm/pyqt5_binding/__init__.py,sha256=JbKOFJNIObJReotRHlN5-vi7oSMyc_v8K_uHjeJLcHg,62
|
|
12
12
|
nova/mvvm/pyqt5_binding/binding.py,sha256=Nki03KgafysfJFQ86evYvL8lprbrYNajFNgfihk7KA4,1462
|
|
13
13
|
nova/mvvm/pyqt5_binding/pyqt5_worker.py,sha256=bf0Yj2cnX3xljFHQmWy6B9bCildhNSVEDXSqahprCXU,2133
|
|
@@ -15,9 +15,9 @@ nova/mvvm/pyqt6_binding/__init__.py,sha256=plms0W4kJjFpZReGOC7lE3SYX1ceGTLbdAJ1G
|
|
|
15
15
|
nova/mvvm/pyqt6_binding/binding.py,sha256=uZNHx23Pf9DwGf3qKfQdmjOhmaXcppNd8pjkh363WP4,1365
|
|
16
16
|
nova/mvvm/pyqt6_binding/pyqt6_worker.py,sha256=y8g_mfPC1xIcpyKq7ZZqxrn9Txp5gPJrsBjAwS5FHhY,2133
|
|
17
17
|
nova/mvvm/trame_binding/__init__.py,sha256=uTdEW9VxtVubSbTLpoD3pC8a-KMgbKkZFlRbucvuQSE,62
|
|
18
|
-
nova/mvvm/trame_binding/binding.py,sha256=
|
|
18
|
+
nova/mvvm/trame_binding/binding.py,sha256=m3pcadwhDEmcvEQ24BvU65vpUFjX3QoZQlQmeVcPwmg,12481
|
|
19
19
|
nova/mvvm/trame_binding/trame_worker.py,sha256=JmrneFU11Hi8TtY558R3yIWW8UGiUeVNO7HvM1uWTVs,3778
|
|
20
|
-
nova_mvvm-0.
|
|
21
|
-
nova_mvvm-0.
|
|
22
|
-
nova_mvvm-0.
|
|
23
|
-
nova_mvvm-0.
|
|
20
|
+
nova_mvvm-0.14.0.dist-info/METADATA,sha256=xspULchDL9PyMWCDu2oa2uzpdIhDEQ6yZgIp7ItuV5I,1090
|
|
21
|
+
nova_mvvm-0.14.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
22
|
+
nova_mvvm-0.14.0.dist-info/licenses/LICENSE,sha256=MOqZ8tPMKy8ZETJ2-HEvFTZ7dYNlg3gXmBkV-Y9i8bw,1061
|
|
23
|
+
nova_mvvm-0.14.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|