diff --git a/README.md b/README.md index 965edc6..d73b7d8 100644 --- a/README.md +++ b/README.md @@ -27,17 +27,17 @@ ExpressionTuple((, 1)) `etuple`s can also be evaluated: ```python ->>> et.eval_obj +>>> et.evaled_obj 3 ``` Evaluated `etuple`s are cached: ```python >>> et = etuple(add, "a", "b") ->>> et.eval_obj +>>> et.evaled_obj 'ab' ->>> et.eval_obj is et.eval_obj +>>> et.evaled_obj is et.evaled_obj True ``` @@ -46,7 +46,7 @@ Reconstructed `etuple`s and their evaluation results are preserved across tuple >>> et_new = (et[0],) + et[1:] >>> et_new is et True ->>> et_new.eval_obj is et.eval_obj +>>> et_new.evaled_obj is et.evaled_obj True ``` @@ -115,7 +115,7 @@ def apply_Operator(rator, rands): >>> pprint(et) e(+, e(*, 1, 2), 3) ->>> et.eval_obj is add_node +>>> et.evaled_obj is add_node True ``` diff --git a/etuples/core.py b/etuples/core.py index 6c9f22e..b1174ce 100644 --- a/etuples/core.py +++ b/etuples/core.py @@ -1,5 +1,6 @@ import inspect import reprlib +import warnings from collections import deque from collections.abc import Generator, Sequence @@ -107,7 +108,7 @@ class ExpressionTuple(Sequence): TODO: Should probably use weakrefs for that. """ - __slots__ = ("_eval_obj", "_tuple", "_parent") + __slots__ = ("_evaled_obj", "_tuple", "_parent") null = object() def __new__(cls, seq=None, **kwargs): @@ -115,7 +116,7 @@ def __new__(cls, seq=None, **kwargs): # XXX: This doesn't actually remove the entry from the kwargs # passed to __init__! # It does, however, remove it for the check below. - kwargs.pop("eval_obj", None) + kwargs.pop("evaled_obj", None) if seq is not None and not kwargs and type(seq) == cls: return seq @@ -127,13 +128,13 @@ def __new__(cls, seq=None, **kwargs): def __init__(self, seq=None, **kwargs): """Create an expression tuple. - If the keyword 'eval_obj' is given, the `ExpressionTuple`'s + If the keyword 'evaled_obj' is given, the `ExpressionTuple`'s evaluated object is set to the corresponding value. XXX: There is no verification/check that the arguments evaluate to the - user-specified 'eval_obj', so be careful. + user-specified 'evaled_obj', so be careful. """ - _eval_obj = kwargs.pop("eval_obj", self.null) + _evaled_obj = kwargs.pop("evaled_obj", self.null) etuple_kwargs = tuple(KwdPair(k, v) for k, v in kwargs.items()) if seq: @@ -142,20 +143,29 @@ def __init__(self, seq=None, **kwargs): self._tuple = etuple_kwargs # TODO: Consider making these a weakrefs. - self._eval_obj = _eval_obj + self._evaled_obj = _evaled_obj self._parent = None @property - def eval_obj(self): + def evaled_obj(self): """Return the evaluation of this expression tuple.""" return trampoline_eval(self._eval_step()) + @property + def eval_obj(self): + warnings.warn( + "`eval_obj` is deprecated; use `evaled_obj`.", + DeprecationWarning, + stacklevel=2, + ) + return trampoline_eval(self._eval_step()) + def _eval_step(self): if len(self._tuple) == 0: raise InvalidExpression("Empty expression.") - if self._eval_obj is not self.null: - yield self._eval_obj + if self._evaled_obj is not self.null: + yield self._evaled_obj else: op = self._tuple[0] @@ -182,22 +192,22 @@ def _eval_step(self): op_sig = inspect.signature(op) except ValueError: # This handles some builtin function types - _eval_obj = op(*(evaled_args + [kw.value for kw in evaled_kwargs])) + _evaled_obj = op(*(evaled_args + [kw.value for kw in evaled_kwargs])) else: op_args = op_sig.bind( *evaled_args, **{kw.arg: kw.value for kw in evaled_kwargs} ) op_args.apply_defaults() - _eval_obj = op(*op_args.args, **op_args.kwargs) + _evaled_obj = op(*op_args.args, **op_args.kwargs) - # assert not isinstance(_eval_obj, ExpressionTuple) + # assert not isinstance(_evaled_obj, ExpressionTuple) - self._eval_obj = _eval_obj - yield self._eval_obj + self._evaled_obj = _evaled_obj + yield self._evaled_obj - @eval_obj.setter - def eval_obj(self, obj): + @evaled_obj.setter + def evaled_obj(self, obj): raise ValueError("Value of evaluated expression cannot be set!") def __add__(self, x): diff --git a/etuples/dispatch.py b/etuples/dispatch.py index e7e7a86..37bb0bc 100644 --- a/etuples/dispatch.py +++ b/etuples/dispatch.py @@ -94,7 +94,7 @@ def apply_Sequence(rator, rands): @apply.register(Callable, ExpressionTuple) def apply_ExpressionTuple(rator, rands): - return ((rator,) + rands).eval_obj + return ((rator,) + rands).evaled_obj # These are used to maintain some parity with the old `kanren.term` API @@ -160,6 +160,6 @@ def etuplize_step( ) et_args.append(e) - yield etuple(et_op, *et_args, eval_obj=x) + yield etuple(et_op, *et_args, evaled_obj=x) return trampoline_eval(etuplize_step(x)) diff --git a/tests/test_core.py b/tests/test_core.py index 5a592a7..80a1afb 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -37,32 +37,35 @@ def test_ExpressionTuple(capsys): assert 2 * e0 == ExpressionTuple((add, 1, 2, add, 1, 2)) e1 = ExpressionTuple((add, e0, 3)) - assert e1.eval_obj == 6 + assert e1.evaled_obj == 6 - # ("_eval_obj", "_tuple", "_parent") + # ("_evaled_obj", "_tuple", "_parent") e2 = e1[1:2] assert e2._parent is e1 assert e2 == ExpressionTuple((e0,)) - ExpressionTuple((print, "hi")).eval_obj + ExpressionTuple((print, "hi")).evaled_obj captured = capsys.readouterr() assert captured.out == "hi\n" e3 = ExpressionTuple(()) with pytest.raises(InvalidExpression): - e3.eval_obj + e3.evaled_obj e4 = ExpressionTuple((1,)) with pytest.raises(InvalidExpression): - e4.eval_obj + e4.evaled_obj - assert ExpressionTuple((ExpressionTuple((lambda: add,)), 1, 1)).eval_obj == 2 + assert ExpressionTuple((ExpressionTuple((lambda: add,)), 1, 1)).evaled_obj == 2 assert ExpressionTuple((1, 2)) != ExpressionTuple((1,)) assert ExpressionTuple((1, 2)) != ExpressionTuple((1, 3)) + with pytest.warns(DeprecationWarning): + ExpressionTuple((print, "hi")).eval_obj + def test_etuple(): """Test basic `etuple` functionality.""" @@ -72,17 +75,17 @@ def test_op(*args): e1 = etuple(test_op, 1, 2) - assert e1._eval_obj is ExpressionTuple.null + assert e1._evaled_obj is ExpressionTuple.null with pytest.raises(ValueError): - e1.eval_obj = 1 + e1.evaled_obj = 1 - e1_obj = e1.eval_obj + e1_obj = e1.evaled_obj assert len(e1_obj) == 3 assert all(type(o) == object for o in e1_obj) - # Make sure we don't re-create the cached `eval_obj` - e1_obj_2 = e1.eval_obj + # Make sure we don't re-create the cached `evaled_obj` + e1_obj_2 = e1.evaled_obj assert e1_obj == e1_obj_2 # Confirm that evaluation is recursive @@ -96,12 +99,12 @@ def test_op(*args): assert isinstance(e2[:1], ExpressionTuple) assert e2[1] == e2[1:2][0] - e2_obj = e2.eval_obj + e2_obj = e2.evaled_obj assert type(e2_obj) == tuple assert len(e2_obj) == 4 assert all(type(o) == object for o in e2_obj) - # Make sure that it used `e1`'s original `eval_obj` + # Make sure that it used `e1`'s original `evaled_obj` assert e2_obj[1:] == e1_obj # Confirm that any combination of `tuple`s/`etuple`s in @@ -128,7 +131,7 @@ def test_func(a, b, c=None, d="d-arg", **kwargs): return [a, b, c, d] e1 = etuple(test_func, 1, 2) - assert e1.eval_obj == [1, 2, None, "d-arg"] + assert e1.evaled_obj == [1, 2, None, "d-arg"] # Make sure we handle variadic args properly def test_func2(*args, c=None, d="d-arg", **kwargs): @@ -136,29 +139,29 @@ def test_func2(*args, c=None, d="d-arg", **kwargs): return list(args) + [c, d] e0 = etuple(test_func2, c=3) - assert e0.eval_obj == [3, "d-arg"] + assert e0.evaled_obj == [3, "d-arg"] e11 = etuple(test_func2, 1, 2) - assert e11.eval_obj == [1, 2, None, "d-arg"] + assert e11.evaled_obj == [1, 2, None, "d-arg"] e2 = etuple(test_func, 1, 2, 3) - assert e2.eval_obj == [1, 2, 3, "d-arg"] + assert e2.evaled_obj == [1, 2, 3, "d-arg"] e3 = etuple(test_func, 1, 2, 3, 4) - assert e3.eval_obj == [1, 2, 3, 4] + assert e3.evaled_obj == [1, 2, 3, 4] e4 = etuple(test_func, 1, 2, c=3) - assert e4.eval_obj == [1, 2, 3, "d-arg"] + assert e4.evaled_obj == [1, 2, 3, "d-arg"] e5 = etuple(test_func, 1, 2, d=3) - assert e5.eval_obj == [1, 2, None, 3] + assert e5.evaled_obj == [1, 2, None, 3] e6 = etuple(test_func, 1, 2, 3, d=4) - assert e6.eval_obj == [1, 2, 3, 4] + assert e6.evaled_obj == [1, 2, 3, 4] # Try evaluating nested etuples e7 = etuple(test_func, etuple(add, 1, 0), 2, c=etuple(add, 1, etuple(add, 1, 1))) - assert e7.eval_obj == [1, 2, 3, "d-arg"] + assert e7.evaled_obj == [1, 2, 3, "d-arg"] # Try a function without an obtainable signature object e8 = etuple( @@ -166,12 +169,12 @@ def test_func2(*args, c=None, d="d-arg", **kwargs): etuple(list, ["a", "b", "c", "d"]), start=etuple(add, 1, etuple(add, 1, 1)), ) - assert list(e8.eval_obj) == [(3, "a"), (4, "b"), (5, "c"), (6, "d")] + assert list(e8.evaled_obj) == [(3, "a"), (4, "b"), (5, "c"), (6, "d")] - # Use "eval_obj" kwarg and make sure it doesn't end up in the `_tuple` object - e9 = etuple(add, 1, 2, eval_obj=3) + # Use "evaled_obj" kwarg and make sure it doesn't end up in the `_tuple` object + e9 = etuple(add, 1, 2, evaled_obj=3) assert e9._tuple == (add, 1, 2) - assert e9._eval_obj == 3 + assert e9._evaled_obj == 3 def test_str(): @@ -209,7 +212,7 @@ def gen_long_add_chain(N=None, num=1): def test_reify_recursion_limit(): a = gen_long_add_chain(10) - assert a.eval_obj == 11 + assert a.evaled_obj == 11 r_limit = sys.getrecursionlimit() @@ -217,10 +220,10 @@ def test_reify_recursion_limit(): sys.setrecursionlimit(100) a = gen_long_add_chain(200) - assert a.eval_obj == 201 + assert a.evaled_obj == 201 b = gen_long_add_chain(200, num=2) - assert b.eval_obj == 402 + assert b.evaled_obj == 402 c = gen_long_add_chain(200) assert a == c diff --git a/tests/test_dispatch.py b/tests/test_dispatch.py index 5ebdbee..4f9d045 100644 --- a/tests/test_dispatch.py +++ b/tests/test_dispatch.py @@ -44,17 +44,17 @@ def test_etuple_apply(): assert apply(add, (1, 2)) == 3 assert apply(1, (2,)) == (1, 2) - # Make sure that we don't lose underlying `eval_obj`s + # Make sure that we don't lose underlying `evaled_obj`s # when taking apart and re-creating expression tuples # using `kanren`'s `operator`, `arguments` and `term` # functions. e1 = etuple(add, (object(),), (object(),)) - e1_obj = e1.eval_obj + e1_obj = e1.evaled_obj e1_dup = (rator(e1),) + rands(e1) assert isinstance(e1_dup, ExpressionTuple) - assert e1_dup.eval_obj == e1_obj + assert e1_dup.evaled_obj == e1_obj e1_dup_2 = apply(rator(e1), rands(e1)) assert e1_dup_2 == e1_obj @@ -128,7 +128,7 @@ def test_unification(): assert res == {a_lv: 1} et = etuple(add, 1, 2) - assert et.eval_obj == 3 + assert et.evaled_obj == 3 res = unify(et, cons(a_lv, b_lv)) assert res == {a_lv: add, b_lv: et[1:]} @@ -136,7 +136,7 @@ def test_unification(): # Make sure we've preserved the original object after deconstruction via # `unify` assert res[b_lv]._parent is et - assert ((res[a_lv],) + res[b_lv])._eval_obj == 3 + assert ((res[a_lv],) + res[b_lv])._evaled_obj == 3 # Make sure we've preserved the original object after reconstruction via # `reify`