Spec-Zone.ru › Flask 2.3

Сигналы

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

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

Многие сигналы отражают обработчики Flask, основанные на декораторах, с аналогичными именами. Например, сигнал request_started похож на декоратор 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]

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

Если вы хотите использовать сигналы в своём приложении, вы можете использовать библиотеку 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}')

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

Spec-Zone.ru

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