Reputation: 3088
When handling application config in Flask, it's usually recommended to use the config
dictionary available on the flask.Flask
object, e.g. flask.current_app.config["TESTING"]
.
One recommendation for populating this is to use classes and inheritance to separate config per environment:
class Config:
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite:///:memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
# Example usage:
app = flask.Flask(__name__)
app.config.from_object(ProductionConfig)
db_uri = app.config["DATABASE_URI"]
The disadvantage to this is that despite having defined our keys using class attributes, we access them using string keys, which end up being referenced around the code. If I mistype a key, I won't get a warning in my IDE and will get a KeyError
at runtime. I also don't get type inference for the objects in the config.
It would be nice to be able to populate the Flask config in a similar way, but have better access to these values, like I might access ordinary attributes on an object.
I've thought of a few approaches, but none of them quite seem ideal:
get_database_uri()
- as long as this is the only way the config is accessed, the available keys and the types of their values are known. Requires writing a function for every new config key.Config
object within the Flask config e.g. app.config["config"] = ProductionConfig()
. With a type hint or wrapper function, we get typing, but it's odd to store a config object inside a config, and we lose the automatic setting of Flack-specific values such as TESTING
.app.config
, and use any other solution.Has anyone found any cleaner solutions which also integrate with Flask's config?
Upvotes: 4
Views: 1361
Reputation: 127220
The class used for app.config
can be configured by overriding Flask.config_class
. Write a subclass of flask.config.Config
that defines __getattr__
to look up keys, and assign it to the config_class
attribute.
from flask.app import Flask
from flask.config import Config
class AttrConfig(Config):
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __dir__(self):
out = set(self.keys())
out.update(super().__dir__())
return sorted(out)
class CustomFlask(Flask):
config_class = AttrConfig
app = CustomFlask(__name__)
There's no reliable way to make IDEs understand these attributes though, given that they are loaded dynamically. Implementing __dir__
will make them show up in tab completion from the Python or IPython shell, but IDEs generally don't execute module code to provide introspection, so this won't affect them.
Upvotes: 5