Skip to content

Commit f749489

Browse files
authored
Merge pull request #365 from linkml/add-distribute-to-eval-utils
Adding option to eval to prevent distribution over arrays and dicts.
2 parents 6e33873 + c03e72d commit f749489

File tree

2 files changed

+12
-4
lines changed

2 files changed

+12
-4
lines changed

linkml_runtime/utils/eval_utils.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def eval_conditional(*conds: List[Tuple[bool, Any]]) -> Any:
3131
return val
3232

3333

34+
# (takes_list, func)
3435
funcs = {'max': (True, max),
3536
'min': (True, min),
3637
'len': (True, len),
@@ -43,7 +44,7 @@ class UnsetValueException(Exception):
4344
pass
4445

4546

46-
def eval_expr(expr: str, **kwargs) -> Any:
47+
def eval_expr(expr: str, _distribute=True, **kwargs) -> Any:
4748
"""
4849
Evaluates a given expression, with restricted syntax
4950
@@ -82,6 +83,9 @@ def eval_expr(expr: str, **kwargs) -> Any:
8283
- Similarly `strlen(container.persons.name)` will return a list whose members are the lengths of all names
8384
8485
:param expr: expression to evaluate
86+
:param _distribute: if True, distribute operations over collections and return array
87+
:param kwargs: variables to substitute
88+
:return: result of evaluation
8589
"""
8690
#if kwargs:
8791
# expr = expr.format(**kwargs)
@@ -90,13 +94,15 @@ def eval_expr(expr: str, **kwargs) -> Any:
9094
return None
9195
else:
9296
try:
93-
return eval_(ast.parse(expr, mode='eval').body, kwargs)
97+
return eval_(ast.parse(expr, mode='eval').body, kwargs, distribute=_distribute)
9498
except UnsetValueException:
9599
return None
96100

97101

98102

99-
def eval_(node, bindings={}):
103+
def eval_(node, bindings=None, distribute=True):
104+
if bindings is None:
105+
bindings = {}
100106
if isinstance(node, ast.Num):
101107
return node.n
102108
elif isinstance(node, ast.Str):
@@ -123,7 +129,7 @@ def eval_(node, bindings={}):
123129
# e.g. for person.name, this returns the val of person
124130
v = eval_(node.value, bindings)
125131
# lookup attribute, potentially distributing the results over collections
126-
def _get(obj: Any, k: str, recurse=True) -> Any:
132+
def _get(obj: Any, k: str, recurse=distribute) -> Any:
127133
if isinstance(obj, dict):
128134
# dicts are treated as collections; distribute results
129135
if recurse:

tests/test_utils/test_eval_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ def test_eval_expressions():
7070
assert eval_expr('c.person_index.name', c=c) == ['x', 'x2']
7171
assert eval_expr('c.person_index.address.street', c=c) == ['1 x street', '2 x street']
7272
assert eval_expr('strlen(c.person_index.name)', c=c) == [1, 2]
73+
pd = dict(name='x', aliases=['a', 'b', 'c'], address=Address(street='1 x street'))
74+
assert eval_expr('p.name', p=pd, _distribute=False) == 'x'
7375

7476

7577
def test_no_eval_prohibited():

0 commit comments

Comments
 (0)