Skip to content

Commit ba1eb22

Browse files
Update changelog
1 parent 17428c7 commit ba1eb22

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

docs/changelog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ _This project uses semantic versioning_
88
- BREAKING: Remove suport for Python 3.11 now that pyo3 has dropped support.
99
- Allow mutating methods to update their underlying expression via `Expr.__replace_expr__`, and ensure default rewrites return the mutated receiver when using `mutates_self` or `mutates_first_arg`.
1010
- BREAKING: Store `PyObject` values as `cloudpickle` bytes instead of live references so duplicates merge by value; `.value` now returns a fresh copy and the sort accepts objects like `None` that previously failed.
11-
- Adds `__call__` method to `PyObject` to replace `py_eval_fn` which is now deprecated.
11+
- Adds a `__call__` method (and `call_extended` for kwargs) to `PyObject` to replace `py_eval_fn`, which is now deprecated.
1212
- Improve doctest support, teaching expressions about their `__module__`, `__dir__`, and special methods.
1313
- Surface original Python exceptions from the runtime and tighten pretty-printing of values that cannot be re-parsed to make debugging e-graph executions easier.
1414
- Update the bundled Egglog crate, visualizer, and related dev dependencies (including `ipykernel`) to pick up the latest backend fixes.

docs/reference/python-integration.md

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,29 @@ Currently, we only support a few methods on `PyObject`s, but we plan to add more
146146
deserializes its inputs, performs the operation in Python, and then serializes the result back into a new
147147
`PyObject`, so previously stored values remain unchanged.
148148

149+
### Calling Stored Python Functions
150+
151+
Any `PyObject` whose value is callable can now be invoked directly. Each positional or keyword argument is first
152+
converted into a `PyObject`, the call executes inside Python, and the result is serialized back into a new
153+
`PyObject` expression.
154+
155+
```{code-cell} python
156+
scale = PyObject(lambda x, *, factor=1: x * factor)
157+
result = scale(21)
158+
159+
assert EGraph().extract(result).value == 21
160+
```
161+
162+
When you already have the arguments packaged up, use `call_extended` to forward explicit `*args`/`**kwargs`
163+
collections. This is needed when you want to call any keyword arguments as well which are not supported y the simple form:
164+
165+
```{code-cell} python
166+
args = PyObject((10,))
167+
kwargs = PyObject({"factor": 3})
168+
169+
assert EGraph().extract(scale.call_extended(args, kwargs)).value == 30
170+
```
171+
149172
Conversion to/from a string:
150173

151174
```{code-cell} python
@@ -186,21 +209,12 @@ evalled = py_eval("my_add(one, 2)", locals(), amended_globals)
186209
assert EGraph().extract(evalled).value == 3
187210
```
188211

189-
### Simpler Eval
190-
191-
Instead of using the above low level primitive for evaluating, there is a higher level wrapper function, `py_eval_fn`.
192-
193-
It takes in a Python function and converts it to a function of PyObjects, by using `py_eval` under the hood.
212+
### `py_eval_fn` (deprecated)
194213

195-
The above code code be re-written like this:
196-
197-
```{code-cell} python
198-
def my_add(a, b):
199-
return a + b
214+
The previous helper `py_eval_fn` is still available for backward compatibility, but it now simply returns
215+
`PyObject(fn)` and is deprecated. Prefer constructing a `PyObject` directly and calling it as shown above.
200216

201-
evalled = py_eval_fn(lambda a: my_add(a, 2))(1)
202-
assert EGraph().extract(evalled).value == 3
203-
```
217+
`py_eval_fn` returns a callable that mirrors the old behaviour, so existing code continues to work while you migrate.
204218

205219
## Functions
206220

@@ -376,6 +390,10 @@ if TRUE | FALSE:
376390
print("True!")
377391
```
378392

393+
Preserved methods now support binary operators as well. You can mark implementations like `__add__` or `__lt__` as
394+
preserved when you need their Python behaviour, and the runtime will call your preserved implementation for both the
395+
regular and reflected (`__r*__`) variants.
396+
379397
Note that the following list of methods are only supported as "preserved" since they have to return a specific Python object type:
380398

381399
- `__bool__`
@@ -474,7 +492,7 @@ mutate_egraph.check(eq(x).to(Int(10) + Int(1)))
474492
# incr with the rewrite could also be written like this:
475493
@function(mutates_first_arg=True)
476494
def incr_other(x: Int) -> None:
477-
self.__replace_expr__(x + Int(1))
495+
x.__replace_expr__(x + Int(1))
478496
x = Int(10)
479497
incr_other(x)
480498
mutate_egraph = EGraph()

0 commit comments

Comments
 (0)