Skip to content

Commit 5f331c8

Browse files
authored
Merge pull request #171 from lshaw8317/enableLinspace
Make operands arg optional for lazyexprs
2 parents 402dc5d + 66c7dab commit 5f331c8

File tree

4 files changed

+65
-8
lines changed

4 files changed

+65
-8
lines changed

caterva2/client.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import functools
22
import io
3+
import logging
34
import pathlib
45
import sys
56
from collections.abc import Sequence
@@ -1129,7 +1130,7 @@ def copy(self, src, dst):
11291130
)
11301131
return pathlib.PurePosixPath(result)
11311132

1132-
def lazyexpr(self, name, expression, operands):
1133+
def lazyexpr(self, name, expression, operands=None, compute=False):
11331134
"""
11341135
Creates a lazy expression dataset in personal space.
11351136
@@ -1144,6 +1145,10 @@ def lazyexpr(self, name, expression, operands):
11441145
Expression to be evaluated, which must yield a lazy expression.
11451146
operands : dict
11461147
Mapping of variables in the expression to their corresponding dataset paths.
1148+
compute : bool, optional
1149+
If false, generate lazyexpr and do not compute anything.
1150+
If true, compute lazy expression on creation and save (full) result.
1151+
Default false.
11471152
11481153
Returns
11491154
-------
@@ -1165,8 +1170,14 @@ def lazyexpr(self, name, expression, operands):
11651170
"""
11661171
urlbase, _ = _format_paths(self.urlbase)
11671172
# Convert possible Path objects in operands to strings so that they can be serialized
1168-
operands = {k: str(v) for k, v in operands.items()}
1169-
expr = {"name": name, "expression": expression, "operands": operands}
1173+
if operands is not None:
1174+
operands = {k: str(v) for k, v in operands.items()}
1175+
else:
1176+
logging.warning(
1177+
"User has not provided operands for the LazyExpression. Proceeding with empty operands arg"
1178+
)
1179+
operands = {}
1180+
expr = {"name": name, "expression": expression, "operands": operands, "lazy": not compute}
11701181
dataset = api_utils.post(f"{self.urlbase}/api/lazyexpr/", expr, auth_cookie=self.cookie)
11711182
return pathlib.PurePosixPath(dataset)
11721183

caterva2/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class NewLazyExpr(pydantic.BaseModel):
6565
name: str
6666
expression: str
6767
operands: dict[str, str]
68+
lazy: bool
6869

6970

7071
class MoveCopyPayload(pydantic.BaseModel):

caterva2/services/sub.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ def error(msg):
969969
return fastapi.HTTPException(status_code=400, detail=msg) # bad request
970970

971971
try:
972-
result_path = make_expr(expr.name, expr.expression, expr.operands, user)
972+
result_path = make_expr(expr.name, expr.expression, expr.operands, user, expr.lazy)
973973
except (SyntaxError, ValueError, TypeError) as exc:
974974
raise error(f"Invalid name or expression: {exc}") from exc
975975
except KeyError as ke:

caterva2/tests/test_api.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ def test_lazyexpr(auth_client):
655655
lxinfo = auth_client.get_info(lxpath)
656656
assert lxinfo["shape"] == opinfo["shape"]
657657
assert lxinfo["dtype"] == opinfo["dtype"]
658-
assert lxinfo["expression"] == f"{expression}"
658+
assert lxinfo["expression"] == f"({expression})"
659659
assert lxinfo["operands"] == operands
660660

661661
# Check result data.
@@ -720,8 +720,8 @@ def test_expr_from_expr(auth_client):
720720
lxinfo2 = auth_client.get_info(lxpath2)
721721
assert lxinfo["shape"] == opinfo["shape"] == lxinfo2["shape"]
722722
assert lxinfo["dtype"] == opinfo["dtype"] == lxinfo2["dtype"]
723-
assert lxinfo["expression"] == f"{expression}"
724-
assert lxinfo2["expression"] == f"{expression2}"
723+
assert lxinfo["expression"] == f"({expression})"
724+
assert lxinfo2["expression"] == f"({expression2})"
725725
assert lxinfo["operands"] == operands
726726
assert lxinfo2["operands"] == operands2
727727

@@ -733,9 +733,54 @@ def test_expr_from_expr(auth_client):
733733
np.testing.assert_array_equal((a[:] + 1) * 2, c[:])
734734

735735

736-
# User management
736+
def test_expr_no_operand(auth_client):
737+
if not auth_client:
738+
pytest.skip("authentication support needed")
739+
740+
expression = "linspace(0, 10)"
741+
lxname = "my_expr"
742+
743+
auth_client.subscribe(TEST_CATERVA2_ROOT)
744+
lxpath = auth_client.lazyexpr(lxname, expression)
745+
assert lxpath == pathlib.Path(f"@personal/{lxname}.b2nd")
746+
c = auth_client.get(lxpath)
747+
a = blosc2.linspace(0, 10)
748+
np.testing.assert_array_equal(a[:], c[:])
749+
750+
# Check error when operand should be present but isn't
751+
opnm = "ds"
752+
oppt = f"{TEST_CATERVA2_ROOT}/ds-1d.b2nd"
753+
expression = "ds + linspace(0, 10)"
754+
lxname = "my_expr"
755+
756+
auth_client.subscribe(TEST_CATERVA2_ROOT)
757+
with pytest.raises(Exception) as e_info:
758+
lxpath = auth_client.lazyexpr(lxname, expression)
759+
760+
761+
def test_expr_force_compute(auth_client):
762+
if not auth_client:
763+
pytest.skip("authentication support needed")
764+
765+
expression = "linspace(0, 10)"
766+
lxname = "my_expr"
737767

768+
auth_client.subscribe(TEST_CATERVA2_ROOT)
769+
770+
# Uncomputed lazyexpr is a blosc2 lazyexpr
771+
lxpath = auth_client.lazyexpr(lxname, expression, compute=False)
772+
assert lxpath == pathlib.Path(f"@personal/{lxname}.b2nd")
773+
c = auth_client.get(lxpath)
774+
assert c.meta["expression"] == expression
738775

776+
# Computed lazyexpr is a blosc2 array
777+
lxpath = auth_client.lazyexpr(lxname, expression, compute=True)
778+
assert lxpath == pathlib.Path(f"@personal/{lxname}.b2nd")
779+
c = auth_client.get(lxpath)
780+
assert c.meta.get("expression", None) is None
781+
782+
783+
# User management
739784
def test_adduser(auth_client):
740785
if not auth_client:
741786
pytest.skip("authentication support needed")

0 commit comments

Comments
 (0)