taskflow-tr 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- taskflow_tr-0.1.0/LICENSE +21 -0
- taskflow_tr-0.1.0/PKG-INFO +345 -0
- taskflow_tr-0.1.0/README.md +287 -0
- taskflow_tr-0.1.0/pyproject.toml +57 -0
- taskflow_tr-0.1.0/setup.cfg +4 -0
- taskflow_tr-0.1.0/task_flow/__init__.py +13 -0
- taskflow_tr-0.1.0/task_flow/api.py +121 -0
- taskflow_tr-0.1.0/task_flow/compiler/__init__.py +4 -0
- taskflow_tr-0.1.0/task_flow/compiler/builder.py +61 -0
- taskflow_tr-0.1.0/task_flow/compiler/compiler.py +161 -0
- taskflow_tr-0.1.0/task_flow/ir/__init__.py +3 -0
- taskflow_tr-0.1.0/task_flow/ir/graph.py +68 -0
- taskflow_tr-0.1.0/task_flow/printer/__init__.py +3 -0
- taskflow_tr-0.1.0/task_flow/printer/printer.py +112 -0
- taskflow_tr-0.1.0/task_flow/runtime/__init__.py +3 -0
- taskflow_tr-0.1.0/task_flow/runtime/executor.py +140 -0
- taskflow_tr-0.1.0/taskflow_tr.egg-info/PKG-INFO +345 -0
- taskflow_tr-0.1.0/taskflow_tr.egg-info/SOURCES.txt +24 -0
- taskflow_tr-0.1.0/taskflow_tr.egg-info/dependency_links.txt +1 -0
- taskflow_tr-0.1.0/taskflow_tr.egg-info/requires.txt +7 -0
- taskflow_tr-0.1.0/taskflow_tr.egg-info/top_level.txt +1 -0
- taskflow_tr-0.1.0/tests/test_api.py +62 -0
- taskflow_tr-0.1.0/tests/test_compiler.py +59 -0
- taskflow_tr-0.1.0/tests/test_executor.py +38 -0
- taskflow_tr-0.1.0/tests/test_ir.py +20 -0
- taskflow_tr-0.1.0/tests/test_printer.py +32 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gaoxinge
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: taskflow-tr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Compile Python functions into GraphIR DAGs and execute them inline, with threads, or with processes.
|
|
5
|
+
Author-email: gaoxinge <gaoxx5@gmail.com>
|
|
6
|
+
Maintainer-email: gaoxinge <gaoxx5@gmail.com>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026 gaoxinge
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
|
|
29
|
+
Project-URL: Homepage, https://github.com/gaoxinge/task_flow
|
|
30
|
+
Project-URL: Repository, https://github.com/gaoxinge/task_flow
|
|
31
|
+
Project-URL: Issues, https://github.com/gaoxinge/task_flow/issues
|
|
32
|
+
Project-URL: Documentation, https://github.com/gaoxinge/task_flow#readme
|
|
33
|
+
Keywords: dag,graph-ir,workflow,concurrency,threading,multiprocessing
|
|
34
|
+
Classifier: Development Status :: 3 - Alpha
|
|
35
|
+
Classifier: Intended Audience :: Developers
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
45
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
46
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
47
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
48
|
+
Requires-Python: >=3.8
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
License-File: LICENSE
|
|
51
|
+
Provides-Extra: examples
|
|
52
|
+
Requires-Dist: flask>=2; extra == "examples"
|
|
53
|
+
Requires-Dist: grpcio>=1.48; extra == "examples"
|
|
54
|
+
Requires-Dist: grpcio-tools>=1.48; extra == "examples"
|
|
55
|
+
Requires-Dist: protobuf>=3.20; extra == "examples"
|
|
56
|
+
Requires-Dist: requests>=2; extra == "examples"
|
|
57
|
+
Dynamic: license-file
|
|
58
|
+
|
|
59
|
+
# taskflow-tr
|
|
60
|
+
|
|
61
|
+
taskflow-tr 将一小部分 Python 函数编译成静态 `GraphIR`,可打印计算图,也可使用串行、线程池或进程池执行。PyPI 发行名是 `taskflow-tr`,Python 导入名是 `task_flow`。
|
|
62
|
+
|
|
63
|
+
## 环境与安装
|
|
64
|
+
|
|
65
|
+
项目最低支持 Python 3.8。通过 PyPI 安装核心包:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install taskflow-tr
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
如需运行 README 中的 HTTP 和 gRPC 集成示例:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install "taskflow-tr[examples]"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
项目开发环境使用 [uv](https://docs.astral.sh/uv/) 管理:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv sync --all-groups
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 直接执行函数
|
|
84
|
+
|
|
85
|
+
`@transform` 默认使用 `InlineExecutor`:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from task_flow import transform
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@transform
|
|
92
|
+
def add(a, b):
|
|
93
|
+
return a + b
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
assert add(2, b=1) == 3
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
显式使用线程池或进程池时必须指定工作数量:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
@transform(executor="thread", workers=4)
|
|
103
|
+
def threaded_add(a, b):
|
|
104
|
+
return a + b
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@transform(executor="process", workers=4)
|
|
108
|
+
def process_add(a, b):
|
|
109
|
+
return a + b
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`@transform()` 是非法形式;带括号时必须显式传入 `executor`。
|
|
113
|
+
|
|
114
|
+
## 编译和打印 GraphIR
|
|
115
|
+
|
|
116
|
+
`@compile` 只编译函数,不绑定执行策略。`print()` 默认输出 JSON:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from task_flow import compile
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@compile
|
|
123
|
+
def add(a, b):
|
|
124
|
+
return a + b
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
print(add)
|
|
128
|
+
print(format(add, "dict"))
|
|
129
|
+
print(format(add, "mermaid"))
|
|
130
|
+
print(format(add, "graphviz"))
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
也可以在装饰时改变默认打印格式:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
@compile(format="mermaid")
|
|
137
|
+
def workflow(a, b):
|
|
138
|
+
return a + b
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
print(workflow)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
支持 `dict`、`json`、`mermaid` 和 `graphviz` 四种格式。Graphviz 格式返回 DOT 源码,不要求 Runtime 安装 Graphviz。
|
|
145
|
+
|
|
146
|
+
`@compile()` 是非法形式;带括号时必须显式传入 `format`。
|
|
147
|
+
|
|
148
|
+
## 显式选择 Executor
|
|
149
|
+
|
|
150
|
+
编译结果可以被不同 Executor 重复执行,无需重新编译:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from task_flow import InlineExecutor, ProcessExecutor, ThreadExecutor
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
with InlineExecutor() as executor:
|
|
157
|
+
assert executor.run(add, args=(2,), kwargs={"b": 1}) == 3
|
|
158
|
+
|
|
159
|
+
with ThreadExecutor(thread_num=4) as executor:
|
|
160
|
+
assert executor.run(add, args=(2, 1)) == 3
|
|
161
|
+
|
|
162
|
+
with ProcessExecutor(process_num=4) as executor:
|
|
163
|
+
assert executor.run(add, args=(2, 1)) == 3
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
线程执行适合 I/O 或会释放 GIL 的扩展调用;进程执行适合 CPU 密集型 Python 代码。进程池中的调用函数与参数必须能被 `pickle` 序列化,应优先使用模块顶层命名函数,避免 lambda 和局部函数。
|
|
167
|
+
|
|
168
|
+
## 完整示例
|
|
169
|
+
|
|
170
|
+
### 并行计算与 Graphviz 输出
|
|
171
|
+
|
|
172
|
+
下面的计算图包含四个相互独立的运算节点,线程 Executor 可以并发执行它们:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
import time
|
|
176
|
+
from operator import add, floordiv, mul, sub
|
|
177
|
+
|
|
178
|
+
from task_flow import compile, transform
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def delayed(operation, x, y):
|
|
182
|
+
time.sleep(0.1)
|
|
183
|
+
return operation(x, y)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def add0(x, y):
|
|
187
|
+
return delayed(add, x, y)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def sub0(x, y):
|
|
191
|
+
return delayed(sub, x, y)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def mul0(x, y):
|
|
195
|
+
return delayed(mul, x, y)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def div0(x, y):
|
|
199
|
+
return delayed(floordiv, x, y)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@transform(executor="thread", workers=4)
|
|
203
|
+
def compute_graph(a, b):
|
|
204
|
+
result_add = add0(a, b)
|
|
205
|
+
result_sub = sub0(a, b)
|
|
206
|
+
result_mul = mul0(a, b)
|
|
207
|
+
result_div = div0(a, b)
|
|
208
|
+
return result_add, result_sub, result_mul, result_div
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
assert compute_graph(2, 1) == (3, 1, 2, 2)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@compile(format="graphviz")
|
|
215
|
+
def printable_graph(a, b):
|
|
216
|
+
result_add = a + b
|
|
217
|
+
result_sub = a - b
|
|
218
|
+
result_mul = a * b
|
|
219
|
+
result_div = a // b
|
|
220
|
+
return result_add, result_sub, result_mul, result_div
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
print(printable_graph) # 输出 DOT 源码
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 集成 HTTP 服务
|
|
227
|
+
|
|
228
|
+
HTTP 和 gRPC 只是 `TransformedFunction` 的调用入口,调度逻辑仍由 taskflow-tr 管理。安装示例依赖:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
uv sync --extra examples
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
使用 Flask 暴露计算接口:
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from flask import Flask, jsonify, request
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
app = Flask(__name__)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@app.post("/compute")
|
|
244
|
+
def compute():
|
|
245
|
+
inputs = request.get_json()
|
|
246
|
+
x, y, z, w = compute_graph(inputs["x"], inputs["y"])
|
|
247
|
+
return jsonify({"x": x, "y": y, "z": z, "w": w})
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
if __name__ == "__main__":
|
|
251
|
+
app.run(host="0.0.0.0", port=8000)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
请求示例:
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
import requests
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
response = requests.post(
|
|
261
|
+
"http://127.0.0.1:8000/compute",
|
|
262
|
+
json={"x": 2, "y": 1},
|
|
263
|
+
timeout=30,
|
|
264
|
+
)
|
|
265
|
+
assert response.json() == {"x": 3, "y": 1, "z": 2, "w": 2}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### 集成 gRPC 服务
|
|
269
|
+
|
|
270
|
+
先定义 `compute.proto`:
|
|
271
|
+
|
|
272
|
+
```proto
|
|
273
|
+
syntax = "proto3";
|
|
274
|
+
package example;
|
|
275
|
+
|
|
276
|
+
message Inputs {
|
|
277
|
+
int32 x = 1;
|
|
278
|
+
int32 y = 2;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
message Outputs {
|
|
282
|
+
int32 x = 1;
|
|
283
|
+
int32 y = 2;
|
|
284
|
+
int32 z = 3;
|
|
285
|
+
int32 w = 4;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
service App {
|
|
289
|
+
rpc Compute (Inputs) returns (Outputs);
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
生成 Python 代码:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
uv run python -m grpc_tools.protoc \
|
|
297
|
+
-I. \
|
|
298
|
+
--python_out=. \
|
|
299
|
+
--grpc_python_out=. \
|
|
300
|
+
compute.proto
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
在生成的服务接口中调用同一个 `compute_graph`:
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
from concurrent import futures
|
|
307
|
+
|
|
308
|
+
import grpc
|
|
309
|
+
import compute_pb2
|
|
310
|
+
import compute_pb2_grpc
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class App(compute_pb2_grpc.AppServicer):
|
|
314
|
+
def Compute(self, inputs, context):
|
|
315
|
+
x, y, z, w = compute_graph(inputs.x, inputs.y)
|
|
316
|
+
return compute_pb2.Outputs(x=x, y=y, z=z, w=w)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
server = grpc.server(futures.ThreadPoolExecutor(max_workers=3))
|
|
320
|
+
compute_pb2_grpc.add_AppServicer_to_server(App(), server)
|
|
321
|
+
server.add_insecure_port("0.0.0.0:8000")
|
|
322
|
+
server.start()
|
|
323
|
+
server.wait_for_termination()
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## V1 编译范围
|
|
327
|
+
|
|
328
|
+
V1 支持普通位置或关键字参数、常量、简单赋值、普通函数调用、`+`、`-`、`*`、`/`、`//`,以及 `None`、单值、tuple 和 list 返回。条件、循环、递归、可变参数、默认参数和关键字调用参数尚未纳入 V1。
|
|
329
|
+
|
|
330
|
+
总体流程:
|
|
331
|
+
|
|
332
|
+
```text
|
|
333
|
+
Python Source -> Python AST -> GraphIR -> Executor -> Result
|
|
334
|
+
|
|
|
335
|
+
+-> Printer -> dict / JSON / Mermaid / Graphviz
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
V1 不包含 Planner、ExecutablePlanIR、Dask、Ray 或 MPI 后端。设计说明见 [`docs/v1/dev.md`](docs/v1/dev.md)。
|
|
339
|
+
|
|
340
|
+
## 开发
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
uv run pytest
|
|
344
|
+
uv run python -m benchmarks.benchmark_executor
|
|
345
|
+
```
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# taskflow-tr
|
|
2
|
+
|
|
3
|
+
taskflow-tr 将一小部分 Python 函数编译成静态 `GraphIR`,可打印计算图,也可使用串行、线程池或进程池执行。PyPI 发行名是 `taskflow-tr`,Python 导入名是 `task_flow`。
|
|
4
|
+
|
|
5
|
+
## 环境与安装
|
|
6
|
+
|
|
7
|
+
项目最低支持 Python 3.8。通过 PyPI 安装核心包:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install taskflow-tr
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
如需运行 README 中的 HTTP 和 gRPC 集成示例:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install "taskflow-tr[examples]"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
项目开发环境使用 [uv](https://docs.astral.sh/uv/) 管理:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv sync --all-groups
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 直接执行函数
|
|
26
|
+
|
|
27
|
+
`@transform` 默认使用 `InlineExecutor`:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from task_flow import transform
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@transform
|
|
34
|
+
def add(a, b):
|
|
35
|
+
return a + b
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
assert add(2, b=1) == 3
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
显式使用线程池或进程池时必须指定工作数量:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
@transform(executor="thread", workers=4)
|
|
45
|
+
def threaded_add(a, b):
|
|
46
|
+
return a + b
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@transform(executor="process", workers=4)
|
|
50
|
+
def process_add(a, b):
|
|
51
|
+
return a + b
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`@transform()` 是非法形式;带括号时必须显式传入 `executor`。
|
|
55
|
+
|
|
56
|
+
## 编译和打印 GraphIR
|
|
57
|
+
|
|
58
|
+
`@compile` 只编译函数,不绑定执行策略。`print()` 默认输出 JSON:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from task_flow import compile
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@compile
|
|
65
|
+
def add(a, b):
|
|
66
|
+
return a + b
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
print(add)
|
|
70
|
+
print(format(add, "dict"))
|
|
71
|
+
print(format(add, "mermaid"))
|
|
72
|
+
print(format(add, "graphviz"))
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
也可以在装饰时改变默认打印格式:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
@compile(format="mermaid")
|
|
79
|
+
def workflow(a, b):
|
|
80
|
+
return a + b
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
print(workflow)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
支持 `dict`、`json`、`mermaid` 和 `graphviz` 四种格式。Graphviz 格式返回 DOT 源码,不要求 Runtime 安装 Graphviz。
|
|
87
|
+
|
|
88
|
+
`@compile()` 是非法形式;带括号时必须显式传入 `format`。
|
|
89
|
+
|
|
90
|
+
## 显式选择 Executor
|
|
91
|
+
|
|
92
|
+
编译结果可以被不同 Executor 重复执行,无需重新编译:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from task_flow import InlineExecutor, ProcessExecutor, ThreadExecutor
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
with InlineExecutor() as executor:
|
|
99
|
+
assert executor.run(add, args=(2,), kwargs={"b": 1}) == 3
|
|
100
|
+
|
|
101
|
+
with ThreadExecutor(thread_num=4) as executor:
|
|
102
|
+
assert executor.run(add, args=(2, 1)) == 3
|
|
103
|
+
|
|
104
|
+
with ProcessExecutor(process_num=4) as executor:
|
|
105
|
+
assert executor.run(add, args=(2, 1)) == 3
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
线程执行适合 I/O 或会释放 GIL 的扩展调用;进程执行适合 CPU 密集型 Python 代码。进程池中的调用函数与参数必须能被 `pickle` 序列化,应优先使用模块顶层命名函数,避免 lambda 和局部函数。
|
|
109
|
+
|
|
110
|
+
## 完整示例
|
|
111
|
+
|
|
112
|
+
### 并行计算与 Graphviz 输出
|
|
113
|
+
|
|
114
|
+
下面的计算图包含四个相互独立的运算节点,线程 Executor 可以并发执行它们:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import time
|
|
118
|
+
from operator import add, floordiv, mul, sub
|
|
119
|
+
|
|
120
|
+
from task_flow import compile, transform
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def delayed(operation, x, y):
|
|
124
|
+
time.sleep(0.1)
|
|
125
|
+
return operation(x, y)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def add0(x, y):
|
|
129
|
+
return delayed(add, x, y)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def sub0(x, y):
|
|
133
|
+
return delayed(sub, x, y)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def mul0(x, y):
|
|
137
|
+
return delayed(mul, x, y)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def div0(x, y):
|
|
141
|
+
return delayed(floordiv, x, y)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@transform(executor="thread", workers=4)
|
|
145
|
+
def compute_graph(a, b):
|
|
146
|
+
result_add = add0(a, b)
|
|
147
|
+
result_sub = sub0(a, b)
|
|
148
|
+
result_mul = mul0(a, b)
|
|
149
|
+
result_div = div0(a, b)
|
|
150
|
+
return result_add, result_sub, result_mul, result_div
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
assert compute_graph(2, 1) == (3, 1, 2, 2)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@compile(format="graphviz")
|
|
157
|
+
def printable_graph(a, b):
|
|
158
|
+
result_add = a + b
|
|
159
|
+
result_sub = a - b
|
|
160
|
+
result_mul = a * b
|
|
161
|
+
result_div = a // b
|
|
162
|
+
return result_add, result_sub, result_mul, result_div
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
print(printable_graph) # 输出 DOT 源码
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 集成 HTTP 服务
|
|
169
|
+
|
|
170
|
+
HTTP 和 gRPC 只是 `TransformedFunction` 的调用入口,调度逻辑仍由 taskflow-tr 管理。安装示例依赖:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
uv sync --extra examples
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
使用 Flask 暴露计算接口:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from flask import Flask, jsonify, request
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
app = Flask(__name__)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@app.post("/compute")
|
|
186
|
+
def compute():
|
|
187
|
+
inputs = request.get_json()
|
|
188
|
+
x, y, z, w = compute_graph(inputs["x"], inputs["y"])
|
|
189
|
+
return jsonify({"x": x, "y": y, "z": z, "w": w})
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if __name__ == "__main__":
|
|
193
|
+
app.run(host="0.0.0.0", port=8000)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
请求示例:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
import requests
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
response = requests.post(
|
|
203
|
+
"http://127.0.0.1:8000/compute",
|
|
204
|
+
json={"x": 2, "y": 1},
|
|
205
|
+
timeout=30,
|
|
206
|
+
)
|
|
207
|
+
assert response.json() == {"x": 3, "y": 1, "z": 2, "w": 2}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 集成 gRPC 服务
|
|
211
|
+
|
|
212
|
+
先定义 `compute.proto`:
|
|
213
|
+
|
|
214
|
+
```proto
|
|
215
|
+
syntax = "proto3";
|
|
216
|
+
package example;
|
|
217
|
+
|
|
218
|
+
message Inputs {
|
|
219
|
+
int32 x = 1;
|
|
220
|
+
int32 y = 2;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
message Outputs {
|
|
224
|
+
int32 x = 1;
|
|
225
|
+
int32 y = 2;
|
|
226
|
+
int32 z = 3;
|
|
227
|
+
int32 w = 4;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
service App {
|
|
231
|
+
rpc Compute (Inputs) returns (Outputs);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
生成 Python 代码:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
uv run python -m grpc_tools.protoc \
|
|
239
|
+
-I. \
|
|
240
|
+
--python_out=. \
|
|
241
|
+
--grpc_python_out=. \
|
|
242
|
+
compute.proto
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
在生成的服务接口中调用同一个 `compute_graph`:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from concurrent import futures
|
|
249
|
+
|
|
250
|
+
import grpc
|
|
251
|
+
import compute_pb2
|
|
252
|
+
import compute_pb2_grpc
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class App(compute_pb2_grpc.AppServicer):
|
|
256
|
+
def Compute(self, inputs, context):
|
|
257
|
+
x, y, z, w = compute_graph(inputs.x, inputs.y)
|
|
258
|
+
return compute_pb2.Outputs(x=x, y=y, z=z, w=w)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
server = grpc.server(futures.ThreadPoolExecutor(max_workers=3))
|
|
262
|
+
compute_pb2_grpc.add_AppServicer_to_server(App(), server)
|
|
263
|
+
server.add_insecure_port("0.0.0.0:8000")
|
|
264
|
+
server.start()
|
|
265
|
+
server.wait_for_termination()
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## V1 编译范围
|
|
269
|
+
|
|
270
|
+
V1 支持普通位置或关键字参数、常量、简单赋值、普通函数调用、`+`、`-`、`*`、`/`、`//`,以及 `None`、单值、tuple 和 list 返回。条件、循环、递归、可变参数、默认参数和关键字调用参数尚未纳入 V1。
|
|
271
|
+
|
|
272
|
+
总体流程:
|
|
273
|
+
|
|
274
|
+
```text
|
|
275
|
+
Python Source -> Python AST -> GraphIR -> Executor -> Result
|
|
276
|
+
|
|
|
277
|
+
+-> Printer -> dict / JSON / Mermaid / Graphviz
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
V1 不包含 Planner、ExecutablePlanIR、Dask、Ray 或 MPI 后端。设计说明见 [`docs/v1/dev.md`](docs/v1/dev.md)。
|
|
281
|
+
|
|
282
|
+
## 开发
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
uv run pytest
|
|
286
|
+
uv run python -m benchmarks.benchmark_executor
|
|
287
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "taskflow-tr"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Compile Python functions into GraphIR DAGs and execute them inline, with threads, or with processes."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {file = "LICENSE"}
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [{name = "gaoxinge", email = "gaoxx5@gmail.com"}]
|
|
13
|
+
maintainers = [{name = "gaoxinge", email = "gaoxx5@gmail.com"}]
|
|
14
|
+
keywords = ["dag", "graph-ir", "workflow", "concurrency", "threading", "multiprocessing"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
29
|
+
"Topic :: System :: Distributed Computing",
|
|
30
|
+
]
|
|
31
|
+
dependencies = []
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
examples = ["flask>=2", "grpcio>=1.48", "grpcio-tools>=1.48", "protobuf>=3.20", "requests>=2"]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://github.com/gaoxinge/task_flow"
|
|
38
|
+
Repository = "https://github.com/gaoxinge/task_flow"
|
|
39
|
+
Issues = "https://github.com/gaoxinge/task_flow/issues"
|
|
40
|
+
Documentation = "https://github.com/gaoxinge/task_flow#readme"
|
|
41
|
+
|
|
42
|
+
[dependency-groups]
|
|
43
|
+
dev = ["pytest>=7,<8.4", "ruff>=0.6"]
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
testpaths = ["tests"]
|
|
47
|
+
addopts = "-ra --capture=no"
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
include = ["task_flow*"]
|
|
51
|
+
|
|
52
|
+
[tool.ruff]
|
|
53
|
+
target-version = "py38"
|
|
54
|
+
line-length = 100
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = ["E", "F", "I"]
|