是否可以使用Flask中的“app factory”模式和Click CLI应用程序?

时间:2015-04-14 15:30:07

标签: python flask command-line-interface factory-pattern python-click

想象一下,我有一个包含许多不同命令的大型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工厂模式处理。通常你会在视图之前创建应用程序:

app = Flask("my_app")

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

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

在app工厂模式中,您推迟了#34;注册"蓝图:

# 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)我注册"控制器"哪些是个别命令?

2 个答案:

答案 0 :(得分: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()

分离的命令可以使用点击的常用装饰器。

#commands.py
import click

@click.command()
def my_command():
    pass

答案 1 :(得分: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)

Command类:

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...")

和" app"在一个单独的模块中:

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()

甚至可以使用" app工厂"图案。

但它可能不是一个明确的解决方案。如果你们有任何方法可以改进它,请告诉我。