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 = {
|
intersphinx_mapping = {
|
||||||
"python": ("https://docs.python.org/3/", None),
|
"python": ("https://docs.python.org/3/", None),
|
||||||
"werkzeug": ("http://werkzeug.pocoo.org/docs/", 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),
|
"jinja": ("http://jinja.pocoo.org/docs/", None),
|
||||||
"itsdangerous": ("https://pythonhosted.org/itsdangerous", None),
|
"itsdangerous": ("https://itsdangerous.palletsprojects.com/", None),
|
||||||
"sqlalchemy": ("https://docs.sqlalchemy.org/en/latest/", None),
|
"sqlalchemy": ("https://docs.sqlalchemy.org/", None),
|
||||||
"wtforms": ("https://wtforms.readthedocs.io/en/latest/", None),
|
"wtforms": ("https://wtforms.readthedocs.io/en/stable/", None),
|
||||||
"blinker": ("https://pythonhosted.org/blinker/", None),
|
"blinker": ("https://pythonhosted.org/blinker/", None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
:orphan:
|
.. rst-class:: hide-header
|
||||||
|
|
||||||
.. 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
|
you actually put the result of that calculation into a cache for some
|
||||||
time.
|
time.
|
||||||
|
|
||||||
Flask itself does not provide caching for you, but Werkzeug, one of the
|
Flask itself does not provide caching for you, but `Flask-Caching`_, an
|
||||||
libraries it is based on, has some very basic cache support. It supports
|
extentions for Flask does. Flask-Caching supports various backends, and it is
|
||||||
multiple cache backends, normally you want to use a memcached server.
|
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
|
.. _Flask-Caching: https://flask-caching.readthedocs.io/en/latest/
|
||||||
: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
|
|
||||||
|
|
|
||||||
|
|
@ -41,5 +41,6 @@ user in a cookie in a :meth:`~flask.Flask.before_request` callback::
|
||||||
@after_this_request
|
@after_this_request
|
||||||
def remember_language(response):
|
def remember_language(response):
|
||||||
response.set_cookie('user_lang', language)
|
response.set_cookie('user_lang', language)
|
||||||
|
return response
|
||||||
|
|
||||||
g.language = language
|
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
|
# we cannot prevent users from trashing it themselves in a custom
|
||||||
# trap_http_exception method so that's their fault then.
|
# trap_http_exception method so that's their fault then.
|
||||||
|
|
||||||
# MultiDict passes the key to the exception, but that's ignored
|
if isinstance(e, BadRequestKeyError):
|
||||||
# when generating the response message. Set an informative
|
if self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]:
|
||||||
# description for key errors in debug mode or when trapping errors.
|
# Werkzeug < 0.15 doesn't add the KeyError to the 400
|
||||||
if (
|
# message, add it in manually.
|
||||||
(self.debug or self.config['TRAP_BAD_REQUEST_ERRORS'])
|
description = e.get_description()
|
||||||
and isinstance(e, BadRequestKeyError)
|
|
||||||
# only set it if it's still the default description
|
if e.args[0] not in description:
|
||||||
and e.description is BadRequestKeyError.description
|
e.description = "KeyError: '{}'".format(*e.args)
|
||||||
):
|
else:
|
||||||
e.description = "KeyError: '{0}'".format(*e.args)
|
# Werkzeug >= 0.15 does add it, remove it in production
|
||||||
|
e.args = ()
|
||||||
|
|
||||||
if isinstance(e, HTTPException) and not self.trap_http_exception(e):
|
if isinstance(e, HTTPException) and not self.trap_http_exception(e):
|
||||||
return self.handle_http_exception(e)
|
return self.handle_http_exception(e)
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,7 @@ class ScriptInfo(object):
|
||||||
app = call_factory(self, self.create_app)
|
app = call_factory(self, self.create_app)
|
||||||
else:
|
else:
|
||||||
if self.app_import_path:
|
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)
|
import_name = prepare_import(path)
|
||||||
app = locate_app(self, import_name, name)
|
app = locate_app(self, import_name, name)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1045,7 +1045,7 @@ def test_trapping_of_bad_request_key_errors(app, client):
|
||||||
with pytest.raises(KeyError) as e:
|
with pytest.raises(KeyError) as e:
|
||||||
client.get("/key")
|
client.get("/key")
|
||||||
assert e.errisinstance(BadRequest)
|
assert e.errisinstance(BadRequest)
|
||||||
assert 'missing_key' in e.value.description
|
assert 'missing_key' in e.value.get_description()
|
||||||
rv = client.get('/abort')
|
rv = client.get('/abort')
|
||||||
assert rv.status_code == 400
|
assert rv.status_code == 400
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -261,8 +261,21 @@ def test_get_version(test_apps, capsys):
|
||||||
def test_scriptinfo(test_apps, monkeypatch):
|
def test_scriptinfo(test_apps, monkeypatch):
|
||||||
"""Test of ScriptInfo."""
|
"""Test of ScriptInfo."""
|
||||||
obj = ScriptInfo(app_import_path="cliapp.app:testapp")
|
obj = ScriptInfo(app_import_path="cliapp.app:testapp")
|
||||||
assert obj.load_app().name == "testapp"
|
app = obj.load_app()
|
||||||
assert obj.load_app().name == "testapp"
|
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):
|
def create_app(info):
|
||||||
return Flask("createapp")
|
return Flask("createapp")
|
||||||
|
|
@ -270,7 +283,7 @@ def test_scriptinfo(test_apps, monkeypatch):
|
||||||
obj = ScriptInfo(create_app=create_app)
|
obj = ScriptInfo(create_app=create_app)
|
||||||
app = obj.load_app()
|
app = obj.load_app()
|
||||||
assert app.name == "createapp"
|
assert app.name == "createapp"
|
||||||
assert obj.load_app() == app
|
assert obj.load_app() is app
|
||||||
|
|
||||||
obj = ScriptInfo()
|
obj = ScriptInfo()
|
||||||
pytest.raises(NoAppException, obj.load_app)
|
pytest.raises(NoAppException, obj.load_app)
|
||||||
|
|
|
||||||
11
tox.ini
11
tox.ini
|
|
@ -39,18 +39,9 @@ commands =
|
||||||
|
|
||||||
[testenv:docs-html]
|
[testenv:docs-html]
|
||||||
deps =
|
deps =
|
||||||
sphinx
|
-r docs/requirements.txt
|
||||||
pallets-sphinx-themes
|
|
||||||
sphinxcontrib-log-cabinet
|
|
||||||
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
|
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]
|
[testenv:coverage-report]
|
||||||
deps = coverage
|
deps = coverage
|
||||||
skip_install = true
|
skip_install = true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue