Browse Source

Improved configuration support.

pull/1638/head
Armin Ronacher 15 years ago
parent
commit
4d16486132
  1. 5
      docs/api.rst
  2. 105
      flask.py
  3. 24
      tests/flask_tests.py

5
docs/api.rst

@ -284,3 +284,8 @@ Template Rendering
.. autofunction:: render_template_string
.. autofunction:: get_template_attribute
Configuration
-------------
.. autoclass:: Config

105
flask.py

@ -19,7 +19,8 @@ from itertools import chain
from jinja2 import Environment, PackageLoader, FileSystemLoader
from werkzeug import Request as RequestBase, Response as ResponseBase, \
LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
ImmutableDict, cached_property, wrap_file, Headers
ImmutableDict, cached_property, wrap_file, Headers, \
import_string
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, InternalServerError
from werkzeug.contrib.securecookie import SecureCookie
@ -630,6 +631,90 @@ class ConfigAttribute(object):
obj.config[self.__name__] = value
class Config(dict):
"""Works exactly like a dict but provides ways to fill it from files
or special dictionaries. There are two common patterns to populate the
config.
Either you can fill the config from a config file::
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
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__)
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.
:param root_path: path to which files are read relative from. When the
config object is created by the application, this is
the application's :attr:`~flask.Flask.root_path`.
:param defaults: an optional dictionary of default values
"""
def __init__(self, root_path, defaults=None):
dict.__init__(self, defaults or {})
self.root_path = root_path
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.
:param filename: the filename of the config. This can either be an
absolute filename or a filename relative to the
root path.
"""
filename = os.path.join(self.root_path, filename)
d = type(sys)('config')
d.__file__ = filename
execfile(filename, d.__dict__)
self.from_module(d)
def from_module(self, module):
"""Updates the values from the given module. A module can be of one
of the following two types:
- a string: in this case the module with that name will be imported
- an actual module reference: that module is used directly
Just the uppercase variables in that module are stored in the config
after lowercasing. Example usage::
app.config.from_module('yourapplication.default_config')
from yourapplication import default_config
app.config.from_module(default_config)
You should not use this function to load the actual configuration but
rather configuration defaults. The actual config should be loaded
with :meth;`from_pyfile` and ideally from a location not within the
package because the package might be installed system wide.
:param module: an import name or module
"""
if isinstance(module, basestring):
d = import_string(module).__dict__
else:
d = module.__dict__
for key, value in d.iteritems():
if key.isupper():
self[key.lower()] = value
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
class Flask(_PackageBoundObject):
"""The flask object implements a WSGI application and acts as the central
object. It is passed the name of the module or package of the
@ -675,12 +760,12 @@ class Flask(_PackageBoundObject):
secret_key = ConfigAttribute('secret_key')
#: The secure cookie uses this for the name of the session cookie
session_cookie_name = ConfigAttribute('session.cookie_name')
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.
permanent_session_lifetime = ConfigAttribute('session.permanent_lifetime')
permanent_session_lifetime = ConfigAttribute('permanent_session_lifetime')
#: Enable this if you want to use the X-Sendfile feature. Keep in
#: mind that the server has to support this. This only affects files
@ -711,18 +796,18 @@ class Flask(_PackageBoundObject):
default_config = ImmutableDict({
'debug': False,
'secret_key': None,
'session.cookie_name': 'session',
'session.permanent_lifetime': timedelta(days=31),
'session_cookie_name': 'session',
'permanent_session_lifetime': timedelta(days=31),
'use_x_sendfile': False
})
def __init__(self, import_name, config=None):
def __init__(self, import_name):
_PackageBoundObject.__init__(self, import_name)
#: the configuration dictionary
self.config = self.default_config.copy()
if config:
self.config.update(config)
#: the configuration dictionary as :class:`Config`. This behaves
#: exactly like a regular dictionary but supports additional methods
#: to load a config from files.
self.config = Config(self.root_path, self.default_config)
#: a dictionary of all view functions registered. The keys will
#: be function names which are also used to generate URLs and

24
tests/flask_tests.py

@ -27,6 +27,11 @@ sys.path.append(os.path.join(example_path, 'flaskr'))
sys.path.append(os.path.join(example_path, 'minitwit'))
# config keys used for the ConfigTestCase
TEST_KEY = 'foo'
SECRET_KEY = 'devkey'
@contextmanager
def catch_stderr():
old_stderr = sys.stderr
@ -686,6 +691,24 @@ class LoggingTestCase(unittest.TestCase):
assert rv.data == 'Hello Server Error'
class ConfigTestCase(unittest.TestCase):
def common_module_test(self, app):
assert app.secret_key == 'devkey'
assert app.config['test_key'] == 'foo'
assert 'ConfigTestCase' not in app.config
def test_config_from_file(self):
app = flask.Flask(__name__)
app.config.from_pyfile('flask_tests.py')
self.common_module_test(app)
def test_config_from_module(self):
app = flask.Flask(__name__)
app.config.from_module(__name__)
self.common_module_test(app)
def suite():
from minitwit_tests import MiniTwitTestCase
from flaskr_tests import FlaskrTestCase
@ -696,6 +719,7 @@ def suite():
suite.addTest(unittest.makeSuite(ModuleTestCase))
suite.addTest(unittest.makeSuite(SendfileTestCase))
suite.addTest(unittest.makeSuite(LoggingTestCase))
suite.addTest(unittest.makeSuite(ConfigTestCase))
if flask.json_available:
suite.addTest(unittest.makeSuite(JSONTestCase))
suite.addTest(unittest.makeSuite(MiniTwitTestCase))

Loading…
Cancel
Save