You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/reference/python-integration.md
+90-19Lines changed: 90 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,17 +6,88 @@ file_format: mystnb
6
6
7
7
Alongside [the support for builtin `egglog` functionality](./egglog-translation.md), `egglog` also provides functionality to more easily integrate with the Python ecosystem.
8
8
9
-
## Retrieving Primitive Values
9
+
## Retrieving Values
10
10
11
-
If you have a egglog primitive, you can turn it into a Python object by using `egraph.eval(...)` method:
11
+
If you have an egglog value, you might want to convert it from an expression to a native Python object. This is done through a number of helper functions:
12
+
13
+
For a primitive value (like `i64`, `f64`, `Bool`, `String`, or `PyObject`), use `get_literal_value(expr)` or the `.value` property:
12
14
13
15
```{code-cell} python
14
16
from __future__ import annotations
15
17
16
18
from egglog import *
17
19
18
-
egraph = EGraph()
19
-
assert egraph.eval(i64(1) + 20) == 21
20
+
assert get_literal_value(i64(42)) == 42
21
+
assert get_literal_value(i64(42) + i64(1)) == None # This is because i64(42) + i64(1) is a call expression, not a literal
22
+
assert i64(42).value == 42
23
+
assert get_literal_value(f64(3.14)) == 3.14
24
+
assert Bool(True).value is True
25
+
assert String("hello").value == "hello"
26
+
assert PyObject([1,2,3]).value == [1,2,3]
27
+
```
28
+
29
+
To check if an expression is a let value and get its name, use `get_let_name(expr)`:
30
+
31
+
```{code-cell} python
32
+
x = EGraph().let("my_var", i64(1))
33
+
assert get_let_name(x) == "my_var"
34
+
```
35
+
36
+
To check if an expression is a variable and get its name, use `get_var_name(expr)`:
37
+
38
+
```{code-cell} python
39
+
from egglog import var, get_var_name
40
+
v = var("x", i64)
41
+
assert get_var_name(v) == "x"
42
+
```
43
+
44
+
For a callable (method, function, classmethod, or constructor), use `get_callable_fn(expr)` to get the underlying Python function:
45
+
46
+
```{code-cell} python
47
+
expr = i64(1) + i64(2)
48
+
fn = get_callable_fn(expr)
49
+
assert fn == i64.__add__
50
+
```
51
+
52
+
To get the arguments to a callable, use `get_callable_args(expr)`. If you want to match against a specific callable, use `get_callable_args(expr, fn)`, where `fn` is the Python function you want to match against. This will return `None` if the callable does not match the function, and if it does match, the args will be properly typed:
You can use Python's structural pattern matching (`match`/`case`) to destructure egglog primitives:
64
+
65
+
```{code-cell} python
66
+
x = i64(5)
67
+
match i64(5):
68
+
case i64(i):
69
+
print(f"Integer literal: {i}")
70
+
```
71
+
72
+
You can add custom support for pattern matching against your classes by adding `__match_args__` to your class:
73
+
74
+
```python
75
+
classMyExpr(Expr):
76
+
def__init__(self, value: StringLike): ...
77
+
78
+
__match_args__= ("value",)
79
+
80
+
@method(preserve=True)
81
+
@property
82
+
defvalue(self) -> str:
83
+
match get_callable_args(self, MyExpr):
84
+
case (String(value),):
85
+
return value
86
+
raise ExprValueError(self, "MyExpr")
87
+
88
+
match MyExpr("hello"):
89
+
case MyExpr(value):
90
+
print(f"Matched MyExpr with value: {value}")
20
91
```
21
92
22
93
## Python Object Sort
@@ -53,10 +124,10 @@ Creating hashable objects is safer, since while the rule might create new Python
53
124
54
125
### Retrieving Python Objects
55
126
56
-
Like other primitives, we can retrieve the Python object from the e-graph by using the `egraph.eval(...)` method:
127
+
Like other primitives, we can retrieve the Python object from the e-graph by using the `.value` property:
57
128
58
129
```{code-cell} python
59
-
assert egraph.eval(lst) == [1, 2, 3]
130
+
assert lst.value == [1, 2, 3]
60
131
```
61
132
62
133
### Builtin methods
@@ -66,29 +137,29 @@ Currently, we only support a few methods on `PyObject`s, but we plan to add more
66
137
Conversion to/from a string:
67
138
68
139
```{code-cell} python
69
-
egraph.eval(PyObject('hi').to_string())
140
+
EGraph().extract(PyObject('hi').to_string())
70
141
```
71
142
72
143
```{code-cell} python
73
-
egraph.eval(PyObject.from_string("1"))
144
+
EGraph().extract(PyObject.from_string("1"))
74
145
```
75
146
76
147
Conversion from an int:
77
148
78
149
```{code-cell} python
79
-
egraph.eval(PyObject.from_int(1))
150
+
EGraph().extract(PyObject.from_int(1))
80
151
```
81
152
82
153
We also support evaluating arbitrary Python code, given some locals and globals. This technically allows us to implement any Python method:
83
154
84
155
```{code-cell} python
85
-
egraph.eval(py_eval("1 + 2"))
156
+
EGraph().extract(py_eval("1 + 2"))
86
157
```
87
158
88
159
Executing Python code is also supported. In this case, the return value will be the updated globals dict, which will be copied first before using.
89
160
90
161
```{code-cell} python
91
-
egraph.eval(py_exec("x = 1 + 2"))
162
+
EGraph().extract(py_exec("x = 1 + 2"))
92
163
```
93
164
94
165
Alongside this, we support a function `dict_update` method, which can allow you to combine some local egglog expressions alongside, say, the locals and globals of the Python code you are evaluating.
0 commit comments