Settings

Theme

Show HN: Pyp – Easily, magically, run Python at the shell

github.com

285 points by hauntsaninja 6 years ago · 43 comments

Reader

j88439h84 6 years ago

Mario is a very featureful application of similar ideas, and includes multiple built-in transformations, concurrency support, user-defined commands, and extensive documentation.

https://github.com/python-mario/mario

chrisweekly 6 years ago

My cousin helped create xonsh (https://xon.sh), a POSIX-compliant "python as drop-in shell replacement", years ago.

  • hauntsaninjaOP 6 years ago

    xonsh is great! I'm a fan of your cousin's work :-) I mentioned it in https://github.com/hauntsaninja/pyp#related-projects for if you want even more Python in your shell.

  • rahimnathwani 6 years ago

    Cool! It's mentioned in pyp's README:

    "xonsh is a shell whose language is a superset of Python; this is more ambitious and pretty different from pyp. pyp is easier to use for the one-liner piping use case, but if you need more Python in your shell, check out xonsh."

  • joseph8th 6 years ago

    Thanks! Xonsh is my daily driver. With the vox plugin, don't even need virtualenv

jeswin 6 years ago

Nice. I made something similar, but for JavaScript https://github.com/jeswin/basho

It's very useful to be able to automate with a language you already know. I use it quite a bit now, for instance to figure out which of my git repos need a push or a pull.

gmfawcett 6 years ago

This made me nostalgic for a utility I wrote back in 2005 called `pyline`:

http://code.activestate.com/recipes/437932-pyline-a-grep-lik...

I'm sure I wasn't the first to scratch that particular itch. It always seemed odd, back then, that Python didn't have better support for the Perl one-liner style of programming.

  • Drdrdrq 6 years ago

    It still seems odd to me. While these projects are nice, if the functionality is not supported in the core system, it is rarely worth the additional trouble (imho). Situation is similar to gitless - git's cli interface sucks, but it is available on every dev machine.

dbieber 6 years ago

This is fantastic -- I will certainly turn to this instead of awk/sed in many instances. I expect I'll use this in place of particularly tricky greps too. Really nice that you can seamless interleave standard bash commands with pyp commands, if e.g. an existing bash tool can get a piece of the job done more simply.

I already turn to Python in place of Bash whenever any nontrivial amount of complexity is needed or if any reuse is expected in the script I'm writing (I do this with Python Fire). Now I think I'll be making that Bash->Python switch at even lower levels of complexity, and even when no reuse is expected at all.

tehalex 6 years ago

Here's the ruby equivalent:

https://github.com/thisredone/rb

  • freedomben 6 years ago

    Was hoping to find a comment just like yours! Thanks!

    I use Ruby from the cli frequently for stuff that exceeds an easy jq expression or if jq is not availabel but Ruby is (which happens a lot on RHEL systems when EPEL is not enabled, which is the case in a lot of banks) Ruby's awk -> perl -> ruby heritage endows it with a pretty good cli mode, but sometimes it's verbose. For example parsing json:

        curl https://something \
          | ruby -r json -e "puts JSON.parse(STDIN.read)['data']['ip']"
    
    But obviously that's wordier than it needs. (To pre-empt the "just use jq!" people, I agree. For simple examples I would just use jq. This example here is just an example).
  • eloahx 6 years ago

    Awesome! Thanks!

dagmx 6 years ago

There exists a very similar tool for several years with the exact same name

https://opensource.imageworks.com/pyp.html

  • hauntsaninjaOP 6 years ago

    Yup, that was the inspiration for this project and its name. See https://github.com/hauntsaninja/pyp#related-projects for more discussion. By no means is this a new idea, but I think this iteration has some convenient twists :-)

    • nightcracker 6 years ago

      Yanking another projects name is more than just 'inspiration'. I would consider it a very hostile action.

      • hauntsaninjaOP 6 years ago

        Given that the original project has been unmaintained for several years and is Python 2 only, I think it's not unreasonable. There's very little reason to have both installed, so I don't think it's a problem for users either.

        It's also just the perfect name! It's punny, it's short and it acknowledges prior art. And of course, if the original author took issue with it, I would change the name.

Evanbenn 6 years ago

Brilliant, just the right amount of magic. The explain feature is what really sold me that you thought about users! I wonder if I can put a bit of humanfriendly support in there with a pr.

daenz 6 years ago

Very cool! I'm the author of sh.py, and seeing projects that use just the right amount of magic to accomplish something unique and powerful is inspiring. Great work and good luck with pyp!

akubera 6 years ago

Also related is pythonpy[0], which added a similarly named command 'py', but apparently that project has died.

This looks like a big improvement, with math functions auto-imported, and the --script option is a fantastic idea.

Good job.

[0.0] https://pypi.org/project/pythonpy

[0.1] https://news.ycombinator.com/item?id=8158976

vertak 6 years ago

This is very cool. This is faster than postman for me if I was to use something like you showed in the jq example for quickly playing around with a public api’s json response.

slightwinder 6 years ago

Something like this should be baked straight into CPython itself. There are so many similar projects, there clearly is some demand for this. This could boost pythons usage for shell-jobs significantly if people have a reliable installation of this behaviour.

kortex 6 years ago

This is going straight into the toolkit. The main thing that keeps me from doing more inline python is all of the printing boilerplate.

Well, two things, the second thing being performance.

How's the startup time? I find it hard to get python utils under a few hundred ms just to spin up.

  • hauntsaninjaOP 6 years ago

    Thanks for checking pyp out!

    Startup time hasn't been particularly perceptible in my use of it. Just ran a quick benchmark on my (old, not powerful) laptop:

      ~ λ hyperfine 'pyp x'
      Benchmark #1: pyp x
        Time (mean ± σ):      75.9 ms ±   1.2 ms    [User: 55.9 ms, System: 15.1 ms]
        Range (min … max):    74.5 ms …  79.0 ms    36 runs
    
    Since there isn't any input, this should just measure startup and AST transformation time.
rasengan 6 years ago

I saw this and just made this real quick - a JS equivalent.

https://github.com/realrasengan/jpipe

  • eloahx 6 years ago

    Thanks! This will make things a little easier for me as I move in to sysadminning and JS development.

    Surprised it didnt exist already

hauntsaninjaOP 6 years ago

Thanks to everyone who checked pyp out!

Not sure if this comment will ever get seen, but I just added the ability to configure pyp with statically analysed Python, so it's easy to define your own functions, import aliases, or use libraries like pipetools to make your life easier! More details at https://github.com/hauntsaninja/pyp#pyp-is-configurable

gigatexal 6 years ago

Ok, I've been looking for something like this. Very cool indeed!

beamatronic 6 years ago

A lot of these projects remind me of the Smalltalk image concept - I used to develop on Digitalk Smalltalk/V on Windows 3.1 in the mid 90s.

isignal 6 years ago

Pretty cool!

How would one accomplish the following with pyp?

* Wait until one sees a line with a start marker (say 'start:')

* Perform on an operation on the lines until an end marker ('end:') is reached.

Would you do a whole-input operation on stdin to extract the relevant lines first and then operate on the individual lines? Or would you maintain boolean conditionals within the loop?

  • hauntsaninjaOP 6 years ago

    There are a number of possible options! And remember you can always pipe pyp to pyp. Here are some ideas, in order of what came to mind, where the operation is incrementing.

      pyp 'lines[lines.index("start") + 1:]' | pyp 'lines[:lines.index("end")]' | pyp 'int(x) + 1'
      
      pyp 'start = lines.index("start"); end = lines.index("end", start); map(lambda x: int(x) + 1, lines[start + 1:end])'
      
      pyp 'dropwhile(lambda x: x != "start", lines)' | pyp 'lines[1:]' | pyp 'takewhile(lambda x: x != "end", lines)' | pyp 'int(x) + 1'
      
      pyp 'map(lambda x: int(x) + 1, takewhile(lambda x: x != "end", islice(dropwhile(lambda x: x != "start", lines), 1, None)))'
      
      pyp -b 'cond = False' 'if cond and x == "end": break' 'if cond: print(int(x) + 1)' 'if x == "start": cond = True'
  • Erwin 6 years ago

    Having moved from Perl to Python many years ago, this is one of those things that Perl has magic to handle, for example:

        perl -lne '$sum += $_ if /start:/ .. /end:/; END{print $sum}' file.txt
    
    With the -n option, perl runs your code for every line in the input (stdin, or filenames). $_ holds the content of the line. And the .. operator is a stateful (!) operator that becomes true when its left part becomes true, until the right part becomes true.

    So I write 95% of my code in Python, but for command line magic, it's hard to beat Perl.

beagle3 6 years ago

Ubercool. There's a similar project called "pythonpy" which I had used in the past.

It seems to have disappeared off the face of the earth, the only thing I found is this fork: https://github.com/fish2000/pythonpy-fork

Hello71 6 years ago

I was going to ask "isn't this basically just awk" but the README quickly cleared that up! Excellent documentation.

One thing I'm wondering is how's the startup time/performance in general? awk is kinda slow overall, but Python starts a little too slow to be used in shell scripting.

  • Hello71 6 years ago

    Looks like there are no dependencies, so it should start pretty fast.

    And sure enough, it's both easy and reasonably fast to run:

      $ time seq 1 10000 | awk '{a+=$0}END{print a}'
      50005000
      seq 1 10000  0.00s user 0.00s system 91% cpu 0.002 total
      awk '{a+=$0}END{print a}'  0.01s user 0.00s system 95% cpu 0.008 total
      $ time seq 1 10000 | python3 pyp.py 'sum(map(int, lines))'
      50005000
      seq 1 10000  0.00s user 0.00s system 80% cpu 0.002 total
      python3 pyp.py 'sum(map(int, lines))'  0.06s user 0.01s system 99% cpu 0.070 total
    
    I wouldn't run it in a loop, but it's definitely a contender even in high-ish performance situations.
emblaegh 6 years ago

I remember using this waay back in the day when this was still hosted on google code, and having a really good experience with it, despite some performance issues. I thought the project was dead, but I'm glad it's still going on.

geophile 6 years ago

Along similar lines: https://github.com/geophile/marcel

michaelmior 6 years ago

An older attempt at this is piep

https://github.com/timbertson/piep

auscompgeek 6 years ago

Yet another similar project: https://github.com/edk0/spy

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection