pipeline-toolkit 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.
- pipeline_toolkit-0.1.0/LICENSE +21 -0
- pipeline_toolkit-0.1.0/PKG-INFO +477 -0
- pipeline_toolkit-0.1.0/README.md +447 -0
- pipeline_toolkit-0.1.0/pyproject.toml +55 -0
- pipeline_toolkit-0.1.0/setup.cfg +4 -0
- pipeline_toolkit-0.1.0/src/pipeline/__init__.py +24 -0
- pipeline_toolkit-0.1.0/src/pipeline/pipeline.py +317 -0
- pipeline_toolkit-0.1.0/src/pipeline/stack.py +132 -0
- pipeline_toolkit-0.1.0/src/pipeline/tap.py +43 -0
- pipeline_toolkit-0.1.0/src/pipeline_toolkit.egg-info/PKG-INFO +477 -0
- pipeline_toolkit-0.1.0/src/pipeline_toolkit.egg-info/SOURCES.txt +11 -0
- pipeline_toolkit-0.1.0/src/pipeline_toolkit.egg-info/dependency_links.txt +1 -0
- pipeline_toolkit-0.1.0/src/pipeline_toolkit.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hoàng Long
|
|
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,477 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pipeline-toolkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A small functional pipeline toolkit for Python.
|
|
5
|
+
Author-email: Hoàng Long <hoanglongcodes@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Hoang-Long2012/pipeline-toolkit
|
|
8
|
+
Project-URL: Repository, https://github.com/Hoang-Long2012/pipeline-toolkit
|
|
9
|
+
Project-URL: Issues, https://github.com/Hoang-Long2012/pipeline-toolkit/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/Hoang-Long2012/pipeline-toolkit/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: pipeline,functional programming,functional pipeline,workflow,async,asynchronous,threading,utilities
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# Pipeline Toolkit
|
|
32
|
+
|
|
33
|
+
A small functional pipeline toolkit for Python.
|
|
34
|
+
|
|
35
|
+
`pipeline-toolkit` provides a simple way to build sequential pipelines from ordinary Python callables. Each step receives the result of the previous step, while the pipeline runs asynchronously in a worker thread.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- Sequential functional pipeline execution
|
|
40
|
+
- Asynchronous execution using a worker thread
|
|
41
|
+
- Positional and keyword arguments for pipeline steps
|
|
42
|
+
- Stop, skip, wait, and rerun execution
|
|
43
|
+
- Manual synchronous step execution
|
|
44
|
+
- Result and error history using a stack
|
|
45
|
+
- Pipeline modification with `add()`, `insert()`, `pop()`, and `clear()`
|
|
46
|
+
- Small utility modules for functional workflows
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
Install from PyPI:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install pipeline-toolkit
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or install directly from GitHub:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install git+https://github.com/Hoang-Long2012/pipeline-toolkit.git
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
A pipeline is created from an iterable of steps. Each step is a tuple whose first item is a callable.
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from pipeline import Pipeline
|
|
68
|
+
|
|
69
|
+
def add(value, amount):
|
|
70
|
+
return value + amount
|
|
71
|
+
|
|
72
|
+
def multiply(value, factor):
|
|
73
|
+
return value * factor
|
|
74
|
+
|
|
75
|
+
pipeline = Pipeline([
|
|
76
|
+
(add, (5,)),
|
|
77
|
+
(multiply, (2,)),
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
pipeline.run(10).wait()
|
|
81
|
+
|
|
82
|
+
print(pipeline.results.get())
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The execution flow is:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
10
|
|
89
|
+
↓
|
|
90
|
+
add(10, 5)
|
|
91
|
+
↓
|
|
92
|
+
15
|
|
93
|
+
↓
|
|
94
|
+
multiply(15, 2)
|
|
95
|
+
↓
|
|
96
|
+
30
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The final result is `30`.
|
|
100
|
+
|
|
101
|
+
## Pipeline Steps
|
|
102
|
+
|
|
103
|
+
Each step can use one of four supported forms.
|
|
104
|
+
|
|
105
|
+
### Callable only
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
(function,)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The callable receives the previous result:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
pipeline = Pipeline([
|
|
115
|
+
(str.upper,),
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
pipeline.run("hello").wait()
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Positional arguments
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
(function, args)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
where `args` is a tuple:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
pipeline = Pipeline([
|
|
131
|
+
(add, (5,)),
|
|
132
|
+
(multiply, (2,)),
|
|
133
|
+
])
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A step such as:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
(add, (5,))
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
is executed as:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
add(previous_result, 5)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Keyword arguments
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
(function, kwargs)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
where `kwargs` is a mapping:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
pipeline = Pipeline([
|
|
158
|
+
(pow, {"exp": 2}),
|
|
159
|
+
])
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The step is executed as:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
pow(previous_result, exp=2)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Positional and keyword arguments
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
(function, args, kwargs)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
For example:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
pipeline = Pipeline([
|
|
178
|
+
(my_function, (1, 2), {"option": True}),
|
|
179
|
+
])
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The callable receives the previous result followed by the supplied positional and keyword arguments.
|
|
183
|
+
|
|
184
|
+
## Execution
|
|
185
|
+
|
|
186
|
+
### `run()`
|
|
187
|
+
|
|
188
|
+
Start the pipeline asynchronously.
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
pipeline.run(default=None, delay=0, daemon=False, stop_on_error=True)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The `default` value becomes the initial result and is passed to the first step.
|
|
195
|
+
|
|
196
|
+
`delay` specifies the delay between steps.
|
|
197
|
+
|
|
198
|
+
`daemon` controls whether the worker thread is a daemon thread.
|
|
199
|
+
|
|
200
|
+
`stop_on_error` controls whether execution stops after the first exception.
|
|
201
|
+
|
|
202
|
+
`run()` returns the pipeline instance, allowing calls such as:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
pipeline.run(10).wait()
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The pipeline is snapshotted when execution starts. Changes made to `pipeline.pipeline` after `run()` begins do not affect the current execution.
|
|
209
|
+
|
|
210
|
+
### `wait()`
|
|
211
|
+
|
|
212
|
+
Wait for the current execution to finish.
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
pipeline.wait()
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
It returns the pipeline instance.
|
|
219
|
+
|
|
220
|
+
### `stop()`
|
|
221
|
+
|
|
222
|
+
Request the running pipeline to stop and wait for its worker thread to terminate.
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
step = pipeline.stop()
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The return value is the current one-based step index when execution is stopped, or `0` if the pipeline was not running.
|
|
229
|
+
|
|
230
|
+
### `skip()`
|
|
231
|
+
|
|
232
|
+
Request the worker to skip the next step that reaches its skip check.
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
pipeline.skip()
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
The method returns the pipeline instance.
|
|
239
|
+
|
|
240
|
+
### `rerun()`
|
|
241
|
+
|
|
242
|
+
Stop the current execution and start the pipeline again.
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
pipeline.rerun(10)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Arguments are passed directly to `run()`.
|
|
249
|
+
|
|
250
|
+
## Manual Step Execution
|
|
251
|
+
|
|
252
|
+
`run_step()` executes one configured step synchronously.
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
result = pipeline.run_step(2, 10)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Unlike `run()`, this method:
|
|
259
|
+
|
|
260
|
+
- does not create a worker thread
|
|
261
|
+
- does not modify the worker thread or pipeline execution state
|
|
262
|
+
- does not store the result in `results`
|
|
263
|
+
- does not store exceptions in `errors`
|
|
264
|
+
- allows exceptions to propagate to the caller
|
|
265
|
+
|
|
266
|
+
This makes it useful when a single pipeline step needs to be executed manually.
|
|
267
|
+
|
|
268
|
+
## Results and Errors
|
|
269
|
+
|
|
270
|
+
The pipeline provides two `Stack` instances:
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
pipeline.results
|
|
274
|
+
pipeline.errors
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`results` contains the initial value and the results produced by executed steps.
|
|
278
|
+
|
|
279
|
+
For example:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
pipeline.run(10).wait()
|
|
283
|
+
|
|
284
|
+
print(pipeline.results.get())
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`errors` contains exceptions raised by pipeline steps.
|
|
288
|
+
|
|
289
|
+
When `stop_on_error=True`, execution stops after the first exception.
|
|
290
|
+
|
|
291
|
+
When `stop_on_error=False`, the exception is stored in `errors` and execution continues with the previous result.
|
|
292
|
+
|
|
293
|
+
## Managing Pipeline Steps
|
|
294
|
+
|
|
295
|
+
Pipeline steps can be modified before or between executions.
|
|
296
|
+
|
|
297
|
+
### `add()`
|
|
298
|
+
|
|
299
|
+
Append a step:
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
pipeline.add((str.upper,))
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `insert()`
|
|
306
|
+
|
|
307
|
+
Insert a step at a one-based position:
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
pipeline.insert(2, (str.strip,))
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### `pop()`
|
|
314
|
+
|
|
315
|
+
Remove and return a step:
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
step = pipeline.pop(1)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Pipeline indexes are one-based.
|
|
322
|
+
|
|
323
|
+
### `clear()`
|
|
324
|
+
|
|
325
|
+
Remove all configured steps:
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
pipeline.clear()
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Pipeline State
|
|
332
|
+
|
|
333
|
+
The `running` property indicates whether the worker thread is currently running:
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
if pipeline.running:
|
|
337
|
+
print("Pipeline is running")
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
The `step` attribute contains the one-based index of the currently executing step. It is `0` when the pipeline is not running.
|
|
341
|
+
|
|
342
|
+
A `Pipeline` instance can also be used as a boolean:
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
if pipeline:
|
|
346
|
+
print("Pipeline is running")
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Calling a pipeline instance is equivalent to calling `run()`:
|
|
350
|
+
|
|
351
|
+
```python
|
|
352
|
+
pipeline(10)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
is equivalent to:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
pipeline.run(10)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
The length of a pipeline is the number of configured steps:
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
len(pipeline)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
A callable can be checked with the `in` operator:
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
if add in pipeline:
|
|
371
|
+
print("add is part of the pipeline")
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Callable membership uses identity comparison.
|
|
375
|
+
|
|
376
|
+
## Utilities
|
|
377
|
+
|
|
378
|
+
### `Stack`
|
|
379
|
+
|
|
380
|
+
`Stack` is a simple LIFO stack container with optional capacity limits.
|
|
381
|
+
|
|
382
|
+
It supports common stack operations such as pushing, retrieving, peeking, and removing items, with dedicated exceptions for overflow and underflow conditions.
|
|
383
|
+
|
|
384
|
+
Import it directly from its submodule:
|
|
385
|
+
|
|
386
|
+
```python
|
|
387
|
+
from pipeline.stack import Stack
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
`Stack` is also used internally by `Pipeline` for storing results and errors.
|
|
391
|
+
|
|
392
|
+
For example:
|
|
393
|
+
|
|
394
|
+
```python
|
|
395
|
+
from pipeline.stack import Stack
|
|
396
|
+
|
|
397
|
+
stack = Stack()
|
|
398
|
+
|
|
399
|
+
stack.push("first")
|
|
400
|
+
stack.push("second")
|
|
401
|
+
|
|
402
|
+
print(stack.get())
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
For detailed stack operations and behavior, see the `pipeline.stack` module.
|
|
406
|
+
|
|
407
|
+
### `tap`
|
|
408
|
+
|
|
409
|
+
`tap` is a small functional utility for performing a side effect while keeping the pipeline value available for subsequent processing.
|
|
410
|
+
|
|
411
|
+
`tap` performs a side effect on a deep copy of the current value and returns the original value unchanged.
|
|
412
|
+
|
|
413
|
+
Import it directly from its submodule:
|
|
414
|
+
|
|
415
|
+
```python
|
|
416
|
+
from pipeline.tap import tap
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
For example:
|
|
420
|
+
|
|
421
|
+
```python
|
|
422
|
+
from pipeline import Pipeline
|
|
423
|
+
from pipeline.tap import tap
|
|
424
|
+
|
|
425
|
+
def add(value, amount):
|
|
426
|
+
return value + amount
|
|
427
|
+
|
|
428
|
+
pipeline = Pipeline([
|
|
429
|
+
(add, (5,)),
|
|
430
|
+
(tap, (print,)),
|
|
431
|
+
(add, (10,)),
|
|
432
|
+
])
|
|
433
|
+
|
|
434
|
+
pipeline.run(10).wait()
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Utilities are provided as separate submodules rather than being exported from the top-level `pipeline` package.
|
|
438
|
+
|
|
439
|
+
## API Overview
|
|
440
|
+
|
|
441
|
+
### `Pipeline`
|
|
442
|
+
|
|
443
|
+
| Member | Description |
|
|
444
|
+
| ------------ | ------------------------------------- |
|
|
445
|
+
| `run()` | Start asynchronous pipeline execution |
|
|
446
|
+
| `run_step()` | Execute one step synchronously |
|
|
447
|
+
| `stop()` | Stop the current execution |
|
|
448
|
+
| `skip()` | Request the next step to be skipped |
|
|
449
|
+
| `wait()` | Wait for the current execution |
|
|
450
|
+
| `rerun()` | Restart the pipeline |
|
|
451
|
+
| `add()` | Append a step |
|
|
452
|
+
| `insert()` | Insert a step |
|
|
453
|
+
| `pop()` | Remove and return a step |
|
|
454
|
+
| `clear()` | Remove all steps |
|
|
455
|
+
| `running` | Whether the worker is running |
|
|
456
|
+
| `step` | Current one-based step index |
|
|
457
|
+
| `results` | Stack of initial value and results |
|
|
458
|
+
| `errors` | Stack of raised exceptions |
|
|
459
|
+
|
|
460
|
+
## Requirements
|
|
461
|
+
|
|
462
|
+
* Python 3.8 or newer
|
|
463
|
+
|
|
464
|
+
## Changelog
|
|
465
|
+
|
|
466
|
+
See changelog from: [CHANGELOG.md](https://github.com/Hoang-Long2012/pipeline-toolkit/blob/main/CHANGELOG.md)
|
|
467
|
+
|
|
468
|
+
## License
|
|
469
|
+
|
|
470
|
+
This project is licensed under the MIT License. See [LICENSE](https://github.com/Hoang-Long2012/pipeline-toolkit/blob/main/LICENSE) for details.
|
|
471
|
+
|
|
472
|
+
## Contribution
|
|
473
|
+
|
|
474
|
+
If you'd like to contribute, feel free to submit a pull request.
|
|
475
|
+
If you'd like to report a bug or request a feature, please open an issue.
|
|
476
|
+
|
|
477
|
+
Copyright (C) 2026 Hoàng Long
|