Армин Роначер. Обработка конфигурации Flask, 2012

Перевод статьи: Configuration Handling

Автор: Армин Роначер (Armin Ronacher)

Новинка версии 0.3

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

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

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

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

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

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

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

app.debug = True

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

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

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

Сам 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 (что защищает переменную от доступа со стороны скриптов, работающих внутри браузера - прим. перев.). По умолчанию - истина.
SESSION_COOKIE_SECURE Указывает, должен ли у переменной браузера устанавливаться флаг secure (что позволяет передавать переменную только по защищённому протоколу HTTPS - прим. перев.). По умолчанию - ложь.
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 Если истина, Flask не выполняет обработчиков ошибок исключений HTTP, но вместо этого трактует исключение как любое другое и передаёт исключение выше. Этот параметр полезен для отладки сложных случаев, когда нужно найти, где именно произошло исключение HTTP.
TRAP_BAD_REQUEST_ERRORS Внутренние структуры данных Werkzeug, работающие с данными запроса порождают ошибки с особым ключом, также являющимся исключением запроса. Также, многие операции могут неявно приводить к исключениям BadRequest в случае ошибок целостности. Поскольку для отладки важно знать, где именно произошла ошибка, этот флаг может использоваться для отладки в подобных случаях. Если этот параметр истинен, произойдёт обычная выдача результата трассировки.
PREFERRED_URL_SCHEME Схема, которую нужно использовать для генерации URL'ов, если она не указана явно. По умолчанию - http.

3. Подробнее о 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

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

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

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

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.

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

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

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

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

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

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')

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

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

Новинка версии 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 не указан, по умолчанию используются следующие места:

Как только стало возможным загружать объект конфигурации из файлов с относительными именами, мы добавили возможность загружать файлы с именами относительно каталога экземпляра. При помощи переключателя 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()

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

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

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

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

Этот и другие переводы можно найти на сайте проекта перевода документации по Flask. Автор проекта - Виталий Кузьмин aka ferm32.

Написать автору перевода