bash-like functionalities in command-line Python script
Problem
You have an interactive Python script that reads input from the command line. You want to add bash-like functionalities to it like moving the cursor with the arrows, jump to the front with Home, jump to the end with End, browse previous commands with the up arrow, etc.
Solution
You won’t believe what is needed for this:
import readline
Yes, that’s it. Just import it and you are good to go.
Minimal example:
import readline while True: inp = raw_input("> ") print "You entered", inp
Alternative
Before discovering the readline
module, I used to start my scripts with “rlwrap
“:
rlwrap my_script.py
It does the trick too. I put the line above in a script called “my_script.sh
” and I launched this latter one. However, “import readline
” is simpler.
docopt: a pythonic command line arguments parser that kicks your ass
Docopt is a parser for command line arguments. There are other alternatives like optparse and argparse but docopt is the king. Why? Because it cannot be any simpler :) All you have to do is write an interface description to your script that is well-known from man pages and from the help of other command line applications.
Example (taken from the docopt site):
Naval Fate. Usage: naval_fate.py ship new <name>... naval_fate.py ship <name> move <x> <y> [--speed=<kn>] naval_fate.py ship shoot <x> <y> naval_fate.py mine (set|remove) <x> <y> [--moored|--drifting] naval_fate.py -h | --help naval_fate.py --version Options: -h --help Show this screen. --version Show version. --speed=<kn> Speed in knots [default: 10]. --moored Moored (anchored) mine. --drifting Drifting mine.
Then you simply pass this string to docopt. When you execute the script, the parameters will be parsed by docopt according to the textual description above. Say you launch this script with the following parameters:
./naval_fate.py ship Guardian move 10 50 --speed=20
Docopt will parse it and return the following dictionary:
{"--drifting": false, "--help": false, "--moored": false, "--speed": "20", "--version": false, "<name>": ["Guardian"], "<x>": "10", "<y>": "50", "mine": false, "move": true, "new": false, "remove": false, "set": false, "ship": true, "shoot": false}
That’s all. You can try docopt online here.
If you got interested, watch the presentation of its author (you will find it on the top of the home page of the project).
It’s available on GitHub, where you can also check out some more examples.
Usage: either install it via pip
, or just simply add the file docopt.py
next to your script.
Terminate a script after X seconds
Problem
In Qt, there is a class called QTimer. With QTimer you can, for instance, terminate your application in X seconds. Example:
#!/usr/bin/env python import sys from PySide.QtCore import * from PySide.QtGui import * SEC = 1000 # 1 sec. is 1000 msec. def main(): app = QApplication(sys.argv) form = QDialog() form.show() # suicide in 3 seconds: QTimer.singleShot(3 * SEC, app.quit) app.exec_() if __name__ == "__main__": main()
Question: how to have the same effect in a command-line application?
Solution
I came up with the following solution:
#!/usr/bin/env python import sys from time import sleep import signal class MyTimer(object): """ Similar to Qt's QTimer. Call a function in a specified time. Time is given in sec. Usage: mt = MyTimer() mt.singleShot(<sec>, <function_name>) After setting it, you can still disable it: mt.disable() If you call it several times, any previously scheduled alarm will be canceled (only one alarm can be scheduled at any time). """ def singleShot(self, sec, func): self.f = func signal.signal(signal.SIGALRM, self.handler) signal.alarm(sec) def handler(self, *args): self.f() def disable(self): signal.alarm(0) def main(): while True: print '.', sleep(0.5) if __name__ == "__main__": mt = MyTimer() mt.singleShot(3, sys.exit) main()
As can be seen, the main
function has an eternal loop. However, this program will terminate in 3 seconds. I’m imitating QTimer’s singleshot
. The differences: (1) you must create a MyTimer object, and (2) time is given in seconds, not in milliseconds. You can also write it in one line if you want: MyTimer().singleShot(3, sys.exit)
.
It is written in a general form, so instead of sys.exit
, you can also call a different function.
get screen resolution
Problem
You need the current screen resolution.
Solution #1 (command-line)
xrandr | grep '*'
Sample output:
1920x1080 50.0* 51.0 52.0
This tip is from here.
Solution #2 (Python)
Let’s see the previous solution in Python:
from jabbapylib.process import process def get_screen_resolution(): """ Screen resolution (as a tuple). """ result = [x for x in process.get_simple_cmd_output('xrandr').split('\n') if '*' in x][0] result = tuple([int(x) for x in result.split()[0].split('x')]) return result
Sample output:
(1920, 1080)
process.get_simple_cmd_output
is part of my jabbapylib library. get_simple_cmd_output()
simply executes an external command and returns its output as a string.
pyp – Python Power at the Prompt (The Pyed Piper)
“Pyp is a Linux command-line text manipulation tool similar to awk or sed, but which uses standard python string and list methods as well as custom functions evolved to generate fast results in an intense production environment. Pyed Pyper was developed at Sony Pictures Imageworks to facilitate the construction of complex image manipulation “one-liner” commands during visual effects work on Alice in Wonderland, Green Lantern, and the upcoming The Amazing Spiderman.” (source)
Installable via jabbatron.
This post is a reminder for me. As soon as I go through its documentation, I will write some notes about it.