Browse Source

Update docs to the new CLI patterns

pull/1820/head
Armin Ronacher 8 years ago
parent
commit
a7d829c618
  1. 7
      docs/api.rst
  2. 141
      docs/cli.rst
  3. 3
      docs/patterns/appfactories.rst
  4. 44
      docs/quickstart.rst
  5. 1
      docs/tutorial/dbcon.rst
  6. 2
      docs/tutorial/dbinit.rst
  7. 13
      docs/tutorial/setup.rst
  8. 12
      examples/flaskr/README
  9. 8
      examples/minitwit/README
  10. 22
      flask/cli.py

7
docs/api.rst

@ -892,13 +892,6 @@ Command Line Interface
Marks a function so that an instance of :class:`ScriptInfo` is passed Marks a function so that an instance of :class:`ScriptInfo` is passed
as first argument to the click callback. as first argument to the click callback.
.. autofunction:: script_info_option
A special decorator that informs a click callback to be passed the
script info object as first argument. This is normally not useful
unless you implement very special commands like the run command which
does not want the application to be loaded yet.
.. autodata:: run_command .. autodata:: run_command
.. autodata:: shell_command .. autodata:: shell_command

141
docs/cli.rst

@ -15,41 +15,38 @@ applications.
Basic Usage Basic Usage
----------- -----------
After installation of Flask you will now find a :command:`flask` script installed After installation of Flask you will now find a :command:`flask` script
into your virtualenv. If you don't want to install Flask or you have a installed into your virtualenv. If you don't want to install Flask or you
special use-case you can also use ``python -m flask`` to accomplish exactly have a special use-case you can also use ``python -m flask`` to accomplish
the same. exactly the same.
The way this script works is by providing access to all the commands on The way this script works is by providing access to all the commands on
your Flask application's :attr:`Flask.cli` instance as well as some your Flask application's :attr:`Flask.cli` instance as well as some
built-in commands that are always there. Flask extensions can also built-in commands that are always there. Flask extensions can also
register more commands there if they desire so. register more commands there if they desire so.
For the :command:`flask` script to work, an application needs to be discovered. For the :command:`flask` script to work, an application needs to be
The two most common ways are either an environment variable discovered. This is achieved by exporting the ``FLASK_APP`` environment
(``FLASK_APP``) or the :option:`--app` / :option:`-a` parameter. It should be the variable. It can be either set to an import path or to a filename of a
import path for your application or the path to a Python file. In the Python module that contains a Flask application.
latter case Flask will attempt to setup the Python path for you
automatically and discover the module name but that might not always work.
In that imported file the name of the app needs to be called ``app`` or In that imported file the name of the app needs to be called ``app`` or
optionally be specified after a colon. optionally be specified after a colon. For instance
`mymodule:application` would tell it to use the `application` object in
the :file:`mymodule.py` file.
Given a :file:`hello.py` file with the application in it named ``app`` this is Given a :file:`hello.py` file with the application in it named ``app``
how it can be run. this is how it can be run.
Environment variables (On Windows use ``set`` instead of ``export``):: Environment variables (On Windows use ``set`` instead of ``export``)::
export FLASK_APP=hello export FLASK_APP=hello
flask run flask run
Parameters:: Or with a filename::
flask --app=hello run export FLASK_APP=/path/to/hello.py
flask run
File names::
flask --app=hello.py run
Virtualenv Integration Virtualenv Integration
---------------------- ----------------------
@ -62,16 +59,20 @@ automatically also activate the correct application name.
Debug Flag Debug Flag
---------- ----------
The :command:`flask` script can be run with :option:`--debug` or :option:`--no-debug` to The :command:`flask` script can also be instructed to enable the debug
automatically flip the debug flag of the application. This can also be mode of the application automatically by exporting ``FLASK_DEBUG``. If
configured by setting ``FLASK_DEBUG`` to ``1`` or ``0``. set to ``1`` debug is enabled or ``0`` disables it.
Or with a filename::
export FLASK_DEBUG=1
Running a Shell Running a Shell
--------------- ---------------
To run an interactive Python shell you can use the ``shell`` command:: To run an interactive Python shell you can use the ``shell`` command::
flask --app=hello shell flask shell
This will start up an interactive Python shell, setup the correct This will start up an interactive Python shell, setup the correct
application context and setup the local variables in the shell. This is application context and setup the local variables in the shell. This is
@ -86,6 +87,7 @@ easily. Flask uses `click`_ for the command interface which makes
creating custom commands very easy. For instance if you want a shell creating custom commands very easy. For instance if you want a shell
command to initialize the database you can do this:: command to initialize the database you can do this::
import click
from flask import Flask from flask import Flask
app = Flask(__name__) app = Flask(__name__)
@ -93,11 +95,11 @@ command to initialize the database you can do this::
@app.cli.command() @app.cli.command()
def initdb(): def initdb():
"""Initialize the database.""" """Initialize the database."""
print 'Init the db' click.echo('Init the db')
The command will then show up on the command line:: The command will then show up on the command line::
$ flask -a hello.py initdb $ flask initdb
Init the db Init the db
Application Context Application Context
@ -122,12 +124,12 @@ Factory Functions
----------------- -----------------
In case you are using factory functions to create your application (see In case you are using factory functions to create your application (see
:ref:`app-factories`) you will discover that the :command:`flask` command cannot :ref:`app-factories`) you will discover that the :command:`flask` command
work with them directly. Flask won't be able to figure out how to cannot work with them directly. Flask won't be able to figure out how to
instantiate your application properly by itself. Because of this reason instantiate your application properly by itself. Because of this reason
the recommendation is to create a separate file that instantiates the recommendation is to create a separate file that instantiates
applications. This is by far not the only way to make this work. Another applications. This is not the only way to make this work. Another is the
is the :ref:`custom-scripts` support. :ref:`custom-scripts` support.
For instance if you have a factory function that creates an application For instance if you have a factory function that creates an application
from a filename you could make a separate file that creates such an from a filename you could make a separate file that creates such an
@ -152,9 +154,9 @@ From this point onwards :command:`flask` will find your application.
Custom Scripts Custom Scripts
-------------- --------------
While the most common way is to use the :command:`flask` command, you can also While the most common way is to use the :command:`flask` command, you can
make your own "driver scripts". Since Flask uses click for the scripts also make your own "driver scripts". Since Flask uses click for the
there is no reason you cannot hook these scripts into any click scripts there is no reason you cannot hook these scripts into any click
application. There is one big caveat and that is, that commands application. There is one big caveat and that is, that commands
registered to :attr:`Flask.cli` will expect to be (indirectly at least) registered to :attr:`Flask.cli` will expect to be (indirectly at least)
launched from a :class:`flask.cli.FlaskGroup` click group. This is launched from a :class:`flask.cli.FlaskGroup` click group. This is
@ -162,38 +164,32 @@ necessary so that the commands know which Flask application they have to
work with. work with.
To understand why you might want custom scripts you need to understand how To understand why you might want custom scripts you need to understand how
click finds and executes the Flask application. If you use the :command:`flask` click finds and executes the Flask application. If you use the
script you specify the application to work with on the command line or :command:`flask` script you specify the application to work with on the
environment variable as an import name. This is simple but it has some command line or environment variable as an import name. This is simple
limitations. Primarily it does not work with application factory but it has some limitations. Primarily it does not work with application
functions (see :ref:`app-factories`). factory functions (see :ref:`app-factories`).
With a custom script you don't have this problem as you can fully With a custom script you don't have this problem as you can fully
customize how the application will be created. This is very useful if you customize how the application will be created. This is very useful if you
write reusable applications that you want to ship to users and they should write reusable applications that you want to ship to users and they should
be presented with a custom management script. be presented with a custom management script.
If you are used to writing click applications this will look familiar but
at the same time, slightly different because of how commands are loaded.
We won't go into detail now about the differences but if you are curious
you can have a look at the :ref:`script-info-object` section to learn all
about it.
To explain all of this, here is an example :file:`manage.py` script that To explain all of this, here is an example :file:`manage.py` script that
manages a hypothetical wiki application. We will go through the details manages a hypothetical wiki application. We will go through the details
afterwards:: afterwards::
import os
import click import click
from flask.cli import FlaskGroup, script_info_option from flask.cli import FlaskGroup
def create_wiki_app(info): def create_wiki_app(info):
from yourwiki import create_app from yourwiki import create_app
config = info.data.get('config') or 'wikiconfig.py' return create_app(
return create_app(config=config) config=os.environ.get('WIKI_CONFIG', 'wikiconfig.py'))
@click.group(cls=FlaskGroup, create_app=create_wiki_app) @click.group(cls=FlaskGroup, create_app=create_wiki_app)
@script_info_option('--config', script_info_key='config') def cli():
def cli(**params):
"""This is a management script for the wiki application.""" """This is a management script for the wiki application."""
if __name__ == '__main__': if __name__ == '__main__':
@ -204,56 +200,17 @@ step.
1. First we import the ``click`` library as well as the click extensions 1. First we import the ``click`` library as well as the click extensions
from the ``flask.cli`` package. Primarily we are here interested from the ``flask.cli`` package. Primarily we are here interested
in the :class:`~flask.cli.FlaskGroup` click group and the in the :class:`~flask.cli.FlaskGroup` click group.
:func:`~flask.cli.script_info_option` decorator.
2. The next thing we do is defining a function that is invoked with the 2. The next thing we do is defining a function that is invoked with the
script info object (:ref:`script-info-object`) from Flask and its script info object (:class:`~flask.cli.ScriptInfo`) from Flask and its
purpose is to fully import and create the application. This can purpose is to fully import and create the application. This can
either directly import an application object or create it (see either directly import an application object or create it (see
:ref:`app-factories`). :ref:`app-factories`). In this case we load the config from an
environment variable.
What is ``info.data``? It's a dictionary of arbitrary data on the
script info that can be filled by options or through other means. We
will come back to this later.
3. Next step is to create a :class:`FlaskGroup`. In this case we just 3. Next step is to create a :class:`FlaskGroup`. In this case we just
make an empty function with a help doc string that just does nothing make an empty function with a help doc string that just does nothing
and then pass the ``create_wiki_app`` function as a factory function. and then pass the ``create_wiki_app`` function as a factory function.
Whenever click now needs to operate on a Flask application it will 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. call that function with the script info and ask for it to be created.
4. In step 2 you could see that the config is passed to the actual 4. All is rounded up by invoking the script.
creation function. This config comes from the :func:`script_info_option`
decorator for the main script. It accepts a :option:`--config` option and
then stores it in the script info so we can use it to create the
application.
5. All is rounded up by invoking the script.
.. _script-info-object:
The Script Info
---------------
The Flask script integration might be confusing at first, but there is a reason
why it's done this way. The reason for this is that Flask wants to
both provide custom commands to click as well as not loading your
application unless it has to. The reason for this is added flexibility.
This way an application can provide custom commands, but even in the
absence of an application the :command:`flask` script is still operational on a
basic level. In addition to that it means that the individual commands
have the option to avoid creating an instance of the Flask application
unless required. This is very useful as it allows the server commands for
instance to load the application on a first request instead of
immediately, therefore giving a better debug experience.
All of this is provided through the :class:`flask.cli.ScriptInfo` object
and some helper utilities around. The basic way it operates is that when
the :class:`flask.cli.FlaskGroup` executes as a script it creates a script
info and keeps it around. From that point onwards modifications on the
script info can be done through click options. To simplify this pattern
the :func:`flask.cli.script_info_option` decorator was added.
Once Flask actually needs the individual Flask application it will invoke
the :meth:`flask.cli.ScriptInfo.load_app` method. This happens when the
server starts, when the shell is launched or when the script looks for an
application-provided click command.

3
docs/patterns/appfactories.rst

@ -99,7 +99,8 @@ an application::
It can then be used with the :command:`flask` command:: It can then be used with the :command:`flask` command::
flask --app=exampleapp run export FLASK_APP=exampleapp
flask run
Factory Improvements Factory Improvements
-------------------- --------------------

44
docs/quickstart.rst

@ -42,14 +42,20 @@ interpreter. Make sure to not call your application :file:`flask.py` because th
would conflict with Flask itself. would conflict with Flask itself.
To run the application you can either use the :command:`flask` command or To run the application you can either use the :command:`flask` command or
python's :option:`-m` switch with Flask:: python's :option:`-m` switch with Flask. Before you can do that you need
to tell your terminal the application to work with by exporting the
`FLASK_APP` environment variable::
$ flask -a hello run $ export FLASK_APP=hello.py
$ flask run
* Running on http://127.0.0.1:5000/ * Running on http://127.0.0.1:5000/
or alternatively:: If you are on Windows you need to use `set` instead of `export`.
$ python -m flask -a hello run Alternatively you can use `python -m flask`::
$ export FLASK_APP=hello.py
$ python -m flask run
* Running on http://127.0.0.1:5000/ * Running on http://127.0.0.1:5000/
This launches a very simple builtin server, which is good enough for testing This launches a very simple builtin server, which is good enough for testing
@ -72,7 +78,7 @@ should see your hello world greeting.
you can make the server publicly available simply by adding you can make the server publicly available simply by adding
``--host=0.0.0.0`` to the command line:: ``--host=0.0.0.0`` to the command line::
flask -a hello run --host=0.0.0.0 flask run --host=0.0.0.0
This tells your operating system to listen on all public IPs. This tells your operating system to listen on all public IPs.
@ -87,28 +93,19 @@ to look at the error message.
Old Version of Flask Old Version of Flask
```````````````````` ````````````````````
Versions of Flask older than 1.0 use to have different ways to start the Versions of Flask older than 0.11 use to have different ways to start the
application. In short, the :command:`flask` command did not exist, and application. In short, the :command:`flask` command did not exist, and
neither did ``python -m flask``. In that case you have two options: neither did ``python -m flask``. In that case you have two options:
either upgrade to newer Flask versions or have a look at the :ref:`server` either upgrade to newer Flask versions or have a look at the :ref:`server`
docs to see the alternative method for running a server. docs to see the alternative method for running a server.
Python older 2.7
````````````````
In case you have a version of Python older than 2.7 ``python -m flask``
does not work. You can either use :command:`flask` or ``python -m
flask.cli`` as an alternative. This is because Python before 2.7 does no
permit packages to act as executable modules. For more information see
:ref:`cli`.
Invalid Import Name Invalid Import Name
``````````````````` ```````````````````
The :option:`-a` argument to :command:`flask` is the name of the module to import. In The :option:`-a` argument to :command:`flask` is the name of the module to
case that module is incorrectly named you will get an import error upon import. In case that module is incorrectly named you will get an import
start (or if debug is enabled when you navigate to the application). It error upon start (or if debug is enabled when you navigate to the
will tell you what it tried to import and why it failed. application). It will tell you what it tried to import and why it failed.
The most common reason is a typo or because you did not actually create an The most common reason is a typo or because you did not actually create an
``app`` object. ``app`` object.
@ -126,10 +123,13 @@ That is not very nice and Flask can do better. If you enable debug
support the server will reload itself on code changes, and it will also support the server will reload itself on code changes, and it will also
provide you with a helpful debugger if things go wrong. provide you with a helpful debugger if things go wrong.
There are different ways to enable the debug mode. The most obvious one To enable debug mode you can export the `FLASK_DEBUG` environment variable
is the :option:`--debug` parameter to the :command:`flask` command:: before running the server::
$ export FLASK_DEBUG=1
$ flask run
flask --debug -a hello run (On Windows you need to use `set` instead of `export`).
This does the following things: This does the following things:

1
docs/tutorial/dbcon.rst

@ -37,7 +37,6 @@ already established connection::
g.sqlite_db = connect_db() g.sqlite_db = connect_db()
return g.sqlite_db return g.sqlite_db
So now we know how to connect, but how do we properly disconnect? For So now we know how to connect, but how do we properly disconnect? For
that, Flask provides us with the :meth:`~flask.Flask.teardown_appcontext` that, Flask provides us with the :meth:`~flask.Flask.teardown_appcontext`
decorator. It's executed every time the application context tears down:: decorator. It's executed every time the application context tears down::

2
docs/tutorial/dbinit.rst

@ -60,7 +60,7 @@ databases will not commit unless you explicitly tell it to.
Now, it is possible to create a database with the :command:`flask` script:: Now, it is possible to create a database with the :command:`flask` script::
flask --app=flaskr initdb flask initdb
Initialized the database. Initialized the database.
.. admonition:: Troubleshooting .. admonition:: Troubleshooting

13
docs/tutorial/setup.rst

@ -91,13 +91,16 @@ tuples.
return rv return rv
With that out of the way, you should be able to start up the application With that out of the way, you should be able to start up the application
without problems. Do this with the following command:: without problems. Do this with the following commands::
flask --app=flaskr --debug run export FLASK_APP=flaskr
export FLASK_DEBUG=1
flask run
The :option:`--debug` flag enables or disables the interactive debugger. *Never (In case you are on Windows you need to use `set` instead of `export`).
leave debug mode activated in a production system*, because it will allow The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger.
users to execute code on the server! *Never leave debug mode activated in a production system*, because it will
allow users to execute code on the server!
You will see a message telling you that server has started along with You will see a message telling you that server has started along with
the address at which you can access it. the address at which you can access it.

12
examples/flaskr/README

@ -13,13 +13,17 @@
export an FLASKR_SETTINGS environment variable export an FLASKR_SETTINGS environment variable
pointing to a configuration file. pointing to a configuration file.
2. initialize the database with this command: 2. Instruct flask to use the right application
flask --app=flaskr initdb export FLASK_APP=flaskr
3. now you can run flaskr: 3. initialize the database with this command:
flask --app=flaskr run flask initdb
4. now you can run flaskr:
flask run
the application will greet you on the application will greet you on
http://localhost:5000/ http://localhost:5000/

8
examples/minitwit/README

@ -14,13 +14,17 @@
export an MINITWIT_SETTINGS environment variable export an MINITWIT_SETTINGS environment variable
pointing to a configuration file. pointing to a configuration file.
2. tell flask about the right application:
export FLASK_APP=minitwit
2. fire up a shell and run this: 2. fire up a shell and run this:
flask --app=minitwit initdb flask initdb
3. now you can run minitwit: 3. now you can run minitwit:
flask --app=minitwit run flask run
the application will greet you on the application will greet you on
http://localhost:5000/ http://localhost:5000/

22
flask/cli.py

@ -165,7 +165,10 @@ class DispatchingApp(object):
class ScriptInfo(object): class ScriptInfo(object):
"""Help object to deal with Flask applications. This is usually not """Help object to deal with Flask applications. This is usually not
necessary to interface with as it's used internally in the dispatching necessary to interface with as it's used internally in the dispatching
to click. to click. In future versions of Flask this object will most likely play
a bigger role. Typically it's created automatically by the
:class:`FlaskGroup` but you can also manually create it and pass it
onwards as click object.
""" """
def __init__(self, app_import_path=None, create_app=None): def __init__(self, app_import_path=None, create_app=None):
@ -174,7 +177,10 @@ class ScriptInfo(object):
app_import_path = find_default_import_path() app_import_path = find_default_import_path()
self.app_import_path = app_import_path self.app_import_path = app_import_path
else: else:
self.app_import_path = None app_import_path = None
#: Optionally the import path for the Flask application.
self.app_import_path = app_import_path
#: Optionally a function that is passed the script info to create #: Optionally a function that is passed the script info to create
#: the instance of the application. #: the instance of the application.
self.create_app = create_app self.create_app = create_app
@ -194,10 +200,12 @@ class ScriptInfo(object):
if self.create_app is not None: if self.create_app is not None:
rv = self.create_app(self) rv = self.create_app(self)
else: else:
if self.app_import_path is None: if not self.app_import_path:
raise NoAppException('Could not locate Flask application. ' raise NoAppException(
'You did not provide the FLASK_APP ' 'Could not locate Flask application. You did not provide '
'environment variable.') 'the FLASK_APP environment variable.\n\nFor more '
'information see '
'http://flask.pocoo.org/docs/latest/quickstart/')
rv = locate_app(self.app_import_path) rv = locate_app(self.app_import_path)
debug = get_debug_flag() debug = get_debug_flag()
if debug is not None: if debug is not None:
@ -369,6 +377,8 @@ def run_command(info, host, port, reload, debugger, eager_loading,
# we won't print anything. # we won't print anything.
if info.app_import_path is not None: if info.app_import_path is not None:
print(' * Serving Flask app "%s"' % info.app_import_path) print(' * Serving Flask app "%s"' % info.app_import_path)
if debug is not None:
print(' * Forcing debug mode %s' % (debug and 'on' or 'off'))
run_simple(host, port, app, use_reloader=reload, run_simple(host, port, app, use_reloader=reload,
use_debugger=debugger, threaded=with_threads) use_debugger=debugger, threaded=with_threads)

Loading…
Cancel
Save