Skip to content

Commit 3dd8458

Browse files
committed
search for custom attribute with open enum
1 parent f1cfba8 commit 3dd8458

File tree

4 files changed

+139
-33
lines changed

4 files changed

+139
-33
lines changed

src/pynxtools/data/NXtest.nxdl.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@
9999
<item value="1st type open"/>
100100
<item value="2nd type open"/>
101101
</enumeration>
102+
<attribute name="attribute_with_open_enum" optional="true">
103+
<enumeration open="true">
104+
<item value="1st option"/>
105+
<item value="2nd option"/>
106+
</enumeration>
107+
</attribute>
102108
</field>
103109
<attribute name="group_attribute">
104110
</attribute>

src/pynxtools/dataconverter/helpers.py

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ class ValidationProblem(Enum):
8787
UnitWithoutDocumentation = auto()
8888
InvalidUnit = auto()
8989
InvalidEnum = auto()
90-
OpenEnumWithNewItem = auto()
90+
OpenEnumWithCorrectNewItem = auto()
91+
OpenEnumWithIncorrectNewItem = auto()
9192
MissingRequiredGroup = auto()
9293
MissingRequiredField = auto()
9394
MissingRequiredAttribute = auto()
@@ -154,10 +155,21 @@ def _log(self, path: str, log_type: ValidationProblem, value: Optional[Any], *ar
154155
logger.warning(
155156
f"The value at {path} should be one of the following: {value}."
156157
)
157-
elif log_type == ValidationProblem.OpenEnumWithNewItem:
158+
elif log_type == ValidationProblem.OpenEnumWithCorrectNewItem:
158159
logger.info(
159-
f"The value at {path} does not match with the enumerated items from the open enumeration: {value}."
160+
f"The value '{args[0]}' at {path} does not match with the enumerated items from the open enumeration: {value}."
160161
)
162+
elif log_type == ValidationProblem.OpenEnumWithIncorrectNewItem:
163+
actual_value, custom_attr = args
164+
165+
log_text = f"The value '{actual_value}' at {path} does not match with the enumerated items from the open enumeration: {value}."
166+
if custom_attr == "custom_missing":
167+
log_text += f" When a different value is used, a boolean 'custom' attribute must be added."
168+
logger.warning(log_text)
169+
elif custom_attr == "custom_false":
170+
log_text += f" When a different value is used, the boolean 'custom' attribute cannot be False."
171+
logger.warning(log_text)
172+
161173
elif log_type == ValidationProblem.MissingRequiredGroup:
162174
logger.warning(f"The required group {path} hasn't been supplied.")
163175
elif log_type == ValidationProblem.MissingRequiredField:
@@ -287,9 +299,9 @@ def collect_and_log(
287299
# info messages should not fail validation
288300
if log_type in (
289301
ValidationProblem.UnitWithoutDocumentation,
290-
ValidationProblem.OpenEnumWithNewItem,
291302
ValidationProblem.CompressionStrengthZero,
292303
ValidationProblem.MissingNXclass,
304+
ValidationProblem.OpenEnumWithCorrectNewItem,
293305
):
294306
if self.logging and message not in self.data["info"]:
295307
self._log(path, log_type, value, *args, **kwargs)
@@ -804,9 +816,7 @@ def convert_int_to_float(value):
804816
return value
805817

806818

807-
def is_valid_data_field(
808-
value: Any, nxdl_type: str, nxdl_enum: list, nxdl_enum_open: bool, path: str
809-
) -> Any:
819+
def is_valid_data_field(value: Any, nxdl_type: str, path: str) -> Any:
810820
"""Checks whether a given value is valid according to the type defined in the NXDL."""
811821

812822
def validate_data_value(
@@ -843,25 +853,25 @@ def validate_data_value(
843853
path, ValidationProblem.InvalidDatetime, value
844854
)
845855

846-
if nxdl_enum is not None:
847-
if (
848-
isinstance(value, np.ndarray)
849-
and isinstance(nxdl_enum, list)
850-
and isinstance(nxdl_enum[0], list)
851-
):
852-
enum_value = list(value)
853-
else:
854-
enum_value = value
855-
856-
if enum_value not in nxdl_enum:
857-
if nxdl_enum_open:
858-
collector.collect_and_log(
859-
path, ValidationProblem.OpenEnumWithNewItem, nxdl_enum
860-
)
861-
else:
862-
collector.collect_and_log(
863-
path, ValidationProblem.InvalidEnum, nxdl_enum
864-
)
856+
# if nxdl_enum is not None:
857+
# if (
858+
# isinstance(value, np.ndarray)
859+
# and isinstance(nxdl_enum, list)
860+
# and isinstance(nxdl_enum[0], list)
861+
# ):
862+
# enum_value = list(value)
863+
# else:
864+
# enum_value = value
865+
866+
# if enum_value not in nxdl_enum:
867+
# if nxdl_enum_open:
868+
# collector.collect_and_log(
869+
# path, ValidationProblem.OpenEnumWithNewItem, nxdl_enum
870+
# )
871+
# else:
872+
# collector.collect_and_log(
873+
# path, ValidationProblem.InvalidEnum, nxdl_enum
874+
# )
865875

866876
return value
867877

src/pynxtools/dataconverter/validation.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,9 +1342,14 @@ def handle_field(node: NexusNode, keys: Mapping[str, Any], prev_path: str):
13421342
mapping[variant_path] = is_valid_data_field(
13431343
keys[variant],
13441344
node.dtype,
1345+
variant_path,
1346+
)
1347+
is_valid_enum(
1348+
mapping[variant_path],
13451349
node.items,
13461350
node.open_enum,
13471351
variant_path,
1352+
mapping,
13481353
)
13491354

13501355
check_reserved_suffix(variant_path, keys)
@@ -1410,9 +1415,14 @@ def handle_attribute(node: NexusNode, keys: Mapping[str, Any], prev_path: str):
14101415
f"{prev_path}/{variant if variant.startswith('@') else f'@{variant}'}"
14111416
],
14121417
node.dtype,
1418+
variant_path,
1419+
)
1420+
is_valid_enum(
1421+
mapping[variant_path],
14131422
node.items,
14141423
node.open_enum,
14151424
variant_path,
1425+
mapping,
14161426
)
14171427
check_reserved_prefix(
14181428
variant_path, get_definition(variant_path), "attribute"
@@ -1641,7 +1651,16 @@ def is_documented(key: str, tree: NexusNode) -> bool:
16411651
keys_to_remove.append(key)
16421652
return False
16431653
resolved_link[key] = is_valid_data_field(
1644-
resolved_link[key], node.dtype, node.items, node.open_enum, key
1654+
resolved_link[key],
1655+
node.dtype,
1656+
key,
1657+
)
1658+
is_valid_enum(
1659+
resolved_link[key],
1660+
node.items,
1661+
node.open_enum,
1662+
key,
1663+
mapping,
16451664
)
16461665

16471666
return True
@@ -1656,7 +1675,16 @@ def is_documented(key: str, tree: NexusNode) -> bool:
16561675

16571676
# Check general validity
16581677
mapping[key] = is_valid_data_field(
1659-
mapping[key], node.dtype, node.items, node.open_enum, key
1678+
mapping[key],
1679+
node.dtype,
1680+
key,
1681+
)
1682+
is_valid_enum(
1683+
mapping[key],
1684+
node.items,
1685+
node.open_enum,
1686+
key,
1687+
mapping,
16601688
)
16611689

16621690
# Check main field exists for units

tests/dataconverter/test_validation.py

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -870,16 +870,78 @@ def format_error_message(msg: str) -> str:
870870
),
871871
pytest.param(
872872
alter_dict(
873-
TEMPLATE,
874-
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2",
875-
"a very different type",
873+
alter_dict(
874+
alter_dict(
875+
alter_dict(
876+
TEMPLATE,
877+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2",
878+
"a very different type",
879+
),
880+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@custom",
881+
True,
882+
),
883+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@attribute_with_open_enum",
884+
"3rd option",
885+
),
886+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@attribute_with_open_enum_custom",
887+
True,
876888
),
877889
[
878-
"The value at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type2 does not match with the "
879-
"enumerated items from the open enumeration: ['1st type open', '2nd type open']."
890+
"The value 'a very different type' at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type2 does not match "
891+
"with the enumerated items from the open enumeration: ['1st type open', '2nd type open'].",
892+
"The value '3rd option' at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@attribute_with_open_enum "
893+
"does not match with the enumerated items from the open enumeration: ['1st option', '2nd option'].",
880894
],
881895
id="open-enum-with-new-item",
882896
),
897+
pytest.param(
898+
alter_dict(
899+
alter_dict(
900+
alter_dict(
901+
alter_dict(
902+
TEMPLATE,
903+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2",
904+
"a very different type",
905+
),
906+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@custom",
907+
False,
908+
),
909+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@attribute_with_open_enum",
910+
"3rd option",
911+
),
912+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@attribute_with_open_enum_custom",
913+
False,
914+
),
915+
[
916+
"The value 'a very different type' at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type2 does not match "
917+
"with the enumerated items from the open enumeration: ['1st type open', '2nd type open']. "
918+
"When a different value is used, the boolean 'custom' attribute cannot be False.",
919+
"The value '3rd option' at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@attribute_with_open_enum "
920+
"does not match with the enumerated items from the open enumeration: ['1st option', '2nd option']. "
921+
"When a different value is used, the boolean 'custom' attribute cannot be False.",
922+
],
923+
id="open-enum-with-new-item-custom-false",
924+
),
925+
pytest.param(
926+
alter_dict(
927+
alter_dict(
928+
TEMPLATE,
929+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2",
930+
"a very different type",
931+
),
932+
"/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@attribute_with_open_enum",
933+
"3rd option",
934+
),
935+
[
936+
"The value 'a very different type' at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type2 does not match "
937+
"with the enumerated items from the open enumeration: ['1st type open', '2nd type open']. "
938+
"When a different value is used, a boolean 'custom' attribute must be added.",
939+
"The value '3rd option' at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type2/@attribute_with_open_enum "
940+
"does not match with the enumerated items from the open enumeration: ['1st option', '2nd option']. "
941+
"When a different value is used, a boolean 'custom' attribute must be added.",
942+
],
943+
id="open-enum-with-new-item-custom-missing",
944+
),
883945
pytest.param(
884946
set_to_none_in_dict(
885947
TEMPLATE, "/ENTRY[my_entry]/optional_parent/required_child", "required"

0 commit comments

Comments
 (0)