• en
  • Language: ru
  • Documentation version: 1.1.x

T

T

T

Назначение контекста

Запуск интерактивной оболочки Python в контексте данного приложения Flask. Приложение заполнит пространство имен по умолчанию этой оболочки в соответствии со своей конфигурацией.

Flask автоматически проталкивает контекст запроса при обработке запроса. Функции представления, обработчики ошибок и другие функции, выполняемые во время запроса, будут иметь доступ к прокси request, который указывает на объект запроса для текущего запроса.

Срок службы контекста

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

Контекст уникален для каждого потока (или другого типа рабочего). request нельзя передавать другому потоку, у другого потока будет другой стек контекста и он не будет знать о запросе, на который указывал родительский поток.

Контекстные локали реализованы в Werkzeug. Смотрите Context Locals для получения дополнительной информации о том, как это работает внутри.

Ручное перемещение контекста

Если вы попытаетесь получить доступ к request или к чему-либо, что его использует, вне контекста запроса, вы получите это сообщение об ошибке:

RuntimeError: Working outside of request context.

This typically means that you attempted to use functionality that
needed an active HTTP request. Consult the documentation on testing
for information about how to avoid this problem.

Обычно это происходит только при тестировании кода, который ожидает активного запроса. Один из вариантов - использовать test client для имитации полного запроса. Или вы можете использовать test_request_context() в блоке with, и все, что выполняется в блоке, будет иметь доступ к request, заполненному вашими тестовыми данными.

def generate_report(year):
    format = request.args.get('format')
    ...

with app.test_request_context(
        '/make_report/2017', data={'format': 'short'}):
    generate_report()

Если вы видите эту ошибку в другом месте вашего кода, не связанном с тестированием, это, скорее всего, указывает на то, что вам следует перенести этот код в функцию представления.

Информацию о том, как использовать контекст запроса из интерактивной оболочки Python, смотрите в разделе Работа с оболочкой.

Как работает контекст

Метод Flask.wsgi_app() вызывается для обработки каждого запроса. Он управляет контекстами во время запроса. Внутри контексты запроса и приложения работают как стеки, _request_ctx_stack и _app_ctx_stack. Когда контексты сдвигаются в стек, прокси, которые зависят от них, становятся доступными и указывают на информацию из верхнего контекста в стеке.

Когда начинается запрос, создается и проталкивается RequestContext, который создает и проталкивает сначала AppContext, если контекст для этого приложения еще не является верхним контекстом. Пока эти контексты проталкиваются, прокси current_app, g, request и session доступны исходному потоку, обрабатывающему запрос.

Поскольку контексты являются стеками, другие контексты могут быть подтолкнуты для изменения прокси во время запроса. Хотя это не совсем обычный шаблон, он может использоваться в продвинутых приложениях, например, для внутренних перенаправлений или для объединения различных приложений в цепочку.

После диспетчеризации запроса и генерации и отправки ответа контекст запроса разворачивается, а затем разворачивается контекст приложения. Непосредственно перед разворачиванием контекста выполняются функции teardown_request() и teardown_appcontext(). Они выполняются, даже если во время диспетчеризации произошло необработанное исключение.

Обратные вызовы и ошибки

Flask отправляет запрос на нескольких этапах, которые могут повлиять на запрос, ответ и обработку ошибок. Контексты активны на всех этих этапах.

В Blueprint можно добавить обработчики этих событий, специфичные для блюпринта. Обработчики для блюпринта будут выполняться, если блюпринт владеет маршрутом, соответствующим запросу.

  1. Перед каждым запросом вызываются функции before_request(). Если одна из этих функций возвращает значение, остальные функции пропускаются. Возвращаемое значение рассматривается как ответ, и функция представления не вызывается.

  2. Если функции before_request() не вернули ответ, вызывается функция представления для найденного маршрута и возвращает ответ.

  3. Возвращаемое значение представления преобразуется в реальный объект ответа и передается в функции after_request(). Каждая функция возвращает измененный или новый объект ответа.

  4. После возврата ответа контексты разворачиваются, что вызывает функции teardown_request() и teardown_appcontext(). Эти функции вызываются даже в том случае, если в любой момент времени было вызвано необработанное исключение.

Если исключение возникает до функций разрыва, Flask пытается сопоставить его с функцией errorhandler(), которая обработает исключение и вернет ответ. Если обработчик ошибки не найден или сам обработчик вызывает исключение, Flask возвращает общий ответ 500 Internal Server Error. Функции разрыва по-прежнему вызываются, и им передается объект исключения.

Если включен режим отладки, необработанные исключения не преобразуются в ответ 500 и вместо этого передаются на сервер WSGI. Это позволяет серверу разработки представить интерактивному отладчику обратную трассировку.

Обратные вызовы при разрушении

Функции обратного вызова при разрыве не зависят от диспетчеризации запроса и вызываются контекстами при их разрыве. Эти функции вызываются даже в случае необработанного исключения во время диспетчеризации, а также для контекстов, выталкиваемых вручную. Это означает, что нет никакой гарантии, что другие части диспетчеризации запроса были запущены первыми. Обязательно напишите эти функции таким образом, чтобы они не зависели от других обратных вызовов и не давали сбоев.

Во время тестирования может быть полезно отложить выскакивание контекстов после завершения запроса, чтобы к их данным можно было получить доступ в тестовой функции. Используйте test_client() как блок with для сохранения контекстов до выхода блока with.

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def hello():
    print('during view')
    return 'Hello, World!'

@app.teardown_request
def show_teardown(exception):
    print('after with block')

with app.test_request_context():
    print('during with block')

# teardown functions are called after the context with block exits

with app.test_client() as client:
    client.get('/')
    # the contexts are not popped even though the request ended
    print(request.path)

# the contexts are popped and teardown functions are called after
# the client with block exits

Сигналы

Если signals_available истинно, посылаются следующие сигналы:

  1. request_started отправляется до вызова функций before_request().

  2. request_finished отправляется после вызова функций after_request().

  3. got_request_exception отправляется, когда исключение начинает обрабатываться, но до того, как будет найдено или вызвано errorhandler().

  4. request_tearing_down отправляется после вызова функций teardown_request().

Сохранение контекста при ошибке

По завершении запроса контекст запроса разворачивается, а все связанные с ним данные уничтожаются. Если во время разработки возникла ошибка, полезно отложить уничтожение данных для отладки.

Когда сервер разработки работает в режиме разработки (переменная окружения FLASK_ENV установлена в значение 'development'), ошибка и данные будут сохранены и показаны в интерактивном отладчике.

Это поведение можно контролировать с помощью конфигурации PRESERVE_CONTEXT_ON_EXCEPTION. Как описано выше, в среде разработки по умолчанию установлено значение True.

Не включайте PRESERVE_CONTEXT_ON_EXCEPTION в производстве, так как это приведет к утечке памяти при исключениях.

Примечания о доверенностях

Некоторые объекты, предоставляемые Flask, являются прокси для других объектов. Доступ к прокси осуществляется одинаково для каждого рабочего потока, но указывает на уникальный объект, привязанный к каждому рабочему за кулисами, как описано на этой странице.

В большинстве случаев вам не нужно заботиться об этом, но есть некоторые исключения, когда полезно знать, что этот объект на самом деле является прокси:

  • Прокси-объекты не могут подделывать свои типы под реальные типы объектов. Если вы хотите выполнить проверку экземпляра, вы должны сделать это на проксируемом объекте.

  • Ссылка на проксируемый объект необходима в некоторых ситуациях, например, для отправки Сигналы или передачи данных фоновому потоку.

Если вам нужно получить доступ к базовому объекту, который проксируется, используйте метод _get_current_object():

app = current_app._get_current_object()
my_signal.send(app)