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.
$ pip install rlpython
rlpython can be used to try or debug any valid Python code.
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.
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.
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()
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
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()
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])