## Toy REPL

Let us first write a tiny Python program to create a toy read-eval-print-loop (REPL) that does only one thing: add all integers entered as input into the REPL prompt. Here is the program:

    while True:
        try:
            line = input('> ')
            print(sum([int(n) for n in line.split()]))
        except ValueError as e:
            print('error:', e)
        except (KeyboardInterrupt, EOFError):
            break

Here is how it works:

    $ python3 repl.py
    > 10 20 30
    60
    > 40 50 60
    150
    >

If we now type `↑` (the up arrow key) or `ctrl`+`p` to bring back the previous input, we see something like the following instead:

    > ^[[A^[[A^P^P

It shows the keys typed literally rather than bringing up previous input like most other interactive programs with a command-line interface do. The other programs that do bring up the previous input are able to do so because they provide line editing and history capability, often with the help of a line editing and history library like GNU Readline (libreadline) or BSD Editline (libedit).

Can we have a similar line editing and history capability for our toy REPL? After all, the Python REPL itself offers such a line editing facility. Surely there must be a way to have this facility for our own programs too. Indeed there is!

## Line Editing and History

To enable line editing and history in our toy REPL, we just need to add `import readline` to our program. Here is how our program would look:

    import readline

    while True:
        try:
            line = input('> ')
            print(sum([int(n) for n in line.split()]))
        except ValueError as e:
            print('error:', e)
        except (KeyboardInterrupt, EOFError):
            break

Now `↑`, `ctrl`+`p`, etc. work as expected.

    $ python3 repl.py
    > 10 20 30
    60
    > 40 50 60
    150
    > 40 50 60

The last line of input in the example above is obtained by typing either `↑` or `ctrl`+`p`. In fact, all of the line editing keys like `ctrl`+`a` to go to the beginning of the line, `ctrl`+`k` to kill the line after the cursor, etc. work as expected. The exact list of default key-bindings supported depends on the underlying line editing library being used by the `readline` module. The underlying library may be either the GNU Readline library or the BSD Editline library. There are some minor differences regarding the list of default key-bindings between these two libraries.

## History File

What we have done so far achieves the goal of bringing up previous inputs from the history. However, it does not bring back inputs from a previous invocation of the REPL. For example, if we start our toy REPL, enter some inputs, then quit it (say, by typing `ctrl`+`c`), start our toy REPL again and type `↑` or `ctrl`+`p`, it does not bring back the input from the previous invocation. For a full-blown REPL meant for sophisicated usage, we may want to preserve the history between different invocations of the REPL. This can be achieved by using the `read_history_file()` and `write_history_file()` functions as shown below:

    import readline
    import os

    HISTORY_FILE = os.path.expanduser('~/.repl_history')
    if os.path.exists(HISTORY_FILE):
        readline.read_history_file(HISTORY_FILE)

    while True:
        try:
            line = input('> ')
            readline.write_history_file(HISTORY_FILE)
            print(sum([int(n) for n in line.split()]))
        except ValueError as e:
            print('error:', e)
        except (KeyboardInterrupt, EOFError):
            break

For more information on how to use this module, see the Python readline documentation.

## Readline Wrapper

At this point, it is worth mentioning that there are many interactive CLI tools that do not have line editing and history capabilities. They behave like our first toy REPL example in this post. Fortunately, there is the wonderful readline wrapper utility known as `rlwrap` that can be used to enable line editing and history in such tools. This utility can often be easily installed from package repositories of various operating systems. Here is a demonstration of this tool:

    $ rlwrap cat
    hello
    hello
    world
    world
    world

The last line of input in the example above is obtained by typing either `↑` or `ctrl`+`p`. In the above example, the input history is automatically saved to `~/.cat_history`, so it is possible to bring back inputs from a previous invocation of the command.

## Obligatory Joke

Finally, an obligatory XKCD comic to conclude this post:

Python by Randall Munroe (Source: https://xkcd.com/353/)

While the days of achieving air flight with a single `import` statement might still be a few decades away, we do have the luxury to enable line editing and history in our REPLs with a single such statement right now.