Spec-Zone.ru › Flask 1.1

Сигналы

Журнал изменений

Новое в версии 0.6.

Начиная с Flask 0.6, интегрирована поддержка сигналов в Flask. Эта поддержка обеспечивается отличной библиотекой blinker и будет корректно работать даже если она недоступна.

Что такое сигналы? Сигналы помогают вам декомпозировать приложения, отправляя уведомления, когда действия происходят в других частях основного фреймворка или других расширениях Flask. Короче говоря, сигналы позволяют определённым отправителям уведомлять подписчиков о том, что произошло что-то важное.

Flask поставляется с несколькими сигналами, а другие расширения могут предоставлять больше. Также имейте в виду, что сигналы предназначены для уведомления подписчиков и не должны поощрять подписчиков изменять данные. Вы заметите, что существуют сигналы, которые, похоже, выполняют те же действия, что и некоторые встроенные декораторы (например, request_started очень похож на before_request()). Однако, существуют различия в их работе. Обработчик ядра before_request(), например, выполняется в определённом порядке и может прервать запрос, вернув ответ. В отличие от этого, все обработчики сигналов выполняются в неопределённом порядке и не изменяют никакие данные.

Большое преимущество сигналов перед обработчиками заключается в том, что к ним можно безопасно подписываться на очень короткий промежуток времени. Такие временные подписки полезны, например, для тестирования модулей. Допустим, вы хотите узнать, какие шаблоны были отрисованы в рамках запроса: сигналы позволяют вам сделать именно это.

Подписка на сигналы

Чтобы подписаться на сигнал, можно использовать метод connect() сигнала. Первый аргумент — функция, которая должна быть вызвана при отправке сигнала, второй (необязательный) аргумент задаёт отправителя. Чтобы отписаться от сигнала, можно использовать метод 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

Убедитесь, что вы подписываетесь с дополнительным **extra аргументом, чтобы ваши вызовы не завершались ошибкой, если Flask добавит новые аргументы в сигналы.

Весь рендеринг шаблонов в коде, выпущенном приложением app в теле with блока, теперь будет записан в переменной 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]

Изменения API Blinker

Метод connected_to() появился в Blinker с версией 1.1.

Создание сигналов

Если вы хотите использовать сигналы в своём приложении, вы можете напрямую использовать библиотеку blinker. Наиболее распространённый случай — именованные сигналы в пользовательском Namespace.. Это то, что рекомендуется в большинстве случаев:

from blinker import Namespace
my_signals = Namespace()

Теперь вы можете создавать новые сигналы таким образом:

model_saved = my_signals.signal('model-saved')

Имя сигнала здесь делает его уникальным, а также упрощает отладку. Вы можете получить имя сигнала с помощью атрибута name.

Для разработчиков расширений

Если вы пишете расширение Flask и хотите обеспечить плавную работу при отсутствии blinker, вы можете сделать это, используя класс 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 и другие, по мере необходимости. Обратите внимание на ограничения, описанные в Отправка сигналов и сигнале 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)

Основные сигналы

Посмотрите на Сигналы для получения списка всех встроенных сигналов.

© 2007–2020 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/1.0.x/signals/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API