Python Tab 补全,无需按照 cmd 的要求直接在类中定义参数

问题描述 投票:0回答:1

我们的程序有一个用 python 编写的命令行界面。我们想向此界面添加制表符补全功能。除了一个问题之外,cmd 模块似乎是显而易见的选择。它要求所有可能的参数以方法的形式在类中定义。

我们的 CLI 中有许多命令,模块可以在某种插件界面中添加特定于它们的新命令。我们通过为每个潜在参数定义一个命令类来实现这一点,它定义了命令、它的描述和行为等。事实证明,这对我们来说更干净、更灵活,我不想通过尝试在一个方法中放入数十种方法来改变这一点班级;特别是因为我不知道可以运行的所有可能的命令,因为有些命令依赖于稍后添加的第三方模块。

那么有没有一种方法可以实现漂亮的 Tab 补全,而不必直接在 cmd 类中提前预定义每个可能的命令?

理想情况下,我们还希望允许使用制表符补全某些参数,而不仅仅是命令。我还希望能够捕获一些热键;但是,如果我找不到可以更轻松地添加热键处理的第三方工具,那么这还不足以重新发明终端为我们所做的一切。这必须在 Linux 和 Windows 上运行,所以我也不希望任何依赖于操作系统的终端黑客攻击。

我可以在不直接将命令预定义为类的方法的情况下让 CMD 模块工作吗?如果失败的话,是否还有其他第三方 python 工具可能支持比 cmd 模块允许的更复杂的命令行行为?

python command-line-interface python-3.9
1个回答
0
投票

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模块的源代码。在我看来,你的成长可能已经超出了它所提供的范围。自己重新实现它可能并不像您想象的那么难。它会给你完全的控制权。

© www.soinside.com 2019 - 2024. All rights reserved.