forked from orbit-oss/flask
Merge branch '1.0-maintenance'
This commit is contained in:
commit
f7d50d4b67
10 changed files with 43 additions and 85 deletions
|
|
@ -22,11 +22,11 @@ extensions = [
|
|||
intersphinx_mapping = {
|
||||
"python": ("https://docs.python.org/3/", None),
|
||||
"werkzeug": ("http://werkzeug.pocoo.org/docs/", None),
|
||||
"click": ("http://click.pocoo.org/", None),
|
||||
"click": ("https://click.palletsprojects.com/", None),
|
||||
"jinja": ("http://jinja.pocoo.org/docs/", None),
|
||||
"itsdangerous": ("https://pythonhosted.org/itsdangerous", None),
|
||||
"sqlalchemy": ("https://docs.sqlalchemy.org/en/latest/", None),
|
||||
"wtforms": ("https://wtforms.readthedocs.io/en/latest/", None),
|
||||
"itsdangerous": ("https://itsdangerous.palletsprojects.com/", None),
|
||||
"sqlalchemy": ("https://docs.sqlalchemy.org/", None),
|
||||
"wtforms": ("https://wtforms.readthedocs.io/en/stable/", None),
|
||||
"blinker": ("https://pythonhosted.org/blinker/", None),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
:orphan:
|
||||
.. rst-class:: hide-header
|
||||
|
||||
.. rst-class:: hide-header
|
||||
|
||||
|
|
|
|||
|
|
@ -10,60 +10,9 @@ still be good enough if they were 5 minutes old. So then the idea is that
|
|||
you actually put the result of that calculation into a cache for some
|
||||
time.
|
||||
|
||||
Flask itself does not provide caching for you, but Werkzeug, one of the
|
||||
libraries it is based on, has some very basic cache support. It supports
|
||||
multiple cache backends, normally you want to use a memcached server.
|
||||
Flask itself does not provide caching for you, but `Flask-Caching`_, an
|
||||
extentions for Flask does. Flask-Caching supports various backends, and it is
|
||||
even possible to develop your own caching backend.
|
||||
|
||||
Setting up a Cache
|
||||
------------------
|
||||
|
||||
You create a cache object once and keep it around, similar to how
|
||||
:class:`~flask.Flask` objects are created. If you are using the
|
||||
development server you can create a
|
||||
:class:`~werkzeug.contrib.cache.SimpleCache` object, that one is a simple
|
||||
cache that keeps the item stored in the memory of the Python interpreter::
|
||||
|
||||
from werkzeug.contrib.cache import SimpleCache
|
||||
cache = SimpleCache()
|
||||
|
||||
If you want to use memcached, make sure to have one of the memcache modules
|
||||
supported (you get them from `PyPI <https://pypi.org/>`_) and a
|
||||
memcached server running somewhere. This is how you connect to such an
|
||||
memcached server then::
|
||||
|
||||
from werkzeug.contrib.cache import MemcachedCache
|
||||
cache = MemcachedCache(['127.0.0.1:11211'])
|
||||
|
||||
If you are using App Engine, you can connect to the App Engine memcache
|
||||
server easily::
|
||||
|
||||
from werkzeug.contrib.cache import GAEMemcachedCache
|
||||
cache = GAEMemcachedCache()
|
||||
|
||||
Using a Cache
|
||||
-------------
|
||||
|
||||
Now how can one use such a cache? There are two very important
|
||||
operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and
|
||||
:meth:`~werkzeug.contrib.cache.BaseCache.set`. This is how to use them:
|
||||
|
||||
To get an item from the cache call
|
||||
:meth:`~werkzeug.contrib.cache.BaseCache.get` with a string as key name.
|
||||
If something is in the cache, it is returned. Otherwise that function
|
||||
will return ``None``::
|
||||
|
||||
rv = cache.get('my-item')
|
||||
|
||||
To add items to the cache, use the :meth:`~werkzeug.contrib.cache.BaseCache.set`
|
||||
method instead. The first argument is the key and the second the value
|
||||
that should be set. Also a timeout can be provided after which the cache
|
||||
will automatically remove item.
|
||||
|
||||
Here a full example how this looks like normally::
|
||||
|
||||
def get_my_item():
|
||||
rv = cache.get('my-item')
|
||||
if rv is None:
|
||||
rv = calculate_value()
|
||||
cache.set('my-item', rv, timeout=5 * 60)
|
||||
return rv
|
||||
.. _Flask-Caching: https://flask-caching.readthedocs.io/en/latest/
|
||||
|
|
|
|||
|
|
@ -41,5 +41,6 @@ user in a cookie in a :meth:`~flask.Flask.before_request` callback::
|
|||
@after_this_request
|
||||
def remember_language(response):
|
||||
response.set_cookie('user_lang', language)
|
||||
return response
|
||||
|
||||
g.language = language
|
||||
|
|
|
|||
3
docs/requirements.txt
Normal file
3
docs/requirements.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Sphinx~=1.8.0
|
||||
Pallets-Sphinx-Themes~=1.1.0
|
||||
sphinxcontrib-log-cabinet~=1.0.0
|
||||
21
flask/app.py
21
flask/app.py
|
|
@ -1698,16 +1698,17 @@ class Flask(_PackageBoundObject):
|
|||
# we cannot prevent users from trashing it themselves in a custom
|
||||
# trap_http_exception method so that's their fault then.
|
||||
|
||||
# MultiDict passes the key to the exception, but that's ignored
|
||||
# when generating the response message. Set an informative
|
||||
# description for key errors in debug mode or when trapping errors.
|
||||
if (
|
||||
(self.debug or self.config['TRAP_BAD_REQUEST_ERRORS'])
|
||||
and isinstance(e, BadRequestKeyError)
|
||||
# only set it if it's still the default description
|
||||
and e.description is BadRequestKeyError.description
|
||||
):
|
||||
e.description = "KeyError: '{0}'".format(*e.args)
|
||||
if isinstance(e, BadRequestKeyError):
|
||||
if self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]:
|
||||
# Werkzeug < 0.15 doesn't add the KeyError to the 400
|
||||
# message, add it in manually.
|
||||
description = e.get_description()
|
||||
|
||||
if e.args[0] not in description:
|
||||
e.description = "KeyError: '{}'".format(*e.args)
|
||||
else:
|
||||
# Werkzeug >= 0.15 does add it, remove it in production
|
||||
e.args = ()
|
||||
|
||||
if isinstance(e, HTTPException) and not self.trap_http_exception(e):
|
||||
return self.handle_http_exception(e)
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ class ScriptInfo(object):
|
|||
app = call_factory(self, self.create_app)
|
||||
else:
|
||||
if self.app_import_path:
|
||||
path, name = (self.app_import_path.split(':', 1) + [None])[:2]
|
||||
path, name = (re.split(r':(?![\\/])', self.app_import_path, 1) + [None])[:2]
|
||||
import_name = prepare_import(path)
|
||||
app = locate_app(self, import_name, name)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1045,7 +1045,7 @@ def test_trapping_of_bad_request_key_errors(app, client):
|
|||
with pytest.raises(KeyError) as e:
|
||||
client.get("/key")
|
||||
assert e.errisinstance(BadRequest)
|
||||
assert 'missing_key' in e.value.description
|
||||
assert 'missing_key' in e.value.get_description()
|
||||
rv = client.get('/abort')
|
||||
assert rv.status_code == 400
|
||||
|
||||
|
|
|
|||
|
|
@ -261,8 +261,21 @@ def test_get_version(test_apps, capsys):
|
|||
def test_scriptinfo(test_apps, monkeypatch):
|
||||
"""Test of ScriptInfo."""
|
||||
obj = ScriptInfo(app_import_path="cliapp.app:testapp")
|
||||
assert obj.load_app().name == "testapp"
|
||||
assert obj.load_app().name == "testapp"
|
||||
app = obj.load_app()
|
||||
assert app.name == "testapp"
|
||||
assert obj.load_app() is app
|
||||
|
||||
# import app with module's absolute path
|
||||
cli_app_path = os.path.abspath(os.path.join(
|
||||
os.path.dirname(__file__), 'test_apps', 'cliapp', 'app.py'))
|
||||
obj = ScriptInfo(app_import_path=cli_app_path)
|
||||
app = obj.load_app()
|
||||
assert app.name == 'testapp'
|
||||
assert obj.load_app() is app
|
||||
obj = ScriptInfo(app_import_path=cli_app_path + ':testapp')
|
||||
app = obj.load_app()
|
||||
assert app.name == 'testapp'
|
||||
assert obj.load_app() is app
|
||||
|
||||
def create_app(info):
|
||||
return Flask("createapp")
|
||||
|
|
@ -270,7 +283,7 @@ def test_scriptinfo(test_apps, monkeypatch):
|
|||
obj = ScriptInfo(create_app=create_app)
|
||||
app = obj.load_app()
|
||||
assert app.name == "createapp"
|
||||
assert obj.load_app() == app
|
||||
assert obj.load_app() is app
|
||||
|
||||
obj = ScriptInfo()
|
||||
pytest.raises(NoAppException, obj.load_app)
|
||||
|
|
|
|||
11
tox.ini
11
tox.ini
|
|
@ -39,18 +39,9 @@ commands =
|
|||
|
||||
[testenv:docs-html]
|
||||
deps =
|
||||
sphinx
|
||||
pallets-sphinx-themes
|
||||
sphinxcontrib-log-cabinet
|
||||
-r docs/requirements.txt
|
||||
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
|
||||
|
||||
[testenv:docs-linkcheck]
|
||||
deps =
|
||||
sphinx
|
||||
pallets-sphinx-themes
|
||||
sphinxcontrib-log-cabinet
|
||||
commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs {envtmpdir}/linkcheck
|
||||
|
||||
[testenv:coverage-report]
|
||||
deps = coverage
|
||||
skip_install = true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue