forked from orbit-oss/flask
Merge branch 'master' of github.com:mitsuhiko/flask
This commit is contained in:
commit
36e86be06d
11 changed files with 146 additions and 9 deletions
3
CHANGES
3
CHANGES
|
|
@ -17,6 +17,9 @@ Version 1.0
|
|||
- Made Flask support custom JSON mimetypes for incoming data.
|
||||
- Added support for returning tuples in the form ``(response, headers)``
|
||||
from a view function.
|
||||
- Added :meth:`flask.Config.from_json`.
|
||||
- Added :attr:`flask.Flask.config_class`.
|
||||
- Added :meth:`flask.config.Config.get_namespace`.
|
||||
|
||||
Version 0.10.2
|
||||
--------------
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'Flask'
|
||||
copyright = u'2013, Armin Ronacher'
|
||||
copyright = u'2014, Armin Ronacher'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ The following configuration values are used internally by Flask:
|
|||
ascii-encoded JSON. If this is set to
|
||||
``False`` Flask will not encode to ASCII
|
||||
and output strings as-is and return
|
||||
unicode strings. ``jsonfiy`` will
|
||||
unicode strings. ``jsonify`` will
|
||||
automatically encode it in ``utf-8``
|
||||
then for transport for instance.
|
||||
``JSON_SORT_KEYS`` By default Flask will serialize JSON
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ of the box (see :ref:`debug-mode`). If you would like to use another Python
|
|||
debugger, note that debuggers interfere with each other. You have to set some
|
||||
options in order to use your favorite debugger:
|
||||
|
||||
* ``debug`` - whether to enable debug mode and catch exceptinos
|
||||
* ``debug`` - whether to enable debug mode and catch exceptions
|
||||
* ``use_debugger`` - whether to use the internal Flask debugger
|
||||
* ``use_reloader`` - whether to reload and fork the process on exception
|
||||
|
||||
|
|
|
|||
|
|
@ -258,6 +258,8 @@ accessing URLs. By default, a route only answers to `GET` requests, but that
|
|||
can be changed by providing the `methods` argument to the
|
||||
:meth:`~flask.Flask.route` decorator. Here are some examples::
|
||||
|
||||
from flask import request
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
|
|
|
|||
21
flask/app.py
21
flask/app.py
|
|
@ -175,6 +175,17 @@ class Flask(_PackageBoundObject):
|
|||
_set_request_globals_class)
|
||||
del _get_request_globals_class, _set_request_globals_class
|
||||
|
||||
#: The class that is used for the ``config`` attribute of this app.
|
||||
#: Defaults to :class:`~flask.Config`.
|
||||
#:
|
||||
#: Example use cases for a custom class:
|
||||
#:
|
||||
#: 1. Default values for certain config options.
|
||||
#: 2. Access to config values through attributes in addition to keys.
|
||||
#:
|
||||
#: .. versionadded:: 1.0
|
||||
config_class = Config
|
||||
|
||||
#: The debug flag. Set this to `True` to enable debugging of the
|
||||
#: application. In debug mode the debugger will kick in when an unhandled
|
||||
#: exception occurs and the integrated server will automatically reload
|
||||
|
|
@ -610,7 +621,7 @@ class Flask(_PackageBoundObject):
|
|||
root_path = self.root_path
|
||||
if instance_relative:
|
||||
root_path = self.instance_path
|
||||
return Config(root_path, self.default_config)
|
||||
return self.config_class(root_path, self.default_config)
|
||||
|
||||
def auto_find_instance_path(self):
|
||||
"""Tries to locate the instance path if it was not provided to the
|
||||
|
|
@ -1208,9 +1219,11 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
@setupmethod
|
||||
def after_request(self, f):
|
||||
"""Register a function to be run after each request. Your function
|
||||
must take one parameter, a :attr:`response_class` object and return
|
||||
a new response object or the same (see :meth:`process_response`).
|
||||
"""Register a function to be run after each request.
|
||||
|
||||
Your function must take one parameter, an instance of
|
||||
:attr:`response_class` and return a new response object or the
|
||||
same (see :meth:`process_response`).
|
||||
|
||||
As of Flask 0.7 this function might not be executed at the end of the
|
||||
request in case an unhandled exception occurred.
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class BlueprintSetupState(object):
|
|||
class Blueprint(_PackageBoundObject):
|
||||
"""Represents a blueprint. A blueprint is an object that records
|
||||
functions that will be called with the
|
||||
:class:`~flask.blueprint.BlueprintSetupState` later to register functions
|
||||
:class:`~flask.blueprints.BlueprintSetupState` later to register functions
|
||||
or other things on the main application. See :ref:`blueprints` for more
|
||||
information.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ import os
|
|||
import errno
|
||||
|
||||
from werkzeug.utils import import_string
|
||||
from ._compat import string_types
|
||||
from ._compat import string_types, iteritems
|
||||
from . import json
|
||||
|
||||
|
||||
class ConfigAttribute(object):
|
||||
|
|
@ -164,5 +165,69 @@ class Config(dict):
|
|||
if key.isupper():
|
||||
self[key] = getattr(obj, key)
|
||||
|
||||
def from_json(self, filename, silent=False):
|
||||
"""Updates the values in the config from a JSON file. This function
|
||||
behaves as if the JSON object was a dictionary and passed ot the
|
||||
:meth:`from_object` function.
|
||||
|
||||
:param filename: the filename of the JSON file. This can either be an
|
||||
absolute filename or a filename relative to the
|
||||
root path.
|
||||
:param silent: set to `True` if you want silent failure for missing
|
||||
files.
|
||||
|
||||
.. versionadded:: 1.0
|
||||
"""
|
||||
filename = os.path.join(self.root_path, filename)
|
||||
|
||||
try:
|
||||
with open(filename) as json_file:
|
||||
obj = json.loads(json_file.read())
|
||||
except IOError as e:
|
||||
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
|
||||
return False
|
||||
e.strerror = 'Unable to load configuration file (%s)' % e.strerror
|
||||
raise
|
||||
for key in obj.keys():
|
||||
if key.isupper():
|
||||
self[key] = obj[key]
|
||||
return True
|
||||
|
||||
def get_namespace(self, namespace, lowercase=True):
|
||||
"""Returns a dictionary containing a subset of configuration options
|
||||
that match the specified namespace/prefix. Example usage::
|
||||
|
||||
app.config['IMAGE_STORE_TYPE'] = 'fs'
|
||||
app.config['IMAGE_STORE_PATH'] = '/var/app/images'
|
||||
app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
|
||||
image_store_config = app.config.get_namespace('IMAGE_STORE_')
|
||||
|
||||
The resulting dictionary `image_store` would look like::
|
||||
|
||||
{
|
||||
'type': 'fs',
|
||||
'path': '/var/app/images',
|
||||
'base_url': 'http://img.website.com'
|
||||
}
|
||||
|
||||
This is often useful when configuration options map directly to
|
||||
keyword arguments in functions or class constructors.
|
||||
|
||||
:param namespace: a configuration namespace
|
||||
:param lowercase: a flag indicating if the keys of the resulting
|
||||
dictionary should be lowercase
|
||||
|
||||
.. versionadded:: 1.0
|
||||
"""
|
||||
rv = {}
|
||||
for k, v in iteritems(self):
|
||||
if not k.startswith(namespace):
|
||||
continue
|
||||
key = k[len(namespace):]
|
||||
if lowercase:
|
||||
key = key.lower()
|
||||
rv[key] = v
|
||||
return rv
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ class FlaskTestCase(unittest.TestCase):
|
|||
def assert_not_in(self, x, y):
|
||||
self.assertNotIn(x, y)
|
||||
|
||||
def assert_isinstance(self, obj, cls):
|
||||
self.assertIsInstance(obj, cls)
|
||||
|
||||
if sys.version_info[:2] == (2, 6):
|
||||
def assertIn(self, x, y):
|
||||
assert x in y, "%r unexpectedly not in %r" % (x, y)
|
||||
|
|
@ -164,6 +167,9 @@ class FlaskTestCase(unittest.TestCase):
|
|||
def assertNotIn(self, x, y):
|
||||
assert x not in y, "%r unexpectedly in %r" % (x, y)
|
||||
|
||||
def assertIsInstance(self, x, y):
|
||||
assert isinstance(x, y), "not isinstance(%r, %r)" % (x, y)
|
||||
|
||||
|
||||
class _ExceptionCatcher(object):
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,12 @@ class ConfigTestCase(FlaskTestCase):
|
|||
app.config.from_object(__name__)
|
||||
self.common_object_test(app)
|
||||
|
||||
def test_config_from_json(self):
|
||||
app = flask.Flask(__name__)
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
app.config.from_json(os.path.join(current_dir, 'static', 'config.json'))
|
||||
self.common_object_test(app)
|
||||
|
||||
def test_config_from_class(self):
|
||||
class Base(object):
|
||||
TEST_KEY = 'foo'
|
||||
|
|
@ -100,11 +106,49 @@ class ConfigTestCase(FlaskTestCase):
|
|||
self.assert_true(0, 'expected config')
|
||||
self.assert_false(app.config.from_pyfile('missing.cfg', silent=True))
|
||||
|
||||
def test_config_missing_json(self):
|
||||
app = flask.Flask(__name__)
|
||||
try:
|
||||
app.config.from_json('missing.json')
|
||||
except IOError as e:
|
||||
msg = str(e)
|
||||
self.assert_true(msg.startswith('[Errno 2] Unable to load configuration '
|
||||
'file (No such file or directory):'))
|
||||
self.assert_true(msg.endswith("missing.json'"))
|
||||
else:
|
||||
self.assert_true(0, 'expected config')
|
||||
self.assert_false(app.config.from_json('missing.json', silent=True))
|
||||
|
||||
def test_custom_config_class(self):
|
||||
class Config(flask.Config):
|
||||
pass
|
||||
class Flask(flask.Flask):
|
||||
config_class = Config
|
||||
app = Flask(__name__)
|
||||
self.assert_isinstance(app.config, Config)
|
||||
app.config.from_object(__name__)
|
||||
self.common_object_test(app)
|
||||
|
||||
def test_session_lifetime(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = 42
|
||||
self.assert_equal(app.permanent_session_lifetime.seconds, 42)
|
||||
|
||||
def test_get_namespace(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.config['FOO_OPTION_1'] = 'foo option 1'
|
||||
app.config['FOO_OPTION_2'] = 'foo option 2'
|
||||
app.config['BAR_STUFF_1'] = 'bar stuff 1'
|
||||
app.config['BAR_STUFF_2'] = 'bar stuff 2'
|
||||
foo_options = app.config.get_namespace('FOO_')
|
||||
self.assert_equal(2, len(foo_options))
|
||||
self.assert_equal('foo option 1', foo_options['option_1'])
|
||||
self.assert_equal('foo option 2', foo_options['option_2'])
|
||||
bar_options = app.config.get_namespace('BAR_', lowercase=False)
|
||||
self.assert_equal(2, len(bar_options))
|
||||
self.assert_equal('bar stuff 1', bar_options['STUFF_1'])
|
||||
self.assert_equal('bar stuff 2', bar_options['STUFF_2'])
|
||||
|
||||
|
||||
class LimitedLoaderMockWrapper(object):
|
||||
def __init__(self, loader):
|
||||
|
|
|
|||
4
flask/testsuite/static/config.json
Normal file
4
flask/testsuite/static/config.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"TEST_KEY": "foo",
|
||||
"SECRET_KEY": "devkey"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue