Browse Source

Merge branch '1.0-maintenance'

pull/2747/head
David Lord 7 years ago
parent
commit
1fa9185c7e
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
  1. 39
      CHANGES.rst
  2. 13
      docs/api.rst
  3. 24
      docs/cli.rst
  4. 2
      docs/patterns/fileuploads.rst
  5. 6
      docs/quickstart.rst
  6. 22
      flask/app.py
  7. 10
      flask/blueprints.py
  8. 7
      flask/cli.py
  9. 15
      flask/helpers.py
  10. 25
      tests/test_basic.py
  11. 21
      tests/test_blueprints.py
  12. 8
      tests/test_cli.py

39
CHANGES.rst

@ -7,15 +7,40 @@ Flask Changelog
Version 1.1
-----------
unreleased
Unreleased
Version 1.0.2
-------------
Unreleased
Version 1.0.1
-------------
unreleased
Released on April 29 2018
- Fix registering partials (with no ``__name__``) as view functions.
(`#2730`_)
- Don't treat lists returned from view functions the same as tuples.
Only tuples are interpreted as response data. (`#2736`_)
- Extra slashes between a blueprint's ``url_prefix`` and a route URL
are merged. This fixes some backwards compatibility issues with the
change in 1.0. (`#2731`_, `#2742`_)
- Only trap ``BadRequestKeyError`` errors in debug mode, not all
``BadRequest`` errors. This allows ``abort(400)`` to continue
working as expected. (`#2735`_)
- The ``FLASK_SKIP_DOTENV`` environment variable can be set to ``1``
to skip automatically loading dotenv files. (`#2722`_)
.. _#2722: https://github.com/pallets/flask/issues/2722
.. _#2730: https://github.com/pallets/flask/pull/2730
.. _#2731: https://github.com/pallets/flask/issues/2731
.. _#2735: https://github.com/pallets/flask/issues/2735
.. _#2736: https://github.com/pallets/flask/issues/2736
.. _#2742: https://github.com/pallets/flask/issues/2742
- Fix registering partials (with no ``__name__``) as view functions
Version 1.0
-----------
@ -228,6 +253,14 @@ Released on April 26th 2018
.. _#2709: https://github.com/pallets/flask/pull/2709
Version 0.12.4
--------------
Released on April 29 2018
- Repackage 0.12.3 to fix package layout issue. (`#2728`_)
Version 0.12.3
--------------

13
docs/api.rst

@ -717,7 +717,18 @@ definition for a URL that accepts an optional page::
pass
This specifies that ``/users/`` will be the URL for page one and
``/users/page/N`` will be the URL for page `N`.
``/users/page/N`` will be the URL for page ``N``.
If a URL contains a default value, it will be redirected to its simpler
form with a 301 redirect. In the above example, ``/users/page/1`` will
be redirected to ``/users/``. If your route handles ``GET`` and ``POST``
requests, make sure the default route only handles ``GET``, as redirects
can't preserve form data. ::
@app.route('/region/', defaults={'id': 1})
@app.route('/region/<id>', methods=['GET', 'POST'])
def region(id):
pass
Here are the parameters that :meth:`~flask.Flask.route` and
:meth:`~flask.Flask.add_url_rule` accept. The only difference is that

24
docs/cli.rst

@ -201,6 +201,30 @@ These can be added to the ``.flaskenv`` file just like ``FLASK_APP`` to
control default command options.
Disable dotenv
~~~~~~~~~~~~~~
The ``flask`` command will show a message if it detects dotenv files but
python-dotenv is not installed.
.. code-block:: none
flask run
* Tip: There are .env files present. Do "pip install python-dotenv" to use them.
You can tell Flask not to load dotenv files even when python-dotenv is
installed by setting the ``FLASK_SKIP_DOTENV`` environment variable.
This can be useful if you want to load them manually, or if you're using
a project runner that loads them already. Keep in mind that the
environment variables must be set before the app loads or it won't
configure as expected.
.. code-block:: none
export FLASK_SKIP_DOTENV=1
flask run
Environment Variables From virtualenv
-------------------------------------

2
docs/patterns/fileuploads.rst

@ -65,7 +65,7 @@ the file and redirects the user to the URL for the uploaded file::
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('upload_file',
return redirect(url_for('uploaded_file',
filename=filename))
return '''
<!doctype html>

6
docs/quickstart.rst

@ -293,7 +293,7 @@ Python shell. See :ref:`context-locals`. ::
@app.route('/user/<username>')
def profile(username):
return '{}'s profile'.format(username)
return '{}\'s profile'.format(username)
with app.test_request_context():
print(url_for('index'))
@ -315,6 +315,8 @@ a route only answers to ``GET`` requests. You can use the ``methods`` argument
of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods.
::
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
@ -323,7 +325,7 @@ of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods.
return show_the_login_form()
If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method
and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise,
and handles ``HEAD`` requests according to the `HTTP RFC`_. Likewise,
``OPTIONS`` is automatically implemented for you.
.. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt

22
flask/app.py

@ -27,9 +27,11 @@ from ._compat import integer_types, reraise, string_types, text_type
from .config import Config, ConfigAttribute
from .ctx import AppContext, RequestContext, _AppCtxGlobals
from .globals import _request_ctx_stack, g, request, session
from .helpers import _PackageBoundObject, \
_endpoint_from_view_func, find_package, get_env, get_debug_flag, \
get_flashed_messages, locked_cached_property, url_for
from .helpers import (
_PackageBoundObject,
_endpoint_from_view_func, find_package, get_env, get_debug_flag,
get_flashed_messages, locked_cached_property, url_for, get_load_dotenv
)
from .logging import create_logger
from .sessions import SecureCookieSessionInterface
from .signals import appcontext_tearing_down, got_request_exception, \
@ -904,7 +906,7 @@ class Flask(_PackageBoundObject):
explain_ignored_app_run()
return
if load_dotenv:
if get_load_dotenv(load_dotenv):
cli.load_dotenv()
# if set, let env vars override previous values
@ -1663,8 +1665,14 @@ class Flask(_PackageBoundObject):
trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS']
# if unset, trap based on debug mode
if (trap_bad_request is None and self.debug) or trap_bad_request:
# if unset, trap key errors in debug mode
if (
trap_bad_request is None and self.debug
and isinstance(e, BadRequestKeyError)
):
return True
if trap_bad_request:
return isinstance(e, BadRequest)
return False
@ -1923,7 +1931,7 @@ class Flask(_PackageBoundObject):
status = headers = None
# unpack tuple returns
if isinstance(rv, (tuple, list)):
if isinstance(rv, tuple):
len_rv = len(rv)
# a 3-tuple is unpacked directly

10
flask/blueprints.py

@ -49,12 +49,10 @@ class BlueprintSetupState(object):
url_prefix = self.options.get('url_prefix')
if url_prefix is None:
url_prefix = self.blueprint.url_prefix
if url_prefix:
url_prefix = url_prefix.rstrip('/')
#: The prefix that should be used for all URLs defined on the
#: blueprint.
if url_prefix and url_prefix[-1] == '/':
url_prefix = url_prefix[:-1]
self.url_prefix = url_prefix
#: A dictionary with URL defaults that is added to each and every
@ -67,8 +65,8 @@ class BlueprintSetupState(object):
to the application. The endpoint is automatically prefixed with the
blueprint's name.
"""
if self.url_prefix:
rule = self.url_prefix + rule
if self.url_prefix is not None:
rule = '/'.join((self.url_prefix, rule.lstrip('/')))
options.setdefault('subdomain', self.subdomain)
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)

7
flask/cli.py

@ -28,7 +28,7 @@ from werkzeug.utils import import_string
from . import __version__
from ._compat import getargspec, iteritems, reraise, text_type
from .globals import current_app
from .helpers import get_debug_flag, get_env
from .helpers import get_debug_flag, get_env, get_load_dotenv
try:
import dotenv
@ -544,7 +544,7 @@ class FlaskGroup(AppGroup):
# script that is loaded here also attempts to start a server.
os.environ['FLASK_RUN_FROM_CLI'] = 'true'
if self.load_dotenv:
if get_load_dotenv(self.load_dotenv):
load_dotenv()
obj = kwargs.get('obj')
@ -583,12 +583,11 @@ def load_dotenv(path=None):
.. versionadded:: 1.0
"""
if dotenv is None:
if path or os.path.exists('.env') or os.path.exists('.flaskenv'):
click.secho(
' * Tip: There are .env files present.'
' Do "pip install python-dotenv" to use them',
' Do "pip install python-dotenv" to use them.',
fg='yellow')
return

15
flask/helpers.py

@ -68,6 +68,21 @@ def get_debug_flag():
return val.lower() not in ('0', 'false', 'no')
def get_load_dotenv(default=True):
"""Get whether the user has disabled loading dotenv files by setting
:envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the
files.
:param default: What to return if the env var isn't set.
"""
val = os.environ.get('FLASK_SKIP_DOTENV')
if not val:
return default
return val.lower() in ('0', 'false', 'no')
def _endpoint_from_view_func(view_func):
"""Internal helper that returns the default endpoint for a given
function. This always is the function name.

25
tests/test_basic.py

@ -1027,21 +1027,34 @@ def test_errorhandler_precedence(app, client):
def test_trapping_of_bad_request_key_errors(app, client):
@app.route('/fail')
@app.route('/key')
def fail():
flask.request.form['missing_key']
rv = client.get('/fail')
@app.route('/abort')
def allow_abort():
flask.abort(400)
rv = client.get('/key')
assert rv.status_code == 400
assert b'missing_key' not in rv.data
rv = client.get('/abort')
assert rv.status_code == 400
app.config['TRAP_BAD_REQUEST_ERRORS'] = True
app.debug = True
with pytest.raises(KeyError) as e:
client.get("/fail")
client.get("/key")
assert e.errisinstance(BadRequest)
assert 'missing_key' in e.value.description
rv = client.get('/abort')
assert rv.status_code == 400
app.debug = False
app.config['TRAP_BAD_REQUEST_ERRORS'] = True
with pytest.raises(KeyError):
client.get('/key')
with pytest.raises(BadRequest):
client.get('/abort')
def test_trapping_of_all_http_exceptions(app, client):

21
tests/test_blueprints.py

@ -115,17 +115,22 @@ def test_blueprint_app_error_handling(app, client):
assert client.get('/nope').data == b'you shall not pass'
def test_blueprint_prefix_slash(app, client):
bp = flask.Blueprint('test', __name__, url_prefix='/bar/')
@bp.route('/foo')
def foo():
@pytest.mark.parametrize(('prefix', 'rule', 'url'), (
('/foo/', '/bar', '/foo/bar'),
('/foo/', 'bar', '/foo/bar'),
('/foo', '/bar', '/foo/bar'),
('/foo/', '//bar', '/foo/bar'),
('/foo//', '/bar', '/foo/bar'),
))
def test_blueprint_prefix_slash(app, client, prefix, rule, url):
bp = flask.Blueprint('test', __name__, url_prefix=prefix)
@bp.route(rule)
def index():
return '', 204
app.register_blueprint(bp)
app.register_blueprint(bp, url_prefix='/spam/')
assert client.get('/bar/foo').status_code == 204
assert client.get('/spam/foo').status_code == 204
assert client.get(url).status_code == 204
def test_blueprint_url_defaults(app, client):

8
tests/test_cli.py

@ -474,6 +474,14 @@ def test_dotenv_optional(monkeypatch):
assert 'FOO' not in os.environ
@need_dotenv
def test_disable_dotenv_from_env(monkeypatch, runner):
monkeypatch.chdir(test_path)
monkeypatch.setitem(os.environ, 'FLASK_SKIP_DOTENV', '1')
runner.invoke(FlaskGroup())
assert 'FOO' not in os.environ
def test_run_cert_path():
# no key
with pytest.raises(click.BadParameter):

Loading…
Cancel
Save