- I'm running into issues with newlines / complicated statements
- What's in your config?
- What are pyp's dependencies?
- Can I customise the shebang on the output of
--script
? - The output of
--explain
is a little weirdly formatted - What is start up performance like?
- What is overall performance like?
- Can I use pyp with PyPy?
You have a couple options here! As mentioned in the README, you can pass in another string to pyp to get a newline. You can also take advantage of features of your shell, such as multiline strings or ANSI C-quoted strings. Let's look at examples!
Passing multiple strings to pyp:
$ pyp --explain 'if int(x) >= 100: x += " is big"; print(x)' 'else: print(x + " is small")'
#!/usr/bin/env python3
import sys
for x in sys.stdin:
x = x.rstrip('\n')
if int(x) >= 100:
x += ' is big'
print(x)
else:
print(x + ' is small')
Using a multiline string:
$ pyp --explain '
> if int(x) >= 100:
> x += " is big"
> print(x)
> else:
> print(x + " is small")
> '
Using an ANSI C-quoted string:
$ pyp --explain $'if int(x) >= 100:\n x += " is big"\n print(x)\nelse:\n print(x + " is small")'
My config is pretty simple:
import numpy as np
n = int(x)
j = json.loads(stdin)
f = x.split()
# like f, but returns None if index is of bounds
ff = defaultdict(lambda: None, dict(enumerate(x.split())))
d = defaultdict(list)
If run on Python 3.9 or later, pyp has no dependencies.
On earlier Python versions, pyp is dependent on astunparse
for --explain
/--script
to work as promised. Note you can use pyp.py
as a Python script without
installing astunparse
and everything else will work. (In fact --script
will still output a
script that does what you want it to, but it won't be particularly readable...)
Yes! Just add the shebang you want to your config file.
pyp
will use the shebang of the file pointed to by PYP_CONFIG_PATH
as the shebang for the output
of --explain
/--script
.
Try running with Python 3.9 or later! The AST unparser that the standard library includes in 3.9 and later fixes a lot of formatting nits that are present in third party AST unparsers.
You can also pipe the output of pyp --explain
to an autoformatter like black:
$ pyp --explain x | black --quiet -
It's not perceptible in my use of pyp. And remember that if you're piping input into pyp
,
processes start in parallel, so this is zero extra wall time if your piped input has any latency.
Here's a benchmark that should basically just be measuring the fixed costs of start up and AST transformation (run on my laptop):
hyperfine -w 10 -m 100 'pyp x'
Benchmark #1: pyp x
Time (mean ± σ): 56.3 ms ± 1.0 ms [User: 41.2 ms, System: 11.4 ms]
Range (min … max): 53.9 ms … 60.3 ms 100 runs
One note here, as mentioned in the README, is that if you use wildcard imports (from x import *
)
in your config file, pyp might need to
perform those imports to resolve undefined names (although pyp avoids this if possible). If this is
causing you latency, just import what you need instead (i.e., change your config to read
from x import y, z
).
Better than awk! ;-)
$ hyperfine -w 3 -m 10 "seq 1 999999 | pyp 'sum(map(int, stdin))'"
Benchmark #1: seq 1 999999 | pyp 'sum(map(int, stdin))'
Time (mean ± σ): 258.2 ms ± 5.6 ms [User: 422.3 ms, System: 17.0 ms]
Range (min … max): 252.1 ms … 270.7 ms 11 runs
$ hyperfine -w 3 -m 10 'seq 1 999999 | awk "{s += $0} END {print s}"'
Benchmark #1: seq 1 999999 | awk "{s += $0} END {print s}"
Time (mean ± σ): 405.3 ms ± 3.4 ms [User: 599.6 ms, System: 5.5 ms]
Range (min … max): 399.4 ms … 410.9 ms 10 runs
More seriously, random micro benchmark aside, pyp should be fast enough that you shouldn't worry about performance until you know you need to, as with Python itself.
One note here is that the the magic variable lines
is currently always a list. Hence, if you find
yourself processing really large input, it might be better to use the magic variable stdin
(this is just sys.stdin
). For the above example:
$ hyperfine -w 3 -m 10 "seq 1 999999 | pyp 'sum(map(int, lines))'"
Benchmark #1: seq 1 999999 | pyp 'sum(map(int, lines))'
Time (mean ± σ): 378.9 ms ± 3.2 ms [User: 530.2 ms, System: 38.5 ms]
Range (min … max): 375.4 ms … 384.6 ms 10 runs
You should be able to use PyPy! Tests currently pass against PyPy (run tox -e pypy3
).
You can install pyp using PyPy with something like pypy3 -m pip install pypyp
.
Running pyp sys.version
will tell you whether it worked. Note that pyp requires at least
Python 3.8; this constraint naturally applies when using PyPy as well.