I wrote a little module for making quick CLIs in Python and I wanted to get your thoughts on areas of improvement (as well as to see what can break it).
One of the issues I had from the start was with looping and multi-threading, I wound up just adding a 1 second delay to make sure I've enough time to do some basic processing, but it seems a bit hackish. I'd like to perform some basic tests on the variable first (e.g. make sure it's not going to cause an overflow) and then actually copy the variable into an entirely separate memory space, although I'm not terribly knowledgeable about python's memory management practices and could use some guidance in the right direction.
A quick brief on how it works, functions which are to be made available to the user are stored in a dictionary with command:function key-pair, if a user enters a command which isn't recognized it will simply print the help screen message, right now it only has support for space separated values. It should be fairly trivial to add additional deliminator functionality like commas, dashes, etc. but I am also a bit worried about adding too many features as well and making it cumbersome to work with.
You can download the source on the GitHub repo or install with pip/pip3:
$ pip3 install -i https://test.pypi.org/simple/ pycli
And I've included the source code for reference:
#!/usr/bin/python3
"""Basic CLI in Python3"""
import threading as th
import time
cont=True
text=None
class InputThread:
def __init__(self):
global cont
global text
while True:
self.loop_thread()
time.sleep(1)
text=None
cont=True
def loop_thread(self):
global cont
global text
text = input()
cont=False
class CLI:
def __init__(self):
self.cmds = {
'help':self.help,
}
self.init_message = ''
def help(self, args=None):
"""Use help 'command' for more information on a specific command."""
if args==None:
print("Available Commands:")
for key in self.cmds.keys():
print(key)
print(self.help.__doc__)
else:
print(self.cmds[args[0]].__doc__)
def set_message(self, message):
"""Sets the initialization message"""
self.init_message = message
def add_function(self, name, function):
"""Adds a function to the command dictionary"""
self.cmds[name] = function
def cli(self, a):
global cont
args = a.split(' ')
if args[0] not in self.cmds.keys():
print('Command "', args[0], '" Not Found.')
self.help()
else:
if len(args) == 1:
self.cmds[args[0]]()
else:
self.cmds[args[0]](args[1:])
cont=True
def loop(self):
global cont
global text
print(self.init_message)
th.Thread(target=InputThread, args=(), name='user_input_thread', daemon=True).start()
while True:
if not cont:
self.cli(text)
if __name__ in '__main__':
x = CLI().loop()
and here's an example CLI made using the module:
#!/usr/bin/python3
from pycli import CLI as cli
import random
def random_function(x, y):
"""Generates a random number between x and y.
Usage:
random x y
"""
print(random.randint(x,y))
if __name__ in '__main__':
x = cli()
x.set_message("""Welcome to my super awesome python CLI!
With this new tool you can quickly generate command line
interfaces that capture user input without having to figure
out how to handle all those inputs yourself!
""")
x.add_function('random', random_function)
x.loop()
I've only tested this on Linux Mint (don't have Windows or Mac), so if you've got one of those OS's can you let me know if the code works as expected and any bugs?