Spec-Zone.ru › Flask 3.0

Сигналы

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

Сигналы реализованы с помощью библиотеки 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}')

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

Spec-Zone.ru

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