Skip to content

Commit f1a5297

Browse files
authored
Merge pull request #127 from tdixon97/main
2 parents 4e548e8 + f5ed430 commit f1a5297

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

src/lgdo/types/table.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import logging
99
from collections.abc import Mapping
10+
from types import ModuleType
1011
from typing import Any
1112
from warnings import warn
1213

@@ -266,6 +267,7 @@ def eval(
266267
self,
267268
expr: str,
268269
parameters: Mapping[str, str] | None = None,
270+
modules: Mapping[str, ModuleType] | None = None,
269271
) -> LGDO:
270272
"""Apply column operations to the table and return a new LGDO.
271273
@@ -299,6 +301,10 @@ def eval(
299301
a dictionary of function parameters. Passed to
300302
:func:`numexpr.evaluate`` as `local_dict` argument or to
301303
:func:`eval` as `locals` argument.
304+
modules
305+
a dictionary of additional modules used by the expression. If this is not `None`
306+
then :func:`eval`is used and the expression can depend on any modules from this dictionary in
307+
addition to awkward and numpy. These are passed to :func:`eval` as `globals` argument.
302308
303309
Examples
304310
--------
@@ -339,8 +345,8 @@ def eval(
339345
msg = f"evaluating {expr!r} with locals={(self_unwrap | parameters)} and {has_ak=}"
340346
log.debug(msg)
341347

342-
# use numexpr if we are only dealing with numpy data types
343-
if not has_ak:
348+
# use numexpr if we are only dealing with numpy data types (and no global dictionary)
349+
if not has_ak and modules is None:
344350
out_data = ne.evaluate(
345351
expr,
346352
local_dict=(self_unwrap | parameters),
@@ -366,6 +372,9 @@ def eval(
366372

367373
# resort to good ol' eval()
368374
globs = {"ak": ak, "np": np}
375+
if modules is not None:
376+
globs = globs | modules
377+
369378
out_data = eval(expr, globs, (self_unwrap | parameters))
370379

371380
msg = f"...the result is {out_data!r}"
@@ -380,6 +389,10 @@ def eval(
380389
if np.isscalar(out_data):
381390
return Scalar(out_data)
382391

392+
# if out_data is already an LGDO just return it
393+
if isinstance(out_data, LGDO):
394+
return out_data
395+
383396
msg = (
384397
f"evaluation resulted in a {type(out_data)} object, "
385398
"I don't know which LGDO this corresponds to"

tests/types/test_table_eval.py

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

3+
import hist
34
import numpy as np
5+
import pytest
46

57
import lgdo
68

@@ -31,6 +33,7 @@ def test_eval_dependency():
3133
),
3234
}
3335
)
36+
3437
r = obj.eval("sum(a)")
3538
assert isinstance(r, lgdo.Scalar)
3639

@@ -77,3 +80,11 @@ def test_eval_dependency():
7780
assert isinstance(r, lgdo.Scalar)
7881

7982
assert obj.eval("np.sum(a) + ak.sum(e)")
83+
84+
# test with modules argument, the simplest is using directly lgdo
85+
res = obj.eval("lgdo.Array([1,2,3])", {}, modules={"lgdo": lgdo})
86+
assert res == lgdo.Array([1, 2, 3])
87+
88+
# check bad type
89+
with pytest.raises(RuntimeError):
90+
obj.eval("hist.Hist()", modules={"hist": hist})

0 commit comments

Comments
 (0)