pyxshell.pipeline contains the main implementation of pyxshell: the PipeLine class. It also features the pipe() decorator to allow you to create your own pipeline components from simple Python generator functions.
A coroutine wrapper which enables pipelining syntax.
PipeLine allows you to flatten once-nested code just by wrapping your generators. The class provides combinators in the form of operators, allowing you to plug two generators together without having to nest lots of function calls. For example:
>>> def summer(stdin):
... sum = 0
... for item in stdin:
... sum += item
... yield sum
>>> pipeline = PipeLine(lambda: iter([1, 2, 3, 4])) | PipeLine(summer)
>>> pipeline
<PipeLine: <lambda> | summer>
>>> for item in pipeline:
... print(item)
1
3
6
10
The yielded output of each generator in the chain becomes the input for the next. The rules for writing a pipeline function are simple: PipeLine requires a callable which accepts a single argument (the input), and returns an iterator. The only exception is the first part of the pipeline, which should accept no arguments (as there will be no input).
To create pipeline functions, use the pipe() decorator:
>>> @pipe
... def my_generator():
... yield 1
... yield 2
... yield 3
>>> pl = my_generator()
>>> pl
<PipeLine: my_generator>
>>> for item in pl:
... print(item)
1
2
3
If your pipeline accepts input, an iterator will be provided as the first argument to the function:
>>> @pipe
... def add_one(input):
... for item in input:
... yield item + 1
>>> pl = my_generator() | add_one()
>>> pl
<PipeLine: my_generator | add_one>
>>> for item in pl:
... print(item)
2
3
4
Even with input, your functions can still accept other parameters:
>>> @pipe
... def adder(input, amount):
... for item in input:
... yield item + amount
>>> pl = my_generator() | adder(3)
>>> pl
<PipeLine: my_generator | adder>
>>> for item in pl:
... print(item)
4
5
6
Some operators are overridden to provide pipeline combinators (methods which take multiple pipelines and return a new pipeline). For example, multiplying two pipelines gets you their cross product:
>>> pl = my_generator() | (adder(3) * adder(6))
>>> pl
<PipeLine: my_generator | adder * adder>
>>> for item in pl:
... print(item)
(4, 7)
(4, 8)
(4, 9)
(5, 7)
(5, 8)
(5, 9)
(6, 7)
(6, 8)
(6, 9)
Adding two pipelines will chain the same input through both:
>>> pl = my_generator() | (adder(3) + adder(12))
>>> pl
<PipeLine: my_generator | adder + adder>
>>> for item in pl:
... print(item)
4
5
6
13
14
15
Wrap a function as a pipeline.
>>> @pipe
... def printer(stdin, outfile=None):
... for item in stdin:
... print(item, file=outfile)
... yield item
>>> @pipe
... def echo(*values):
... for value in values:
... yield value
>>> p = printer()
>>> p
<PipeLine: printer>
>>> p = echo(1, 2, 3) | p
>>> p
<PipeLine: echo | printer>
>>> output = list(p)
1
2
3
>>> output
[1, 2, 3]