mirror of https://github.com/pallets/flask.git
Improved configuration support.
This commit is contained in:
parent
e84140aba2
commit
c4cac0abc1
|
|
@ -278,3 +278,8 @@ Template Rendering
|
|||
.. autofunction:: render_template_string
|
||||
|
||||
.. autofunction:: get_template_attribute
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
.. autoclass:: Config
|
||||
|
|
|
|||
105
flask.py
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
|
||||
|
|
@ -621,6 +622,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
|
||||
|
|
@ -666,12 +751,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
|
||||
|
|
@ -702,18 +787,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -669,6 +674,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
|
||||
|
|
@ -679,6 +702,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…
Reference in New Issue