7

想象一下,我有一个带有许多不同命令的大型 CLI 应用程序(例如 image-magick)。

click.group我想把这个应用程序组织成模块等。所以,某处会有一个大师:

#main.py file
@click.group()
def my_app():
    pass

if __name__ == "__main__":
    my_app()

可以在定义命令的每个模块中导入:

from main import my_app

# command_x.py
@my_app.command() 
def command_x():
    pass

问题是我遇到了循环导入问题,因为main.py文件一无所知command_x.py,我必须在调用主要部分之前导入它。

这也发生在 Flask 中,通常处理应用程序工厂模式。通常你会在视图之前创建应用程序:

app = Flask("my_app")

@my_app.route("/")
def view_x():
   pass

if __name__ == "__main__":
    app.run()

在应用工厂模式中,您推迟了蓝图的“注册”:

# blueprints.py
blueprint = Blueprint(yaddayadda)

@blueprint.route("/")
def view_x():
    pass

并创建一个知道如何构建应用程序和注册蓝图的工厂:

#app_factory.py
from blueprints import view_x

def create_app():
    app = Flask()
    view_x.init_app(app)
    return app

然后您可以创建一个脚本来运行该应用程序:

#main.py

from app_factory import create_app

if __name__ == "__main__":
    app = create_app()
    app.run()

Click 可以使用类似的模式吗?我可以创建一个“单击应用程序”(可能是扩展click.Group),在其中注册作为单个命令的“控制器”吗?

4

2 回答 2

3

也许晚了,但我也在寻找一种将命令放到单独模块中的解决方案。只需使用装饰器从模块中注入命令:

#main.py file
import click
import commands

def lazyloader(f):
    # f is an instance of click.Group
    f.add_command(commands.my_command)
    return f

@lazyloader
@click.group()
def my_app():
    pass

if __name__ == "__main__":
    my_app()

分隔的命令可以使用来自 click 的常用装饰器。

#commands.py
import click

@click.command()
def my_command():
    pass
于 2015-12-18T14:20:58.770 回答
1

好的,所以我想了一下,似乎以下可以工作。这可能不是最终解决方案,但似乎是第一步。

我可以扩展 MultiCommand 类:

# my_click_classes.py

import click

class ClickApp(click.MultiCommand):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.commands = {}

    def add_command(self, command_name, command):
        self.commands.update({command_name: command})

    def list_commands(self, ctx):
        return [name for name, _ in self.commands.items()]

    def get_command(self, ctx, name):
        return self.commands.get(name)

和命令类:

class MyCommand(click.Command):

    def init_app(self, app):
        return app.add_command(self.name, self)

def mycommand(*args, **kwargs):
    return click.command(cls=MyCommand)

这允许您在单独的模块中定义命令:

# commands.py

from my_click_classes import command

@command
def run():
    print("run!!!")

@command
def walk():
    print("walk...")

和单独模块中的“应用程序”:

from my_click_classes import ClickApp
from commands import run, walk

app = ClickApp()
run.init_app(app)
walk.init_app(app)

if __name__ == '__main__':
   app()

甚至使用“应用程序工厂”模式。

不过,这可能不是一个确定的解决方案。如果你们能看到任何改进它的方法,请告诉我。

于 2015-04-14T18:13:54.487 回答