简体   繁体   中英

python cmd.Cmd dynamically add a command

I am trying to build a cli framework where commands need to be added to dynamically. What I want to achieve is - I will have a minimum class which inherit from cmd.Cmd and later on I will write my commands in separate classes and load those commands along with main classes.

The following is what I tried but while trying to execute the command showmore, it throw TypeError

import cmd

class MyExtraCmds(cmd.Cmd):

  def do_showmore(self, *args):
    print (type(self))
    print ("Show more command")

class MyCmd(cmd.Cmd):

  def __init__(self, target=None, user=None, passwd=None):
    cmd.Cmd.__init__(self)

  def do_show(self, *args):
    print (type(self))
    print ("Show command")

  def do_EOF(self, line):
    return True

if __name__ == "__main__":
    setattr(  MyCmd, 'do_showmore', MyExtraCmds.do_showmore)
    print (dir(MyCmd))
    target = MyCmd()
    target.cmdloop()

EDIT 2: I think you must be using Python 2, because in Python 3, this just works...

MyExtrasCommand is a different class, so this won't work, because for a normal InstanceMethod, the first parameter must be an instance of the class.

But Python classes are open; you can add to them later. The easiest thing to do is start off with methods that are not currently in a class, and then they will be properly added to the class at run-time. For example, this will work fine:

import cmd

def do_showmore(self, *args):
    print (type(self))
    print ("Show more command")

class MyCmd(cmd.Cmd):

    def __init__(self, target=None, user=None, passwd=None):
        cmd.Cmd.__init__(self)

    def do_show(self, *args):
        print (type(self))
        print ("Show command")

    def do_EOF(self, line):
        return True

if __name__ == "__main__":
    setattr(  MyCmd, 'do_showmore', do_showmore)
    print (dir(MyCmd))
    target = MyCmd()
    target.cmdloop()

If you print the type of MyCmd.do_showmore, you will see that your function has been properly wrapped into an InstanceMethod.

EDIT (for information only -- you probably don't really want to do this...)

Since everything in Python is open and inspectable, you could make your initial approach work by changing a single line. The following will work in Python 2.7:

setattr(  MyCmd, 'do_showmore', MyExtraCmds.do_showmore.im_func)

For Python 2.7, adding .im_func to the end of this line says "grab the underlying function from the MyExtraCmds.do_showmore unbound method and use it." I mention this because sometimes there are good reasons to use deep dark voodoo like this. But your described use-case doesn't seem like it would fit in here, and it could be very confusing if, for example, you added additional class variables to MyExtraCmds and expected do_showmore to be able to access them. The function that you strip out of MyExtraCmds and add into MyCmd will no longer have any knowledge of MyExtraCmds so that would be misleading and confusing.

For me it works when I just set it with the equal operator. It does work as a valid command.

p = MyCmd()
p.do_extra = extra_command
p.cmdloop()

and I found a workaround to make it work for autocomplete and help. Reimplement get_names() in your class (I don't know why they used self. class instead of self there)

class MyCmd(cmd.Cmd):
  def get_names(self):
    return dir(self)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM