pipescript 0.0.1__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.
- pipescript-0.0.1/MANIFEST.in +4 -0
- pipescript-0.0.1/PKG-INFO +256 -0
- pipescript-0.0.1/README.md +211 -0
- pipescript-0.0.1/docs/HISTORY.rst +6 -0
- pipescript-0.0.1/docs/LICENSE.txt +11 -0
- pipescript-0.0.1/pipescript/__init__.py +47 -0
- pipescript-0.0.1/pipescript/_version.py +658 -0
- pipescript-0.0.1/pipescript/api.py +85 -0
- pipescript-0.0.1/pipescript/completion_patch.py +30 -0
- pipescript-0.0.1/pipescript/constants.py +3 -0
- pipescript-0.0.1/pipescript/extract_names.py +60 -0
- pipescript-0.0.1/pipescript/macro_tracer.py +247 -0
- pipescript-0.0.1/pipescript/pipeline_tracer.py +793 -0
- pipescript-0.0.1/pipescript/placeholders.py +177 -0
- pipescript-0.0.1/pipescript/traceback_patch.py +39 -0
- pipescript-0.0.1/pipescript/utils.py +15 -0
- pipescript-0.0.1/pipescript/version.py +23 -0
- pipescript-0.0.1/pipescript.egg-info/PKG-INFO +256 -0
- pipescript-0.0.1/pipescript.egg-info/SOURCES.txt +26 -0
- pipescript-0.0.1/pipescript.egg-info/dependency_links.txt +1 -0
- pipescript-0.0.1/pipescript.egg-info/not-zip-safe +1 -0
- pipescript-0.0.1/pipescript.egg-info/requires.txt +24 -0
- pipescript-0.0.1/pipescript.egg-info/top_level.txt +1 -0
- pipescript-0.0.1/pyproject.toml +43 -0
- pipescript-0.0.1/setup.cfg +72 -0
- pipescript-0.0.1/setup.py +7 -0
- pipescript-0.0.1/versioneer.py +2205 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pipescript
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Powerful pipeline syntax for IPython and Jupyter
|
|
5
|
+
Home-page: https://github.com/smacke/pipescript
|
|
6
|
+
Author: Stephen Macke
|
|
7
|
+
Author-email: stephen.macke@gmail.com
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown; charset=UTF-8
|
|
21
|
+
License-File: docs/LICENSE.txt
|
|
22
|
+
Requires-Dist: ipyflow-core>=0.0.221
|
|
23
|
+
Requires-Dist: pyccolo>=0.0.77
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: black; extra == "test"
|
|
26
|
+
Requires-Dist: hypothesis; extra == "test"
|
|
27
|
+
Requires-Dist: isort; extra == "test"
|
|
28
|
+
Requires-Dist: mypy; extra == "test"
|
|
29
|
+
Requires-Dist: pytest; extra == "test"
|
|
30
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
31
|
+
Requires-Dist: ruff; extra == "test"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: build; extra == "dev"
|
|
34
|
+
Requires-Dist: pycln; extra == "dev"
|
|
35
|
+
Requires-Dist: twine; extra == "dev"
|
|
36
|
+
Requires-Dist: versioneer; extra == "dev"
|
|
37
|
+
Requires-Dist: black; extra == "dev"
|
|
38
|
+
Requires-Dist: hypothesis; extra == "dev"
|
|
39
|
+
Requires-Dist: isort; extra == "dev"
|
|
40
|
+
Requires-Dist: mypy; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
43
|
+
Requires-Dist: ruff; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
pipescript
|
|
47
|
+
=======
|
|
48
|
+
|
|
49
|
+
[](https://github.com/smacke/pipescript/actions)
|
|
50
|
+
[](http://mypy-lang.org/)
|
|
51
|
+
[](https://opensource.org/licenses/BSD-3-Clause)
|
|
52
|
+
[](https://pypi.org/project/pipescript)
|
|
53
|
+
[](https://pypi.org/project/pipescript)
|
|
54
|
+
|
|
55
|
+
pipescript is an IPython extension that brings a pipe operator `|>` and
|
|
56
|
+
powerful placeholder syntax extensions to IPython and Jupyter. It is:
|
|
57
|
+
- Just a library you can install from PyPI, compatible with a wide range of Python 3
|
|
58
|
+
versions -- no fancy installation instructions, no complicated language distribution
|
|
59
|
+
to install
|
|
60
|
+
- Intended for Jupyter notebooks, for the IPython REPL, or for any interactive
|
|
61
|
+
Python environment built on top of IPython
|
|
62
|
+
- Fully compatible with all existing Python standard and third-party libraries that
|
|
63
|
+
you already know and love
|
|
64
|
+
|
|
65
|
+
If you're familiar with the [magrittr](https://magrittr.tidyverse.org/) package
|
|
66
|
+
for R, then you'll be right at home with pipescript.
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
## Getting Started
|
|
70
|
+
|
|
71
|
+
Run the following in IPython or Jupyter to install pipescript and load
|
|
72
|
+
the extension:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
%pip install pipescript
|
|
76
|
+
%load_ext pipescript
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The `%load_ext pipescript` invocation is what enables the new pipe syntax
|
|
80
|
+
in your current session.
|
|
81
|
+
|
|
82
|
+
## Features by Example
|
|
83
|
+
|
|
84
|
+
Let's look at a few examples to give a flavor of what you can do with pipescript:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# Display a sorted version of a tuple
|
|
88
|
+
>>> tup = (3, 4, 1, 5, 6)
|
|
89
|
+
>>> tup |> sorted |> tuple
|
|
90
|
+
(1, 3, 4, 5, 6)
|
|
91
|
+
```
|
|
92
|
+
The above example showcases the `|>`, or "pipe", operator, which is a much-loved
|
|
93
|
+
feature of functional programming that has become increasingly mainstream. Its
|
|
94
|
+
primary benefit is that the flow of execution follows natural left-to-right
|
|
95
|
+
reading / writing order of the code. Whether or not such pipeline syntax is
|
|
96
|
+
available, it's not uncommon for programmers to execute pipelines like the above
|
|
97
|
+
multiple times during to verify the computation at each step, particularly in
|
|
98
|
+
interactive programming environments like Jupyter. With `|>`, this type of
|
|
99
|
+
incremental verification becomes a breeze: first execute `tup |> sorted`, then
|
|
100
|
+
append ` |> tuple` to execute the full chain `tup |> sorted |> tuple`, each time
|
|
101
|
+
using the last-expression rendering capabilities of the notebook or REPL to
|
|
102
|
+
inspect and verify the result.
|
|
103
|
+
|
|
104
|
+
### Placeholders
|
|
105
|
+
|
|
106
|
+
The power of the `|>` operator is amplified via placeholder syntax for implicit
|
|
107
|
+
lambda construction: for pipescript, we use `$` to stand in for function arguments
|
|
108
|
+
and induce function creation:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
# Sort a list in reverse order
|
|
112
|
+
>>> lst = [3, 4, 1, 5, 6]
|
|
113
|
+
>>> lst |> sorted($, reverse=True)
|
|
114
|
+
[6, 5, 4, 3, 1]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`$` is analogous to magrittr's `.` placeholder. It can also be used outside
|
|
118
|
+
of pipeline contexts:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# Sort a list in reverse order and print the result
|
|
122
|
+
lst = [3, 4, 1, 5, 6]
|
|
123
|
+
reverse_sorter = sorted($, reverse=True)
|
|
124
|
+
|
|
125
|
+
# The following are equivalent:
|
|
126
|
+
print(reverse_sorter(lst))
|
|
127
|
+
lst |> reverse_sorter |> print
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Each time `$` appears, it represents a new argument, so `sorted($, reverse=$)`
|
|
131
|
+
represents a function with two arguments:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import random
|
|
135
|
+
|
|
136
|
+
# Sort a list in either ascending or descending order with probablility 0.5:
|
|
137
|
+
lst = [3, 4, 1, 5, 6]
|
|
138
|
+
sorter = sorted($, reverse=$)
|
|
139
|
+
reverse = random.random() < 0.5
|
|
140
|
+
|
|
141
|
+
# The following are equivalent:
|
|
142
|
+
print(sorter(lst, reverse))
|
|
143
|
+
lst |> sorter($, reverse) |> print
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Placeholders can appear anywhere -- not just as arguments to function calls:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
# Sort a list and find the position of element 4:
|
|
150
|
+
>>> lst = [3, 4, 1, 5, 6]
|
|
151
|
+
>>> lst |> sorted |> $.index(3)
|
|
152
|
+
1
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Named Placeholders
|
|
156
|
+
|
|
157
|
+
There are situations that would benefit from referencing the same placeholder multiple times, for which
|
|
158
|
+
pipescript permits *named placeholders* by prefixing `$` to an identifier:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
# Pair even entries from a range with their adjacent odd entry
|
|
162
|
+
range(6) |> list |> zip($v[::2], $v[1::2]) |> list
|
|
163
|
+
>>> [(0, 1), (2, 3), (4, 5)]
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
In the above example, we could have used any name for `$v`, the important
|
|
167
|
+
thing is that the same name was used -- otherwise pipescript would have
|
|
168
|
+
induced a function with two arguments instead of one.
|
|
169
|
+
|
|
170
|
+
### Undetermined Pipelines
|
|
171
|
+
|
|
172
|
+
Similar to magrittr's behavior, if any number of placeholders appear in the first
|
|
173
|
+
step of an pipescript pipeline, this *undetermined pipeline* will represent a function:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
>>> second_largest_value = $ |> sorted($, reverse=True) |> $[1]
|
|
177
|
+
>>> [3, 8, 1, 5, 6] |> second_largest_value
|
|
178
|
+
6
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Macros and Curry Syntax
|
|
182
|
+
|
|
183
|
+
### Helper Utilities
|
|
184
|
+
|
|
185
|
+
### Additional Operators
|
|
186
|
+
|
|
187
|
+
## Placeholder Scope
|
|
188
|
+
|
|
189
|
+
A natural question is: how does pipescript know what part of the code should
|
|
190
|
+
be included in the body of the function induced by placeholder use? The
|
|
191
|
+
rules are as follows:
|
|
192
|
+
|
|
193
|
+
1. If there is a macro or pipeline step enclosing the placeholder, the induced
|
|
194
|
+
function body includes the "smallest" such enclosing macro or pipeline step.
|
|
195
|
+
2. Otherwise, the function body expands to include the nearest "chain"
|
|
196
|
+
of function calls, attribute accesses, and / or subscript accesses.
|
|
197
|
+
|
|
198
|
+
An example of a "chain" would be something like `np.array($).T.astype(int)`,
|
|
199
|
+
which induces a lambda that converts its argument to a numpy array,
|
|
200
|
+
transposes it, and then converts the result to use `int64` dtype. That is,
|
|
201
|
+
the lambda body expands to include not just `np.array($)`, but the entire
|
|
202
|
+
"chain" in the expression.
|
|
203
|
+
|
|
204
|
+
To see a concrete example of where this matters, consider the following
|
|
205
|
+
two placeholder expressions:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
# The following sorters do different things!
|
|
209
|
+
sorter1 = sorted($, key=$[1])
|
|
210
|
+
sorter2 = sorted($, key=f[$[1]])
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`sorter1` is a function that takes two arguments: a sequence, and a list of
|
|
214
|
+
functions, the second of which will be used to compute the sort key, which it then
|
|
215
|
+
uses to sort the first argument.
|
|
216
|
+
`sorter2`, on the other hand, is a function that takes a single argument, which
|
|
217
|
+
is a sequence that it sorts using the second element of each value in said
|
|
218
|
+
sequence value as sort key. In most cases, `sorter2` probably gives the desired
|
|
219
|
+
behavior.
|
|
220
|
+
|
|
221
|
+
## Performance Overhead
|
|
222
|
+
|
|
223
|
+
## More Examples
|
|
224
|
+
I developed pipescript while working on
|
|
225
|
+
[Advent of Code 2025](https://adventofcode.com/2025) in parallel,
|
|
226
|
+
and used it for most of the input processesing portions of my solutions,
|
|
227
|
+
which you can find at https://github.com/smacke/aoc2025.
|
|
228
|
+
|
|
229
|
+
## What pipescript is and is not
|
|
230
|
+
|
|
231
|
+
pipescript is not a general purpose functional programming language on top of
|
|
232
|
+
Python. It is very much not intended for production use cases, and instead
|
|
233
|
+
caters toward quick-and-dirty one-off / scratchpad type computations in IPython
|
|
234
|
+
and Jupyter specifically. In short, pipescript aims to provide simple but powerful
|
|
235
|
+
pipeline and placeholder syntax to interactive Python programming environments.
|
|
236
|
+
|
|
237
|
+
All the different pipeline operators like `|>`, `<|`, `*|>`, etc. essentially
|
|
238
|
+
transpile down to an instrumented variant of the bitwise-or (`|`) operator, and
|
|
239
|
+
therefore every new operator left-associates at the same level of precedence,
|
|
240
|
+
meaning that pipeline steps run from left to right in the order that they
|
|
241
|
+
appear. pipescript aims to optimize for simplicity, readability / writability, and
|
|
242
|
+
predictability over feature completeness (though I'd like to think it strikes a
|
|
243
|
+
fairly good balance in this regard).
|
|
244
|
+
|
|
245
|
+
## How it works
|
|
246
|
+
|
|
247
|
+
## Inspiration
|
|
248
|
+
|
|
249
|
+
pipescript draws inspiration largely from
|
|
250
|
+
[magrittr](https://magrittr.tidyverse.org/), but also from efforts like
|
|
251
|
+
[coconut](https://coconut-lang.org/) (a functional superset of Python),
|
|
252
|
+
as well as from libraries like [Pipe](https://github.com/JulienPalard/Pipe) which
|
|
253
|
+
take a different approach to fill Python's pipe gap with operator overloading hacks.
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
Code in this project licensed under the [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
pipescript
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
[](https://github.com/smacke/pipescript/actions)
|
|
5
|
+
[](http://mypy-lang.org/)
|
|
6
|
+
[](https://opensource.org/licenses/BSD-3-Clause)
|
|
7
|
+
[](https://pypi.org/project/pipescript)
|
|
8
|
+
[](https://pypi.org/project/pipescript)
|
|
9
|
+
|
|
10
|
+
pipescript is an IPython extension that brings a pipe operator `|>` and
|
|
11
|
+
powerful placeholder syntax extensions to IPython and Jupyter. It is:
|
|
12
|
+
- Just a library you can install from PyPI, compatible with a wide range of Python 3
|
|
13
|
+
versions -- no fancy installation instructions, no complicated language distribution
|
|
14
|
+
to install
|
|
15
|
+
- Intended for Jupyter notebooks, for the IPython REPL, or for any interactive
|
|
16
|
+
Python environment built on top of IPython
|
|
17
|
+
- Fully compatible with all existing Python standard and third-party libraries that
|
|
18
|
+
you already know and love
|
|
19
|
+
|
|
20
|
+
If you're familiar with the [magrittr](https://magrittr.tidyverse.org/) package
|
|
21
|
+
for R, then you'll be right at home with pipescript.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Getting Started
|
|
25
|
+
|
|
26
|
+
Run the following in IPython or Jupyter to install pipescript and load
|
|
27
|
+
the extension:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
%pip install pipescript
|
|
31
|
+
%load_ext pipescript
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The `%load_ext pipescript` invocation is what enables the new pipe syntax
|
|
35
|
+
in your current session.
|
|
36
|
+
|
|
37
|
+
## Features by Example
|
|
38
|
+
|
|
39
|
+
Let's look at a few examples to give a flavor of what you can do with pipescript:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
# Display a sorted version of a tuple
|
|
43
|
+
>>> tup = (3, 4, 1, 5, 6)
|
|
44
|
+
>>> tup |> sorted |> tuple
|
|
45
|
+
(1, 3, 4, 5, 6)
|
|
46
|
+
```
|
|
47
|
+
The above example showcases the `|>`, or "pipe", operator, which is a much-loved
|
|
48
|
+
feature of functional programming that has become increasingly mainstream. Its
|
|
49
|
+
primary benefit is that the flow of execution follows natural left-to-right
|
|
50
|
+
reading / writing order of the code. Whether or not such pipeline syntax is
|
|
51
|
+
available, it's not uncommon for programmers to execute pipelines like the above
|
|
52
|
+
multiple times during to verify the computation at each step, particularly in
|
|
53
|
+
interactive programming environments like Jupyter. With `|>`, this type of
|
|
54
|
+
incremental verification becomes a breeze: first execute `tup |> sorted`, then
|
|
55
|
+
append ` |> tuple` to execute the full chain `tup |> sorted |> tuple`, each time
|
|
56
|
+
using the last-expression rendering capabilities of the notebook or REPL to
|
|
57
|
+
inspect and verify the result.
|
|
58
|
+
|
|
59
|
+
### Placeholders
|
|
60
|
+
|
|
61
|
+
The power of the `|>` operator is amplified via placeholder syntax for implicit
|
|
62
|
+
lambda construction: for pipescript, we use `$` to stand in for function arguments
|
|
63
|
+
and induce function creation:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# Sort a list in reverse order
|
|
67
|
+
>>> lst = [3, 4, 1, 5, 6]
|
|
68
|
+
>>> lst |> sorted($, reverse=True)
|
|
69
|
+
[6, 5, 4, 3, 1]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`$` is analogous to magrittr's `.` placeholder. It can also be used outside
|
|
73
|
+
of pipeline contexts:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
# Sort a list in reverse order and print the result
|
|
77
|
+
lst = [3, 4, 1, 5, 6]
|
|
78
|
+
reverse_sorter = sorted($, reverse=True)
|
|
79
|
+
|
|
80
|
+
# The following are equivalent:
|
|
81
|
+
print(reverse_sorter(lst))
|
|
82
|
+
lst |> reverse_sorter |> print
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Each time `$` appears, it represents a new argument, so `sorted($, reverse=$)`
|
|
86
|
+
represents a function with two arguments:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import random
|
|
90
|
+
|
|
91
|
+
# Sort a list in either ascending or descending order with probablility 0.5:
|
|
92
|
+
lst = [3, 4, 1, 5, 6]
|
|
93
|
+
sorter = sorted($, reverse=$)
|
|
94
|
+
reverse = random.random() < 0.5
|
|
95
|
+
|
|
96
|
+
# The following are equivalent:
|
|
97
|
+
print(sorter(lst, reverse))
|
|
98
|
+
lst |> sorter($, reverse) |> print
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Placeholders can appear anywhere -- not just as arguments to function calls:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
# Sort a list and find the position of element 4:
|
|
105
|
+
>>> lst = [3, 4, 1, 5, 6]
|
|
106
|
+
>>> lst |> sorted |> $.index(3)
|
|
107
|
+
1
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Named Placeholders
|
|
111
|
+
|
|
112
|
+
There are situations that would benefit from referencing the same placeholder multiple times, for which
|
|
113
|
+
pipescript permits *named placeholders* by prefixing `$` to an identifier:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
# Pair even entries from a range with their adjacent odd entry
|
|
117
|
+
range(6) |> list |> zip($v[::2], $v[1::2]) |> list
|
|
118
|
+
>>> [(0, 1), (2, 3), (4, 5)]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
In the above example, we could have used any name for `$v`, the important
|
|
122
|
+
thing is that the same name was used -- otherwise pipescript would have
|
|
123
|
+
induced a function with two arguments instead of one.
|
|
124
|
+
|
|
125
|
+
### Undetermined Pipelines
|
|
126
|
+
|
|
127
|
+
Similar to magrittr's behavior, if any number of placeholders appear in the first
|
|
128
|
+
step of an pipescript pipeline, this *undetermined pipeline* will represent a function:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
>>> second_largest_value = $ |> sorted($, reverse=True) |> $[1]
|
|
132
|
+
>>> [3, 8, 1, 5, 6] |> second_largest_value
|
|
133
|
+
6
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Macros and Curry Syntax
|
|
137
|
+
|
|
138
|
+
### Helper Utilities
|
|
139
|
+
|
|
140
|
+
### Additional Operators
|
|
141
|
+
|
|
142
|
+
## Placeholder Scope
|
|
143
|
+
|
|
144
|
+
A natural question is: how does pipescript know what part of the code should
|
|
145
|
+
be included in the body of the function induced by placeholder use? The
|
|
146
|
+
rules are as follows:
|
|
147
|
+
|
|
148
|
+
1. If there is a macro or pipeline step enclosing the placeholder, the induced
|
|
149
|
+
function body includes the "smallest" such enclosing macro or pipeline step.
|
|
150
|
+
2. Otherwise, the function body expands to include the nearest "chain"
|
|
151
|
+
of function calls, attribute accesses, and / or subscript accesses.
|
|
152
|
+
|
|
153
|
+
An example of a "chain" would be something like `np.array($).T.astype(int)`,
|
|
154
|
+
which induces a lambda that converts its argument to a numpy array,
|
|
155
|
+
transposes it, and then converts the result to use `int64` dtype. That is,
|
|
156
|
+
the lambda body expands to include not just `np.array($)`, but the entire
|
|
157
|
+
"chain" in the expression.
|
|
158
|
+
|
|
159
|
+
To see a concrete example of where this matters, consider the following
|
|
160
|
+
two placeholder expressions:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# The following sorters do different things!
|
|
164
|
+
sorter1 = sorted($, key=$[1])
|
|
165
|
+
sorter2 = sorted($, key=f[$[1]])
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`sorter1` is a function that takes two arguments: a sequence, and a list of
|
|
169
|
+
functions, the second of which will be used to compute the sort key, which it then
|
|
170
|
+
uses to sort the first argument.
|
|
171
|
+
`sorter2`, on the other hand, is a function that takes a single argument, which
|
|
172
|
+
is a sequence that it sorts using the second element of each value in said
|
|
173
|
+
sequence value as sort key. In most cases, `sorter2` probably gives the desired
|
|
174
|
+
behavior.
|
|
175
|
+
|
|
176
|
+
## Performance Overhead
|
|
177
|
+
|
|
178
|
+
## More Examples
|
|
179
|
+
I developed pipescript while working on
|
|
180
|
+
[Advent of Code 2025](https://adventofcode.com/2025) in parallel,
|
|
181
|
+
and used it for most of the input processesing portions of my solutions,
|
|
182
|
+
which you can find at https://github.com/smacke/aoc2025.
|
|
183
|
+
|
|
184
|
+
## What pipescript is and is not
|
|
185
|
+
|
|
186
|
+
pipescript is not a general purpose functional programming language on top of
|
|
187
|
+
Python. It is very much not intended for production use cases, and instead
|
|
188
|
+
caters toward quick-and-dirty one-off / scratchpad type computations in IPython
|
|
189
|
+
and Jupyter specifically. In short, pipescript aims to provide simple but powerful
|
|
190
|
+
pipeline and placeholder syntax to interactive Python programming environments.
|
|
191
|
+
|
|
192
|
+
All the different pipeline operators like `|>`, `<|`, `*|>`, etc. essentially
|
|
193
|
+
transpile down to an instrumented variant of the bitwise-or (`|`) operator, and
|
|
194
|
+
therefore every new operator left-associates at the same level of precedence,
|
|
195
|
+
meaning that pipeline steps run from left to right in the order that they
|
|
196
|
+
appear. pipescript aims to optimize for simplicity, readability / writability, and
|
|
197
|
+
predictability over feature completeness (though I'd like to think it strikes a
|
|
198
|
+
fairly good balance in this regard).
|
|
199
|
+
|
|
200
|
+
## How it works
|
|
201
|
+
|
|
202
|
+
## Inspiration
|
|
203
|
+
|
|
204
|
+
pipescript draws inspiration largely from
|
|
205
|
+
[magrittr](https://magrittr.tidyverse.org/), but also from efforts like
|
|
206
|
+
[coconut](https://coconut-lang.org/) (a functional superset of Python),
|
|
207
|
+
as well as from libraries like [Pipe](https://github.com/JulienPalard/Pipe) which
|
|
208
|
+
take a different approach to fill Python's pipe gap with operator overloading hacks.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
Code in this project licensed under the [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright 2025 Stephen Macke
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
|
+
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
|
+
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pipescript: powerful pipeline syntax for IPython and Jupyter.
|
|
3
|
+
Just run `%load_ext pipescript` to begin using pipe operators, placeholders, and more.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from IPython.core.interactiveshell import InteractiveShell
|
|
9
|
+
|
|
10
|
+
from . import _version # noqa: E402
|
|
11
|
+
from .completion_patch import patch_completer, unpatch_completer
|
|
12
|
+
|
|
13
|
+
__version__ = _version.get_versions()["version"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def load_ipython_extension(shell: InteractiveShell) -> None:
|
|
17
|
+
from ipyflow.shell.interactiveshell import IPyflowInteractiveShell
|
|
18
|
+
|
|
19
|
+
from pipescript.macro_tracer import MacroTracer
|
|
20
|
+
from pipescript.pipeline_tracer import PipelineTracer
|
|
21
|
+
|
|
22
|
+
if not isinstance(shell, IPyflowInteractiveShell):
|
|
23
|
+
shell.run_line_magic("load_ext", "ipyflow.shell")
|
|
24
|
+
shell.run_line_magic("flow", "deregister all")
|
|
25
|
+
assert isinstance(shell, IPyflowInteractiveShell)
|
|
26
|
+
shell.run_line_magic(
|
|
27
|
+
"flow", f"register {PipelineTracer.__module__}.{PipelineTracer.__name__}"
|
|
28
|
+
)
|
|
29
|
+
shell.run_line_magic(
|
|
30
|
+
"flow",
|
|
31
|
+
f"register {MacroTracer.__module__}.{MacroTracer.__name__}",
|
|
32
|
+
)
|
|
33
|
+
patch_completer(shell.Completer)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def unload_ipython_extension(shell: InteractiveShell) -> None:
|
|
37
|
+
from pipescript.macro_tracer import MacroTracer
|
|
38
|
+
from pipescript.pipeline_tracer import PipelineTracer
|
|
39
|
+
|
|
40
|
+
unpatch_completer(shell.Completer)
|
|
41
|
+
shell.run_line_magic(
|
|
42
|
+
"flow",
|
|
43
|
+
f"deregister {MacroTracer.__module__}.{MacroTracer.__name__}",
|
|
44
|
+
)
|
|
45
|
+
shell.run_line_magic(
|
|
46
|
+
"flow", f"deregister {PipelineTracer.__module__}.{PipelineTracer.__name__}"
|
|
47
|
+
)
|