How to get first letter commands using python's cmd? - python

I would like to type "c" in my commandline, hit enter and have it run the "command" command. This code does what I want, but it does not use cmd. I would like to use cmd:
import sys
def command():
print("This is a command")
def quit():
print("goodbye")
sys.exit()
def take(item=None):
if item:
print("You take %s" % item)
else:
print("What would you like to take?")
commands = {
'command': command,
'quit': quit,
'take': take,
}
def complete(text, state):
print("Here is the text: %s and here is the state %s" % (text, state))
def my_loop():
while True:
c = raw_input("\n>")
if c.strip():
c = c.split(' ', 1)
for command in commands:
if command.startswith(c[0]):c[0] = command
func = commands.get(c[0], None)
if func:
if len(c) == 1:func()
else:func(c[1])
else:print("I don't understand that command")
my_loop()
Here is the same using cmd, but it does not run the "command" command when I type "c" and hit enter.
import sys, cmd
class Mcmd(cmd.Cmd):
prompt = '\n>'
def default(self, arg):
print("I do not understand that command. Type 'help' for a list of commands")
def do_command(self, line):
print("This is a command")
def do_quit(self, arg):
print("See you")
return True
Mcmd().cmdloop()
How can I get the start of the command to trigger the "command" or "quit" command using cmd?
("c", "co", "com", "comm"...)
all trigger the "command" function.
I was considering using the new textwrap module, but textwrap has problems being cross-platform.
Is there any other way to do this?
thank you,

You already managed to figure out how to override the default method, so you can extend that to look up a compatible method from within your Mcmd class. Try something like this:
def default(self, line):
command, arg, line = self.parseline(line)
func = [getattr(self, n) for n in self.get_names()
if n.startswith('do_' + command)]
if len(func) == 1:
return func[0](arg)
print("I do not understand that command. Type 'help' for a list of commands")
Running that
>a
I do not understand that command. Type 'help' for a list of commands
>c
This is a command
>ca
I do not understand that command. Type 'help' for a list of commands
>co
This is a command
>q
See you
While technically a duplicate of Aliases for commands with Python cmd module the answer isn't quite complete; it doesn't properly address ambiguous cases (say you got both do_command and do_copy, what should happen on the input of c and co?). Here it only returns if there is a singular match.
Alternatively Python cmd module command aliases is a possible answer, but it's just tedious.

Related

How to run function if the command is the name of a function?

So I'm wondering how to run a function from another file so if the command is the same as the function name?
discord.py implements it exactly how I want to do it. where it can just call the commands.
my code looks like this
main.py
from ai_main import CommandManager
bot = CommandManager(prefix="!")
#bot.call_fun
def hello():
print("hello")
bot.run()
ai_main.py
class CommandManager:
def __init__(self, prefix="!"):
print("INIT")
self.prefix = prefix
def run(self):
user_input = input("Enter command: ")
self.check_for_command(user_input)
def check_for_command(self, user_input):
if user_input[0] == self.prefix:
string = ""
for char in user_input[1:]:
if char == " ":
break
else:
string += char
if string != "":
self.call_fun(string)
def call_fun(self, command):
# command.run() ??? how to call it?
return command
You want to store an instance of the function inside a mapping as part of the CommandManager class. Later, you can use the mapping to find the stored function.
You can also make the run method use a While loop to allow calling multiple commands.
class CommandManager:
def __init__(self, prefix="!"):
"""
The CommandManager allows users to register functions for
calling them in an interactive shell!
Each command can be registered in the CommandManager by wrapping
it with the add_command decorator. This will store the function in
the manager by looking at it's __name__ attribute and later allow
users to reference the function by that name.
All commands given to the CommandManager must start with "!",
followed by the name of the registered function.
"""
self.prefix = prefix
self.commands = {}
def run(self):
"""
Run the CommandManager repeatedly by asking for user input.
"""
while True:
user_input = input("Enter command: ")
self.run_command(user_input)
def run_command(self, user_input):
"""
Parses input given by the user to determine an appropriate
command to call. Commands may only be run one at a time and
must start with "!"
"""
user_input = user_input.strip()
if user_input[0] != self.prefix:
print(f"Invalid command: '{user_input}'. Must start with '!'")
return
# Strip out the prifix and use the first word as the command name
command_name = user_input[1:].strip().split(" ")[0]
# Check that we have a command registered for that name.
if command_name not in self.commands:
print(f"Unknown command: '{user_input}'.")
return
self.commands[command_name]()
def add_command(self, command):
"""
Register the command to the manager. Uses the __name__ attribute of
the function to store a reference to the function.
"""
self.commands[command.__name__] = command
return command
from ai_main import CommandManager
bot = CommandManager()
#bot.add_command
def hello():
print("hello")
if __name__ == "__main__":
bot.run()

Creating a shell command line application with Python and Click

I'm using click (http://click.pocoo.org/3/) to create a command line application, but I don't know how to create a shell for this application.
Suppose I'm writing a program called test and I have commands called subtest1 and subtest2
I was able to make it work from terminal like:
$ test subtest1
$ test subtest2
But what I was thinking about is a shell, so I could do:
$ test
>> subtest1
>> subtest2
Is this possible with click?
This is not impossible with click, but there's no built-in support for that either. The first you would have to do is making your group callback invokable without a subcommand by passing invoke_without_command=True into the group decorator (as described here). Then your group callback would have to implement a REPL. Python has the cmd framework for doing this in the standard library. Making the click subcommands available there involves overriding cmd.Cmd.default, like in the code snippet below. Getting all the details right, like help, should be doable in a few lines.
import click
import cmd
class REPL(cmd.Cmd):
def __init__(self, ctx):
cmd.Cmd.__init__(self)
self.ctx = ctx
def default(self, line):
subcommand = cli.commands.get(line)
if subcommand:
self.ctx.invoke(subcommand)
else:
return cmd.Cmd.default(self, line)
#click.group(invoke_without_command=True)
#click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
repl = REPL(ctx)
repl.cmdloop()
#cli.command()
def a():
"""The `a` command prints an 'a'."""
print "a"
#cli.command()
def b():
"""The `b` command prints a 'b'."""
print "b"
if __name__ == "__main__":
cli()
There is now a library called click_repl that does most of the work for you. Thought I'd share my efforts in getting this to work.
The one difficulty is that you have to make a specific command the repl command, but we can repurpose #fpbhb's approach to allow calling that command by default if another one isn't provided.
This is a fully working example that supports all click options, with command history, as well as being able to call commands directly without entering the REPL:
import click
import click_repl
import os
from prompt_toolkit.history import FileHistory
#click.group(invoke_without_command=True)
#click.pass_context
def cli(ctx):
"""Pleasantries CLI"""
if ctx.invoked_subcommand is None:
ctx.invoke(repl)
#cli.command()
#click.option('--name', default='world')
def hello(name):
"""Say hello"""
click.echo('Hello, {}!'.format(name))
#cli.command()
#click.option('--name', default='moon')
def goodnight(name):
"""Say goodnight"""
click.echo('Goodnight, {}.'.format(name))
#cli.command()
def repl():
"""Start an interactive session"""
prompt_kwargs = {
'history': FileHistory(os.path.expanduser('~/.repl_history'))
}
click_repl.repl(click.get_current_context(), prompt_kwargs=prompt_kwargs)
if __name__ == '__main__':
cli(obj={})
Here's what it looks like to use the REPL:
$ python pleasantries.py
> hello
Hello, world!
> goodnight --name fpbhb
Goodnight, fpbhb.
And to use the command line subcommands directly:
$ python pleasntries.py goodnight
Goodnight, moon.
I know this is super old, but I've been working on fpbhb's solution to support options as well. I'm sure this could use some more work, but here is a basic example of how it could be done:
import click
import cmd
import sys
from click import BaseCommand, UsageError
class REPL(cmd.Cmd):
def __init__(self, ctx):
cmd.Cmd.__init__(self)
self.ctx = ctx
def default(self, line):
subcommand = line.split()[0]
args = line.split()[1:]
subcommand = cli.commands.get(subcommand)
if subcommand:
try:
subcommand.parse_args(self.ctx, args)
self.ctx.forward(subcommand)
except UsageError as e:
print(e.format_message())
else:
return cmd.Cmd.default(self, line)
#click.group(invoke_without_command=True)
#click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
repl = REPL(ctx)
repl.cmdloop()
#cli.command()
#click.option('--foo', required=True)
def a(foo):
print("a")
print(foo)
return 'banana'
#cli.command()
#click.option('--foo', required=True)
def b(foo):
print("b")
print(foo)
if __name__ == "__main__":
cli()
I was trying to do something similar to the OP, but with additional options / nested sub-sub-commands. The first answer using the builtin cmd module did not work in my case; maybe with some more fiddling.. But I did just run across click-shell. Haven't had a chance to test it extensively, but so far, it seems to work exactly as expected.

main program while loop

I have a little Python program that uses keyboard input to run certain commands.
I setup everything in one main program loop but now I'm wondering, do I even need a main loop?
Here is what I mean:
mainProgramLoop = 1
while mainProgramLoop == 1:
print ("\nType in the command. ")
keyBoardInput= input("command:")
if keyBoardInput == "a":
#do this
elif keyBoardInput == "b":
#do this
Do I actually need that while loop?
Thanks!
No, you do not need a main loop if you use the cmd.Cmd class included with Python:
#! /usr/bin/env python3
import cmd
class App(cmd.Cmd):
prompt = 'Type in a command: '
def do_a(self, arg):
print('I am doing whatever action "a" is.')
def do_b(self, arg):
print('I am doing whatever action "b" is.')
if __name__ == '__main__':
App().cmdloop()
The documentation for the cmd module includes an example near the bottom to help get you started.

Python (unbound method wrongCommand.wrong)

Am currently having a bit of trouble with my code. I am making a very basic RPG, and have came across this problem:
(unbound method wrongCommand.wrong)
Am also running python 2.7.5, and windows 7.
Here's my code:
import os
class wrongCommand():
def wrong():
os.system("cls")
print "Sorry, the command that you entered is invalid."
print "Please try again."
def main():
print "Welcome to the game!"
print "What do you want to do?"
print "1.) Start game"
print "2.) More information/Credits"
print "3.) Exit the game"
mm = raw_input("> ")
if mm != "1" and mm != "2" and mm != "3":
print wrongCommand.wrong
main();
main()
So first, you'd want to change
print wrongCommand.wrong
To
print wrongCommand.wrong()
(Note: addition of open and close parens)
But then you'd get the lines printed from the wrong method as well as the return value of that method, which is currently None.
So then I'd probably change
print wrongCommand.wrong()
To simply
wrongCommand.wrong()
(Note: dropping of the print statement)
Alternatively, you could have wrong() return a string, rather than print one, and then this line
print wrongCommand.wrong()
Would be fine.
You will either have to call the wrong() method off of a class instance, e.g.
wc = wrongCommand() # Create a new instance
wc.wrong()
or simply
wrongCommand().wrong()
In either case you'll have to change your wrong() method definition to
def wrong(self):
#...
Or you'll get an error like "wrong() expects exactly 1 argument, got none".
Or you can define the wrong method as a class method or static method:
#staticmethod
def wrong():
# ...
or
#classmethod
def wrong(cls):
#...

giving a command/shell type interaction in python

So say I have this graph
class Graph:
def __init__(self):
self.nodes =[]
self.edges = {}
def add_node(self,value):
self.nodes.append(value)
def is_node_present(self,node):
return True if node in self.nodes else False
Now what I want to do is something have user interact with this class..
Something like:
> g = Graph()
query executed
> g.add_node(2)
query executed
>g.is_node_present(2)
True
You know something like this.. (until user presses some secret button to quit)
How do i do this in python
Thanks
You want to look at http://docs.python.org/2/library/cmd.html as it handles the processing loop etc.
Dough Hellman http://www.doughellmann.com/PyMOTW/cmd/ is always a great resource of examples.
From Dough
import cmd
class HelloWorld(cmd.Cmd):
"""Simple command processor example."""
def do_greet(self, person):
"""greet [person]
Greet the named person"""
if person:
print "hi,", person
else:
print 'hi'
def do_EOF(self, line):
return True
def postloop(self):
print
if __name__ == '__main__':
HelloWorld().cmdloop()
Example
$ python cmd_arguments.py
(Cmd) help
Documented commands (type help ):
========================================
greet
Undocumented commands:
======================
EOF help
(Cmd) help greet
greet [person]
Greet the named person
Again all from Dough Hellman :D
You can do this with raw_input()
To quit you have to press Crtl+C
A small sample script:
import readline # allows use of arrow keys (up/down) in raw_input()
# Main function
def main():
# endless command loop
while True:
try:
command = raw_input('$ ')
except KeyboardInterrupt:
print # end programm with new line
exit()
parseCommand(command)
def parseCommand(command):
print 'not implemented yet'
if (__name__ == '__main__'):
main()
Very simple python shell-like environment using exec:
cmd = raw_input("> ")
while cmd:
try:
exec(cmd)
except Exception, e:
print str(e)
cmd = raw_input("> ")
As a side note, using exec is dangerous, and should be executed only by trusted users. This allows users to run any command they wish on your system.

Categories

Resources