Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/app/static/doc/flask-docs/_sources/extensiondev.txt
diff options
context:
space:
mode:
Diffstat (limited to 'app/static/doc/flask-docs/_sources/extensiondev.txt')
-rw-r--r--app/static/doc/flask-docs/_sources/extensiondev.txt387
1 files changed, 387 insertions, 0 deletions
diff --git a/app/static/doc/flask-docs/_sources/extensiondev.txt b/app/static/doc/flask-docs/_sources/extensiondev.txt
new file mode 100644
index 0000000..ee0d5e6
--- /dev/null
+++ b/app/static/doc/flask-docs/_sources/extensiondev.txt
@@ -0,0 +1,387 @@
+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.
+
+.. _Flask Extension Registry: http://flask.pocoo.org/extensions/
+
+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.
+
+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 behaviour.
+
+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 as BSD or MIT or a more liberal license
+to be enlisted on 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 your application is currently connecting to SQLite like the
+documentation suggests (:ref:`sqlite3`) you will need to provide a few
+functions and before / after request handlers. So how does the extension
+know the name of the application object?
+
+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 behaviour. 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 a
+manager object that handles opening and closing database connections.
+
+The Extension Code
+------------------
+
+Here's the contents of the `flask_sqlite3.py` for copy/paste::
+
+ from __future__ import absolute_import
+ import sqlite3
+
+ from flask import _request_ctx_stack
+
+ class SQLite3(object):
+
+ def __init__(self, app):
+ self.app = app
+ self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
+ self.app.teardown_request(self.teardown_request)
+ self.app.before_request(self.before_request)
+
+ def connect(self):
+ return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
+
+ def before_request(self):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db = self.connect()
+
+ def teardown_request(self, exception):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db.close()
+
+ def get_db(self):
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ return ctx.sqlite3_db
+
+So here's what these lines of code do:
+
+1. The ``__future__`` import is necessary to activate absolute imports.
+ Otherwise we could not call our module `sqlite3.py` and import the
+ top-level `sqlite3` module which actually implements the connection to
+ SQLite.
+2. We create a class for our extension that requires a supplied `app` object,
+ sets a configuration for the database if it's not there
+ (:meth:`dict.setdefault`), and attaches `before_request` and
+ `teardown_request` handlers.
+3. Next, we define a `connect` function that opens a database connection.
+4. Then we set up the request handlers we bound to the app above. Note here
+ that we're attaching our database connection to the top request context via
+ `_request_ctx_stack.top`. Extensions should use the top context and not the
+ `g` object to store things like database connections.
+5. Finally, we add a `get_db` function that simplifies access to the context's
+ database.
+
+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')
+ manager = SQLite3(app)
+ db = manager.get_db()
+
+You can then use the database from views like this::
+
+ @app.route('/')
+ def show_all():
+ cur = db.cursor()
+ cur.execute(...)
+
+Opening a database connection from outside a view function is simple.
+
+>>> from yourapplication import db
+>>> cur = db.cursor()
+>>> cur.execute(...)
+
+Adding an `init_app` Function
+-----------------------------
+
+In practice, you'll almost always want to permit users to initialize your
+extension and provide an app object after the fact. This can help avoid
+circular import problems when a user is breaking their app into multiple files.
+Our extension could add an `init_app` function as follows::
+
+ 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):
+ self.app = app
+ self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
+ self.app.teardown_request(self.teardown_request)
+ self.app.before_request(self.before_request)
+
+ def connect(self):
+ return sqlite3.connect(app.config['SQLITE3_DATABASE'])
+
+ def before_request(self):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db = self.connect()
+
+ def teardown_request(self, exception):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db.close()
+
+ def get_db(self):
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ return ctx.sqlite3_db
+
+The user could then initialize the extension in one file::
+
+ manager = SQLite3()
+
+and bind their app to the extension in another file::
+
+ manager.init_app(app)
+
+End-Of-Request Behavior
+-----------------------
+
+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 must provide exactly one package or module
+ named ``flask_extensionname``. They might also reside inside a
+ ``flaskext`` namespace packages though this is discouraged now.
+2. 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, in case of ``python setup.py test``
+ dependencies for tests alone can be specified in the `setup.py`
+ file. The test suite also has to be part of the distribution.
+3. 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.
+
+4. The license must be BSD/MIT/WTFPL licensed.
+5. The naming scheme for official extensions is *Flask-ExtensionName* or
+ *ExtensionName-Flask*.
+6. 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.
+7. The extension must have documentation that uses one of the two Flask
+ themes for Sphinx documentation.
+8. 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``).
+9. The ``zip_safe`` flag in the setup script must be set to ``False``,
+ even if the extension would be safe for zipping.
+10. An extension currently has to support Python 2.5, 2.6 as well as
+ Python 2.7
+
+
+.. _ext-import-transition:
+
+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.
+
+
+.. _OAuth extension: http://packages.python.org/Flask-OAuth/
+.. _mailinglist: http://flask.pocoo.org/mailinglist/
+.. _IRC channel: http://flask.pocoo.org/community/irc/