-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 448a2b4
Showing
5 changed files
with
143 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright 2018 Robin Hilliard | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
of the Software, and to permit persons to whom the Software is furnished to do | ||
so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
## Elixir–Style Pipes for Python | ||
|
||
In the Elixir programming language the `|>` pipe operator allows you to chain together | ||
multiple function calls so that this: | ||
|
||
```$elixir | ||
c(b(a(1, 2), 3, 4)) | ||
``` | ||
|
||
can be written more readably as: | ||
|
||
```$elixir | ||
1 |> a(2) |> b(3, 4) |> c() | ||
``` | ||
|
||
All the pipe operator does is pass its left operand as the first argument of the right operand, | ||
so that `a |> b(...)` becomes `b(a, ...)`. | ||
|
||
Various pipe implementations in Python to date allow a list of functions to be applied | ||
to an initial value, but do not support the partial, missing first argument syntax of Elixir. | ||
|
||
This library provides a function decorator that causes Python `>>` right shift operators within the | ||
function to act exactly like Elixir pipes: | ||
|
||
```$python | ||
from pipes import pipes | ||
def add(a, b): | ||
return a + b | ||
def times(a, b): | ||
return a * b | ||
@pipes | ||
def calc() | ||
print 1 >> add(2) >> times(3) # prints 9 | ||
``` | ||
|
||
Functions can have any number of arguments: | ||
|
||
```$python | ||
def add3(a, b, c): | ||
return a + b + c | ||
@pipes | ||
def calc() | ||
print 1 >> add3(2, 3) # prints 6 | ||
``` | ||
|
||
There should be a small amount of processing overhead the first time the function is called, | ||
otherwise there should be no difference to the conventionally nested call code. | ||
|
||
This is initial alpha code. It has been tested on Python 2.7.14 using simple functions. It has | ||
not been tested using bound methods or Python 3. Source line attributes are preserved so | ||
debuggers should be able to follow the code as it executes. Pull requests and bug reports gratefully accepted. | ||
|
||
Robin Hilliard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from ast import parse, NodeTransformer, RShift, increment_lineno | ||
from inspect import getsource | ||
|
||
|
||
class _PipeTransformer(NodeTransformer): | ||
|
||
def visit_BinOp(self, node): | ||
if isinstance(node.op, RShift): | ||
# rewrite a >> b(...) as b(a, ...) | ||
node.right.args.insert(0, node.left) | ||
return self.visit(node.right) | ||
|
||
else: | ||
return node | ||
|
||
|
||
def pipes(func): | ||
# name of our replacement function | ||
pipe_func_name = '__pipes_{}'.format(func.func_code.co_name) | ||
|
||
# variable context where decorator added | ||
ctx = func.func_globals | ||
|
||
# We only modify the function once | ||
if pipe_func_name not in ctx: | ||
# AST data structure representing parsed function code | ||
tree = parse(getsource(func)) | ||
|
||
# Fix line numbers so that debuggers still work | ||
increment_lineno(tree, func.func_code.co_firstlineno - 1) | ||
|
||
# Update name of function to compile | ||
tree.body[0].name = pipe_func_name | ||
|
||
# remove the pipe decorator so that we don't recursively call it again | ||
tree.body[0].decorator_list = \ | ||
[d for d in tree.body[0].decorator_list if d.id != 'pipes'] | ||
|
||
# Apply the visit_BinOp transformation | ||
tree = _PipeTransformer().visit(tree) | ||
|
||
# now compile the AST into an altered function definition | ||
code = compile( | ||
tree, | ||
filename=__file__, | ||
mode="exec") | ||
|
||
# and execute the definition in the original context, | ||
exec(code, ctx) | ||
|
||
# return the modified function - original is never called | ||
return ctx[pipe_func_name] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[metadata] | ||
description-file=README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from distutils.core import setup | ||
setup( | ||
name='pipes', | ||
packages=['pipes'], | ||
version='0.1.0', | ||
description='A decorator that changes the >> operator to mimic Elixir-style function piping', | ||
author='Robin Hilliard', | ||
author_email='[email protected]', | ||
url='https://github.com/robinhilliar/pipes', | ||
download_url='https://github.com/robinhilliard/pipes/archive/0.1.tar.gz', | ||
keywords=['elixir', 'function', 'pipe'], | ||
classifiers=[], | ||
) |