Python 如何将我的 Click 命令(每个命令都有一组子命令)拆分为多个文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34643620/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 15:18:09  来源:igfitidea点击:

How can I split my Click commands, each with a set of sub-commands, into multiple files?

pythoncommand-line-interfacepython-click

提问by Brad T

I have one large click application that I've developed, but navigating through the different commands/subcommands is getting rough. How do I organize my commands into separate files? Is it possible to organize commands and their subcommands into separate classes?

我开发了一个大型单击应用程序,但是浏览不同的命令/子命令变得越来越困难。如何将我的命令组织到单独的文件中?是否可以将命令及其子命令组织到单独的类中?

Here's an example of how I would like to separate it:

这是我想如何分离它的一个例子:

init

在里面

import click

@click.group()
@click.version_option()
def cli():
    pass #Entry Point

command_cloudflare.py

command_cloudflare.py

@cli.group()
@click.pass_context
def cloudflare(ctx):
    pass

@cloudflare.group('zone')
def cloudflare_zone():
    pass

@cloudflare_zone.command('add')
@click.option('--jumpstart', '-j', default=True)
@click.option('--organization', '-o', default='')
@click.argument('url')
@click.pass_obj
@__cf_error_handler
def cloudflare_zone_add(ctx, url, jumpstart, organization):
    pass

@cloudflare.group('record')
def cloudflare_record():
    pass

@cloudflare_record.command('add')
@click.option('--ttl', '-t')
@click.argument('domain')
@click.argument('name')
@click.argument('type')
@click.argument('content')
@click.pass_obj
@__cf_error_handler
def cloudflare_record_add(ctx, domain, name, type, content, ttl):
    pass

@cloudflare_record.command('edit')
@click.option('--ttl', '-t')
@click.argument('domain')
@click.argument('name')
@click.argument('type')
@click.argument('content')
@click.pass_obj
@__cf_error_handler
def cloudflare_record_edit(ctx, domain):
    pass

command_uptimerobot.py

command_uptimerobot.py

@cli.group()
@click.pass_context
def uptimerobot(ctx):
    pass

@uptimerobot.command('add')
@click.option('--alert', '-a', default=True)
@click.argument('name')
@click.argument('url')
@click.pass_obj
def uptimerobot_add(ctx, name, url, alert):
    pass

@uptimerobot.command('delete')
@click.argument('names', nargs=-1, required=True)
@click.pass_obj
def uptimerobot_delete(ctx, names):
    pass

采纳答案by jdno

The downside of using CommandCollectionfor this is that it merges your commands and works only with command groups. The imho better alternative is to use add_commandto achieve the same result.

使用CommandCollection它的缺点是它合并了您的命令并且仅适用于命令组。imho 更好的选择是使用add_command来达到相同的结果。

I have a project with the following tree:

我有一个带有以下树的项目:

cli/
├── __init__.py
├── cli.py
├── group1
│?? ├── __init__.py
│?? ├── commands.py
└── group2
    ├── __init__.py
    └── commands.py

Each subcommand has its own module, what makes it incredibly easy to manage even complex implementations with many more helper classes and files. In each module, the commands.pyfile contains the @clickannotations. Example group2/commands.py:

每个子命令都有自己的模块,这使得管理具有更多帮助程序类和文件的复杂实现变得非常容易。在每个模块中,该commands.py文件都包含@click注释。示例group2/commands.py

import click


@click.command()
def version():
    """Display the current version."""
    click.echo(_read_version())

If necessary, you could easily create more classes in the module, and importand use them here, thus giving your CLI the full power of Python's classes and modules.

如有必要,您可以轻松地在模块中创建更多类,并import在此处使用它们,从而为您的 CLI 提供 Python 类和模块的全部功能。

My cli.pyis the entry point for the whole CLI:

Mycli.py是整个 CLI 的入口点:

import click

from .group1 import commands as group1
from .group2 import commands as group2

@click.group()
def entry_point():
    pass

entry_point.add_command(group1.command_group)
entry_point.add_command(group2.version)

With this setup, it is very easy to separate your commands by concerns, and also build additional functionality around them that they might need. It has served me very well so far...

通过这种设置,可以很容易地按关注点分离您的命令,并围绕它们构建他们可能需要的附加功能。到目前为止,它对我很有帮助......

Reference: http://click.pocoo.org/6/quickstart/#nesting-commands

参考:http: //click.pocoo.org/6/quickstart/#nesting-commands

回答by Achim

I'm not an click expert, but it should work by just importing your files into the main one. I would move all commands in separate files and have one main file importing the other ones. That way it is easier to control the exact order, in case it is important for you. So your main file would just look like:

我不是点击专家,但只需将您的文件导入主文件即可。我会将所有命令移动到单独的文件中,并让一个主文件导入其他文件。这样可以更容易地控制确切的顺序,以防它对您很重要。所以你的主文件看起来像:

import commands_main
import commands_cloudflare
import commands_uptimerobot

回答by Oscar David Arbeláez

I'm looking for something like this at the moment, in your case is simple because you have groups in each of the files, you can solve this problem as explained in the documentation:

我目前正在寻找类似的东西,在您的情况下很简单,因为您在每个文件中都有组,您可以按照文档中的说明解决这个问题:

In the init.pyfile:

init.py文件中:

import click

from command_cloudflare import cloudflare
from command_uptimerobot import uptimerobot

cli = click.CommandCollection(sources=[cloudflare, uptimerobot])

if __name__ == '__main__':
    cli()

The best part of this solution is that is totally compliant with pep8 and other linters because you don't need to import something you wouldn't use and you don't need to import * from anywhere.

这个解决方案最好的部分是完全符合 pep8 和其他 linter,因为你不需要导入你不会使用的东西,也不需要从任何地方导入 *。

回答by Diego Castro

Suppose your project have the following structure:

假设您的项目具有以下结构:

project/
├── __init__.py
├── init.py
└── commands
    ├── __init__.py
    └── cloudflare.py

Groups are nothing more than multiple commands and groups can be nested. You can separate your groups into modules and import them on you init.pyfile and add them to the cligroup using the add_command.

组只不过是多个命令,组可以嵌套。您可以将您的组分成模块并将它们导入到您的init.py文件中,然后cli使用 add_command将它们添加到组中。

Here is a init.pyexample:

下面是一个init.py例子:

import click
from .commands.cloudflare import cloudflare


@click.group()
def cli():
    pass


cli.add_command(cloudflare)

You have to import the cloudflare group which lives inside the cloudflare.py file. Your commands/cloudflare.pywould look like this:

您必须导入位于 cloudflare.py 文件中的 cloudflare 组。你commands/cloudflare.py会是这样的:

import click


@click.group()
def cloudflare():
    pass


@cloudflare.command()
def zone():
    click.echo('This is the zone subcommand of the cloudflare command')

Then you can run the cloudflare command like this:

然后你可以像这样运行 cloudflare 命令:

$ python init.py cloudflare zone

This information is not very explicit on the documentation but if you look at the source code, which is very well commented, you can see how groups can be nested.

该信息在文档中不是很明确,但是如果您查看注释非常好的源代码,您可以看到如何嵌套组。

回答by garlicbread

It took me a while to figure this out but I figured I'd put this here to remind myself when I forget how to do i again I think part of the problem is that the add_command function is mentioned on click's github page but not the main examples page

我花了一段时间才弄清楚这一点,但我想我会把它放在这里是为了提醒自己,当我再次忘记该怎么做时,我认为问题的一部分是在 click 的 github 页面上提到了 add_command 函数,但不是主要的示例页面

first lets create an initial python file called root.py

首先让我们创建一个名为 root.py 的初始 python 文件

import click
from cli_compile import cli_compile
from cli_tools import cli_tools

@click.group()
def main():
    """Demo"""

if __name__ == '__main__':
    main.add_command(cli_tools)
    main.add_command(cli_compile)
    main()

Next lets put some tools commands in a file called cli_tools.py

接下来让我们将一些工具命令放在一个名为 cli_tools.py 的文件中

import click

# Command Group
@click.group(name='tools')
def cli_tools():
    """Tool related commands"""
    pass

@cli_tools.command(name='install', help='test install')
@click.option('--test1', default='1', help='test option')
def install_cmd(test1):
    click.echo('Hello world')

@cli_tools.command(name='search', help='test search')
@click.option('--test1', default='1', help='test option')
def search_cmd(test1):
    click.echo('Hello world')

if __name__ == '__main__':
    cli_tools()

Next lets put some compile commands in a file called cli_compile.py

接下来让我们将一些编译命令放在一个名为 cli_compile.py 的文件中

import click

@click.group(name='compile')
def cli_compile():
    """Commands related to compiling"""
    pass

@cli_compile.command(name='install2', help='test install')
def install2_cmd():
    click.echo('Hello world')

@cli_compile.command(name='search2', help='test search')
def search2_cmd():
    click.echo('Hello world')

if __name__ == '__main__':
    cli_compile()

running root.py should now give us

运行 root.py 现在应该给我们

Usage: root.py [OPTIONS] COMMAND [ARGS]...

  Demo

Options:
  --help  Show this message and exit.

Commands:
  compile  Commands related to compiling
  tools    Tool related commands

running "root.py compile" should give us

运行“root.py compile”应该给我们

Usage: root.py compile [OPTIONS] COMMAND [ARGS]...

  Commands related to compiling

Options:
  --help  Show this message and exit.

Commands:
  install2  test install
  search2   test search

You'll also notice you can run the cli_tools.py or cli_compile.py directly as well as I included a main statement in there

您还会注意到您可以直接运行 cli_tools.py 或 cli_compile.py 并且我在其中包含了一个主要语句