Kushal Das: Developing Command Line Interpreters using python-cmd2


Many of you already know that I love command line applications. Let it be a
simple command line tool, or something more complex with a full command line
interface/interpreter (CLI) attached to it. Back in college days, I tried to
write a few small applications in Java with broken implementations of CLI.
Later when I started working with Python, I wanted to implement CLI(s) for
various projects. Python already has a few great modules in the standard
library, but, I am going to talk about one external library which I prefer to
use a lot. Sometimes even for fun 🙂

Welcome to python-cmd2

python-cmd2 is a Python module which is written on top of the cmd module of
the standard library. It can be used as a drop-in replacement. Through out this
tutorial, we will learn how to use it for simple applications.

Installation

You can install it using pip, or standard package managers.

$ pip install cmd2
$ sudo dnf install python3-cmd2

First application

#!/usr/bin/env python3

from cmd2 import Cmd


class REPL(Cmd):

    def __init__(self):
        Cmd.__init__(self)


if __name__ == '__main__':
    app = REPL()
    app.cmdloop()

We created a class called REPL, and later called the cmdloop method from an
object of the same class. This will give us a minimal CLI. We can type ! and
then any bash command to execute. Below, I called the ls command. You can
also start the Python interpreter by using py command.

$ python3 mycli.py 
(Cmd) 
(Cmd) !ls
a_test.png  badge.png  main.py	mycli.py
(Cmd) py
Python 3.5.2 (default, Sep 14 2016, 11:28:32) 
[GCC 6.2.1 20160901 (Red Hat 6.2.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(REPL)

        py <command>: Executes a Python command.
        py: Enters interactive Python mode.
        End with ``Ctrl-D`` (Unix) / ``Ctrl-Z`` (Windows), ``quit()``, '`exit()``.
        Non-python commands can be issued with ``cmd("your command")``.
        Run python code from external files with ``run("filename.py")``
        
>>> 
(Cmd) 

You can press Ctrl+d to quit or use quit/exit commands.

Let us add some commands

But, before that, we should add a better prompt. We can have a different prompt
by changing the prompt variable of the Cmd class. We can also add some banner
by adding text to the intro variable.

#!/usr/bin/env python3
from cmd2 import Cmd

class REPL(Cmd):
    prompt = "life> "
    intro = "Welcome to the real world!"

    def __init__(self):
        Cmd.__init__(self)


if __name__ == '__main__':
    app = REPL()
    app.cmdloop()
$ python3 mycli.py 
Welcome to the real world!
life> 

Any method inside our REPL class which starts with do_ will become a
command in our tool. For example, we will add a loadaverage command to show
the load average of our system. We will read /proc/loadavg file in our Linux
computers to find this value.

#!/usr/bin/env python3

from cmd2 import Cmd


class REPL(Cmd):
    prompt = "life> "
    intro = "Welcome to the real world!"

    def __init__(self):
        Cmd.__init__(self)

    def do_loadaverage(self, line):
        with open('/proc/loadavg') as fobj:
            data = fobj.read()
        print(data)

if __name__ == '__main__':
    app = REPL()
    app.cmdloop()

The output looks like:

$ python3 mycli.py 
Welcome to the real world!
life> loadaverage
0.42 0.23 0.24 1/1024 16516

life> loadaverage
0.39 0.23 0.24 1/1025 16517

life> loadaverage
0.39 0.23 0.24 1/1025 16517

If you do not know about the values in this file, the first three values
indicate the CPU/IO utilization of the last one, five and ten minutes back.
Then we have the number of currently running processes and the total number of
processes. The final column shows the last process ID used. You can also see
that TAB will autocomplete the command in our shell. We can go back to the past
commands by pressing the arrow keys. We can also press Ctrl+r to do a reverse
search like the standard bash shell. This feature comes from the readline
module. We can use that more, and add a history file to our tool.

import os
import atexit
import readline
from cmd2 import Cmd

history_file = os.path.expanduser('~/.mycli_history')
if not os.path.exists(history_file):
    with open(history_file, "w") as fobj:
        fobj.write("")
readline.read_history_file(history_file)
atexit.register(readline.write_history_file, history_file)



class REPL(Cmd):
    prompt = "life> "
    intro = "Welcome to the real world!"

    def __init__(self):
        Cmd.__init__(self)

    def do_loadaverage(self, line):
        with open('/proc/loadavg') as fobj:
            data = fobj.read()
        print(data)

if __name__ == '__main__':
    app = REPL()
    app.cmdloop()

Taking input in the commands

We can use the positional argument in our do_ methods to have arguments in
our commands. Whatever input you are passing to the command, comes to the
line variable in our example. We can use the same to do anything. For
example, we can take any URL as input, and then check the status. We will use
requests module for this example. We also used the Cmd.colorize method to add
colors to our output text. I have added one extra command to make the tool more
useful.

#!/usr/bin/env python3

import os
import atexit
import readline
import requests
from cmd2 import Cmd

history_file = os.path.expanduser('~/.mycli_history')
if not os.path.exists(history_file):
    with open(history_file, "w") as fobj:
        fobj.write("")
readline.read_history_file(history_file)
atexit.register(readline.write_history_file, history_file)



class REPL(Cmd):
    prompt = "life> "
    intro = "Welcome to the real world!"

    def __init__(self):
        Cmd.__init__(self)

    def do_loadaverage(self, line):
        with open('/proc/loadavg') as fobj:
            data = fobj.read()
        print(data)

    def do_status(self, line):
        if line:
            resp = requests.get(line)
            if resp.status_code == 200:
                print(self.colorize("200", "green"))
            else:
                print(self.colorize(str(resp.status_code), "red"))

	def do_alternativefacts(self, line):
    		print(self.colorize("Lies! Pure lies, and more lies.", "red"))

if __name__ == '__main__':
    app = REPL()
    app.cmdloop()

Building these little shells can be a lot of fun. The
documentation has all the details, but, you
should start reading from the standard lib cmd
documentation
. There is also the
video
from PyCon 2010.


Source From: fedoraplanet.org.
Original article title: Kushal Das: Developing Command Line Interpreters using python-cmd2.
This full article can be read at: Kushal Das: Developing Command Line Interpreters using python-cmd2.

Advertisement


Random Article You May Like

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*