A way to specify required environment variables
It would be nice to have a way to specify which environment variables are required to be set, so the flask app will quit early when there are environment variables missing.
@okke-formsma thanks for the suggestion!
Do you have an idea on how this will work? Is there a special way you define certain values that are required?
I think this is a good idea, I am trying to think about how it plays with #2, since that says "if you didn't predefine a setting, then it won't get loaded".
@brettlangdon, I was thinking along the lines of setting the required fields to a specific value, something like
from flask_env import RequiredEnvironmentValue
class StagingConfig(metaclass=MetaFlaskEnv):
ENV_PREFIX = 'FLASK_'
SECRET_KEY = RequiredEnvironmentValue
ELASTICSEARCH_HOST = RequiredEnvironmentValue
SQLALCHEMY_DATABASE_URI = RequiredEnvironmentValue
Alright, I just had a idea... feel free to tell me I am crazy or complicating things. I was trying to think about other use cases, like mapping env variables to different names, or explicitly casting values to a specific type, or making certain ones required, etc etc. So I came up with the following idea/solution.
from flask_env import MetaFlaskEnv, Loader
class Settings(metaclass=MetaFlaskEnv):
# This value is required, raise exception if it is not provided
SECRET_KEY = Loader.required()
# Load this value from the `DB_URI` env variable, and it is required to be set
SQLALCHEMY_DATABASE_URI = Loader.name('DB_URI').required()
# Load this value from `PORT`, default to `8000`, and explicitly cast the value to an `int`
PORT = Loader.default(8000).cast(int)
# Can still assign defaults like normal
DEBUG = True
Elasticsearch_HOST = 'localhost'
That's nice. I'm not a very big fan of the chained commands. Maybe just use optional parameters instead? Also, I think variables should be implicitly required, unless a default value is provided.
from flask_env import MetaFlaskEnv, EnvLoader
class Settings(metaclass=MetaFlaskEnv):
# This value is required, raise exception if it is not provided
SECRET_KEY = EnvLoader()
# Load this value from the `DB_URI` env variable, and it is required to be set
SQLALCHEMY_DATABASE_URI = EnvLoader(name='DB_URI')
# Load this value from `PORT`, default to `8000`, and explicitly cast the value to an `int`
PORT = EnvLoader(default=8000, cast=int)
# Can still assign defaults like normal
DEBUG = True
Elasticsearch_HOST = 'localhost'
I gave this a stab for myself, feel free to use it in any way.
This is based on the descriptor pattern (https://docs.python.org/2/howto/descriptor.html)
class EnvVar():
def __init__(self, name=None, default=None, cast=None):
""" Load environment name `name`. Use default value `default`. Raises an ValueError if no environment variable
is found and no default is set. Will cast automatically to the type `cast` or to the type of `default` if not
set.
Example:
DevelopmentConfig():
DEBUG = EnvVar('FLASK_DEBUG', default=False)
PROFILE_RATE = EnvVar(cast=float)
SQLALCHEMY_DATABASE_URI = EnvVar('DB')
"""
self.name = name
self.default = default
if cast is None:
self.cast = type(default)
else:
self.cast = cast
self.value = None
def __get__(self, obj, objtype=None):
value = os.getenv(self.name)
if value is None:
if self.default is None:
raise ValueError("Environment variable `{}` was expected to be set.".format(self.name))
else:
return self.default
if self.cast is None:
return value
return self.cast(value)
def __set__(self, obj, value):
raise ValueError("This variable can not be set but will be initiated from environment values.")