Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 422 lines (330 sloc) 16.661 kb

Flask Extension Development

Flask, being a microframework, often requires some repetitive steps to get a third party library working. Because very often these steps could be abstracted to support multiple projects the Flask Extension Registry was created.

If you want to create your own Flask extension for something that does not exist yet, this guide to extension development will help you get your extension running in no time and to feel like users would expect your extension to behave.

Anatomy of an Extension

Extensions are all located in a package called flask_something where "something" is the name of the library you want to bridge. So for example if you plan to add support for a library named simplexml to Flask, you would name your extension's package flask_simplexml.

The name of the actual extension (the human readable name) however would be something like "Flask-SimpleXML". Make sure to include the name "Flask" somewhere in that name and that you check the capitalization. This is how users can then register dependencies to your extension in their setup.py files.

Flask sets up a redirect package called :data:`flask.ext` where users should import the extensions from. If you for instance have a package called flask_something users would import it as flask.ext.something. This is done to transition from the old namespace packages. See :ref:`ext-import-transition` for more details.

System Message: ERROR/3 (<string>, line 32); backlink

Unknown interpreted text role "data".

System Message: ERROR/3 (<string>, line 32); backlink

Unknown interpreted text role "ref".

But how do extensions look like themselves? An extension has to ensure that it works with multiple Flask application instances at once. This is a requirement because many people will use patterns like the :ref:`app-factories` pattern to create their application as needed to aid unittests and to support multiple configurations. Because of that it is crucial that your application supports that kind of behavior.

System Message: ERROR/3 (<string>, line 38); backlink

Unknown interpreted text role "ref".

Most importantly the extension must be shipped with a setup.py file and registered on PyPI. Also the development checkout link should work so that people can easily install the development version into their virtualenv without having to download the library by hand.

Flask extensions must be licensed under a BSD, MIT or more liberal license to be able to be enlisted in the Flask Extension Registry. Keep in mind that the Flask Extension Registry is a moderated place and libraries will be reviewed upfront if they behave as required.

"Hello Flaskext!"

So let's get started with creating such a Flask extension. The extension we want to create here will provide very basic support for SQLite3.

First we create the following folder structure:

flask-sqlite3/
    flask_sqlite3.py
    LICENSE
    README

Here's the contents of the most important files:

setup.py

The next file that is absolutely required is the setup.py file which is used to install your Flask extension. The following contents are something you can work with:

"""
Flask-SQLite3
-------------

This is the description for that library
"""
from setuptools import setup


setup(
    name='Flask-SQLite3',
    version='1.0',
    url='http://example.com/flask-sqlite3/',
    license='BSD',
    author='Your Name',
    author_email='your-email@example.com',
    description='Very short description',
    long_description=__doc__,
    py_modules=['flask_sqlite3'],
    # if you would be using a package instead use packages instead
    # of py_modules:
    # packages=['flask_sqlite3'],
    zip_safe=False,
    include_package_data=True,
    platforms='any',
    install_requires=[
        'Flask'
    ],
    classifiers=[
        'Environment :: Web Environment',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: BSD License',
        'Operating System :: OS Independent',
        'Programming Language :: Python',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
        'Topic :: Software Development :: Libraries :: Python Modules'
    ]
)

That's a lot of code but you can really just copy/paste that from existing extensions and adapt.

flask_sqlite3.py

Now this is where your extension code goes. But how exactly should such an extension look like? What are the best practices? Continue reading for some insight.

Initializing Extensions

Many extensions will need some kind of initialization step. For example, consider an application that's currently connecting to SQLite like the documentation suggests (:ref:`sqlite3`). So how does the extension know the name of the application object?

System Message: ERROR/3 (<string>, line 129); backlink

Unknown interpreted text role "ref".

Quite simple: you pass it to it.

There are two recommended ways for an extension to initialize:

initialization functions:

If your extension is called helloworld you might have a function called init_helloworld(app[, extra_args]) that initializes the extension for that application. It could attach before / after handlers etc.

classes:

Classes work mostly like initialization functions but can later be used to further change the behavior. For an example look at how the OAuth extension works: there is an OAuth object that provides some helper functions like OAuth.remote_app to create a reference to a remote application that uses OAuth.

What to use depends on what you have in mind. For the SQLite 3 extension we will use the class-based approach because it will provide users with an object that handles opening and closing database connections.

What's important about classes is that they encourage to be shared around on module level. In that case, the object itself must not under any circumstances store any application specific state and must be shareable between different application.

The Extension Code

Here's the contents of the flask_sqlite3.py for copy/paste:

import sqlite3

# Find the stack on which we want to store the database connection.
# Starting with Flask 0.9, the _app_ctx_stack is the correct one,
# before that we need to use the _request_ctx_stack.
try:
    from flask import _app_ctx_stack as stack
except ImportError:
    from flask import _request_ctx_stack as stack


class SQLite3(object):

    def __init__(self, app=None):
        if app is not None:
            self.app = app
            self.init_app(self.app)
        else:
            self.app = None

    def init_app(self, app):
        app.config.setdefault('SQLITE3_DATABASE', ':memory:')
        # Use the newstyle teardown_appcontext if it's available,
        # otherwise fall back to the request context
        if hasattr(app, 'teardown_appcontext'):
            app.teardown_appcontext(self.teardown)
        else:
            app.teardown_request(self.teardown)

    def connect(self):
        return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])

    def teardown(self, exception):
        ctx = stack.top
        if hasattr(ctx, 'sqlite3_db'):
            ctx.sqlite3_db.close()

    @property
    def connection(self):
        ctx = stack.top
        if ctx is not None:
            if not hasattr(ctx, 'sqlite3_db'):
                ctx.sqlite3_db = self.connect()
            return ctx.sqlite3_db

So here's what these lines of code do:

  1. The __init__ method takes an optional app object and, if supplied, will call init_app.

  2. The init_app method exists so that the SQLite3 object can be instantiated without requiring an app object. This method supports the factory pattern for creating applications. The init_app will set the configuration for the database, defaulting to an in memory database if no configuration is supplied. In addition, the init_app method attaches the teardown handler. It will try to use the newstyle app context handler and if it does not exist, falls back to the request context one.

  3. Next, we define a connect method that opens a database connection.

  4. Finally, we add a connection property that on first access opens the database connection and stores it on the context. This is also the recommended way to handling resources: fetch resources lazily the first time they are used.

    Note here that we're attaching our database connection to the top application context via _app_ctx_stack.top. Extensions should use the top context for storing their own information with a sufficiently complex name. Note that we're falling back to the _request_ctx_stack.top if the application is using an older version of Flask that does not support it.

So why did we decide on a class-based approach here? Because using our extension looks something like this:

from flask import Flask
from flask_sqlite3 import SQLite3

app = Flask(__name__)
app.config.from_pyfile('the-config.cfg')
db = SQLite3(app)

You can then use the database from views like this:

@app.route('/')
def show_all():
    cur = db.connection.cursor()
    cur.execute(...)

Likewise if you are outside of a request but you are using Flask 0.9 or later with the app context support, you can use the database in the same way:

with app.app_context():
    cur = db.connection.cursor()
    cur.execute(...)

At the end of the with block the teardown handles will be executed automatically.

Additionally, the init_app method is used to support the factory pattern for creating apps:

db = Sqlite3()
# Then later on.
app = create_app('the-config.cfg')
db.init_app(app)

Keep in mind that supporting this factory pattern for creating apps is required for approved flask extensions (described below).

Note on init_app

As you noticed, init_app does not assign app to self. This is intentional! Class based Flask extensions must only store the application on the object when the application was passed to the constructor. This tells the extension: I am not interested in using multiple applications.

When the extension needs to find the current application and it does not have a reference to it, it must either use the :data:`~flask.current_app` context local or change the API in a way that you can pass the application explicitly.

System Message: ERROR/3 (<string>, line 285); backlink

Unknown interpreted text role "data".

Using _app_ctx_stack

In the example above, before every request, a sqlite3_db variable is assigned to _app_ctx_stack.top. In a view function, this variable is accessible using the connection property of SQLite3. During the teardown of a request, the sqlite3_db connection is closed. By using this pattern, the same connection to the sqlite3 database is accessible to anything that needs it for the duration of the request.

If the :data:`~flask._app_ctx_stack` does not exist because the user uses an old version of Flask, it is recommended to fall back to :data:`~flask._request_ctx_stack` which is bound to a request.

System Message: ERROR/3 (<string>, line 301); backlink

Unknown interpreted text role "data".

System Message: ERROR/3 (<string>, line 301); backlink

Unknown interpreted text role "data".

Teardown Behavior

This is only relevant if you want to support Flask 0.6 and older

Due to the change in Flask 0.7 regarding functions that are run at the end of the request your extension will have to be extra careful there if it wants to continue to support older versions of Flask. The following pattern is a good way to support both:

def close_connection(response):
    ctx = _request_ctx_stack.top
    ctx.sqlite3_db.close()
    return response

if hasattr(app, 'teardown_request'):
    app.teardown_request(close_connection)
else:
    app.after_request(close_connection)

Strictly speaking the above code is wrong, because teardown functions are passed the exception and typically don't return anything. However because the return value is discarded this will just work assuming that the code in between does not touch the passed parameter.

Learn from Others

This documentation only touches the bare minimum for extension development. If you want to learn more, it's a very good idea to check out existing extensions on the Flask Extension Registry. If you feel lost there is still the mailinglist and the IRC channel to get some ideas for nice looking APIs. Especially if you do something nobody before you did, it might be a very good idea to get some more input. This not only to get an idea about what people might want to have from an extension, but also to avoid having multiple developers working on pretty much the same side by side.

Remember: good API design is hard, so introduce your project on the mailinglist, and let other developers give you a helping hand with designing the API.

The best Flask extensions are extensions that share common idioms for the API. And this can only work if collaboration happens early.

Approved Extensions

Flask also has the concept of approved extensions. Approved extensions are tested as part of Flask itself to ensure extensions do not break on new releases. These approved extensions are listed on the Flask Extension Registry and marked appropriately. If you want your own extension to be approved you have to follow these guidelines:

  1. An approved Flask extension requires a maintainer. In the event an extension author would like to move beyond the project, the project should find a new maintainer including full source hosting transition and PyPI access. If no maintainer is available, give access to the Flask core team.
  2. An approved Flask extension must provide exactly one package or module named flask_extensionname. They might also reside inside a flaskext namespace packages though this is discouraged now.
  3. It must ship a testing suite that can either be invoked with make test or python setup.py test. For test suites invoked with make test the extension has to ensure that all dependencies for the test are installed automatically. If tests are invoked with python setup.py test, test dependencies can be specified in the setup.py file. The test suite also has to be part of the distribution.
  4. APIs of approved extensions will be checked for the following characteristics:
    • an approved extension has to support multiple applications running in the same Python process.
    • it must be possible to use the factory pattern for creating applications.
  5. The license must be BSD/MIT/WTFPL licensed.
  6. The naming scheme for official extensions is Flask-ExtensionName or ExtensionName-Flask.
  7. Approved extensions must define all their dependencies in the setup.py file unless a dependency cannot be met because it is not available on PyPI.
  8. The extension must have documentation that uses one of the two Flask themes for Sphinx documentation.
  9. The setup.py description (and thus the PyPI description) has to link to the documentation, website (if there is one) and there must be a link to automatically install the development version (PackageName==dev).
  10. The zip_safe flag in the setup script must be set to False, even if the extension would be safe for zipping.
  11. An extension currently has to support Python 2.5, 2.6 as well as Python 2.7

Extension Import Transition

For a while we recommended using namespace packages for Flask extensions. This turned out to be problematic in practice because many different competing namespace package systems exist and pip would automatically switch between different systems and this caused a lot of problems for users.

Instead we now recommend naming packages flask_foo instead of the now deprecated flaskext.foo. Flask 0.8 introduces a redirect import system that lets uses import from flask.ext.foo and it will try flask_foo first and if that fails flaskext.foo.

Flask extensions should urge users to import from flask.ext.foo instead of flask_foo or flaskext_foo so that extensions can transition to the new package name without affecting users.

Something went wrong with that request. Please try again.