フラスコ拡張の開発
マイクロフレームワークであるFlaskは、サードパーティのライブラリを機能させるために、多くの場合、いくつかの反復手順を必要とします。 そのような拡張機能の多くは、 PyPI ですでに利用可能です。
まだ存在しないものに対して独自のFlask拡張機能を作成する場合、拡張機能開発のこのガイドは、拡張機能をすぐに実行し、ユーザーが拡張機能の動作を期待しているように感じるのに役立ちます。
拡張機能の構造
拡張機能はすべてflask_something
というパッケージに含まれています。ここで、「something」はブリッジするライブラリの名前です。 したがって、たとえば、 simplexml という名前のライブラリのサポートをFlaskに追加する場合は、拡張機能のパッケージにflask_simplexml
という名前を付けます。
ただし、実際の拡張機能の名前(人間が読める形式の名前)は「Flask-SimpleXML」のようになります。 その名前のどこかに「Flask」という名前を含め、大文字と小文字を確認してください。 これは、ユーザーがsetup.py
ファイルにあなたの拡張機能への依存関係を登録する方法です。
しかし、拡張機能はどのように見えますか? 拡張機能は、複数のFlaskアプリケーションインスタンスで同時に機能することを確認する必要があります。 多くの人が Application Factory パターンのようなパターンを使用して、単体テストを支援し、複数の構成をサポートするために必要に応じてアプリケーションを作成するため、これは要件です。 そのため、アプリケーションがそのような動作をサポートすることが重要です。
最も重要なことは、拡張機能はsetup.py
ファイルと一緒に出荷され、PyPIに登録されている必要があります。 また、開発チェックアウトリンクが機能するため、ライブラリを手動でダウンロードしなくても、開発バージョンをvirtualenvに簡単にインストールできます。
Flask拡張機能は、Flask拡張機能レジストリにリストされるために、BSD、MIT、またはより自由なライセンスの下でライセンスされている必要があります。 Flask Extension Registryは管理された場所であり、ライブラリが必要に応じて動作する場合は、ライブラリが事前にレビューされることに注意してください。
「こんにちはFlaskext!」
それでは、そのようなFlask拡張機能の作成を始めましょう。 ここで作成する拡張機能は、SQLite3の非常に基本的なサポートを提供します。
まず、次のフォルダ構造を作成します。
flask-sqlite3/
flask_sqlite3.py
LICENSE
README
最も重要なファイルの内容は次のとおりです。
setup.py
絶対に必要な次のファイルは、Flask拡張機能のインストールに使用されるsetup.py
ファイルです。 次のコンテンツは、操作できるものです。
"""
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='[email protected]',
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'
]
)
これは多くのコードですが、実際には既存の拡張機能からコピーして貼り付け、適応させることができます。
フラスコ_sqlite3.py
これが拡張コードの行き先です。 しかし、そのような拡張機能はどのように正確に見えるべきでしょうか? ベストプラクティスは何ですか? いくつかの洞察のために読み続けてください。
拡張機能の初期化
多くの拡張機能では、何らかの初期化手順が必要になります。 たとえば、ドキュメントが示唆しているように、現在SQLiteに接続しているアプリケーションについて考えてみます( FlaskでのSQLite3の使用)。 では、拡張機能はどのようにしてアプリケーションオブジェクトの名前を知るのでしょうか。
非常に簡単です:あなたはそれにそれを渡します。
拡張機能を初期化するための2つの推奨される方法があります。
初期化関数:
拡張機能が helloworld と呼ばれる場合、そのアプリケーションの拡張機能を初期化する
init_helloworld(app[, extra_args])
と呼ばれる関数がある可能性があります。 ハンドラーなどの前後にアタッチできます。
クラス:
クラスはほとんど初期化関数のように機能しますが、後で動作をさらに変更するために使用できます。 OAuth拡張機能の仕組みの例を見てみましょう。 OAuth.remote_app などのヘルパー関数を提供してリモートへの参照を作成する OAuth オブジェクトがあります。 OAuthを使用するアプリケーション。
何を使用するかは、あなたが何を考えているかによって異なります。 SQLite 3拡張機能では、データベース接続の開閉を処理するオブジェクトをユーザーに提供するため、クラスベースのアプローチを使用します。
クラスを設計するときは、モジュールレベルで簡単に再利用できるようにすることが重要です。 つまり、オブジェクト自体はいかなる状況でもアプリケーション固有の状態を格納してはならず、異なるアプリケーション間で共有可能でなければなりません。
拡張コード
コピー/貼り付け用の flask_sqlite3.py の内容は次のとおりです。
import sqlite3
from flask import current_app, _app_ctx_stack
class SQLite3(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)
def init_app(self, app):
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
app.teardown_appcontext(self.teardown)
def connect(self):
return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
def teardown(self, exception):
ctx = _app_ctx_stack.top
if hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db.close()
@property
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
したがって、これらのコード行の機能は次のとおりです。
__init__
メソッドは、オプションのアプリオブジェクトを受け取り、指定されている場合はinit_app
を呼び出します。init_app
メソッドが存在するため、アプリオブジェクトを必要とせずにSQLite3
オブジェクトをインスタンス化できます。 このメソッドは、アプリケーションを作成するためのファクトリパターンをサポートします。init_app
はデータベースの構成を設定し、構成が指定されていない場合はデフォルトでインメモリデータベースに設定されます。 さらに、init_app
メソッドは、teardown
ハンドラーをアタッチします。次に、データベース接続を開く
connect
メソッドを定義します。最後に、
connection
プロパティを追加します。このプロパティは、最初のアクセス時にデータベース接続を開き、コンテキストに保存します。 これは、リソースを処理するための推奨される方法でもあります。最初に使用するときに、リソースを遅延フェッチします。ここでは、
_app_ctx_stack.top
を介してデータベース接続を最上位のアプリケーションコンテキストに接続していることに注意してください。 拡張機能は、十分に複雑な名前で独自の情報を格納するためにトップコンテキストを使用する必要があります。
では、なぜここでクラスベースのアプローチを決定したのでしょうか。 拡張機能を使用すると、次のようになります。
from flask import Flask
from flask_sqlite3 import SQLite3
app = Flask(__name__)
app.config.from_pyfile('the-config.cfg')
db = SQLite3(app)
次に、次のようなビューからデータベースを使用できます。
@app.route('/')
def show_all():
cur = db.connection.cursor()
cur.execute(...)
同様に、リクエストの範囲外の場合は、アプリコンテキストをプッシュすることでデータベースを使用できます。
with app.app_context():
cur = db.connection.cursor()
cur.execute(...)
with
ブロックの最後に、ティアダウンハンドルが自動的に実行されます。
さらに、init_app
メソッドは、アプリを作成するためのファクトリパターンをサポートするために使用されます。
db = SQLite3()
# Then later on.
app = create_app('the-config.cfg')
db.init_app(app)
アプリを作成するためにこのファクトリパターンをサポートすることは、承認されたフラスコ拡張機能(以下で説明)に必要であることに注意してください。
init_app
に関する注記
お気づきのように、init_app
はapp
をself
に割り当てません。 これは意図的なものです。 クラスベースのFlask拡張機能は、アプリケーションがコンストラクターに渡されたときにのみ、アプリケーションをオブジェクトに格納する必要があります。 これは拡張機能を示しています。私は複数のアプリケーションを使用することに興味がありません。
拡張機能が現在のアプリケーションを見つける必要があり、それへの参照がない場合は、ローカルの current_app コンテキストを使用するか、アプリケーションを明示的に渡すことができるようにAPIを変更する必要があります。
_app_ctx_stackの使用
上記の例では、すべてのリクエストの前に、sqlite3_db
変数が_app_ctx_stack.top
に割り当てられています。 ビュー関数では、この変数はSQLite3
のconnection
プロパティを使用してアクセスできます。 リクエストのティアダウン中、sqlite3_db
接続は閉じられます。 このパターンを使用することにより、sqlite3データベースへの same 接続は、リクエストの期間中、それを必要とするすべての人がアクセスできます。
他の人から学ぶ
このドキュメントは、拡張機能開発の最低限にしか触れていません。 詳細を知りたい場合は、 PyPI の既存の拡張機能を確認することをお勧めします。 迷ったと感じた場合でも、メーリングリストと Discordサーバーがあり、見栄えの良いAPIのアイデアを得ることができます。 特に、以前に誰もやったことがないことをした場合は、もう少し入力を得るのは非常に良い考えかもしれません。 これにより、拡張機能から人々が何を望んでいるかについての有用なフィードバックが生成されるだけでなく、ほぼ同じ問題に複数の開発者が単独で取り組むことを回避できます。
覚えておいてください:優れたAPI設計は難しいので、メーリングリストにプロジェクトを紹介し、他の開発者にAPIの設計を手伝ってもらいましょう。
最高のFlask拡張機能は、APIの共通のイディオムを共有する拡張機能です。 そして、これはコラボレーションが早期に行われた場合にのみ機能します。
承認された拡張機能
Flaskには、以前は承認された拡張機能の概念がありました。 これらには、サポートと互換性の検証が含まれていました。 このリストは時間の経過とともに維持するのが難しくなりましたが、ガイドラインは、Flaskエコシステムの一貫性と互換性を維持するのに役立つため、現在維持および開発されているすべての拡張機能に関連しています。
承認されたFlask拡張機能には、メンテナが必要です。 拡張機能の作成者がプロジェクトを超えて移動したい場合、プロジェクトは新しいメンテナを見つけて、リポジトリ、ドキュメント、PyPI、およびその他のサービスへのアクセスを転送する必要があります。 メンテナがいない場合は、パレットコアチームにアクセスできるようにします。
命名スキームは、 Flask-ExtensionName または ExtensionName-Flask です。
flask_extension_name
という名前のパッケージまたはモジュールを1つだけ提供する必要があります。拡張機能は、BSDまたはMITライセンスである必要があります。 オープンソースであり、公開されている必要があります。
拡張機能のAPIには、次の特性が必要です。
同じPythonプロセスで実行される複数のアプリケーションをサポートする必要があります。
self.app
の代わりにcurrent_app
を使用して、アプリケーションインスタンスごとに構成と状態を保存します。アプリケーションの作成にファクトリパターンを使用できる必要があります。
ext.init_app()
パターンを使用してください。
リポジトリのクローンから、依存関係のある拡張機能が
pip install -e .
でインストール可能である必要があります。tox -e py
またはpytest
で呼び出すことができるテストスイートを出荷する必要があります。tox
を使用しない場合は、テストの依存関係をrequirements.txt
ファイルで指定する必要があります。 テストはsdistディストリビューションの一部である必要があります。ドキュメントでは、公式パレットテーマの
flask
テーマを使用する必要があります。 ドキュメントまたはプロジェクトのWebサイトへのリンクは、PyPIメタデータまたはreadmeに含まれている必要があります。最大限の互換性を得るには、拡張機能はFlaskがサポートするのと同じバージョンのPythonをサポートする必要があります。 2020年の時点で3.6+が推奨されています。
setup.py
のpython_requires=">= 3.6"
を使用して、サポートされているバージョンを示します。