6

Immaginate di avere una grande applicazione CLI con molti comandi diversi (pensate, per esempio, image-magick).È possibile utilizzare il modello "app factory" da Flask con Click CLI applications?

ho voluto organizzare questa applicazione in moduli, ecc Quindi, ci sarebbe stato un maestro click.group da qualche parte:

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

if __name__ == "__main__": 
    my_app() 

che può essere importato in ogni modulo che definiscono un comando:

from main import my_app 

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

Il problema è che mi imbatto in un problema di importazione circolare, dal momento che il file non sa nulla di command_x.py e dovrei importarlo prima di chiamare la sezione principale.

Questo accade anche in Flask e viene solitamente gestito con il modello di fabbrica dell'app. Di solito si avrebbe l'applicazione in fase di creazione, prima i punti di vista:

app = Flask("my_app") 

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

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

Nel modello App Factory si posticipare la "registrazione" dei progetti:

# blueprints.py 
blueprint = Blueprint(yaddayadda) 

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

e fare una fabbrica che sa come costruire l'applicazione e registrare i progetti:

#app_factory.py 
from blueprints import view_x 

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

e si può quindi creare uno script per eseguire l'applicazione:

#main.py 

from app_factory import create_app 

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

È possibile utilizzare un modello simile con Click? Potrei semplicemente creare una "click application" (magari estendendo click.Group) dove registro i "controller" che sono i singoli comandi?

risposta

1

Ok, quindi ho pensato un po 'e sembra che quanto segue potrebbe funzionare. Probabilmente non è una soluzione finale, ma sembra essere un primo passo.

posso estendere la classe 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) 

E la classe di comando:

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) 

Questo ti permette di avere i comandi definiti in moduli separati:

# commands.py 

from my_click_classes import command 

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

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

e la "app" in un modulo separato:

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

Oppure utilizzare il modello "app factory".

Forse non è una soluzione definitiva però. Se voi ragazzi potete vedere un modo per migliorarlo, fatemelo sapere.

3

Forse in ritardo, ma stavo anche cercando una soluzione per mettere i comandi per separare i moduli. È sufficiente utilizzare un decoratore per iniettare comandi da moduli:

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

Il comando separato può usare le solite decoratori dal clic.

#commands.py 
import click 

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