mirror of https://github.com/pallets/flask.git
rewrite extension development docs
This commit is contained in:
parent
0a01248b5c
commit
b46bfcfa63
|
@ -1,310 +1,284 @@
|
||||||
Flask Extension Development
|
Flask Extension Development
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
Flask, being a microframework, often requires some repetitive steps to get
|
.. currentmodule:: flask
|
||||||
a third party library working. Many such extensions are already available
|
|
||||||
on `PyPI`_.
|
|
||||||
|
|
||||||
If you want to create your own Flask extension for something that does not
|
Extensions are extra packages that add functionality to a Flask
|
||||||
exist yet, this guide to extension development will help you get your
|
application. While `PyPI`_ contains many Flask extensions, you may not
|
||||||
extension running in no time and to feel like users would expect your
|
find one that fits your need. If this is the case, you can create your
|
||||||
extension to behave.
|
own, and publish it for others to use as well.
|
||||||
|
|
||||||
Anatomy of an Extension
|
This guide will show how to create a Flask extension, and some of the
|
||||||
-----------------------
|
common patterns and requirements involved. Since extensions can do
|
||||||
|
anything, this guide won't be able to cover every possibility.
|
||||||
|
|
||||||
Extensions are all located in a package called ``flask_something``
|
The best ways to learn about extensions are to look at how other
|
||||||
where "something" is the name of the library you want to bridge. So for
|
extensions you use are written, and discuss with others. Discuss your
|
||||||
example if you plan to add support for a library named `simplexml` to
|
design ideas with others on our `Discord Chat`_ or
|
||||||
Flask, you would name your extension's package ``flask_simplexml``.
|
`GitHub Discussions`_.
|
||||||
|
|
||||||
The name of the actual extension (the human readable name) however would
|
The best extensions share common patterns, so that anyone familiar with
|
||||||
be something like "Flask-SimpleXML". Make sure to include the name
|
using one extension won't feel completely lost with another. This can
|
||||||
"Flask" somewhere in that name and that you check the capitalization.
|
only work if collaboration happens early.
|
||||||
This is how users can then register dependencies to your extension in
|
|
||||||
their :file:`setup.py` files.
|
|
||||||
|
|
||||||
But what 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
|
|
||||||
:doc:`/patterns/appfactories` 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.
|
|
||||||
|
|
||||||
Most importantly the extension must be shipped with a :file:`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
|
|
||||||
in order to be listed 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 :file:`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(
|
Naming
|
||||||
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
|
A Flask extension typically has ``flask`` in its name as a prefix or
|
||||||
extensions and adapt.
|
suffix. If it wraps another library, it should include the library name
|
||||||
|
as well. This makes it easy to search for extensions, and makes their
|
||||||
|
purpose clearer.
|
||||||
|
|
||||||
flask_sqlite3.py
|
A general Python packaging recommendation is that the install name from
|
||||||
````````````````
|
the package index and the name used in ``import`` statements should be
|
||||||
|
related. The import name is lowercase, with words separated by
|
||||||
|
underscores (``_``). The install name is either lower case or title
|
||||||
|
case, with words separated by dashes (``-``). If it wraps another
|
||||||
|
library, prefer using the same case as that library's name.
|
||||||
|
|
||||||
Now this is where your extension code goes. But how exactly should such
|
Here are some example install and import names:
|
||||||
an extension look like? What are the best practices? Continue reading
|
|
||||||
for some insight.
|
|
||||||
|
|
||||||
Initializing Extensions
|
- ``Flask-Name`` imported as ``flask_name``
|
||||||
-----------------------
|
- ``flask-name-lower`` imported as ``flask_name_lower``
|
||||||
|
- ``Flask-ComboName`` imported as ``flask_comboname``
|
||||||
Many extensions will need some kind of initialization step. For example,
|
- ``Name-Flask`` imported as ``name_flask``
|
||||||
consider an application that's currently connecting to SQLite like the
|
|
||||||
documentation suggests (:doc:`/patterns/sqlite3`). 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 behavior.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
When designing your classes, it's important to make them easily reusable
|
|
||||||
at the module level. This means the object itself must not under any
|
|
||||||
circumstances store any application specific state and must be shareable
|
|
||||||
between different applications.
|
|
||||||
|
|
||||||
The Extension Code
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Here's the contents of the `flask_sqlite3.py` for copy/paste::
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
from flask import current_app, _app_ctx_stack
|
|
||||||
|
|
||||||
|
|
||||||
class SQLite3(object):
|
The Extension Class and Initialization
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
All extensions will need some entry point that initializes the
|
||||||
|
extension with the application. The most common pattern is to create a
|
||||||
|
class that represents the extension's configuration and behavior, with
|
||||||
|
an ``init_app`` method to apply the extension instance to the given
|
||||||
|
application instance.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class HelloExtension:
|
||||||
def __init__(self, app=None):
|
def __init__(self, app=None):
|
||||||
self.app = app
|
|
||||||
if app is not None:
|
if app is not None:
|
||||||
self.init_app(app)
|
self.init_app(app)
|
||||||
|
|
||||||
def init_app(self, app):
|
def init_app(self, app):
|
||||||
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
|
app.before_request(...)
|
||||||
app.teardown_appcontext(self.teardown)
|
|
||||||
|
|
||||||
def connect(self):
|
It is important that the app is not stored on the extension, don't do
|
||||||
return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
|
``self.app = app``. The only time the extension should have direct
|
||||||
|
access to an app is during ``init_app``, otherwise it should use
|
||||||
|
:data:`current_app`.
|
||||||
|
|
||||||
def teardown(self, exception):
|
This allows the extension to support the application factory pattern,
|
||||||
ctx = _app_ctx_stack.top
|
avoids circular import issues when importing the extension instance
|
||||||
if hasattr(ctx, 'sqlite3_db'):
|
elsewhere in a user's code, and makes testing with different
|
||||||
ctx.sqlite3_db.close()
|
configurations easier.
|
||||||
|
|
||||||
@property
|
.. code-block:: python
|
||||||
def connection(self):
|
|
||||||
ctx = _app_ctx_stack.top
|
|
||||||
if ctx is not None:
|
|
||||||
if not hasattr(ctx, 'sqlite3_db'):
|
|
||||||
ctx.sqlite3_db = self.connect()
|
|
||||||
return ctx.sqlite3_db
|
|
||||||
|
|
||||||
|
hello = HelloExtension()
|
||||||
|
|
||||||
So here's what these lines of code do:
|
def create_app():
|
||||||
|
|
||||||
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.
|
|
||||||
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.
|
|
||||||
|
|
||||||
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 = Flask(__name__)
|
||||||
app.config.from_pyfile('the-config.cfg')
|
hello.init_app(app)
|
||||||
db = SQLite3(app)
|
return app
|
||||||
|
|
||||||
You can then use the database from views like this::
|
Above, the ``hello`` extension instance exists independently of the
|
||||||
|
application. This means that other modules in a user's project can do
|
||||||
|
``from project import hello`` and use the extension in blueprints before
|
||||||
|
the app exists.
|
||||||
|
|
||||||
@app.route('/')
|
The :attr:`Flask.extensions` dict can be used to store a reference to
|
||||||
def show_all():
|
the extension on the application, or some other state specific to the
|
||||||
cur = db.connection.cursor()
|
application. Be aware that this is a single namespace, so use a name
|
||||||
cur.execute(...)
|
unique to your extension, such as the extension's name without the
|
||||||
|
"flask" prefix.
|
||||||
|
|
||||||
Likewise if you are outside of a request you can use the database by
|
|
||||||
pushing an app context::
|
|
||||||
|
|
||||||
with app.app_context():
|
Adding Behavior
|
||||||
cur = db.connection.cursor()
|
---------------
|
||||||
cur.execute(...)
|
|
||||||
|
|
||||||
At the end of the ``with`` block the teardown handles will be executed
|
There are many ways that an extension can add behavior. Any setup
|
||||||
automatically.
|
methods that are available on the :class:`Flask` object can be used
|
||||||
|
during an extension's ``init_app`` method.
|
||||||
|
|
||||||
Additionally, the ``init_app`` method is used to support the factory pattern
|
A common pattern is to use :meth:`~Flask.before_request` to initialize
|
||||||
for creating apps::
|
some data or a connection at the beginning of each request, then
|
||||||
|
:meth:`~Flask.teardown_request` to clean it up at the end. This can be
|
||||||
|
stored on :data:`g`, discussed more below.
|
||||||
|
|
||||||
db = SQLite3()
|
A more lazy approach is to provide a method that initializes and caches
|
||||||
# Then later on.
|
the data or connection. For example, a ``ext.get_db`` method could
|
||||||
app = create_app('the-config.cfg')
|
create a database connection the first time it's called, so that a view
|
||||||
|
that doesn't use the database doesn't create a connection.
|
||||||
|
|
||||||
|
Besides doing something before and after every view, your extension
|
||||||
|
might want to add some specific views as well. In this case, you could
|
||||||
|
define a :class:`Blueprint`, then call :meth:`~Flask.register_blueprint`
|
||||||
|
during ``init_app`` to add the blueprint to the app.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration Techniques
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
There can be multiple levels and sources of configuration for an
|
||||||
|
extension. You should consider what parts of your extension fall into
|
||||||
|
each one.
|
||||||
|
|
||||||
|
- Configuration per application instance, through ``app.config``
|
||||||
|
values. This is configuration that could reasonably change for each
|
||||||
|
deployment of an application. A common example is a URL to an
|
||||||
|
external resource, such as a database. Configuration keys should
|
||||||
|
start with the extension's name so that they don't interfere with
|
||||||
|
other extensions.
|
||||||
|
- Configuration per extension instance, through ``__init__``
|
||||||
|
arguments. This configuration usually affects how the extension
|
||||||
|
is used, such that it wouldn't make sense to change it per
|
||||||
|
deployment.
|
||||||
|
- Configuration per extension instance, through instance attributes
|
||||||
|
and decorator methods. It might be more ergonomic to assign to
|
||||||
|
``ext.value``, or use a ``@ext.register`` decorator to register a
|
||||||
|
function, after the extension instance has been created.
|
||||||
|
- Global configuration through class attributes. Changing a class
|
||||||
|
attribute like ``Ext.connection_class`` can customize default
|
||||||
|
behavior without making a subclass. This could be combined
|
||||||
|
per-extension configuration to override defaults.
|
||||||
|
- Subclassing and overriding methods and attributes. Making the API of
|
||||||
|
the extension itself something that can be overridden provides a
|
||||||
|
very powerful tool for advanced customization.
|
||||||
|
|
||||||
|
The :class:`~flask.Flask` object itself uses all of these techniques.
|
||||||
|
|
||||||
|
It's up to you to decide what configuration is appropriate for your
|
||||||
|
extension, based on what you need and what you want to support.
|
||||||
|
|
||||||
|
Configuration should not be changed after the application setup phase is
|
||||||
|
complete and the server begins handling requests. Configuration is
|
||||||
|
global, any changes to it are not guaranteed to be visible to other
|
||||||
|
workers.
|
||||||
|
|
||||||
|
|
||||||
|
Data During a Request
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
When writing a Flask application, the :data:`~flask.g` object is used to
|
||||||
|
store information during a request. For example the
|
||||||
|
:doc:`tutorial <tutorial/database>` stores a connection to a SQLite
|
||||||
|
database as ``g.db``. Extensions can also use this, with some care.
|
||||||
|
Since ``g`` is a single global namespace, extensions must use unique
|
||||||
|
names that won't collide with user data. For example, use the extension
|
||||||
|
name as a prefix, or as a namespace.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# an internal prefix with the extension name
|
||||||
|
g._hello_user_id = 2
|
||||||
|
|
||||||
|
# or an internal prefix as a namespace
|
||||||
|
from types import SimpleNamespace
|
||||||
|
g._hello = SimpleNamespace()
|
||||||
|
g._hello.user_id = 2
|
||||||
|
|
||||||
|
The data in ``g`` lasts for an application context. An application
|
||||||
|
context is active when a request context is, or when a CLI command is
|
||||||
|
run. If you're storing something that should be closed, use
|
||||||
|
:meth:`~flask.Flask.teardown_appcontext` to ensure that it gets closed
|
||||||
|
when the application context ends. If it should only be valid during a
|
||||||
|
request, or would not be used in the CLI outside a reqeust, use
|
||||||
|
:meth:`~flask.Flask.teardown_request`.
|
||||||
|
|
||||||
|
An older technique for storing context data was to store it on
|
||||||
|
``_app_ctx_stack.top`` or ``_request_ctx_stack.top``. However, this just
|
||||||
|
moves the same namespace collision problem elsewhere (although less
|
||||||
|
likely) and modifies objects that are very internal to Flask's
|
||||||
|
operations. Prefer storing data under a unique name in ``g``.
|
||||||
|
|
||||||
|
|
||||||
|
Views and Models
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Your extension views might want to interact with specific models in your
|
||||||
|
database, or some other extension or data connected to your application.
|
||||||
|
For example, let's consider a ``Flask-SimpleBlog`` extension that works
|
||||||
|
with Flask-SQLAlchemy to provide a ``Post`` model and views to write
|
||||||
|
and read posts.
|
||||||
|
|
||||||
|
The ``Post`` model needs to subclass the Flask-SQLAlchemy ``db.Model``
|
||||||
|
object, but that's only available once you've created an instance of
|
||||||
|
that extension, not when your extension is defining its views. So how
|
||||||
|
can the view code, defined before the model exists, access the model?
|
||||||
|
|
||||||
|
One method could be to use :doc:`views`. During ``__init__``, create
|
||||||
|
the model, then create the views by passing the model to the view
|
||||||
|
class's :meth:`~views.View.as_view` method.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class PostAPI(MethodView):
|
||||||
|
def __init__(self, model):
|
||||||
|
self.model = model
|
||||||
|
|
||||||
|
def get(id):
|
||||||
|
post = self.model.query.get(id)
|
||||||
|
return jsonify(post.to_json())
|
||||||
|
|
||||||
|
class BlogExtension:
|
||||||
|
def __init__(self, db):
|
||||||
|
class Post(db.Model):
|
||||||
|
id = db.Column(primary_key=True)
|
||||||
|
title = db.Column(db.String, nullable=False)
|
||||||
|
|
||||||
|
self.post_model = Post
|
||||||
|
|
||||||
|
def init_app(self, app):
|
||||||
|
api_view = PostAPI.as_view(model=self.post_model)
|
||||||
|
|
||||||
|
db = SQLAlchemy()
|
||||||
|
blog = BlogExtension(db)
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
blog.init_app(app)
|
||||||
|
|
||||||
Keep in mind that supporting this factory pattern for creating apps is required
|
Another technique could be to use an attribute on the extension, such as
|
||||||
for approved flask extensions (described below).
|
``self.post_model`` from above. Add the extension to ``app.extensions``
|
||||||
|
in ``init_app``, then access
|
||||||
|
``current_app.extensions["simple_blog"].post_model`` from views.
|
||||||
|
|
||||||
.. admonition:: Note on ``init_app``
|
You may also want to provide base classes so that users can provide
|
||||||
|
their own ``Post`` model that conforms to the API your extension
|
||||||
|
expects. So they could implement ``class Post(blog.BasePost)``, then
|
||||||
|
set it as ``blog.post_model``.
|
||||||
|
|
||||||
As you noticed, ``init_app`` does not assign ``app`` to ``self``. This
|
As you can see, this can get a bit complex. Unfortunately, there's no
|
||||||
is intentional! Class based Flask extensions must only store the
|
perfect solution here, only different strategies and tradeoffs depending
|
||||||
application on the object when the application was passed to the
|
on your needs and how much customization you want to offer. Luckily,
|
||||||
constructor. This tells the extension: I am not interested in using
|
this sort of resource dependency is not a common need for most
|
||||||
multiple applications.
|
extensions. Remember, if you need help with design, ask on our
|
||||||
|
`Discord Chat`_ or `GitHub Discussions`_.
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
Using _app_ctx_stack
|
Recommended Extension Guidelines
|
||||||
--------------------
|
--------------------------------
|
||||||
|
|
||||||
In the example above, before every request, a ``sqlite3_db`` variable is
|
Flask previously had the concept of "approved extensions", where the
|
||||||
assigned to ``_app_ctx_stack.top``. In a view function, this variable is
|
Flask maintainers evaluated the quality, support, and compatibility of
|
||||||
accessible using the ``connection`` property of ``SQLite3``. During the
|
the extensions before listing them. While the list became too difficult
|
||||||
teardown of a request, the ``sqlite3_db`` connection is closed. By using
|
to maintain over time, the guidelines are still relevant to all
|
||||||
this pattern, the *same* connection to the sqlite3 database is accessible
|
extensions maintained and developed today, as they help the Flask
|
||||||
to anything that needs it for the duration of the request.
|
|
||||||
|
|
||||||
|
|
||||||
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 `PyPI`_. If you feel lost there is `Discord Chat`_ or
|
|
||||||
`GitHub Discussions`_ 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 generates useful feedback on what people might want from
|
|
||||||
an extension, but also avoids having multiple developers working in isolation
|
|
||||||
on pretty much the same problem.
|
|
||||||
|
|
||||||
Remember: good API design is hard, so introduce your project on
|
|
||||||
`Discord Chat`_ or `GitHub Discussions`_, 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 previously had the concept of approved extensions. These came with
|
|
||||||
some vetting of support and compatibility. While this list became too
|
|
||||||
difficult to maintain over time, the guidelines are still relevant to
|
|
||||||
all extensions maintained and developed today, as they help the Flask
|
|
||||||
ecosystem remain consistent and compatible.
|
ecosystem remain consistent and compatible.
|
||||||
|
|
||||||
0. An approved Flask extension requires a maintainer. In the event an
|
1. An extension requires a maintainer. In the event an extension author
|
||||||
extension author would like to move beyond the project, the project
|
would like to move beyond the project, the project should find a new
|
||||||
should find a new maintainer and transfer access to the repository,
|
maintainer and transfer access to the repository, documentation,
|
||||||
documentation, PyPI, and any other services. If no maintainer
|
PyPI, and any other services. The `Pallets-Eco`_ organization on
|
||||||
is available, give access to the Pallets core team.
|
GitHub allows for community maintenance with oversight from the
|
||||||
1. The naming scheme is *Flask-ExtensionName* or *ExtensionName-Flask*.
|
Pallets maintainers.
|
||||||
|
2. The naming scheme is *Flask-ExtensionName* or *ExtensionName-Flask*.
|
||||||
It must provide exactly one package or module named
|
It must provide exactly one package or module named
|
||||||
``flask_extension_name``.
|
``flask_extension_name``.
|
||||||
2. The extension must be BSD or MIT licensed. It must be open source
|
3. The extension must use an open source license. The Python web
|
||||||
and publicly available.
|
ecosystem tends to prefer BSD or MIT. It must be open source and
|
||||||
3. The extension's API must have the following characteristics:
|
publicly available.
|
||||||
|
4. The extension's API must have the following characteristics:
|
||||||
|
|
||||||
- It must support multiple applications running in the same Python
|
- It must support multiple applications running in the same Python
|
||||||
process. Use ``current_app`` instead of ``self.app``, store
|
process. Use ``current_app`` instead of ``self.app``, store
|
||||||
|
@ -312,21 +286,25 @@ ecosystem remain consistent and compatible.
|
||||||
- It must be possible to use the factory pattern for creating
|
- It must be possible to use the factory pattern for creating
|
||||||
applications. Use the ``ext.init_app()`` pattern.
|
applications. Use the ``ext.init_app()`` pattern.
|
||||||
|
|
||||||
4. From a clone of the repository, an extension with its dependencies
|
5. From a clone of the repository, an extension with its dependencies
|
||||||
must be installable with ``pip install -e .``.
|
must be installable in editable mode with ``pip install -e .``.
|
||||||
5. It must ship a testing suite that can be invoked with ``tox -e py``
|
6. It must ship tests that can be invoked with a common tool like
|
||||||
or ``pytest``. If not using ``tox``, the test dependencies should be
|
``tox -e py``, ``nox -s test`` or ``pytest``. If not using ``tox``,
|
||||||
specified in a ``requirements.txt`` file. The tests must be part of
|
the test dependencies should be specified in a requirements file.
|
||||||
the sdist distribution.
|
The tests must be part of the sdist distribution.
|
||||||
6. The documentation must use the ``flask`` theme from the
|
7. A link to the documentation or project website must be in the PyPI
|
||||||
`Official Pallets Themes`_. A link to the documentation or project
|
metadata or the readme. The documentation should use the Flask theme
|
||||||
website must be in the PyPI metadata or the readme.
|
from the `Official Pallets Themes`_.
|
||||||
7. For maximum compatibility, the extension should support the same
|
8. The extension's dependencies should not use upper bounds or assume
|
||||||
versions of Python that Flask supports. 3.7+ is recommended as of
|
any particular version scheme, but should use lower bounds to
|
||||||
December 2021. Use ``python_requires=">= 3.7"`` in ``setup.py`` to
|
indicate minimum compatibility support. For example,
|
||||||
indicate supported versions.
|
``sqlalchemy>=1.4``.
|
||||||
|
9. Indicate the versions of Python supported using
|
||||||
|
``python_requires=">=version"``. Flask itself supports Python >=3.7
|
||||||
|
as of December 2021, but this will update over time.
|
||||||
|
|
||||||
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
||||||
.. _Discord Chat: https://discord.gg/pallets
|
.. _Discord Chat: https://discord.gg/pallets
|
||||||
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
|
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
|
||||||
.. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/
|
.. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/
|
||||||
|
.. _Pallets-Eco: https://github.com/pallets-eco
|
||||||
|
|
|
@ -39,10 +39,10 @@ an extension called "Flask-Foo" might be used like this::
|
||||||
Building Extensions
|
Building Extensions
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
While the `PyPI <pypi_>`_ contains many Flask extensions, you may
|
While `PyPI <pypi_>`_ contains many Flask extensions, you may not find
|
||||||
not find an extension that fits your need. If this is the case, you can
|
an extension that fits your need. If this is the case, you can create
|
||||||
create your own. Read :doc:`/extensiondev` to develop your own Flask
|
your own, and publish it for others to use as well. Read
|
||||||
extension.
|
:doc:`extensiondev` to develop your own Flask extension.
|
||||||
|
|
||||||
|
|
||||||
.. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
.. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
||||||
|
|
Loading…
Reference in New Issue