All about Flask Extensions

What is Flask?

  • Micro web-framework, built on top of:
  • It also contains:
    • Configuration primitives
    • Helpers to work with Cookies and Sessions
    • Testing utilities
    • Various other web-helpers (JSON, static files, etc)

Is this enough?

  • Yes!
from flask import Flask, render_template
from . import settings

app = Flask(__name__)
app.config.from_object(settings)

@app.route('/')
def index():
    return render_template('index.html')

But what if?

  • You need work with database?
  • Or with cache?
  • Or with assets?
  • Or with user authentication?
  • Or put your need here?

Meet Flask Extensions

  • Python modules or packages which adds needed abilities to Flask
  • Should named as flask_<ability>
  • Could contain as Flask-related, but just raw Python code
from flask_sqlalchemy import SQLAlchemy
# from flask.ext.sqlalchemy import SQLAlchemy  (preferred way)
...
db = SQLAlchemy(app)
  • It's like Django reusabble apps, but better :)

Popular Flask Extensions

Database, datastore, cache

User authentication

Useful extensions for every Flask application

Useful extensions

My extensions

How to use extensions?

  1. Define extension settings
  2. Import extension class in your application module
  3. Init extension class with or without app
  4. ???
  5. PROFIT!

How to use extensions?

  • Without application factory
# settings.py
SQLALCHEMY_SCHEMA_URI = 'postgresql://username:password@host:port/database'

# app.py
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
db = SQLAlchemy(app)

How to use extensions?

  • Without application factory
# models.py
from .app import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True)

How to use extensions?

  • With application factory
# settings.py
REDIS_URL = 'redis://localhost:6379/0'

# extensions.py
from flask.ext.redis import Redis

redis = Redis()

# app.py
from flask import Flask
from .extensions import redis

def create_app(name, ...):
    app = Flask(name)
    ...
    redis.init_app(app)
    return app

How to use extensions?

  • With application factory
# views.py
from flask import render_template
from .app import create_app

app = create_app('myapp')

@app.route('/')
def index():
    redis = app.extensions['redis']
    with redis.pipeline() as pipe:
        ...
    return render_template('index.html', ...)

Creating custom
Flask Extensions

It's easy!

  • Create Python module or package
  • It should be named flask_<ability>
  • That's all, now put Python code there
# flask_redis.py
from redis import StrictRedis

class Redis(object):
    def __init__(self, app=None):
        if app:
            self.app = app
            self.init_app(app)

    def init_app(self, app):
        url = app.config.get('REDIS_URL') or 'redis://localhost:6379/0'
        connection = StrictRedis.from_url(url)
        app.extensions['redis'] = connection

And it works easy

# commands.py
import logging

from flask import current_app

logger = logging.getLogger(__name__)

def stat(record):
    redis = current_app.extensions.get('redis')

    if not redis:
        app_extensions = sorted(current_app.extensions.iterkeys())
        logger.warning('No redis extension configured for app',
                       extra={'app': current_app,
                              'app_extensions': app_extensions})
        return

    with redis.pipeline() as pipe:
        ...
        pipe.execute()

Notes

  • Extension has to ensure that it works with multiple Flask application at once
    • Extension.__init__(app=None)
    • Extension.init_app(app)
  • init_app shouldn't assign app to self
  • When application passed to constructor, extension not interested in using multiple applications

Notes

  • Extension must be shipped with setup.py and registered on PyPI
  • There is Flask Extension Registry
  • To put extension there it should be licensed under the BSD, MIT or more libreal license
  • And have documentation on Python Packages or Read the Docs

Notes

  • See how other people do, learn from their code
  • Use best practicies and solutions in your code
  • Flask Extensions is great example of sharing common idioms in API
  • Don't be a bully, don't create new bycicle here

Testing custom
Flask Extensions

Welcome Travis CI

  • Continous integration system
  • Perfectly suitable for testing open-source libraries
  • You could test against:
    • Different Python versions
    • Different Flask versions

.travis.yml

language: python
python:
  - 2.6
  - 2.7
  - 3.3
  - pypy
env:
  - FLASK_VERSION=0.8
  - FLASK_VERSION=0.9
  - FLASK_VERSION=0.10.1

.travis.yml

install:
  - pip install -e .
  - pip install -I Flask==$FLASK_VERSION
  - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
script:
  - python tests.py
  - python setup.py test

.travis.yml

matrix:
  allow_failures:
    - python: pypy
  exclude:
    - python: 3.3
      env: FLASK_VERSION=0.8
    - python: 3.3
      env: FLASK_VERSION=0.9

How it works?

  • You make push to GitHub
  • GitHub sends hook with necessary payload to Travis
  • Travis reads your configuration
  • Travis installs venv and runs your tests

Proof

Questions?