Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable plugin list to be an app creation param, useful for plugin tests #220

Merged
merged 7 commits into from Oct 25, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 11 additions & 3 deletions flexmeasures/app.py
@@ -1,7 +1,7 @@
# flake8: noqa: E402
import os
import time
from typing import Optional
from typing import Optional, List

from flask import Flask, g, request
from flask.cli import load_dotenv
Expand All @@ -14,14 +14,20 @@
from rq import Queue


def create(env: Optional[str] = None, path_to_config: Optional[str] = None) -> Flask:
def create(
env: Optional[str] = None,
path_to_config: Optional[str] = None,
plugins: Optional[List[str]] = None,
) -> Flask:
"""
Create a Flask app and configure it.

Set the environment by setting FLASK_ENV as environment variable (also possible in .env).
Or, overwrite any FLASK_ENV setting by passing an env in directly (useful for testing for instance).

A path to a config file can be passed in (otherwise a config file will be searched in the home or instance directories)
A path to a config file can be passed in (otherwise a config file will be searched in the home or instance directories).

Also, a list of plugins can be set. Usually this works as a config setting, but this is useful for automated testing.
"""

from flexmeasures.utils import config_defaults
Expand All @@ -47,6 +53,8 @@ def create(env: Optional[str] = None, path_to_config: Optional[str] = None) -> F
# App configuration

read_config(app, custom_path_to_config=path_to_config)
if plugins:
app.config["FLEXMEASURES_PLUGINS"] += plugins
add_basic_error_handlers(app)
if not app.env in ("development", "documentation") and not app.testing:
init_sentry(app)
Expand Down
49 changes: 35 additions & 14 deletions flexmeasures/utils/config_utils.py
Expand Up @@ -48,35 +48,42 @@ def configure_logging():
loggingDictConfig(flexmeasures_logging_config)


def read_config(app: Flask, custom_path_to_config: Optional[str]):
"""Read configuration from various expected sources, complain if not setup correctly. """

if app.env not in (
def check_app_env(env: Optional[str]):
if env not in (
"documentation",
"development",
"testing",
"staging",
"production",
):
print(
'Flask(flexmeasures) environment needs to be either "documentation", "development", "testing", "staging" or "production".'
f'Flask(flexmeasures) environment needs to be either "documentation", "development", "testing", "staging" or "production". It currently is "{env}".'
)
sys.exit(2)


def read_config(app: Flask, custom_path_to_config: Optional[str]):
"""Read configuration from various expected sources, complain if not setup correctly. """

check_app_env(app.env)

# First, load default config settings
app.config.from_object(
"flexmeasures.utils.config_defaults.%sConfig" % camelize(app.env)
)

# Now, potentially overwrite those from config file
# Now, potentially overwrite those from config file or environment variables

# These two locations are possible (besides the custom path)
path_to_config_home = str(Path.home().joinpath(".flexmeasures.cfg"))
path_to_config_instance = os.path.join(app.instance_path, "flexmeasures.cfg")
if not app.testing: # testing runs completely on defaults
# If no custom path is given, this will try home dir first, then instance dir

# Don't overwrite when testing (that should run completely on defaults)
if not app.testing:
used_path_to_config = read_custom_config(
app, custom_path_to_config, path_to_config_home, path_to_config_instance
)
read_required_env_vars(app)

# Check for missing values.
# Documentation runs fine without them.
Expand Down Expand Up @@ -107,9 +114,16 @@ def read_config(app: Flask, custom_path_to_config: Optional[str]):


def read_custom_config(
app, suggested_path_to_config, path_to_config_home, path_to_config_instance
app: Flask, suggested_path_to_config, path_to_config_home, path_to_config_instance
) -> str:
""" read in a custom config file or env vars. Return the path to the config file."""
"""
Read in a custom config file and env vars.
For the config, there are two fallback options, tried in a specific order:
If no custom path is suggested, we'll try the path in the home dir first,
then in the instance dir.

Return the path to the config file.
"""
if suggested_path_to_config is not None and not os.path.exists(
suggested_path_to_config
):
Expand All @@ -121,16 +135,23 @@ def read_custom_config(
path_to_config = path_to_config_instance
else:
path_to_config = suggested_path_to_config
app.logger.info(f"Loading config from {path_to_config} ...")
try:
app.config.from_pyfile(path_to_config)
except FileNotFoundError:
pass
# Finally, all required variables can be set as env var:
for req_var in required:
app.config[req_var] = os.getenv(req_var, app.config.get(req_var, None))
app.logger.warning(
f"File {path_to_config} could not be found! (work dir is {os.getcwd()})"
)
app.logger.warning(f"File exists: {os.path.exists(path_to_config)}")
return path_to_config


def read_required_env_vars(app: Flask):
""" All required variables and the plugins can be set as env var"""
for var in required:
app.config[var] = os.getenv(var, app.config.get(var, None))


def are_required_settings_complete(app) -> bool:
"""
Check if all settings we expect are not None. Return False if they are not.
Expand Down
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -10,6 +10,7 @@ def load_requirements(use_case):
if not req.strip() == ""
and not req.strip().startswith("#")
and not req.strip().startswith("-c")
and not req.strip().startswith("--find-links")
]
return reqs

Expand Down