信号
バージョン0.6の新機能。
Flask 0.6以降、Flaskでのシグナリングの統合サポートがあります。 このサポートは、優れた blinker ライブラリによって提供され、利用できない場合は正常にフォールバックします。
信号とは何ですか? シグナルは、コアフレームワークまたは別のFlask拡張機能の他の場所でアクションが発生したときに通知を送信することにより、アプリケーションを分離するのに役立ちます。 つまり、シグナルにより、特定の送信者はサブスクライバーに何かが起こったことを通知できます。
Flaskにはいくつかの信号が付属しており、他の拡張機能がさらに多くの信号を提供する可能性があります。 また、シグナルはサブスクライバーに通知することを目的としており、サブスクライバーにデータの変更を促すものではないことにも注意してください。 一部の組み込みデコレータと同じように見えるシグナルがあることに気付くでしょう(例: request_started はbefore_request()
と非常に似ています)。 ただし、それらの動作には違いがあります。 たとえば、コアbefore_request()
ハンドラーは特定の順序で実行され、応答を返すことで要求を早期に中止できます。 対照的に、すべてのシグナルハンドラーは未定義の順序で実行され、データを変更しません。
ハンドラーに対するシグナルの大きな利点は、ほんの一瞬で安全にサブスクライブできることです。 これらの一時的なサブスクリプションは、たとえば単体テストに役立ちます。 リクエストの一部としてレンダリングされたテンプレートを知りたいとします。シグナルを使用すると、まさにそれを実行できます。
シグナルの購読
シグナルをサブスクライブするには、シグナルのconnect()
メソッドを使用できます。 最初の引数は信号が発信されたときに呼び出される関数であり、オプションの2番目の引数は送信者を指定します。 シグナルの購読を解除するには、disconnect()
メソッドを使用できます。
すべてのコアFlaskシグナルの場合、送信者はシグナルを発行したアプリケーションです。 シグナルをサブスクライブするときは、すべてのアプリケーションからのシグナルを本当に聞きたい場合を除いて、必ず送信者も提供してください。 これは、拡張機能を開発している場合に特に当てはまります。
たとえば、単体テストで使用して、レンダリングされたテンプレートとテンプレートに渡された変数を判別できるヘルパーコンテキストマネージャーを次に示します。
from flask import template_rendered
from contextlib import contextmanager
@contextmanager
def captured_templates(app):
recorded = []
def record(sender, template, context, **extra):
recorded.append((template, context))
template_rendered.connect(record, app)
try:
yield recorded
finally:
template_rendered.disconnect(record, app)
これは、テストクライアントと簡単にペアリングできるようになりました。
with captured_templates(app) as templates:
rv = app.test_client().get('/')
assert rv.status_code == 200
assert len(templates) == 1
template, context = templates[0]
assert template.name == 'index.html'
assert len(context['items']) == 10
Flaskがシグナルに新しい引数を導入した場合に呼び出しが失敗しないように、必ず追加の**extra
引数をサブスクライブしてください。
with
ブロックの本体にあるアプリケーション app によって発行されたコードでレンダリングされるすべてのテンプレートは、 templates 変数に記録されるようになります。 テンプレートがレンダリングされるたびに、テンプレートオブジェクトとコンテキストがテンプレートに追加されます。
さらに、便利なヘルパーメソッド(connected_to()
)があり、コンテキストマネージャーを使用して関数をシグナルに一時的にサブスクライブできます。 コンテキストマネージャーの戻り値はそのように指定できないため、引数としてリストを渡す必要があります。
from flask import template_rendered
def captured_templates(app, recorded, **extra):
def record(sender, template, context):
recorded.append((template, context))
return template_rendered.connected_to(record, app)
上記の例は次のようになります。
templates = []
with captured_templates(app, templates, **extra):
...
template, context = templates[0]
BlinkerAPIの変更
connected_to()
メソッドは、バージョン1.1でBlinkerに導入されました。
シグナルの作成
独自のアプリケーションでシグナルを使用する場合は、ブリンカーライブラリを直接使用できます。 最も一般的な使用例は、カスタムNamespace
の名前付きシグナルです。 これは、ほとんどの場合に推奨されるものです。
from blinker import Namespace
my_signals = Namespace()
これで、次のような新しいシグナルを作成できます。
model_saved = my_signals.signal('model-saved')
ここでの信号の名前は、信号を一意にし、デバッグを簡素化します。 name
属性を使用して信号の名前にアクセスできます。
拡張機能開発者向け
Flask拡張機能を作成していて、ブリンカーのインストールが欠落しているために正常に機能を低下させたい場合は、 Flask.signals.Namespace クラスを使用してこれを行うことができます。
信号の送信
信号を発信したい場合は、send()
メソッドを呼び出してください。 送信者を最初の引数として受け入れ、オプションで、シグナルサブスクライバーに転送されるいくつかのキーワード引数を受け入れます。
class Model(object):
...
def save(self):
model_saved.send(self)
常に良い送信者を選ぶようにしてください。 シグナルを発しているクラスがある場合は、送信者としてself
を渡します。 ランダム関数から信号を発信している場合は、current_app._get_current_object()
を送信者として渡すことができます。
送信者としてプロキシを渡す
current_app を送信者としてシグナルに渡さないでください。 代わりにcurrent_app._get_current_object()
を使用してください。 これは、 current_app がプロキシであり、実際のアプリケーションオブジェクトではないためです。
シグナルとFlaskのリクエストコンテキスト
シグナルは、シグナルを受信するときにリクエストコンテキストを完全にサポートします。 コンテキストローカル変数は、 request_started と request_finished の間で一貫して使用できるため、必要に応じて flask.g などに依存できます。 Sending Signals および request_tearing_down シグナルで説明されている制限に注意してください。
デコレータベースのシグナルサブスクリプション
Blinker 1.1では、新しいconnect_via()
デコレータを使用して、シグナルを簡単にサブスクライブすることもできます。
from flask import template_rendered
@template_rendered.connect_via(app)
def when_template_rendered(sender, template, context, **extra):
print 'Template %s is rendered with %s' % (template.name, context)