6

Wyobraź sobie, że mam dużą aplikację CLI z wieloma różnymi poleceniami (pomyśl, na przykład obraz-magia).Czy jest możliwe użycie wzoru "fabryki aplikacji" z Flask za pomocą aplikacji CLI Click?

chciałem zorganizować tę aplikację na moduły i itd. Tak więc, nie będzie mistrzem click.group gdzieś:

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

if __name__ == "__main__": 
    my_app() 

które mogą być importowane w każdym module, które definiują polecenie:

from main import my_app 

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

Problem polega na tym, że napotkam problem z okrężnym importowaniem, ponieważ plik main.py nic nie wie o command_x.py i musiałbym go zaimportować przed wywołaniem sekcji głównej.

To samo dzieje się w kolbie i zwykle dotyczy wzoru fabrycznego aplikacji. Zazwyczaj trzeba aplikację tworzony przed poglądami:

app = Flask("my_app") 

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

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

w strukturze aplikacji fabrycznej ty odroczenia „Rejestracja” z ideowych:

# blueprints.py 
blueprint = Blueprint(yaddayadda) 

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

i zrobić fabrykę, która wie, jak zbudować aplikacja i zarejestrować plany:

#app_factory.py 
from blueprints import view_x 

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

a następnie można utworzyć skrypt, aby uruchomić aplikację:

#main.py 

from app_factory import create_app 

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

Czy podobny wzór można zastosować z kliknięciem? Czy mogę po prostu utworzyć "aplikację kliknięcia" (być może rozszerzającą click.Group), w której rejestruję "kontrolery", które są poszczególnymi poleceniami?

Odpowiedz

1

OK, więc pomyślałem trochę i wygląda na to, że poniższe mogą działać. Prawdopodobnie nie jest to ostateczne rozwiązanie, ale wydaje się być pierwszym krokiem.

mogę rozszerzyć klasę 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) 

a klasa rozkazu:

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) 

To pozwala mieć polecenia zdefiniowane w oddzielnych modułów:

# commands.py 

from my_click_classes import command 

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

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

i "aplikacja" w oddzielnym module:

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

Lub nawet użyj wzoru "fabryka aplikacji".

To może nie jest jednak ostateczne rozwiązanie. Jeśli widzisz jakiś sposób, by to poprawić, daj mi znać.

3

Może za późno, ale szukałem również rozwiązania, aby umieścić polecenia w osobnych modułach. Po prostu użyj dekoratora, aby wprowadzić polecenia z modułów:

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

Oddzielone polecenie może używać zwykłych dekoratorów z kliknięcia.

#commands.py 
import click 

@click.command() 
def my_command(): 
    pass 
Powiązane problemy