如何创建多个 CLI 选项在 python 中标识我的包名称?
How to create multiple CLI options identified my package name in a python?
我想为我的应用程序构建一个具有嵌套功能的 cli 界面。示例:
├── ...
├── setup.py
└── package-name
├──__init__.py
├──command1.py
└──command2.py
package-name command1 --arg .. ..
package-name command2 --arg ..
和
python -m package-name command1 --arg ..
这里要注意的是command1
和command2
是独立的模块,接受不同的命令行参数。因此,将它们链接在一起 __main__.py
可能也是另一个挑战。
我遇到了类似的问题,这些问题在 setup.py
中使用 entry_points
来创建类似的 cli 功能,但它并不是我正在寻找的。我发现了这个类似的问题。
如果你想在一个输入命令中访问多个 sub-cli,你可以在 __main__.py
实现一个 sub-command 管理器,它可以从 [=] 解析 sub-command 44=] 然后分派到目标模块。
1️⃣首先推荐google fire,无需额外代码即可满足大部分场景
这里有一个例子,你可以使用from command1 import xx
将add/multiply函数替换为你的sub-command函数,并使用入口点暴露主函数。
import fire
def add(x, y):
return x + y
def multiply(x, y):
return x * y
def main():
fire.Fire({
'add': add,
'multiply': multiply,
})
if __name__ == '__main__':
main()
我们可以按照下面的方式进行调试:
$ python example.py add 10 20
30
$ python example.py multiply 10 20
200
2️⃣其次,如果出于某种目的需要自己实现,例如使用argparse为每个命令定义选项。一个典型的做法是 Django command, the official demo: Writing custom django-admin commands
核心步骤是:
- 定义一个 BaseCommand
- 在子commands.py中实现了BaseCommand,并将其命名为
Command
。
__main__.py
实现查找命令并调用
# base_command.py
class BaseCommand(object):
def create_parser(self, prog_name, subcommand, **kwargs):
"""
Create and return the ``ArgumentParser`` which will be used to
parse the arguments to this command.
"""
# TODO: create your ArgumentParser
return CommandParser(**kwargs)
def add_arguments(self, parser):
"""
Entry point for subclassed commands to add custom arguments.
"""
pass
def run_from_argv(self, argv):
"""
Entry point for commands to be run from the command line.
"""
parser = self.create_parser(argv[0], argv[1])
options = parser.parse_args(argv[2:])
cmd_options = vars(options)
args = cmd_options.pop('args', ())
self.handle(*args, **cmd_options)
def handle(self, *args, **options):
"""
The actual logic of the command. Subclasses must implement
this method.
"""
raise NotImplementedError('subclasses of BaseCommand must provide a handle() method')
# command1.py
class Command(BaseCommand):
def handle(self, *args, **options):
print("Hello, it is command1!")
# command2.py
class Command(BaseCommand):
def handle(self, *args, **options):
print("Hello, it is command2!")
# __main__.py
def main():
# sub command name is the second argument
command_name = sys.argv[1]
# build sub command module name, and import
# package-name is the module name you mentioned
cmd_package_name = f'package-name.{command_name}'
instance = importlib.import_module(cmd_package_name)
# create instance of sub command, the Command must exist
command = instance.Command()
command.run_from_argv(sys.argv)
if __name__ == "__main__":
main()
我想为我的应用程序构建一个具有嵌套功能的 cli 界面。示例:
├── ...
├── setup.py
└── package-name
├──__init__.py
├──command1.py
└──command2.py
package-name command1 --arg .. ..
package-name command2 --arg ..
和
python -m package-name command1 --arg ..
这里要注意的是command1
和command2
是独立的模块,接受不同的命令行参数。因此,将它们链接在一起 __main__.py
可能也是另一个挑战。
我遇到了类似的问题,这些问题在 setup.py
中使用 entry_points
来创建类似的 cli 功能,但它并不是我正在寻找的。我发现了这个类似的问题。
如果你想在一个输入命令中访问多个 sub-cli,你可以在 __main__.py
实现一个 sub-command 管理器,它可以从 [=] 解析 sub-command 44=] 然后分派到目标模块。
1️⃣首先推荐google fire,无需额外代码即可满足大部分场景
这里有一个例子,你可以使用from command1 import xx
将add/multiply函数替换为你的sub-command函数,并使用入口点暴露主函数。
import fire
def add(x, y):
return x + y
def multiply(x, y):
return x * y
def main():
fire.Fire({
'add': add,
'multiply': multiply,
})
if __name__ == '__main__':
main()
我们可以按照下面的方式进行调试:
$ python example.py add 10 20
30
$ python example.py multiply 10 20
200
2️⃣其次,如果出于某种目的需要自己实现,例如使用argparse为每个命令定义选项。一个典型的做法是 Django command, the official demo: Writing custom django-admin commands
核心步骤是:
- 定义一个 BaseCommand
- 在子commands.py中实现了BaseCommand,并将其命名为
Command
。 __main__.py
实现查找命令并调用
# base_command.py
class BaseCommand(object):
def create_parser(self, prog_name, subcommand, **kwargs):
"""
Create and return the ``ArgumentParser`` which will be used to
parse the arguments to this command.
"""
# TODO: create your ArgumentParser
return CommandParser(**kwargs)
def add_arguments(self, parser):
"""
Entry point for subclassed commands to add custom arguments.
"""
pass
def run_from_argv(self, argv):
"""
Entry point for commands to be run from the command line.
"""
parser = self.create_parser(argv[0], argv[1])
options = parser.parse_args(argv[2:])
cmd_options = vars(options)
args = cmd_options.pop('args', ())
self.handle(*args, **cmd_options)
def handle(self, *args, **options):
"""
The actual logic of the command. Subclasses must implement
this method.
"""
raise NotImplementedError('subclasses of BaseCommand must provide a handle() method')
# command1.py
class Command(BaseCommand):
def handle(self, *args, **options):
print("Hello, it is command1!")
# command2.py
class Command(BaseCommand):
def handle(self, *args, **options):
print("Hello, it is command2!")
# __main__.py
def main():
# sub command name is the second argument
command_name = sys.argv[1]
# build sub command module name, and import
# package-name is the module name you mentioned
cmd_package_name = f'package-name.{command_name}'
instance = importlib.import_module(cmd_package_name)
# create instance of sub command, the Command must exist
command = instance.Command()
command.run_from_argv(sys.argv)
if __name__ == "__main__":
main()