Обработка конфигурации Flask

Добавлено в версии 0.3.

Приложения требуют настройки. Здесь описаны различные настройки, которые можно менять в зависимости от окружения, в котором работает приложение: переключение режима отладки, настройки секретного ключа и т.п.

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

Вне зависимости от того, каким образом загружена конфигурация, существует объект конфигурации, содержащий загруженные параметры: атрибут config объекта Flask. Это место, где Flask содержит свои настройки, а также то место, куда расширения могут поместить собственные настройки. Но здесь можно размещать и конфигурацию вашего приложения.

Основы конфигурации

config на самом деле является подклассом словаря и может изменяться точно так же, как и любой словарь:

app = Flask(__name__)
app.config['DEBUG'] = True

Некоторые параметры конфигурации передаются в объект Flask, которые тоже можно читать и писать:

app.debug = True

Для обновления нескольких ключей за раз можно воспользоваться методом словаря dict.update():

app.config.update(
    DEBUG=True,
    SECRET_KEY='...'
)

Встроенные параметры конфигурации

Сам Flask использует следующие параметры конфигурации:

DEBUG Включить/выключить режим отладки.
TESTING Включить/выключить режим тестирования.
PROPAGATE_EXCEPTIONS Явное включение или отключение исключений. Если не задано или явным образом задано значение None, то подразумевается истина, если истиной является TESTING или DEBUG.
PRESERVE_CONTEXT_ON_EXCEPTION По умолчанию в режиме отладки при возникновении исключения контекст запроса не извлекается из стека, позволяя отладчику анализировать данные. Такое поведение можно отключить с помощью этого параметра. Также можно воспользоваться этим параметром для его принудительного включения, если это может помочь в отладке приложений в эксплуатации (однако, это не рекомендуется).
SECRET_KEY Секретный ключ.
SESSION_COOKIE_NAME Имя переменной (cookie) браузера для хранения сеанса.
SESSION_COOKIE_DOMAIN Домен переменной браузера, используемой для хранения сеанса. Если не задан, переменная браузера будет действительной для всех поддоменов SERVER_NAME.
SESSION_COOKIE_PATH Путь к переменной браузера, используемой для хранения сеанса. Если не задан, переменная браузера будет действительной для всех APPLICATION_ROOT, если не задано значение '/'.
SESSION_COOKIE_HTTPONLY Указывает, должен ли у переменной браузера устанавливаться флаг httponly (что защищает переменную от доступа со стороны скриптов, работающих внутри браузера - прим. перев.). По умолчанию - True.
SESSION_COOKIE_SECURE Указывает, должен ли у переменной браузера устанавливаться флаг secure (что позволяет передавать переменную только по защищённому протоколу HTTPS - прим. перев.). По умолчанию - False.
PERMANENT_SESSION_LIFETIME Непрерывное время жизни сеанса, как объект datetime.timedelta. Начиная с Flask 0.8 этот параметр может быть задан в виде целого числа с количеством секунд.
USE_X_SENDFILE Включить/отключить x-sendfile. (При использовании этой возможности представление может вернуть специально сформированный ответ со ссылкой на статический файл. Получив такой ответ от представления, веб-сервер отдаёт клиенту вместо ответа представления сам статический файл, найдя его по ссылке в локальной файловой системе. Это позволяет перенести нагрузку по отдаче больших файлов на веб-сервер, если перед отдачей файла представление должно решить, можно ли отдавать этот файл клиенту и какой именно файл нужно отдать по этой ссылке - прим. перев.)
LOGGER_NAME Имя средства журналирования.
SERVER_NAME Имя и номер порта сервера. Необходимо для поддержки поддоменов (например: 'myapp.dev:5000'). Отметим, что localhost не поддерживает поддомены, поэтому установка параметра в значение “localhost” не поможет. Настройка SERVER_NAME также по умолчанию включает генерацию URL’ов без контекста запроса, но с контекстом приложения.
APPLICATION_ROOT Если приложение не занимает целый домен или поддомен, с помощью этого параметра можно задать путь к настроенному приложению. Значение этого параметра используется в качестве пути к переменной браузера для хранения сеанса. Если используются домены, значением этого параметра должно быть None.
MAX_CONTENT_LENGTH Если задать значение в байтах, Flask будет отклонять входящие запросы, объём содержимого которых больше этого значения, возвращая код статуса 413.
SEND_FILE_MAX_AGE_DEFAULT: По умолчанию задаёт время кэширования файла для использования совместно с send_static_file() (обработчик статических файлов по умолчанию) и send_file(), в секундах. Заменить это значение для каждого файла индивидуально можно с помощью обработчика get_send_file_max_age() Flask или Blueprint. По умолчанию - 43200 (12 часов).
TRAP_HTTP_EXCEPTIONS Если True, Flask не выполняет обработчиков ошибок исключений HTTP, но вместо этого трактует исключение как любое другое и передаёт исключение выше. Этот параметр полезен для отладки сложных случаев, когда нужно найти, где именно произошло исключение HTTP.
TRAP_BAD_REQUEST_ERRORS Внутренние структуры данных Werkzeug, работающие с данными запроса порождают ошибки с особым ключом, также являющимся исключением запроса. Также, многие операции могут неявно приводить к исключениям BadRequest в случае ошибок целостности. Поскольку для отладки важно знать, где именно произошла ошибка, этот флаг может использоваться для отладки в подобных случаях. Если этот параметр истинен (True), произойдёт обычная выдача результата трассировки.
PREFERRED_URL_SCHEME Схема, которую нужно использовать для генерации URL’ов, если она не указана явно. По умолчанию - http.

Подробнее о SERVER_NAME

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

Помните, что не только Flask не может узнать поддомен, ваш веб-браузер тоже не может. Большинство соверменных браузеров не разрешают междоменные переменные браузера, если в имени сервера нет точек. Поэтому если имя сервера 'localhost', вы не сможете задать переменную браузера для 'localhost' и каждого из его поддоменов. В этом случае выберите другое имя сервера, например 'myapplication.local' и добавьте это имя и поддомены, которые вы хотите использовать, в файл hosts или настройте локальный bind.

Добавлено в версии 0.4: LOGGER_NAME

Добавлено в версии 0.5: SERVER_NAME

Добавлено в версии 0.6: MAX_CONTENT_LENGTH

Добавлено в версии 0.7: PROPAGATE_EXCEPTIONS, PRESERVE_CONTEXT_ON_EXCEPTION

Добавлено в версии 0.8: TRAP_BAD_REQUEST_ERRORS, TRAP_HTTP_EXCEPTIONS, APPLICATION_ROOT, SESSION_COOKIE_DOMAIN, SESSION_COOKIE_PATH, SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SECURE

Добавлено в версии 0.9: PREFERRED_URL_SCHEME

Задание конфигурации с помощью файлов

Конфигурация становится более удобной, если разместить её в отдельном файле. Лучше, если он находится за пределами пакета с приложением. Это позволяет создавать пакеты и распространять приложения с помощью различных инструментов обработки пакетов (distribute-deployment) и впоследствии - изменять конфигурацию.

Далее показан обычный пример::

app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')

Сначала грузится конфигурация из модуля yourapplication.default_settings, а затем её значения заменяет содержимое файла, указанного в переменной окружения YOURAPPLICATION_SETTINGS. Эта переменная окружения может быть задана в Linux или OS X при помощи команды export оболочки перед запуском сервера:

$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
$ python run-app.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader...

В системах Windows воспользуйтесь встроенной командой set:

>set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg

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

Вот пример файла конфигурации:

# Пример конфигурации
DEBUG = False
SECRET_KEY = '?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83'

Убедитесь в том, что файл загружается как можно раньше, чтобы расширения могли получить доступ к собственным настройкам при запуске. Существуют другие методы загрузки объекта конфигурации из отдельных файлов. За более полной информацией обратитесь к документации объекта Config.

Лучшие способы задания конфигурации

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

  1. Создайте ваше приложение внутри функции и зарегистрируйте в ней blueprint’ы. Таким образом вы можете создать несколько экземпляров вашего приложения с разными конфигурациями, что значительно упростит модульное тестирование. Вы можете воспользоваться функцией, чтобы передать в неё необходимую конфигурацию.
  2. Не пишите код, которому требуется конфигурация при импорте. Если ограничиться чтением конфигурации по запросу, возможно будет переконфигурировать объект позже.

Режим разработки и режим эксплуатации

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

app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')

Теперь просто создайте отдельный файл config.py, выполните команду export YOURAPPLICATION_SETTINGS=/path/to/config.py и готово. Однако, существуют альтернативные способы. Например, можно воспользоваться импортом и подклассами.

В мире Django распространён следующий способ: сделать явный импорт конфигурации, добавив строку from yourapplication.default_settings import * в начале файла, а затем заменить значения вручную. Можно сделать также, затем взять из переменной окружения вида YOURAPPLICATION_MODE необходимый режим - production, development и т.п., а затем импортировать заранее определённые файлы, основываясь на этом значении.

Другой любопытный способ - воспользоваться классами и наследованием конфигурации:

class Config(object):
    DEBUG = False
    TESTING = False
    DATABASE_URI = 'sqlite://:memory:'

class ProductionConfig(Config):
    DATABASE_URI = 'mysql://user@localhost/foo'

class DevelopmentConfig(Config):
    DEBUG = True

class TestingConfig(Config):
    TESTING = True

Для включения такой конфигурации, вам просто нужно вызвать from_object():

app.config.from_object('configmodule.ProductionConfig')

There are many different ways and it’s up to you how you want to manage your configuration files. However here a list of good recommendations:

  • keep a default configuration in version control. Either populate the config with this default configuration or import it in your own configuration files before overriding values.
  • use an environment variable to switch between the configurations. This can be done from outside the Python interpreter and makes development and deployment much easier because you can quickly and easily switch between different configs without having to touch the code at all. If you are working often on different projects you can even create your own script for sourcing that activates a virtualenv and exports the development configuration for you.
  • Use a tool like fabric in production to push code and configurations separately to the production server(s). For some details about how to do that, head over to the fabric-deployment pattern.

Есть много разных способов, которые можно выбрать для управления файлами конфигурации. Вот список хороших советов:

  • Храните файл конфигурации по умолчанию в системе управления версиями. Заполните объект конфигурации значениями по умолчанию или импортируйте его в ваших собственных файлах конфигурации перед тем, как заменить значения.
  • Воспользуйтесь переменной окружения для переключения между конфигурациями. Это можно сделать вне интерпретатора Python и это позволит упростить разработку и развёртывание, потому что вы можете быстро и легко переключаться между разными конфигурациями, совсем не прикасаясь к коду. Если вы часто работаете над разными проектами, можно создать собственный скрипт, который будет определять текущий каталог проекта, активировать virtualenv и экспортировать конфигурацию режима разработки.
  • Используйте инструмент fabric для внесения изменений на сервер эксплуатации и раздельных конфигураций на серверы эксплуатации. За более подробным описанием того, как это сделать, обратитесь к главе fabric-deployment.

Каталоги экземпляров

Добавлено в версии 0.8.

Во Flask 0.8 появились каталоги экземпляров. Flask долгое время позволял ссылаться на пути относительно каталога приложения (с помощью Flask.root_path). Поэтому многие разработчики хранили конфигурацию рядом с приложением. К несчастью, это возможно только если приложение не находится внутри пакета, так как в таком случае root_path указывает внутрь пакета.

Во Flask 0.8 был введён новый атрибут: Flask.instance_path. Он вводит новое понятие, которое называется “каталогом экземпляра”. Каталог экземпляра задуман как каталог, не управляемый системой контроля версий и относящийся к развёрнутому приложению. Это подходящее место для того, чтобы поместить в него файлы, изменяемые в процессе работы или файлы конфигурации.

Можно явным образом указать путь к каталогу экземпляра при создании приложения Flask или можно разрешить Flask’у самому выбрать каталог экземпляра. Чтобы задать его явным образом, воспользуйтесь параметром instance_path:

app = Flask(__name__, instance_path='/path/to/instance/folder')

Помните, что если этот путь указан, он должен быть абсолютным.

Если параметр instance_path не указан, по умолчанию используются следующие места:

  • Не установленный модуль:

    /myapp.py
    /instance
    
  • Не установленный пакет:

    /myapp
        /__init__.py
    /instance
    
  • Установленный модуль или пакет:

    $PREFIX/lib/python2.X/site-packages/myapp
    $PREFIX/var/myapp-instance
    

    $PREFIX - это префикс, с которым установлен Python. Это может быть каталог /usr или путь в каталоге с виртуальным окружением, созданном virtualenv. Можно вывести на экран значение sys.prefix, чтобы увидеть его действительное значение.

Как только стало возможным загружать объект конфигурации из файлов с относительными именами, мы добавили возможность загружать файлы с именами относительно каталога экземпляра. При помощи переключателя instance_relative_config в конструкторе приложения можно указать, должны ли интерпретироваться относительные пути файлов “относительно корня приложения” (по умолчанию) или “относительно каталога экземпляра”:

app = Flask(__name__, instance_relative_config=True)

Ниже представлен полный пример настройки Flask для предварительной загрузки конфигурации из модуля и последующей замены параметров значениями из файла в каталоге конфигурации, если он существует:

app = Flask(__name__, instance_relative_config=True)
app.config.from_object('yourapplication.default_settings')
app.config.from_pyfile('application.cfg', silent=True)

Путь к каталогу экземпляра может быть найден при помощи Flask.instance_path. Flask также предоставляет более короткий способ открытия файлов из каталога экземпляра при помощи Flask.open_instance_resource().

Вот пример для обоих способов:

filename = os.path.join(app.instance_path, 'application.cfg')
with open(filename) as f:
    config = f.read()

# или при помощи open_instance_resource:
with app.open_instance_resource('application.cfg') as f:
    config = f.read()

Примечания переводчика

В качестве перевода для термина cookie было использовано понятие «переменных бразуера».

Информация о флагах httponly и secure взята из статьи HTTP cookie.

Информация о x-sendfile взята из статьи Передача файлов с помощью XSendfile с помощью NGINX.