From 9967ed4f586f9255cdf7cbf0f88b6447ae2e3da0 Mon Sep 17 00:00:00 2001 From: domfournier Date: Fri, 27 Mar 2026 14:46:14 -0700 Subject: [PATCH 01/10] Add set_value to forms. Update test --- geoh5py/ui_json/forms.py | 11 +++++++--- geoh5py/ui_json/validations/form.py | 2 +- tests/ui_json/uijson_test.py | 34 +++++++++-------------------- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/geoh5py/ui_json/forms.py b/geoh5py/ui_json/forms.py index 892c05a88..a0c786899 100644 --- a/geoh5py/ui_json/forms.py +++ b/geoh5py/ui_json/forms.py @@ -100,10 +100,10 @@ class BaseForm(BaseModel): model_config = ConfigDict( extra="allow", - frozen=True, populate_by_name=True, loc_by_alias=True, alias_generator=to_camel, + validate_assignment=True, ) label: str @@ -139,8 +139,6 @@ class and lastly fall back on type checking the value field of the form. fields to avoid false positives. :param data: Form data. - :param form_types: Pre-compute all the base classes to check against. - :param indicators: Pre-compute the indicator attributes for each subclass. """ data = {to_snake(k): v for k, v in data.items()} @@ -164,6 +162,13 @@ def flatten(self): def validate_data(self, params: dict[str, Any]): """Validate the form data.""" + def set_value(self, value: Any): + """Set the form value.""" + self.value = value + + if "optional" in self.model_fields_set: + self.enabled = self.value is not None + class StringForm(BaseForm): """ diff --git a/geoh5py/ui_json/validations/form.py b/geoh5py/ui_json/validations/form.py index 280011f10..35d75acab 100644 --- a/geoh5py/ui_json/validations/form.py +++ b/geoh5py/ui_json/validations/form.py @@ -51,6 +51,6 @@ def convert(value: UUID | None) -> str: return f"{{{value!s}}}" return value - if isinstance(value, list): + if isinstance(value, list | tuple): return [convert(v) for v in value] return convert(value) diff --git a/tests/ui_json/uijson_test.py b/tests/ui_json/uijson_test.py index 383e54a58..156fb78f3 100644 --- a/tests/ui_json/uijson_test.py +++ b/tests/ui_json/uijson_test.py @@ -612,7 +612,7 @@ class MyUIJson(BaseUIJson): "my_int_parameter": {"label": "b", "value": 1}, }, ) - result = uijson.fill(my_string_parameter="updated") + result = uijson.set_values(my_string_parameter="updated") assert result is uijson assert uijson.my_string_parameter.value == "updated" @@ -630,31 +630,15 @@ class MyUIJson(BaseUIJson): uijson=MyUIJson, data={"my_string_parameter": {"label": "a", "value": "original"}}, ) - copy = uijson.fill(copy=True, my_string_parameter="updated", title="ok") + copy = uijson.set_values(copy=True, my_string_parameter="updated", title="ok") assert copy is not uijson assert copy.my_string_parameter.value == "updated" assert uijson.my_string_parameter.value == "original" assert copy.title == "ok" - with pytest.raises(TypeError, match="Only string"): - _ = uijson.fill(copy=True, my_string_parameter="updated", title=666) - - -def test_fill_disables_forms_with_falsy_value(tmp_path): - ws = Workspace(tmp_path / "test.geoh5") - - class MyUIJson(BaseUIJson): - my_zero_param: FloatForm - - uijson = generate_test_uijson( - ws, - uijson=MyUIJson, - data={"my_zero_param": {"label": "a", "value": 0.0}}, - ) - uijson.fill() - - assert uijson.my_zero_param.enabled is False + with pytest.raises(ValidationError): + _ = uijson.set_values(copy=True, my_string_parameter="updated", title=666) def test_fill_truthy_value_leaves_updates_empty(tmp_path): @@ -670,7 +654,7 @@ class MyUIJson(BaseUIJson): data={"my_param": {"label": "a", "value": 3.14}}, ) original_enabled = uijson.my_param.enabled - uijson.fill() + uijson.set_values() assert uijson.my_param.enabled == original_enabled assert uijson.my_param.value == 3.14 @@ -685,9 +669,11 @@ class MyUIJson(BaseUIJson): uijson = generate_test_uijson( ws, uijson=MyUIJson, - data={"my_param": {"label": "a", "value": 0.0, "enabled": False}}, + data={ + "my_param": {"label": "a", "value": 0.0, "enabled": False, "optional": True} + }, ) - uijson.fill(my_param=5.0) + uijson.set_values(my_param=5.0) assert uijson.my_param.enabled is True assert uijson.my_param.value == 5.0 @@ -712,7 +698,7 @@ class MyUIJson(BaseUIJson): } }, ) - uijson.fill(my_object_parameter=pts2.uid) + uijson.set_values(my_object_parameter=pts2.uid) assert uijson.my_object_parameter.value == pts2.uid From edd7df229ce687eb0551c829dc9cda207ee740b1 Mon Sep 17 00:00:00 2001 From: domfournier Date: Fri, 27 Mar 2026 15:44:55 -0700 Subject: [PATCH 02/10] Change set value for the is_value case --- geoh5py/ui_json/forms.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/geoh5py/ui_json/forms.py b/geoh5py/ui_json/forms.py index a0c786899..76f583e23 100644 --- a/geoh5py/ui_json/forms.py +++ b/geoh5py/ui_json/forms.py @@ -571,17 +571,24 @@ def property_if_not_is_value(self): and not isinstance(self.property, UUID) # pylint: disable=unsupported-membership-test ): raise ValueError("A property must be provided if is_value is used.") + return self def flatten(self) -> UUID | float | int | None: """Returns the data for the form.""" - if ( - "is_value" in self.model_fields_set # pylint: disable=unsupported-membership-test - and not self.is_value - ): + if "is_value" in self.model_fields_set and not self.is_value: return self.property return self.value + def set_value(self, value: Any): + """Set the form value.""" + try: + self.value = value + self.is_value = True + except ValidationError: + self.is_value = value is None + self.property = value + class MultiSelectDataForm(DataFormMixin, BaseForm): """ From 4a5914ce35246d0ddadd07c67bf6c1c8c7bbe7c0 Mon Sep 17 00:00:00 2001 From: domfournier Date: Fri, 27 Mar 2026 15:46:16 -0700 Subject: [PATCH 03/10] Clean ups and augment test --- geoh5py/ui_json/annotations.py | 8 ++++- geoh5py/ui_json/ui_json.py | 66 +++++++++++----------------------- tests/ui_json/forms_test.py | 21 +++++++---- 3 files changed, 42 insertions(+), 53 deletions(-) diff --git a/geoh5py/ui_json/annotations.py b/geoh5py/ui_json/annotations.py index 283fde29b..5776cbff4 100644 --- a/geoh5py/ui_json/annotations.py +++ b/geoh5py/ui_json/annotations.py @@ -27,6 +27,7 @@ from geoh5py.groups import Group from geoh5py.objects import ObjectBase from geoh5py.shared.validators import ( + none_to_empty_string, to_class, to_list, to_path, @@ -71,6 +72,12 @@ def deprecate(value, info): PlainSerializer(types_to_string, when_used="json"), ] +OptionalPath = Annotated[ + Path | None, + BeforeValidator(empty_string_to_none), + PlainSerializer(none_to_empty_string), +] + OptionalUUID = Annotated[ UUID | None, BeforeValidator(empty_string_to_none), @@ -85,7 +92,6 @@ def deprecate(value, info): PlainSerializer(uuid_to_string), ] - OptionalValueList = Annotated[ float | list[float] | None, BeforeValidator(empty_string_to_none), diff --git a/geoh5py/ui_json/ui_json.py b/geoh5py/ui_json/ui_json.py index 30a4fdf89..575c6bcf3 100644 --- a/geoh5py/ui_json/ui_json.py +++ b/geoh5py/ui_json/ui_json.py @@ -23,14 +23,12 @@ import json import logging from pathlib import Path -from typing import Annotated, Any +from typing import Any from uuid import UUID from pydantic import ( BaseModel, - BeforeValidator, ConfigDict, - PlainSerializer, create_model, field_validator, ) @@ -38,21 +36,15 @@ from geoh5py import Workspace from geoh5py.groups import PropertyGroup, UIJsonGroup from geoh5py.shared import Entity -from geoh5py.shared.utils import fetch_active_workspace, str2uuid, stringify -from geoh5py.shared.validators import none_to_empty_string +from geoh5py.shared.utils import dict_mapper, fetch_active_workspace +from geoh5py.ui_json.annotations import OptionalPath from geoh5py.ui_json.forms import BaseForm from geoh5py.ui_json.validations import ErrorPool, UIJsonError, get_validations -from geoh5py.ui_json.validations.form import empty_string_to_none +from geoh5py.ui_json.validations.form import entity_to_uuid, uuid_to_string logger = logging.getLogger(__name__) -OptionalPath = Annotated[ - Path | None, # pylint: disable=unsupported-binary-operation - BeforeValidator(empty_string_to_none), - PlainSerializer(none_to_empty_string), -] - class BaseUIJson(BaseModel): """ @@ -92,7 +84,7 @@ def __str__(self) -> str: """String level shows the full json representation.""" json_string = self.model_dump_json(indent=4, exclude_unset=True) - for field in type(self).model_fields: + for field in type(self).model_fields.keys(): value = getattr(self, field) if isinstance(value, BaseForm): type_string = type(value).__name__ @@ -152,7 +144,7 @@ def read(cls, path: str | Path) -> BaseUIJson: if cls == BaseUIJson: fields = {} for name, value in kwargs.items(): - if name in BaseUIJson.model_fields: + if name in BaseUIJson.model_fields.keys(): continue if isinstance(value, dict): form_type = BaseForm.infer(value) @@ -194,7 +186,7 @@ def get_groups(self) -> dict[str, list[str]]: group. """ groups: dict[str, list[str]] = {} - for field in self.__class__.model_fields: + for field in self.__class__.model_fields.keys(): form = getattr(self, field) if not isinstance(form, BaseForm): continue @@ -255,7 +247,7 @@ def flatten(self, skip_disabled=False, active_only=False) -> dict[str, Any]: return data - def fill(self, copy: bool = False, **kwargs) -> BaseUIJson: + def set_values(self, copy: bool = False, **kwargs) -> BaseUIJson: """ Fill the UIJson with new values. @@ -265,38 +257,20 @@ def fill(self, copy: bool = False, **kwargs) -> BaseUIJson: :return: A new UIJson object with the updated values. """ - temp_properties = {} - for key, form in dict(self).items(): - if not isinstance(form, BaseForm): - if key in kwargs: - if not isinstance(kwargs[key], str): - raise TypeError( - "Only string values can be updated for non-form fields. " - ) - temp_properties[key] = kwargs[key] - continue - - updates: dict[str, Any] = {} - - # if a value has no default value, set enabled to false - if not bool(form.value) if form.value != [""] else False: - updates["enabled"] = False - - if key in kwargs: - updates["value"] = str2uuid(stringify(kwargs[key])) - updates["enabled"] = True - - if updates: - temp_properties[key] = form.model_copy(update=updates) - - updated_model = self.model_copy(update=temp_properties) + if copy: + uijson = self.model_copy(deep=True) + else: + uijson = self - if not copy: - for field_name in type(self).model_fields: - setattr(self, field_name, getattr(updated_model, field_name)) - return self + demotion = [entity_to_uuid, uuid_to_string] + for key, value in kwargs.items(): + form = getattr(uijson, key, None) + if isinstance(form, BaseForm): + form.set_value(value) + else: + setattr(uijson, key, dict_mapper(value, demotion)) - return updated_model + return uijson def to_params(self, workspace: Workspace | None = None) -> dict[str, Any]: """ diff --git a/tests/ui_json/forms_test.py b/tests/ui_json/forms_test.py index 5f3e8fc22..33a095f1c 100644 --- a/tests/ui_json/forms_test.py +++ b/tests/ui_json/forms_test.py @@ -98,12 +98,6 @@ def test_base_form_config_extra(sample_form): assert form.model_extra == {"extra": "stuff"} -def test_base_form_config_frozen(sample_form): - form = sample_form(label="name", value="test") - with pytest.raises(ValidationError, match="Instance is frozen"): - form.label = "new" - - def test_base_form_config_alias(sample_form): form = sample_form( label="name", @@ -516,6 +510,21 @@ def test_data_or_value_form(): property="", ) + form.set_value(None) + assert form.is_value + + _ = DataOrValueForm( + label="name", + value=1.0, + parent="my_param", + association="Vertex", + data_type="Float", + is_value=True, + property="", + ) + form.set_value(2) + assert form.value == 2 + def test_multichoice_data_form(): data_uid_1 = str(uuid.uuid4()) From c73b884c60fb94730a03ec3adc7ad05fa6ed5927 Mon Sep 17 00:00:00 2001 From: domfournier Date: Mon, 30 Mar 2026 14:51:03 -0700 Subject: [PATCH 04/10] Fix imports --- geoh5py/ui_json/annotations.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/geoh5py/ui_json/annotations.py b/geoh5py/ui_json/annotations.py index c06328827..3cc38f48f 100644 --- a/geoh5py/ui_json/annotations.py +++ b/geoh5py/ui_json/annotations.py @@ -27,9 +27,8 @@ from geoh5py.data import DataAssociationEnum, DataTypeEnum from geoh5py.groups import Group from geoh5py.objects import ObjectBase -from geoh5py.shared.utils import enum_name_to_str, stringify +from geoh5py.shared.utils import enum_name_to_str, none2str, str2none, stringify from geoh5py.shared.validators import ( - none_to_empty_string, to_class, to_list, to_path, @@ -84,7 +83,7 @@ def deprecate(value, info): OptionalPath = Annotated[ Path | None, BeforeValidator(str2none), - PlainSerializer(none_to_empty_string), + PlainSerializer(none2str), ] OptionalUUID = Annotated[ From 7efd63252b2c1fee58f92724ace63f788b8dc1e4 Mon Sep 17 00:00:00 2001 From: domfournier Date: Mon, 30 Mar 2026 14:58:34 -0700 Subject: [PATCH 05/10] Replace duplicated methods --- geoh5py/ui_json/ui_json.py | 10 ++++-- geoh5py/ui_json/validations/form.py | 56 ----------------------------- 2 files changed, 7 insertions(+), 59 deletions(-) delete mode 100644 geoh5py/ui_json/validations/form.py diff --git a/geoh5py/ui_json/ui_json.py b/geoh5py/ui_json/ui_json.py index 75074d0ce..aa800c723 100644 --- a/geoh5py/ui_json/ui_json.py +++ b/geoh5py/ui_json/ui_json.py @@ -36,11 +36,15 @@ from geoh5py import Workspace from geoh5py.groups import PropertyGroup, UIJsonGroup from geoh5py.shared import Entity -from geoh5py.shared.utils import dict_mapper, fetch_active_workspace +from geoh5py.shared.utils import ( + as_str_if_uuid, + dict_mapper, + entity2uuid, + fetch_active_workspace, +) from geoh5py.ui_json.annotations import OptionalPath from geoh5py.ui_json.forms import BaseForm from geoh5py.ui_json.validation import ErrorPool, UIJsonError, get_validations -from geoh5py.ui_json.validations.form import entity_to_uuid, uuid_to_string logger = logging.getLogger(__name__) @@ -262,7 +266,7 @@ def set_values(self, copy: bool = False, **kwargs) -> BaseUIJson: else: uijson = self - demotion = [entity_to_uuid, uuid_to_string] + demotion = [entity2uuid, as_str_if_uuid] for key, value in kwargs.items(): form = getattr(uijson, key, None) if isinstance(form, BaseForm): diff --git a/geoh5py/ui_json/validations/form.py b/geoh5py/ui_json/validations/form.py deleted file mode 100644 index 35d75acab..000000000 --- a/geoh5py/ui_json/validations/form.py +++ /dev/null @@ -1,56 +0,0 @@ -# '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -# Copyright (c) 2020-2026 Mira Geoscience Ltd. ' -# ' -# This file is part of geoh5py. ' -# ' -# geoh5py is free software: you can redistribute it and/or modify ' -# it under the terms of the GNU Lesser General Public License as published by ' -# the Free Software Foundation, either version 3 of the License, or ' -# (at your option) any later version. ' -# ' -# geoh5py is distributed in the hope that it will be useful, ' -# but WITHOUT ANY WARRANTY; without even the implied warranty of ' -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ' -# GNU Lesser General Public License for more details. ' -# ' -# You should have received a copy of the GNU Lesser General Public License ' -# along with geoh5py. If not, see . ' -# '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -from typing import Any -from uuid import UUID - -from geoh5py.shared import Entity - - -def empty_string_to_none(value): - """Promote empty string to uid, and pass all other values.""" - if value == "": - return None - return value - - -def entity_to_uuid(value: Any | list[Entity] | Entity) -> Any | list[UUID] | UUID: - """Demote an Entity to its UUID, and pass all other values.""" - if isinstance(value, list | tuple): - return [entity_to_uuid(val) for val in value] - - if isinstance(value, Entity): - return value.uid - - return value - - -def uuid_to_string(value: UUID | list[UUID] | None) -> str | list[str]: - """Serialize UUID(s) as a string.""" - - def convert(value: UUID | None) -> str: - if value is None: - return "" - if isinstance(value, UUID): - return f"{{{value!s}}}" - return value - - if isinstance(value, list | tuple): - return [convert(v) for v in value] - return convert(value) From 7f2725cf757fcdcf6da15306bf2f3e25791f64a3 Mon Sep 17 00:00:00 2001 From: domfournier Date: Tue, 31 Mar 2026 09:52:11 -0700 Subject: [PATCH 06/10] Test --- geoh5py/ui_json/ui_json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geoh5py/ui_json/ui_json.py b/geoh5py/ui_json/ui_json.py index aa800c723..7a9e8ae34 100644 --- a/geoh5py/ui_json/ui_json.py +++ b/geoh5py/ui_json/ui_json.py @@ -88,7 +88,7 @@ def __str__(self) -> str: """String level shows the full json representation.""" json_string = self.model_dump_json(indent=4, exclude_unset=True) - for field in type(self).model_fields.keys(): + for field in type(self).model_fields: value = getattr(self, field) if isinstance(value, BaseForm): type_string = type(value).__name__ From d5bd675fae01b6c96f09150651e99605a3a66167 Mon Sep 17 00:00:00 2001 From: domfournier Date: Tue, 31 Mar 2026 09:52:46 -0700 Subject: [PATCH 07/10] Revert "Test" This reverts commit 7f2725cf757fcdcf6da15306bf2f3e25791f64a3. --- geoh5py/ui_json/ui_json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geoh5py/ui_json/ui_json.py b/geoh5py/ui_json/ui_json.py index 7a9e8ae34..aa800c723 100644 --- a/geoh5py/ui_json/ui_json.py +++ b/geoh5py/ui_json/ui_json.py @@ -88,7 +88,7 @@ def __str__(self) -> str: """String level shows the full json representation.""" json_string = self.model_dump_json(indent=4, exclude_unset=True) - for field in type(self).model_fields: + for field in type(self).model_fields.keys(): value = getattr(self, field) if isinstance(value, BaseForm): type_string = type(value).__name__ From 82899c77f05c36603770a00d838e187e5cd36ef9 Mon Sep 17 00:00:00 2001 From: domfournier Date: Tue, 31 Mar 2026 09:58:21 -0700 Subject: [PATCH 08/10] Remove keys from model_fields --- geoh5py/ui_json/ui_json.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/geoh5py/ui_json/ui_json.py b/geoh5py/ui_json/ui_json.py index aa800c723..450bca890 100644 --- a/geoh5py/ui_json/ui_json.py +++ b/geoh5py/ui_json/ui_json.py @@ -88,7 +88,7 @@ def __str__(self) -> str: """String level shows the full json representation.""" json_string = self.model_dump_json(indent=4, exclude_unset=True) - for field in type(self).model_fields.keys(): + for field in type(self).model_fields: value = getattr(self, field) if isinstance(value, BaseForm): type_string = type(value).__name__ @@ -148,7 +148,7 @@ def read(cls, path: str | Path) -> BaseUIJson: if cls == BaseUIJson: fields = {} for name, value in kwargs.items(): - if name in BaseUIJson.model_fields.keys(): + if name in BaseUIJson.model_fields: continue if isinstance(value, dict): form_type = BaseForm.infer(value) @@ -190,7 +190,7 @@ def get_groups(self) -> dict[str, list[str]]: group. """ groups: dict[str, list[str]] = {} - for field in self.__class__.model_fields.keys(): + for field in self.__class__.model_fields: form = getattr(self, field) if not isinstance(form, BaseForm): continue From 733b2773293a2e063320feefb126229db5311456 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:53:05 +0000 Subject: [PATCH 09/10] Fix DataOrValueForm.set_value to handle optional/enabled and ordering Agent-Logs-Url: https://github.com/MiraGeoscience/geoh5py/sessions/9b6c6fdc-c7f7-492a-8f0c-723d2b5fec33 Co-authored-by: domfournier <55204635+domfournier@users.noreply.github.com> --- geoh5py/ui_json/forms.py | 6 +++++- recipe.yaml | 2 +- tests/ui_json/forms_test.py | 13 ++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/geoh5py/ui_json/forms.py b/geoh5py/ui_json/forms.py index 55984192c..f8688db6c 100644 --- a/geoh5py/ui_json/forms.py +++ b/geoh5py/ui_json/forms.py @@ -576,8 +576,12 @@ def set_value(self, value: Any): self.value = value self.is_value = True except ValidationError: + if value is not None: + self.property = value self.is_value = value is None - self.property = value + + if "optional" in self.model_fields_set: + self.enabled = value is not None class MultiSelectDataForm(DataFormMixin, BaseForm): diff --git a/recipe.yaml b/recipe.yaml index 613450daa..55952ceec 100644 --- a/recipe.yaml +++ b/recipe.yaml @@ -2,7 +2,7 @@ schema_version: 1 context: name: "geoh5py" - version: "0.0.0.dev0" # This will be replaced by the actual version in the build process + version: "0.12.1rc2.dev279+3df1d110" # This will be replaced by the actual version in the build process python_min: "3.12" module_name: ${{ name|lower|replace("-", "_") }} diff --git a/tests/ui_json/forms_test.py b/tests/ui_json/forms_test.py index 20be12e20..9def33ce4 100644 --- a/tests/ui_json/forms_test.py +++ b/tests/ui_json/forms_test.py @@ -512,7 +512,7 @@ def test_data_or_value_form(): form.set_value(None) assert form.is_value - _ = DataOrValueForm( + optional_form = DataOrValueForm( label="name", value=1.0, parent="my_param", @@ -520,7 +520,18 @@ def test_data_or_value_form(): data_type="Float", is_value=True, property="", + optional=True, ) + assert optional_form.enabled + optional_form.set_value(None) + assert optional_form.is_value + assert not optional_form.enabled + + optional_form.set_value(data_uid) + assert not optional_form.is_value + assert optional_form.property == uuid.UUID(data_uid) + assert optional_form.enabled + form.set_value(2) assert form.value == 2 From 7f4cee7907ec80fa6750f9cbdcdcfa113b88054c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:55:49 +0000 Subject: [PATCH 10/10] Improve DataOrValueForm.set_value clarity in except block Agent-Logs-Url: https://github.com/MiraGeoscience/geoh5py/sessions/9b6c6fdc-c7f7-492a-8f0c-723d2b5fec33 Co-authored-by: domfournier <55204635+domfournier@users.noreply.github.com> --- geoh5py/ui_json/forms.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/geoh5py/ui_json/forms.py b/geoh5py/ui_json/forms.py index f8688db6c..c5f3d7cdc 100644 --- a/geoh5py/ui_json/forms.py +++ b/geoh5py/ui_json/forms.py @@ -578,7 +578,10 @@ def set_value(self, value: Any): except ValidationError: if value is not None: self.property = value - self.is_value = value is None + self.is_value = False + else: + self.is_value = True + self.property = None if "optional" in self.model_fields_set: self.enabled = value is not None