Flask переменная конфигурации, которая поступает из env var, загружается с помощью load_dotenv и извлекается с помощью os.environ.get, иногда None - PullRequest
1 голос
/ 08 мая 2020

Я испытываю очень странное поведение, в котором я не могу разобраться, и это сводит меня с ума.

Я запускаю приложение flask локально, используя цель make. Команда, которую он запускает, следующая:

export FLASK_APP=foodbank_southlondon.launch:main FBSL_ENVIRONMENT=dev FLASK_ENV=development && \
    . $(_VENV_ACTIVATE) && \
    flask run

Вот содержимое launch.py:

import logging
import os

import dotenv

from foodbank_southlondon import api, app, bff, config, oauth
from foodbank_southlondon.api import events, lists, requests
import foodbank_southlondon.views  # noqa: F401
import foodbank_southlondon.errors  # noqa: F401
import foodbank_southlondon.api.errors  # noqa: F401
import foodbank_southlondon.api.events.views  # noqa: F401
import foodbank_southlondon.api.lists.views  # noqa: F401
import foodbank_southlondon.api.requests.views  # noqa: F401
import foodbank_southlondon.bff.views  # noqa: F401


# ENVIRONMENT VARIABLES
_FBSL_ENVIRONMENT_ENV_VAR = "FBSL_ENVIRONMENT"


def main():
    environment = os.environ.get(_FBSL_ENVIRONMENT_ENV_VAR)
    app.logger.setLevel(logging.INFO if environment == "prod" else logging.DEBUG)
    env_file_path = os.path.join(app.root_path, "..", f"{environment}.env")
    app.logger.info(f"Loading .env file, {env_file_path}...")
    dotenv.load_dotenv(env_file_path)
    app.logger.info(f"Loading environment, {environment} ...")
    app.config.from_object(config.CONFIGURATIONS[environment])
    app.logger.info(f"Initialising APIs, OAuth, attaching namespaces and registering blueprints  ...")
    oauth.register(name="google", server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
                   client_kwargs={"scope": "openid email profile"})
    oauth.init_app(app)
    api.rest.init_app(api.blueprint)
    api.rest.add_namespace(events.namespace)
    api.rest.add_namespace(lists.namespace)
    api.rest.add_namespace(requests.namespace)
    app.register_blueprint(api.blueprint, url_prefix="/api")
    bff.rest.init_app(bff.blueprint)
    app.register_blueprint(bff.blueprint, url_prefix="/bff")
    return app

Вот сокращенное содержимое config.py, важно отметить в том, что мой класс конфигурации dev наследуется от базового класса, а базовый класс - единственное место, которое определяет GOOGLE_CLIENT_SECRET и происходит от os.environ.get:

import os


class _Config(object):
    ...
    GOOGLE_CLIENT_SECRET = os.environ.get("FBSL_CLIENT_SECRET")
    ...


class DevelopmentConfig(_Config):
    ... # i don't set GOOGLE_CLIENT_SECRET here


class ProductionConfig(_Config):
    ...

CONFIGURATIONS = {
    "dev": DevelopmentConfig,
    "prod": ProductionConfig
}

Внутри dev.env:

FBSL_CLIENT_SECRET = secret
FBSL_SA_KEY = secret

Дополнительный контекст

Мой бэкэнд обслуживает реакцию SPA как набор файлов c stati. Когда пользователь попадает на мой веб-сайт, например, маршрут localhost: 5000 / my flask немедленно перенаправляет его на вход в Google (используя authlib). Google отправляет их обратно в мой обратный вызов / auth, и если у них есть разрешение на мой сайт, я затем перенаправляю их обратно в /, но на этот раз я обслуживаю их файлы stati c, а не перенаправляю их для входа в систему. Authlib требует, чтобы переменная конфигурации приложения GOOGLE_CLIENT_SECRET была настроена, чтобы иметь возможность авторизовать их с помощью Google.

ПРОБЛЕМА

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

export FLASK_APP=foodbank_southlondon.launch:main FBSL_ENVIRONMENT=dev FLASK_ENV=development && \
. .venv/bin/activate && \
flask run
 * Serving Flask app "foodbank_southlondon.launch:main" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 277-037-026
[2020-05-08 09:21:50,778] INFO in launch: Loading .env file, /home/ac/Projects/Home/foodbank-southlondon/backend/foodbank_southlondon/../dev.env...
[2020-05-08 09:21:50,779] INFO in launch: Loading environment, dev ...
[2020-05-08 09:21:50,779] INFO in launch: Initialising APIs, OAuth, attaching namespaces and registering blueprints  ...
[2020-05-08 09:21:50,919] INFO in launch: Loading .env file, /home/ac/Projects/Home/foodbank-southlondon/backend/foodbank_southlondon/../dev.env...
[2020-05-08 09:21:50,919] INFO in launch: Loading environment, dev ...
[2020-05-08 09:21:50,920] INFO in launch: Initialising APIs, OAuth, attaching namespaces and registering blueprints  ...

Я могу посетить localhost: 5000, и меня перенаправляют на вход, я вхожу в систему и вижу веб-сайт.

Если Затем я останавливаю свой веб-сервер и запускаю его снова в ТОЧНО таким же образом, я вижу тот же вывод терминала EXACT . Если я затем перейду на localhost: 5000, я увижу эту ошибку вместе с ее трассировкой в ​​отладчике:

authlib.integrations.base_client.errors.OAuthError
authlib.integrations.base_client.errors.OAuthError: invalid_request: client_secret is missing.

Если я нажму на отладчик, открою интерактивную оболочку и наберу:

from foodbank_southlondon import app
print(app.config)

Я вижу все переменные конфигурации, и ВСЕ установлено, как ожидалось, кроме двух переменных конфигурации, которые я загружаю с помощью os.environ.get, они обе равны Нет.

Чтобы я мог исправить эта безумная проблема, мне нужно что-то сделать с config.py, чтобы "изменить" его. Например, если я напишу print(GOOGLE_CLIENT_SECRET) прямо под тем местом, где он определен, я не только увижу этот вывод, который не является значением None, но и сам факт моего введения оператора печати исправит его! Но затем, если я остановлю сервер и запустю его снова, у меня снова возникнет та же проблема, и мне нужно будет каким-то образом изменить файл.

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

Я предполагаю, что выиграл ' Я сталкиваюсь с этой проблемой в производственной среде, но это делает разработку настолько болезненной, и это просто безумная проблема. Это как если бы у меня возникла какая-то странная проблема с кешированием переменных среды между остановкой и запуском сервера. Я понятия не имею. Пожалуйста, помогите!

1 Ответ

0 голосов
/ 08 мая 2020

Вздох, это было связано с порядком выполнения ... импортом модуля конфигурации до установки переменной среды и, таким образом, os.environ.get оценивается как None. Сделали переменные класса, которые используют os.environ.get, в свойствах экземпляра (@property), и я создаю экземпляр класса в моем конфигурационном dict, чтобы обойти это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...