我们的程序有一个用 python 编写的命令行界面。我们想向此界面添加制表符补全功能。除了一个问题之外,cmd 模块似乎是显而易见的选择。它要求所有可能的参数以方法的形式在类中定义。
我们的 CLI 中有许多命令,模块可以在某种插件界面中添加特定于它们的新命令。我们通过为每个潜在参数定义一个命令类来实现这一点,它定义了命令、它的描述和行为等。事实证明,这对我们来说更干净、更灵活,我不想通过尝试在一个方法中放入数十种方法来改变这一点班级;特别是因为我不知道可以运行的所有可能的命令,因为有些命令依赖于稍后添加的第三方模块。
那么有没有一种方法可以实现漂亮的 Tab 补全,而不必直接在 cmd 类中提前预定义每个可能的命令?
理想情况下,我们还希望允许使用制表符补全某些参数,而不仅仅是命令。我还希望能够捕获一些热键;但是,如果我找不到可以更轻松地添加热键处理的第三方工具,那么这还不足以重新发明终端为我们所做的一切。这必须在 Linux 和 Windows 上运行,所以我也不希望任何依赖于操作系统的终端黑客攻击。
我可以在不直接将命令预定义为类的方法的情况下让 CMD 模块工作吗?如果失败的话,是否还有其他第三方 python 工具可能支持比 cmd 模块允许的更复杂的命令行行为?
Python 非常动态。您可以使用
setattr(the_class, "the_method_name", the_method_definition)
将新方法添加到类中。您还可以在函数内使用 class X:
语句(该类将存储在局部变量中)。这就是您所需要的。
使用动态插件界面创建命令字典,其中键是带有“do_”前缀的命令名称,值是可调用的(lambda、函数、带有
__call__
的对象)。然后迭代字典并使用 setattr() 将它们组装成一个类。
完整示例:
import cmd
# You can implement commands with a normal function
def aaaabbbb(shell, arg):
"""It has a doc string"""
print("Hello from aaaabbbb %r" % arg)
# It is even possible to add more commands dynamically if you really want to
shell.__class__.do_ccdd = lambda shell, arg: print("Hello from dynamically added ccdd %r" % arg)
# You can implement commands with a callable (object with __call__ method)
class Cccc:
"""It has a class doc string"""
def __call__(self, arg):
print("Hello from cccc %r" % arg)
def complete_cccc(shell, text, line, begidx, endidx):
return [w for w in ['xxxxxx', 'xxxyyy', 'zzzzzz'] if w.startswith(text)]
commands = {
# You can implement commands with a lambda
'do_aaaaaaa': lambda shell, arg: print("Hello from aaaaaaa %r" % arg),
'do_aaaabbbb': aaaabbbb,
'do_cccc': Cccc(),
# You can define completion for a specific command's arguments
'complete_cccc': complete_cccc,
}
# *** Here comes the important part! ***
def main():
class MyShell(cmd.Cmd):
def do_EOF(self, arg):
print()
return True
for name, value in commands.items():
setattr(MyShell, name, value)
MyShell().cmdloop()
if __name__ == '__main__':
main()
这将为您提供命令名称的补全,这是列出命令的内置
help
命令,并且您可以定义特定命令的参数补全。
(顺便说一句:在大多数情况下,在实例上设置attr就足够了。
my_instance = MyShell(); setattr(my_instance, 'method_name', definition)
。但是cmd内部使用dir(self.__class__)
。)
但也可以考虑直接阅读cmd模块的源代码。在我看来,你的成长可能已经超出了它所提供的范围。自己重新实现它可能并不像您想象的那么难。它会给你完全的控制权。