Browse Source

Merge pull request #1 from pallets/master

added new
pull/2770/head
babygame0ver 7 years ago committed by GitHub
parent
commit
0e342302b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 81
      CHANGES.rst
  2. 12
      README.rst
  3. 13
      docs/api.rst
  4. 2
      docs/blueprints.rst
  5. 24
      docs/cli.rst
  6. 2
      docs/conf.py
  7. 2
      docs/config.rst
  8. 8
      docs/flaskstyle.sty
  9. 2
      docs/patterns/fileuploads.rst
  10. 6
      docs/quickstart.rst
  11. 2
      docs/tutorial/static.rst
  12. 2
      docs/upgrading.rst
  13. 2
      flask/__init__.py
  14. 22
      flask/app.py
  15. 15
      flask/blueprints.py
  16. 11
      flask/cli.py
  17. 15
      flask/helpers.py
  18. 8
      setup.py
  19. 25
      tests/test_basic.py
  20. 30
      tests/test_blueprints.py
  21. 8
      tests/test_cli.py

81
CHANGES.rst

@ -4,10 +4,62 @@ Flask Changelog
===============
Version 1.1
-----------
Unreleased
Version 1.0.3
-------------
Unreleased
Version 1.0.2
-------------
Released on May 2nd 2018
- Fix more backwards compatibility issues with merging slashes between
a blueprint prefix and route. (`#2748`_)
- Fix error with ``flask routes`` command when there are no routes.
(`#2751`_)
.. _#2748: https://github.com/pallets/flask/pull/2748
.. _#2751: https://github.com/pallets/flask/issues/2751
Version 1.0.1
-------------
Released on April 29th 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
Version 1.0
-----------
unreleased
Released on April 26th 2018
- **Python 2.6 and 3.3 are no longer supported.** (`pallets/meta#24`_)
- Bump minimum dependency versions to the latest stable versions:
@ -215,6 +267,33 @@ unreleased
.. _#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`_)
.. _#2728: https://github.com/pallets/flask/issues/2728
Version 0.12.3
--------------
Released on April 26th 2018
- :func:`Request.get_json` no longer accepts arbitrary encodings.
Incoming JSON should be encoded using UTF-8 per :rfc:`8259`, but
Flask will autodetect UTF-8, -16, or -32. (`#2692`_)
- Fix a Python warning about imports when using ``python -m flask``.
(`#2666`_)
- Fix a ``ValueError`` caused by invalid ``Range`` requests in some
cases.
.. _#2666: https://github.com/pallets/flask/issues/2666
.. _#2692: https://github.com/pallets/flask/issues/2692
Version 0.12.2
--------------

12
README.rst

@ -43,11 +43,23 @@ A Simple Example
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Donate
------
The Pallets organization develops and supports Flask and the libraries
it uses. In order to grow the community of contributors and users, and
allow the maintainers to devote more time to the projects, `please
donate today`_.
.. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20
Links
-----
* Website: https://www.palletsprojects.com/p/flask/
* Documentation: http://flask.pocoo.org/docs/
* License: `BSD <https://github.com/pallets/flask/blob/master/LICENSE>`_
* Releases: https://pypi.org/project/Flask/
* Code: https://github.com/pallets/flask
* Issue tracker: https://github.com/pallets/flask/issues

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

2
docs/blueprints.rst

@ -158,7 +158,7 @@ It is either an absolute path or relative to the blueprint's location::
admin = Blueprint('admin', __name__, static_folder='static')
By default the rightmost part of the path is where it is exposed on the
web. This can be changed with the ``static_url`` argument. Because the
web. This can be changed with the ``static_url_path`` argument. Because the
folder is called ``static`` here it will be available at the
``url_prefix`` of the blueprint + ``/static``. If the blueprint
has the prefix ``/admin``, the static URL will be ``/admin/static``.

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/conf.py

@ -39,6 +39,7 @@ intersphinx_mapping = {
html_theme = 'flask'
html_context = {
'project_links': [
ProjectLink('Donate to Pallets', 'https://psfmember.org/civicrm/contribute/transact?reset=1&id=20'),
ProjectLink('Flask Website', 'https://palletsprojects.com/p/flask/'),
ProjectLink('PyPI releases', 'https://pypi.org/project/Flask/'),
ProjectLink('Source Code', 'https://github.com/pallets/flask/'),
@ -57,6 +58,7 @@ html_sidebars = {
'index': [
'project.html',
'versions.html',
'carbon_ads.html',
'searchbox.html',
],
'**': [

2
docs/config.rst

@ -112,7 +112,7 @@ The following configuration values are used internally by Flask:
**Do not enable debug mode when deploying in production.**
Default: ``True`` if :data:`ENV` is ``'production'``, or ``False``
Default: ``True`` if :data:`ENV` is ``'development'``, or ``False``
otherwise.
.. py:data:: TESTING

8
docs/flaskstyle.sty

@ -1,11 +1,17 @@
\definecolor{TitleColor}{rgb}{0,0,0}
\definecolor{InnerLinkColor}{rgb}{0,0,0}
% Replace Unicode character 'PARTY POPPER' (U+1F389) with a non-breaking space.
% pdfLaTeX doesn't support Unicode.
\DeclareUnicodeCharacter{1F389}{\nobreakspace}
\renewcommand{\maketitle}{%
\begin{titlepage}%
\let\footnotesize\small
\let\footnoterule\relax
\ifsphinxpdfoutput
% Apply following fix only on PDF output, i.e. pdfoutput macro is not
% undefined
\ifx\pdfoutput\undefined\else
\begingroup
% This \def is required to deal with multi-line authors; it
% changes \\ to ', ' (comma-space), making it pass muster for

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

2
docs/tutorial/static.rst

@ -55,7 +55,7 @@ the following into the ``flaskr/static/style.css`` file:
You can find a less compact version of ``style.css`` in the
:gh:`example code <examples/tutorial/flaskr/static/style.css>`.
Go to http://127.0.0.1/auth/login and the page should look like the
Go to http://127.0.0.1:5000/auth/login and the page should look like the
screenshot below.
.. image:: flaskr_login.png

2
docs/upgrading.rst

@ -215,7 +215,7 @@ good.
To apply the upgrade script do the following:
1. Download the script: `flask-07-upgrade.py
<https://raw.githubusercontent.com/pallets/flask/master/scripts/flask-07-upgrade.py>`_
<https://raw.githubusercontent.com/pallets/flask/0.12.3/scripts/flask-07-upgrade.py>`_
2. Run it in the directory of your application::
python flask-07-upgrade.py > patchfile.diff

2
flask/__init__.py

@ -10,7 +10,7 @@
:license: BSD, see LICENSE for more details.
"""
__version__ = '1.0-dev'
__version__ = '1.1.dev'
# utilities we import from Werkzeug and Jinja2 that are unused
# in the module but are exported as public interface.

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

15
flask/blueprints.py

@ -10,6 +10,7 @@
:license: BSD, see LICENSE for more details.
"""
from functools import update_wrapper
from werkzeug.urls import url_join
from .helpers import _PackageBoundObject, _endpoint_from_view_func
@ -49,12 +50,8 @@ class BlueprintSetupState(object):
url_prefix = self.options.get('url_prefix')
if url_prefix is None:
url_prefix = self.blueprint.url_prefix
#: 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 +64,12 @@ 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:
if rule:
rule = '/'.join((
self.url_prefix.rstrip('/'), rule.lstrip('/')))
else:
rule = self.url_prefix
options.setdefault('subdomain', self.subdomain)
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
@ -201,7 +202,7 @@ class Blueprint(_PackageBoundObject):
"""
if endpoint:
assert '.' not in endpoint, "Blueprint endpoints should not contain dots"
if view_func:
if view_func and hasattr(view_func, '__name__'):
assert '.' not in view_func.__name__, "Blueprint view function name should not contain dots"
self.record(lambda s:
s.add_url_rule(rule, endpoint, view_func, **options))

11
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
@ -826,6 +825,10 @@ def routes_command(sort, all_methods):
"""Show all registered routes with endpoints and methods."""
rules = list(current_app.url_map.iter_rules())
if not rules:
click.echo('No routes were registered.')
return
ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS'))
if sort in ('endpoint', 'rule'):

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.

8
setup.py

@ -2,6 +2,8 @@
# -*- coding: utf-8 -*-
import io
import re
from collections import OrderedDict
from setuptools import setup
with io.open('README.rst', 'rt', encoding='utf8') as f:
@ -14,6 +16,11 @@ setup(
name='Flask',
version=version,
url='https://www.palletsprojects.com/p/flask/',
project_urls=OrderedDict((
('Documentation', 'http://flask.pocoo.org/docs/'),
('Code', 'https://github.com/pallets/flask'),
('Issue tracker', 'https://github.com/pallets/flask/issues'),
)),
license='BSD',
author='Armin Ronacher',
author_email='armin.ronacher@active-4.com',
@ -25,6 +32,7 @@ setup(
include_package_data=True,
zip_safe=False,
platforms='any',
python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
install_requires=[
'Werkzeug>=0.14',
'Jinja2>=2.10',

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

30
tests/test_blueprints.py

@ -9,6 +9,7 @@
:license: BSD, see LICENSE for more details.
"""
import functools
import pytest
import flask
@ -114,17 +115,28 @@ 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', '', '/foo'),
('/foo/', '', '/foo/'),
('', '/bar', '/bar'),
('/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):
@ -382,6 +394,8 @@ def test_route_decorator_custom_endpoint_with_dots(app, client):
)
)
bp.add_url_rule('/bar/456', endpoint='foofoofoo', view_func=functools.partial(foo_foo_foo))
app.register_blueprint(bp, url_prefix='/py')
assert client.get('/py/foo').data == b'bp.foo'

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