pythonflaskpython-clickflask-cli

How to access Flask app context in CLI command


I want to register a CLI command that's defined in a separate file from the Flask app factory. This command needs access to app.config. However, accessing current_app.config from the command raises RuntimeError: Working outside of application context.

app/__init__.py

from flask import Flask
from app.commands import my_command

def create_app():
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_pyfile("config.py")
    app.add_command(my_command)
    return app

instance/config.py

TEST_VARIABLE = "TESTVALUE"

app/commands.py

from flask import current_app

@click.command()
def my_command():
    click.echo(current_app.config["TEST_VARIABLE"])

I expect running flask my_command to output TESTVALUE. However, I get the following error:

RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context().  See the
documentation for more information.

I need to use with app.app_context(): for current_app to work, but I don't have access to app since it's defined in a factory. If I use @app.cli.command() in the factory, this would be no problem, as there I have access to the app variable, and wouldn't even need to push an app context.

def create_app():
    ...
    @app.cli.command()
    def my_command():
        click.echo(current_app.config["TEST_VARIABLE"])
    ...

However, I want to define my commands in other files and have them use values from the app config, while this requires all the commands to be nested in the factory function.

I tried creating an app using the factory inside the command, and that worked, but I don't think doing that is a good idea just to have access to the config variables.

import click
import app

@click.command()
def my_command():
    app_config = app.create_app().config
    click.echo(app_config["TEST_VARIABLE"])

How can I define a command that has access to the application context when using the application factory pattern?


Solution

  • Flask's docs about the CLI discuss this.

    @app.cli.command() automatically ensures there's an app context when the command runs. However, this is less convenient to use when using an app factory because you don't have access to the app when defining the command.

    When using the app factory, you use @click.command() and app.cli.add_command() to separate defining and registering the command, but @click.command doesn't know about the app context. Use the @with_appcontext decorator to ensure the command runs in an app context.

    @click.command()
    @with_appcontext
    def my_command():
        config = current_app.config
        click.echo(config["TEST_VARIABLE"])
    
    def create_app():
        app = Flask(__name__)
        ...
        app.cli.add_command(my_command)
        ...
        return app
    
    $ flask my-command
    TESTVALUE