From 182ee31503ea8a9eefd0d7063bd7fbdde991e13b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 27 May 2010 17:42:01 +0200 Subject: [PATCH] Added chapter about config --- docs/api.rst | 1 + docs/config.rst | 120 +++++++++++++++++++++++++++++++++++ docs/contents.rst.inc | 1 + docs/patterns/distribute.rst | 2 + flask.py | 67 ++++++++++++++++--- 5 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 docs/config.rst diff --git a/docs/api.rst b/docs/api.rst index 38c2321d..f6920199 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -289,3 +289,4 @@ Configuration ------------- .. autoclass:: Config + :members: diff --git a/docs/config.rst b/docs/config.rst new file mode 100644 index 00000000..24bc8b04 --- /dev/null +++ b/docs/config.rst @@ -0,0 +1,120 @@ +Configuration Handling +====================== + +.. versionadded:: 0.5 + +Applications need some kind of configuration. There are different things +you might want to change. Like toggling debug mode, the secret key and a +lot of very similar things. + +The way Flask is designed usually requires the configuration to be +available when the application starts up. You can either hardcode the +configuration in the code which for many small applications is not +actually that bad, but there are better ways. + +Independent of how you load your config, there is a config object +available which holds the loaded configuration values: +The :attr:`~flask.Flask.config` attribute of the :class:`~flask.Flask` +object. This is the place where Flask itself puts certain configuration +values and also where extensions can put their configuration values. But +this is also where you can have your own configuration. + +Configuration Basics +-------------------- + +The :attr:`~flask.Flask.config` is actually a subclass of a dictionary and +can be modified just like any dictionary:: + + app = Flask(__name__) + app.config['DEBUG'] = True + +Certain configuration values are also forwarded to the +:attr:`~flask.Flask` object so that you can read and write them from +there:: + + app.debug = True + +To update multiple keys at once you can use the :meth:`dict.update` +method:: + + app.config.update( + DEBUG=True, + SECRET_KEY='...' + ) + +Builtin Configuration Values +---------------------------- + +The following configuration values are used internally by Flask: + +=============================== ========================================= +``DEBUG`` enable/disable debug mode +``SECRET_KEY`` the secret key +``SESSION_COOKIE_NAME`` the name of the session cookie +``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as + :class:`datetime.timedelta` object. +``USE_X_SENDFILE`` enable/disable x-sendfile +=============================== ========================================= + +Configuring from Files +---------------------- + +Configuration becomes more useful if you can configure from a file. And +ideally that file would be outside of the actual application package that +you can install the package with distribute (:ref:`distribute-deployment`) +and still modify that file afterwards. + +So a common pattern is this:: + + app = Flask(__name__) + app.config.from_object('yourapplication.default_settings') + app.config.from_envvar('YOURAPPLICATION_SETTINGS') + +What this does is first loading the configuration from the +`yourapplication.default_settings` module and then overrides the values +with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS` +environment variable points to. This environment variable can be set on +Linux or OS X with the export command in the shell before starting the +server:: + + $ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg + $ python run-app.py + * Running on http://127.0.0.1:5000/ + * Restarting with reloader... + +On Windows systems use the `set` builtin instead:: + + >set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg + +The configuration files themselves are actual Python files. Only values +in uppercase are actually stored in the config object later on. So make +sure to use uppercase letters for your config keys. + +Here an example configuration file:: + + DEBUG = False + SECRET_KEY = '?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' + +Make sure to load the configuration very early on so that extensions have +the ability to access the configuration when starting up. There are other +methods on the config object as well to load from individual files. For a +complete reference, read the :class:`~flask.Config` object's +documentation. + + +Configuration Best Practices +---------------------------- + +The downside with the approach mentioned earlier is that it makes testing +a little harder. There is no one 100% solution for this problem in +general, but there are a couple of things you can do to improve that +experience: + +1. create your application in a function and register modules on it. + That way you can create multiple instances of your application with + different configurations attached which makes unittesting a lot + easier. You can use this to pass in configuration as needed. + +2. Do not write code that needs the configuration at import time. If you + limit yourself to request-only accesses to the configuration you can + reconfigure the object later on as needed. diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc index 66dc0ead..5fda5931 100644 --- a/docs/contents.rst.inc +++ b/docs/contents.rst.inc @@ -14,6 +14,7 @@ web development. tutorial/index testing errorhandling + config shell patterns/index deploying/index diff --git a/docs/patterns/distribute.rst b/docs/patterns/distribute.rst index 2826550c..a3217f6e 100644 --- a/docs/patterns/distribute.rst +++ b/docs/patterns/distribute.rst @@ -1,3 +1,5 @@ +.. _distribute-deployment: + Deploying with Distribute ========================= diff --git a/flask.py b/flask.py index 84e3d916..077414d5 100644 --- a/flask.py +++ b/flask.py @@ -641,21 +641,33 @@ class Config(dict): app.config.from_pyfile('yourconfig.cfg') Or alternatively you can define the configuration options in the - module that calls :meth:`from_module` or provide an import path to + module that calls :meth:`from_object` or provide an import path to a module that should be loaded. It is also possible to tell it to use the same module and with that provide the configuration values just before the call:: DEBUG = True SECRET_KEY = 'development key' - app.config.from_module(__name__) + app.config.from_object(__name__) In both cases (loading from any Python file or loading from modules), - only uppercase keys are added to the config. The actual keys in the - config are however lowercased so they are converted for you. This makes - it possible to use lowercase values in the config file for temporary - values that are not added to the config or to define the config keys in - the same file that implements the application. + only uppercase keys are added to the config. This makes it possible to use + lowercase values in the config file for temporary values that are not added + to the config or to define the config keys in the same file that implements + the application. + + Probably the most interesting way to load configurations is from an + environment variable pointing to a file:: + + app.config.from_envvar('YOURAPPLICATION_SETTINGS') + + In this case before launching the application you have to set this + environment variable to the file you want to use. On Linux and OS X + use the export statement:: + + export YOURAPPLICATION_SETTINGS='/path/to/config/file' + + On windows use `set` instead. :param root_path: path to which files are read relative from. When the config object is created by the application, this is @@ -667,10 +679,33 @@ class Config(dict): dict.__init__(self, defaults or {}) self.root_path = root_path + def from_envvar(self, variable_name, silent=False): + """Loads a configuration from an environment variable pointing to + a configuration file. This basically is just a shortcut with nicer + error messages for this line of code:: + + app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) + + :param variable_name: name of the environment variable + :param silent: set to `True` if you want silent failing for missing + files. + :return: bool. `True` if able to load config, `False` otherwise. + """ + rv = os.environ.get(variable_name) + if not rv: + if silent: + return False + raise RuntimeError('The environment variable %r is not set ' + 'and as such configuration could not be ' + 'loaded. Set this variable and make it ' + 'point to a configuration file') + self.from_pyfile(rv) + return True + def from_pyfile(self, filename): """Updates the values in the config from a Python file. This function behaves as if the file was imported as module with the - :meth:`from_module` function. + :meth:`from_object` function. :param filename: the filename of the config. This can either be an absolute filename or a filename relative to the @@ -752,19 +787,32 @@ class Flask(_PackageBoundObject): #: application. In debug mode the debugger will kick in when an unhandled #: exception ocurrs and the integrated server will automatically reload #: the application if changes in the code are detected. + #: + #: This attribute can also be configured from the config with the `DEBUG` + #: configuration key. Defaults to `False`. debug = ConfigAttribute('DEBUG') #: if a secret key is set, cryptographic components can use this to #: sign cookies and other things. Set this to a complex random value #: when you want to use the secure cookie for instance. + #: + #: This attribute can also be configured from the config with the + #: `SECRET_KEY` configuration key. Defaults to `None`. secret_key = ConfigAttribute('SECRET_KEY') #: The secure cookie uses this for the name of the session cookie + #: + #: This attribute can also be configured from the config with the + #: `SESSION_COOKIE_NAME` configuration key. Defaults to ``'session'`` session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME') #: A :class:`~datetime.timedelta` which is used to set the expiration #: date of a permanent session. The default is 31 days which makes a #: permanent session survive for roughly one month. + #: + #: This attribute can also be configured from the config with the + #: `PERMANENT_SESSION_LIFETIME` configuration key. Defaults to + #: ``timedelta(days=31)`` permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME') #: Enable this if you want to use the X-Sendfile feature. Keep in @@ -772,6 +820,9 @@ class Flask(_PackageBoundObject): #: sent with the :func:`send_file` method. #: #: .. versionadded:: 0.2 + #: + #: This attribute can also be configured from the config with the + #: `USE_X_SENDFILE` configuration key. Defaults to `False`. use_x_sendfile = ConfigAttribute('USE_X_SENDFILE') #: the logging format used for the debug logger. This is only used when