Releasing pipe v2.0
Introducing partially parametrized pipes!
pipe is a very old project of mine, proudly showing 1.3k stars on github!
I don't maintain it a lot, but today I'm announcing a new release introducing a nice feature: partially parametrized pipes.
If you're familiar with currying or functools.partial you won't get lost.
Show me!
Before pipe 2
, the following were already valid:
>>> from random import randint
>>> from pipe import where, sort
>>>
>>> negative = where(lambda x: x < 0)
>>> positive = where(lambda x: x > 0)
>>>
>>> numbers = [randint(-10, 10) for _ in range(10)]
>>>
>>> numbers | positive | sort
[7, 10]
>>> numbers | negative | sort
[-10, -9, -8, -7, -4, -4, -2]
It allows to name things, naming things make code more readable so I wanted more of it.
It was probably underrated, I like to do this kind of things:
>>> isort = sort(key=lambda x: x**2)
>>> numbers | isort
[0, -2, -4, -4, -7, 7, -8, -9, 10, -10]
But it failed as soon as you try go deeper:
>>> numbers | isort(reverse=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pipe.py", line 100, in __ror__
return self.function(other)
File "pipe.py", line 103, in <lambda>
return Pipe(lambda x: self.function(x, *args, **kwargs))
TypeError: Pipe.__call__.<locals>.<lambda>() got an unexpected keyword argument 'reverse'
Call it a bug, that's what's pipe 2 fixes:
>>> from pipe import where, sort
>>> from random import randint
>>> numbers = [randint(-10, 10) for _ in range(10)]
>>>
>>> isort = sort(key=lambda x: x**2)
>>> numbers | isort(reverse=True)
[-10, -9, 8, -6, 6, -4, 4, 4, 2, 1]
>>>
Or in the other way around:
>>> rsort = sort(reverse=True)
>>> numbers | rsort
[8, 6, 4, 4, 2, 1, -4, -6, -9, -10]
>>> numbers | rsort(key=lambda x: x**2)
[-10, -9, 8, -6, 6, -4, 4, 4, 2, 1]
From here you can build many specialized blocks from a single pipe, here's a better example:
import re
from pipe import Pipe
@Pipe
def grep(iterable, pattern, flags=0, invert=False):
"""Mimics grep."""
for line in iterable:
match = re.match(pattern, line, flags=flags)
if (match and not invert) or (not match and invert):
yield line
Build the small reusable blocks:
igrep = grep(flags=re.I)
vgrep = grep(invert=True)
vigrep = igrep(invert=True)
https = igrep("https://")
not_https = https(invert=True)
...
And they all do what you think they do:
>>> lines = ["https://python.org", "http://detectportal.firefox.com/", "Just no an URL", "HTTPS://afpy.org"]
>>> for url in lines | https:
... print(url)
...
https://python.org
HTTPS://afpy.org
>>> for url in lines | not_https:
... print(url)
...
http://detectportal.firefox.com/
Just no an URL
Remember I said currying? Yes you can abuse the syntax, but please don't:
>>> from pipe import sort
>>> lines | grep()(invert=True)()(flags=re.I)()("https://")() | sort
['Just not an URL', 'http://detectportal.firefox.com/']
As I like to say: « It's not because it's possible that you should do it ».