Browse Source

Merge branch 'master' of github.com:mitsuhiko/flask

pull/888/merge
Armin Ronacher 11 years ago
parent
commit
36e86be06d
  1. 3
      CHANGES
  2. 2
      docs/conf.py
  3. 2
      docs/config.rst
  4. 2
      docs/errorhandling.rst
  5. 2
      docs/quickstart.rst
  6. 21
      flask/app.py
  7. 2
      flask/blueprints.py
  8. 67
      flask/config.py
  9. 6
      flask/testsuite/__init__.py
  10. 44
      flask/testsuite/config.py
  11. 4
      flask/testsuite/static/config.json

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
--------------

2
docs/conf.py

@ -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

2
docs/config.rst

@ -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

2
docs/errorhandling.rst

@ -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

2
docs/quickstart.rst

@ -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

@ -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.

2
flask/blueprints.py

@ -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.

67
flask/config.py

@ -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))

6
flask/testsuite/__init__.py

@ -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):

44
flask/testsuite/config.py

@ -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

@ -0,0 +1,4 @@
{
"TEST_KEY": "foo",
"SECRET_KEY": "devkey"
}
Loading…
Cancel
Save