Skip to content

Commit 15aafb0

Browse files
committed
Add a util function get_attr_or_ref_attr() for retrieving (nested) attribute value; Improve attributes_not_null function
1 parent 509262a commit 15aafb0

2 files changed

Lines changed: 71 additions & 34 deletions

File tree

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
from esdlvalidator.validation.functions import utils
2-
from esdlvalidator.validation.functions.function import FunctionFactory, FunctionCheck, FunctionDefinition, ArgDefinition, FunctionType, CheckResult
2+
from esdlvalidator.validation.functions.function import (
3+
FunctionFactory,
4+
FunctionCheck,
5+
FunctionDefinition,
6+
ArgDefinition,
7+
FunctionType,
8+
CheckResult,
9+
)
310

411

512
@FunctionFactory.register(FunctionType.CHECK, "attributes_not_null")
@@ -8,11 +15,16 @@ class AttributesNotSet(FunctionCheck):
815
def get_function_definition(self):
916
return FunctionDefinition(
1017
"attributes_not_null",
11-
"Check if required attributes are all set (not null).",
18+
"Check if attributes or nested reference attributes are all set (not as a default value or count_as_null value).",
1219
[
13-
ArgDefinition("checks", "A list of dictionaries with structure as {attribute: str, count_as_null: list[Any]} to be checked", True),
14-
ArgDefinition("resultMsgJSON", "Display output in JSON format", False)
15-
]
20+
ArgDefinition(
21+
"checks",
22+
"A list of dictionaries with structure as {attribute: str, count_as_null: list[Any]} to be checked" \
23+
"count_as_null can be an empty list.",
24+
True,
25+
),
26+
ArgDefinition("resultMsgJSON", "Display output in JSON format", False),
27+
],
1628
)
1729

1830
def execute(self):
@@ -23,47 +35,40 @@ def execute(self):
2335

2436
if not isinstance(checks, list):
2537
raise TypeError(f"Invalid function argument. Argument 'checks' should be a list, got {type(checks)}.")
26-
38+
2739
results = []
2840
for dict in checks:
29-
if 'attribute' not in dict or 'count_as_null' not in dict:
41+
if "attribute" not in dict or "count_as_null" not in dict:
3042
raise ValueError("Missing required keys: 'attribute' and/or 'count_as_null'.")
31-
32-
attr = dict['attribute']
33-
count_as_null = dict['count_as_null']
43+
44+
attr = dict["attribute"]
45+
count_as_null = dict["count_as_null"]
3446

3547
if not isinstance(attr, str):
3648
raise TypeError("'attribute' must be a string")
3749
if not isinstance(count_as_null, list):
3850
raise TypeError("'count_as_null' must be a list")
39-
40-
if not utils.has_attribute(value, attr):
41-
r = f"Attribute '{attr}' not found."
42-
results.append(r)
43-
else:
44-
# if attribute and value is set, eIsSet() returns True
45-
attrValueIsSet = self.value.eIsSet(attr)
4651

47-
if not attrValueIsSet:
48-
r = f"Attribute value of '{attr}' should be defined."
49-
results.append(r)
50-
# Handle the case when attribute value is set, but should still be considered as undefined.
51-
else:
52-
attrValue = utils.get_attribute(value, attr)
53-
54-
for nullValue in count_as_null:
55-
if (isinstance(nullValue, str) and str(nullValue).lower() == str(attrValue).lower()) \
56-
or (nullValue == attrValue):
57-
# TODO: more explicit message?
58-
r = f"'{attr}' value cannot be null."
59-
results.append(r)
60-
break
52+
attr_value = utils.get_attr_or_ref_attr(value, attr)
53+
if attr_value == "Not found":
54+
raise ValueError(f"Attribute [{attr}] not found.")
55+
elif attr_value == "Unset":
56+
results.append(f"[{attr}] value should be defined.")
57+
else:
58+
# Handle the case when attribute value is set, but should still be considered as unset.
59+
for nullValue in count_as_null:
60+
if (isinstance(nullValue, str) and str(nullValue).lower() == str(attr_value).lower()) or (
61+
nullValue == attr_value
62+
):
63+
r = f"[{attr}] value cannot be {nullValue}."
64+
results.append(r)
65+
break
6166

6267
if len(results) > 0:
63-
if 'resultMsgJSON' in self.args and self.args['resultMsgJSON']:
64-
msg["message"] = "; ".join(results)
68+
if "resultMsgJSON" in self.args and self.args["resultMsgJSON"]:
69+
msg["message"] = results
6570
return CheckResult(False, msg)
6671
else:
6772
return CheckResult(False, results)
6873
else:
69-
return CheckResult(True)
74+
return CheckResult(True)

esdlvalidator/validation/functions/utils.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,38 @@
33
from pyecore.ecore import EValue
44

55

6+
def get_attr_or_ref_attr(obj, attr_path: str):
7+
"""
8+
Retrieves an attribute value or a nested reference attribute value from an ESDL object.
9+
10+
Args:
11+
obj: The ESDL object to query.
12+
attr_path (str): A dot-separated string representing the attribute path.
13+
For instance, 'power' or 'costInformation.investmentCosts.value'.
14+
The attr_path string is case sensitive.
15+
16+
Returns:
17+
The attribute value if found and set.
18+
'Not found' if the attribute does not exist.
19+
'Unset' if the attribute exists but is not set.
20+
"""
21+
attr, *remaining = attr_path.split(".", 1)
22+
23+
not_found_keyword = "Not found"
24+
if getattr(obj, attr, not_found_keyword) == not_found_keyword:
25+
return not_found_keyword
26+
27+
if not obj.eIsSet(attr):
28+
return "Unset"
29+
30+
value = obj.eGet(attr)
31+
32+
if not remaining:
33+
return value
34+
35+
return get_attr_or_ref_attr(value, remaining[0])
36+
37+
638
def has_attribute(obj, name: str) -> bool:
739
# give a default "nothing_found" since None can be the actual returned value
840
result = get_attribute(obj, name, "nothing_found")

0 commit comments

Comments
 (0)