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.
@@ -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