Skip to content

Commit 1b95b6f

Browse files
committed
Dataset.__setitem__ now converts Models as input using to_data() + from_data()
1 parent b5f3563 commit 1b95b6f

File tree

3 files changed

+48
-18
lines changed

3 files changed

+48
-18
lines changed

src/omnipy/data/dataset.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -227,19 +227,24 @@ def __init__( # noqa: C901
227227
self._init(super_kwargs, **kwargs)
228228

229229
try:
230-
super().__init__(**super_kwargs)
230+
self._primary_validation(super_kwargs)
231231
except ValidationError:
232232
if model_or_dataset_as_input:
233-
super().__init__()
234-
self.from_data(super_kwargs[DATA_KEY])
233+
self._secondary_validation_from_data(super_kwargs)
235234
else:
236235
raise
237236

238-
UserDict.__init__(self, self.data) # noqa
239-
240237
if not self.__doc__:
241238
self._set_standard_field_description()
242239

240+
def _primary_validation(self, super_kwargs):
241+
# Pydantic validation of super_kwargs
242+
super().__init__(**super_kwargs)
243+
244+
def _secondary_validation_from_data(self, super_kwargs):
245+
super().__init__()
246+
self.from_data(super_kwargs[DATA_KEY])
247+
243248
def _init(self, super_kwargs: dict[str, Any], **kwargs: Any) -> None:
244249
...
245250

@@ -390,7 +395,7 @@ def _set_data_file_and_validate(self, key: str, val: ModelT) -> None:
390395

391396
try:
392397
self.data[key] = val
393-
self._validate(key)
398+
self._validate_data_file(key)
394399
except Exception:
395400
if has_prev_value:
396401
self.data[key] = prev_value
@@ -408,7 +413,14 @@ def update_forward_refs(cls, **localns: Any) -> None:
408413
cls.__name__ = remove_forward_ref_notation(cls.__name__)
409414
cls.__qualname__ = remove_forward_ref_notation(cls.__qualname__)
410415

411-
def _validate(self, _data_file: str) -> None:
416+
def _validate_data_file(self, _data_file: str) -> None:
417+
val = self.data[_data_file]
418+
if is_model_instance(val):
419+
self.data[_data_file] = self.get_model_class()(val)
420+
else:
421+
self._force_full_validation()
422+
423+
def _force_full_validation(self):
412424
self.data = self.data # Triggers pydantic validation, as validate_assignment=True
413425

414426
def __iter__(self) -> Iterator:
@@ -461,7 +473,7 @@ def _from_dict_with_callback(self,
461473
for data_file, contents in data.items():
462474
new_model = model_cls()
463475
callback_func(new_model, contents)
464-
self[data_file] = new_model
476+
self.data[data_file] = new_model
465477

466478
def absorb(self, other: 'Dataset'):
467479
self.from_data(other.to_data(), update=True)
@@ -706,7 +718,7 @@ def set_model(self, data_file: str, model: GeneralModelT) -> None:
706718
try:
707719
self._custom_field_models[data_file] = model
708720
if data_file in self.data:
709-
self._validate(data_file)
721+
self._validate_data_file(data_file)
710722
else:
711723
self.data[data_file] = model()
712724
except ValidationError:
@@ -719,15 +731,26 @@ def get_model(self, data_file: str) -> GeneralModelT:
719731
else:
720732
return self.get_model_class()
721733

722-
def _validate(self, data_file: str) -> None:
734+
def from_data(self,
735+
data: dict[str, Any] | Iterator[tuple[str, Any]],
736+
update: bool = True) -> None:
737+
super().from_data(data, update)
738+
for data_file in self:
739+
self._validate_data_file_according_to_custom_field_model(data_file)
740+
self._force_full_validation()
741+
742+
def _validate_data_file(self, data_file: str) -> None:
743+
self._validate_data_file_according_to_custom_field_model(data_file)
744+
self._force_full_validation()
745+
746+
def _validate_data_file_according_to_custom_field_model(self, data_file: str):
723747
if data_file in self._custom_field_models:
724748
model = self._custom_field_models[data_file]
725749
if not is_model_instance(model):
726750
model = Model[model]
727751
data_obj = self._to_data_if_model(self.data[data_file])
728752
parsed_data = self._to_data_if_model(model(data_obj))
729753
self.data[data_file] = parsed_data
730-
super()._validate(data_file) # validates all data according to ModelNewT
731754

732755
@staticmethod
733756
def _to_data_if_model(data_obj: Any):

tests/data/test_dataset.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
MyFwdRefDataset,
2121
MyNestedFwdRefDataset,
2222
ParamUpperStrDataset)
23-
from .helpers.models import NumberModel, StringToLength
23+
from .helpers.models import MyFloatObjModel, NumberModel, StringToLength
2424

2525

2626
def test_no_model():
@@ -435,6 +435,19 @@ def test_set_items_with_tuple_or_list() -> None:
435435
assert dataset['_untitled_4'] == dataset[-1] == Model[int](999)
436436

437437

438+
def test_set_item_converting_models_as_input():
439+
float_model = Model[float](4.5)
440+
my_float_model = MyFloatObjModel(MyFloatObject(int_part=4, float_part=0.5))
441+
442+
float_dataset = Dataset[Model[float]]()
443+
float_dataset['x'] = my_float_model
444+
assert float_dataset['x'].contents == 4.5
445+
446+
my_float_dataset = MyFloatObjDataset()
447+
my_float_dataset['x'] = float_model
448+
assert my_float_dataset['x'].contents == MyFloatObject(int_part=4, float_part=0.5)
449+
450+
438451
def test_del_item_with_str() -> None:
439452
dataset = Dataset[Model[int]](data_file_1=123, data_file_2=456, data_file_3=789)
440453

tests/modules/remote/test_tasks.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,10 @@ def _assert_json_query_results(assert_model_if_dyn_conv_else_val, data: JsonData
152152
assert_model_if_dyn_conv_else_val(data['delillos_2']['lyrics'][0], str, 'Joda sier Arne')
153153

154154

155-
# TODO: Models as input to Dataset.__setitem__ is not converted to the model of the dataset in the
156-
# same way as in the __init__method, causing this test to fail if
157-
# `dynamically_convert_elements_to_models == True`
158-
159-
160155
@pc.parametrize_with_cases('case', cases='.cases.request_types')
161156
async def test_get_from_api_endpoint_without_session(
162157
query_urls: Annotated[HttpUrlDataset, pytest.fixture],
163158
assert_model_if_dyn_conv_else_val: Annotated[AssertModelOrValFunc, pytest.fixture],
164-
skip_test_if_dynamically_convert_elements_to_models: Annotated[None, pytest.fixture],
165159
case: RequestTypeCase,
166160
) -> None:
167161
data = await case.job.run(query_urls, **case.kwargs)

0 commit comments

Comments
 (0)