From 9594876c1f6377a3f8b911d31e8c941295baa281 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 26 May 2016 21:29:01 +0200 Subject: [PATCH] Added plugin support to the cli --- docs/cli.rst | 34 ++++++++++++++++++++++++++++++++++ flask/cli.py | 19 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/docs/cli.rst b/docs/cli.rst index 5af0cf88..d1b0850d 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -214,3 +214,37 @@ step. Whenever click now needs to operate on a Flask application it will call that function with the script info and ask for it to be created. 4. All is rounded up by invoking the script. + +CLI Plugins +----------- + +Flask extensions can always patch the `Flask.cli` instance with more +commands if they want. However there is a second way to add CLI plugins +to Flask which is through `setuptools`. If you make a Python package that +should export a Flask command line plugin you can ship a `setup.py` file +that declares an entrypoint that points to a click command: + +Example `setup.py`:: + + from setuptools import setup + + setup( + name='flask-my-extension', + ... + entry_points=''' + [flask.commands] + my-command=mypackage.commands:cli + ''', + ) + +Inside `mypackage/comamnds.py` you can then export a Click object:: + + import click + + @click.command() + def cli(): + """This is an example command.""" + +Once that package is installed in the same virtualenv as Flask itself you +can run ``flask my-command`` to invoke your command. This is useful to +provide extra functionality that Flask itself cannot ship. diff --git a/flask/cli.py b/flask/cli.py index 6bd9398b..b94e9317 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -282,7 +282,24 @@ class FlaskGroup(AppGroup): self.add_command(run_command) self.add_command(shell_command) + self._loaded_plugin_commands = False + + def _load_plugin_commands(self): + if self._loaded_plugin_commands: + return + try: + import pkg_resources + except ImportError: + self._loaded_plugin_commands = True + return + + for ep in pkg_resources.iter_entry_points('flask.commands'): + self.add_command(ep.load(), ep.name) + self._loaded_plugin_commands = True + def get_command(self, ctx, name): + self._load_plugin_commands() + # We load built-in commands first as these should always be the # same no matter what the app does. If the app does want to # override this it needs to make a custom instance of this group @@ -303,6 +320,8 @@ class FlaskGroup(AppGroup): pass def list_commands(self, ctx): + self._load_plugin_commands() + # The commands available is the list of both the application (if # available) plus the builtin commands. rv = set(click.Group.list_commands(self, ctx))