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.
@@ -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) -> 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)
@@ -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 check_binding, normalize_field_name, rget_list_of_fields, rgetattr, rsetattr
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.13.1
3
+ Version: 0.14.0
4
4
  Summary: A Python Package for Model-View-ViewModel pattern
5
- Author-email: "Yakubov, Sergey" <yakubovs@ornl.gov>
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=kHZLR5Noc581kz-OYRO6EW-jyjB443BF_wVoXY5jBRA,2937
7
- nova/mvvm/_internal/pyqt_communicator.py,sha256=kcEaXCjJZXymNBM-8x5Yk22SUQFrQ7c5yV1-GDYf3DM,3797
8
- nova/mvvm/_internal/utils.py,sha256=2YDUvAmJjjb89ROE4n4GWD4Qqz-rRqVP4ICDGnIcFNc,3303
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=Fz_t2XHoU8hfbf-nDDypk4V_KHH7T7OqYFCl65k4XEE,6352
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=m8B8JSYU1j5qsWSWt7Z_-JaHgwcunCONlg1E47pedxI,12030
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.13.1.dist-info/METADATA,sha256=v_Ne9EZCIjfnYA4jPwGcOV-cdTpcCZQU_Dladf_Y0ks,1027
21
- nova_mvvm-0.13.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
22
- nova_mvvm-0.13.1.dist-info/licenses/LICENSE,sha256=MOqZ8tPMKy8ZETJ2-HEvFTZ7dYNlg3gXmBkV-Y9i8bw,1061
23
- nova_mvvm-0.13.1.dist-info/RECORD,,
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,,