rlpython

GNU Readline based Python REPL without any external dependencies


Keywords
debugging, hacktoberfest, python, repl
License
Other
Install
pip install rlpython==0.10.3

Documentation

rlpython

license MIT Python Version Latest Version

rlpython is a versatile Python REPL (Read-Eval-Print Loop) built on GNU Readline, without any external dependencies. It serves as a command-line tool, can be seamlessly embedded into scripts, and even operates as a network server, allowing for convenient debugging and monitoring.

Installation

$ pip install rlpython

Use-Cases

Python REPL

rlpython can be used to try or debug any valid Python code.

Unix Commands

rlpython supports Unix commands prefixed with !, for example !cat foo.txt. For convenience rlpython supports aliases and comes with some well-known shell aliases built-in.

Code Introspection

rlpython has code introspection built-in that can be used to print the signature or documentation of a method or function, or to jump to its implementation using an editor.

Embedding from Source

rlpython can be embedded in any Python script and be used as a debugger, using rlpython.embed(). The starting rlpython shell then has access to all previous defined variables and imported modules.

import rlpython

rlpython.embed()

Embedding from Source over Network

rlpython can also bind to a socket and receive commands over the network. This is useful if your script runs without stdin and stdout.

import rlpython

rlpython.embed(bind='localhost:5000')  # host and port
rlpython.embed(bind='file://socket')   # unix domain socket

Embedding from Source over Network for Multiple Users

For monitoring, rlpython can be run as a multi-session server, for multiple users.

import asyncio

import rlpython

loop = asyncio.get_event_loop()

with rlpython.embed(bind='localhost:5000', multi_session=True):
    loop.run_forever()

Custom Commands

rlpython has support for Unix-like shell commands, prefixed with %. A list of all loaded commands can be seen using ?. rlpython has a simple API to define custom commands.

This example defines a command named user which prints a list of users with their usernames and full names as a table. If a username is given as a command line argument, only the full name of the given user is printed.

The command implements tab completion, a help string, and -l to limit lines.

import rlpython

from rlpython.utils.argument_parser import ReplArgumentParser
from rlpython.utils.table import write_table

USER = {
    'alice': 'Alice Allison',
    'ally': 'Ally Allison',
    'bob': 'Bob Roberts',
    'carl': 'Carl Carlson',
    'mal': 'Malory Masterson'
}


class UserListCommand:
    """
    Show User list
    """

    NAME = 'user'

    def __init__(self, repl):
        self.repl = repl

    def complete(self, text, state, line_buffer):
        names = sorted(list(USER.keys()))
        candidates = []

        for name in names:
            if name.startswith(text):
                candidates.append(name)

        candidates.append(None)

        return candidates[state]

    def run(self, argv):

        # parse command line arguments
        # ReplArgumentParser is a subclass of argparse.ArgumentParser
        argument_parser = ReplArgumentParser(
            repl=self.repl,
            prog=self.NAME,
        )

        argument_parser.add_argument('username', nargs='?')
        argument_parser.add_argument('-l', '--limit', type=int)

        args = argument_parser.parse_args(argv[1:])

        # show given user
        if args.username:
            if args.username not in USER:
                self.repl.write(
                    f"no user with username '{args.username}' found\n",
                )

                return 1

            # we use repl.write instead of print here, so the command
            # also works over the network
            self.repl.write(f"{USER[args.username]}\n")

        # show all users
        else:
            rows = [['Username', 'Full Name']]

            for index, (user_name, full_name) in enumerate(USER.items()):
                rows.append(
                    [user_name, full_name],
                )

                if args.limit and args.limit == index + 1:
                    break

            write_table(rows, self.repl.write)


rlpython.embed(commands=[UserListCommand])