Spec-Zone.ru › Flask

Сигналы

Сигналы — это лёгкий способ уведомления подписчиков о определённых событиях во время жизненного цикла приложения и каждого запроса. При наступлении события он излучает сигнал, вызывающий каждого подписчика.

Сигналы реализованы с помощью библиотеки Blinker. Для получения подробной информации см. её документацию. Flask предоставляет некоторые встроенные сигналы. Расширения могут предоставлять свои собственные.

Многие сигналы отражают обработчики Flask, основанные на декораторах, с аналогичными именами. Например, сигнал request_started аналогичен декоратору before_request(). Преимущество сигналов перед обработчиками заключается в том, что к ним можно временно подписываться, и они не могут напрямую влиять на приложение. Это полезно для тестирования, метрик, аудита и т. д. Например, если вы хотите узнать, какие шаблоны были рендерены в каких частях каких запросов, существует сигнал, который уведомит вас об этой информации.

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

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

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

Чтобы подписаться на сигнал, вы можете использовать метод connect() сигнала. Первый аргумент — функция, которая должна быть вызвана при излучении сигнала, необязательный второй аргумент указывает отправителя. Чтобы отписаться от сигнала, можно использовать метод disconnect().

Для всех основных сигналов Flask отправителем является приложение, которое отправило сигнал. Когда вы подписываетесь на сигнал, обязательно укажите отправителя, если вы не хотите получать сигналы от всех приложений. Это особенно важно, если вы разрабатываете расширение.

Например, вот контекстный менеджер-помощник, который можно использовать в unit-тесте, чтобы определить, какие шаблоны были рендерены и какие переменные были переданы в шаблон:

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]

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

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

from blinker import Namespace
my_signals = Namespace()

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

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

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

Отправка сигналов

Если вы хотите излучить сигнал, вы можете сделать это, вызвав метод 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.

Подписки на сигналы с помощью декораторов

Вы также можете легко подписаться на сигналы, используя декоратор connect_via():

from flask import template_rendered

@template_rendered.connect_via(app)
def when_template_rendered(sender, template, context, **extra):
    print(f'Template {template.name} is rendered with {context}')

© 2010 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/stable/signals/

Spec-Zone.ru

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