mirror of https://github.com/pallets/flask.git
Merge branch '1.0-maintenance'
This commit is contained in:
commit
161c43649d
|
@ -1,16 +1,16 @@
|
|||
environment:
|
||||
global:
|
||||
TOXENV: py
|
||||
TOXENV: py,codecov
|
||||
|
||||
matrix:
|
||||
- PYTHON: C:\Python36
|
||||
- PYTHON: C:\Python27
|
||||
- PYTHON: C:\Python36-x64
|
||||
- PYTHON: C:\Python27-x64
|
||||
|
||||
init:
|
||||
- SET PATH=%PYTHON%;%PATH%
|
||||
|
||||
install:
|
||||
- python -m pip install -U pip setuptools wheel tox
|
||||
- python -m pip install -U tox
|
||||
|
||||
build: false
|
||||
|
||||
|
@ -21,3 +21,6 @@ branches:
|
|||
only:
|
||||
- master
|
||||
- /^.*-maintenance$/
|
||||
|
||||
cache:
|
||||
- '%LOCALAPPDATA%\pip\Cache'
|
||||
|
|
23
.travis.yml
23
.travis.yml
|
@ -14,27 +14,30 @@ matrix:
|
|||
env: TOXENV=py,codecov
|
||||
- python: 2.7
|
||||
env: TOXENV=py,simplejson,devel,lowest,codecov
|
||||
- python: pypy
|
||||
- python: pypy3
|
||||
env: TOXENV=py,codecov
|
||||
- python: nightly
|
||||
env: TOXENV=py
|
||||
- os: osx
|
||||
language: generic
|
||||
env: TOXENV=py
|
||||
env: TOXENV=py3,py2,codecov
|
||||
cache:
|
||||
pip: false
|
||||
directories:
|
||||
- $HOME/Library/Caches/Homebrew
|
||||
- $HOME/Library/Caches/pip
|
||||
allow_failures:
|
||||
- python: pypy3
|
||||
- python: nightly
|
||||
env: TOXENV=py
|
||||
- os: osx
|
||||
language: generic
|
||||
env: TOXENV=py
|
||||
fast_finish: true
|
||||
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
brew update;
|
||||
brew install python3 redis memcached;
|
||||
virtualenv -p python3 ~/py-env;
|
||||
. ~/py-env/bin/activate;
|
||||
- |
|
||||
if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
|
||||
brew upgrade python
|
||||
brew install python@2;
|
||||
export PATH="/usr/local/opt/python/libexec/bin:${PATH}"
|
||||
fi
|
||||
|
||||
install:
|
||||
|
|
|
@ -15,6 +15,15 @@ Version 1.0.3
|
|||
|
||||
Unreleased
|
||||
|
||||
- :func:`send_file` encodes filenames as ASCII instead of Latin-1
|
||||
(ISO-8859-1). This fixes compatibility with Gunicorn, which is
|
||||
stricter about header encodings than PEP 3333. (`#2766`_)
|
||||
- Allow custom CLIs using ``FlaskGroup`` to set the debug flag without
|
||||
it always being overwritten based on environment variables. (`#2765`_)
|
||||
|
||||
.. _#2766: https://github.com/pallets/flask/issues/2766
|
||||
.. _#2765: https://github.com/pallets/flask/pull/2765
|
||||
|
||||
|
||||
Version 1.0.2
|
||||
-------------
|
||||
|
|
|
@ -132,6 +132,9 @@ Within the activated environment, use the following command to install Flask:
|
|||
|
||||
pip install Flask
|
||||
|
||||
Flask is now installed. Check out the :doc:`/quickstart` or go to the
|
||||
:doc:`Documentation Overview </index>`.
|
||||
|
||||
Living on the edge
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -177,7 +180,7 @@ On Windows, as an administrator:
|
|||
\Python27\python.exe Downloads\get-pip.py
|
||||
\Python27\python.exe -m pip install virtualenv
|
||||
|
||||
Now you can continue to :ref:`install-create-env`.
|
||||
Now you can return above and :ref:`install-create-env`.
|
||||
|
||||
.. _virtualenv: https://virtualenv.pypa.io/
|
||||
.. _get-pip.py: https://bootstrap.pypa.io/get-pip.py
|
||||
|
|
27
flask/cli.py
27
flask/cli.py
|
@ -340,7 +340,8 @@ class ScriptInfo(object):
|
|||
onwards as click object.
|
||||
"""
|
||||
|
||||
def __init__(self, app_import_path=None, create_app=None):
|
||||
def __init__(self, app_import_path=None, create_app=None,
|
||||
set_debug_flag=True):
|
||||
#: Optionally the import path for the Flask application.
|
||||
self.app_import_path = app_import_path or os.environ.get('FLASK_APP')
|
||||
#: Optionally a function that is passed the script info to create
|
||||
|
@ -349,6 +350,7 @@ class ScriptInfo(object):
|
|||
#: A dictionary with arbitrary data that can be associated with
|
||||
#: this script info.
|
||||
self.data = {}
|
||||
self.set_debug_flag = set_debug_flag
|
||||
self._loaded_app = None
|
||||
|
||||
def load_app(self):
|
||||
|
@ -386,12 +388,10 @@ class ScriptInfo(object):
|
|||
'"app.py" module was not found in the current directory.'
|
||||
)
|
||||
|
||||
debug = get_debug_flag()
|
||||
|
||||
# Update the app's debug flag through the descriptor so that other
|
||||
# values repopulate as well.
|
||||
if debug is not None:
|
||||
app.debug = debug
|
||||
if self.set_debug_flag:
|
||||
# Update the app's debug flag through the descriptor so that
|
||||
# other values repopulate as well.
|
||||
app.debug = get_debug_flag()
|
||||
|
||||
self._loaded_app = app
|
||||
return app
|
||||
|
@ -459,6 +459,8 @@ class FlaskGroup(AppGroup):
|
|||
:param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
|
||||
files to set environment variables. Will also change the working
|
||||
directory to the directory containing the first file found.
|
||||
:param set_debug_flag: Set the app's debug flag based on the active
|
||||
environment
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
If installed, python-dotenv will be used to load environment variables
|
||||
|
@ -466,7 +468,8 @@ class FlaskGroup(AppGroup):
|
|||
"""
|
||||
|
||||
def __init__(self, add_default_commands=True, create_app=None,
|
||||
add_version_option=True, load_dotenv=True, **extra):
|
||||
add_version_option=True, load_dotenv=True,
|
||||
set_debug_flag=True, **extra):
|
||||
params = list(extra.pop('params', None) or ())
|
||||
|
||||
if add_version_option:
|
||||
|
@ -475,6 +478,7 @@ class FlaskGroup(AppGroup):
|
|||
AppGroup.__init__(self, params=params, **extra)
|
||||
self.create_app = create_app
|
||||
self.load_dotenv = load_dotenv
|
||||
self.set_debug_flag = set_debug_flag
|
||||
|
||||
if add_default_commands:
|
||||
self.add_command(run_command)
|
||||
|
@ -550,7 +554,8 @@ class FlaskGroup(AppGroup):
|
|||
obj = kwargs.get('obj')
|
||||
|
||||
if obj is None:
|
||||
obj = ScriptInfo(create_app=self.create_app)
|
||||
obj = ScriptInfo(create_app=self.create_app,
|
||||
set_debug_flag=self.set_debug_flag)
|
||||
|
||||
kwargs['obj'] = obj
|
||||
kwargs.setdefault('auto_envvar_prefix', 'FLASK')
|
||||
|
@ -670,7 +675,7 @@ class CertParamType(click.ParamType):
|
|||
|
||||
obj = import_string(value, silent=True)
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
if sys.version_info < (2, 7, 9):
|
||||
if obj:
|
||||
return obj
|
||||
else:
|
||||
|
@ -687,7 +692,7 @@ def _validate_key(ctx, param, value):
|
|||
cert = ctx.params.get('cert')
|
||||
is_adhoc = cert == 'adhoc'
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
if sys.version_info < (2, 7, 9):
|
||||
is_context = cert and not isinstance(cert, (text_type, bytes))
|
||||
else:
|
||||
is_context = isinstance(cert, ssl.SSLContext)
|
||||
|
|
|
@ -506,6 +506,10 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||
|
||||
.. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4
|
||||
|
||||
.. versionchanged:: 1.0.3
|
||||
Filenames are encoded with ASCII instead of Latin-1 for broader
|
||||
compatibility with WSGI servers.
|
||||
|
||||
:param filename_or_fp: the filename of the file to send.
|
||||
This is relative to the :attr:`~Flask.root_path`
|
||||
if a relative path is specified.
|
||||
|
@ -564,11 +568,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||
'sending as attachment')
|
||||
|
||||
try:
|
||||
attachment_filename = attachment_filename.encode('latin-1')
|
||||
attachment_filename = attachment_filename.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
filenames = {
|
||||
'filename': unicodedata.normalize(
|
||||
'NFKD', attachment_filename).encode('latin-1', 'ignore'),
|
||||
'NFKD', attachment_filename).encode('ascii', 'ignore'),
|
||||
'filename*': "UTF-8''%s" % url_quote(attachment_filename),
|
||||
}
|
||||
else:
|
||||
|
|
|
@ -356,6 +356,28 @@ def test_flaskgroup(runner):
|
|||
assert result.output == 'flaskgroup\n'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('set_debug_flag', (True, False))
|
||||
def test_flaskgroup_debug(runner, set_debug_flag):
|
||||
"""Test FlaskGroup debug flag behavior."""
|
||||
|
||||
def create_app(info):
|
||||
app = Flask("flaskgroup")
|
||||
app.debug = True
|
||||
return app
|
||||
|
||||
@click.group(cls=FlaskGroup, create_app=create_app, set_debug_flag=set_debug_flag)
|
||||
def cli(**params):
|
||||
pass
|
||||
|
||||
@cli.command()
|
||||
def test():
|
||||
click.echo(str(current_app.debug))
|
||||
|
||||
result = runner.invoke(cli, ['test'])
|
||||
assert result.exit_code == 0
|
||||
assert result.output == '%s\n' % str(not set_debug_flag)
|
||||
|
||||
|
||||
def test_print_exceptions(runner):
|
||||
"""Print the stacktrace if the CLI."""
|
||||
|
||||
|
@ -537,12 +559,12 @@ def test_run_cert_import(monkeypatch):
|
|||
run_command.make_context('run', ['--cert', 'not_here'])
|
||||
|
||||
# not an SSLContext
|
||||
if sys.version_info >= (2, 7):
|
||||
if sys.version_info >= (2, 7, 9):
|
||||
with pytest.raises(click.BadParameter):
|
||||
run_command.make_context('run', ['--cert', 'flask'])
|
||||
|
||||
# SSLContext
|
||||
if sys.version_info < (2, 7):
|
||||
if sys.version_info < (2, 7, 9):
|
||||
ssl_context = object()
|
||||
else:
|
||||
ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
|
|
|
@ -638,15 +638,22 @@ class TestSendfile(object):
|
|||
assert options['filename'] == 'index.txt'
|
||||
rv.close()
|
||||
|
||||
def test_attachment_with_utf8_filename(self, app, req_ctx):
|
||||
rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=u'Ñandú/pingüino.txt')
|
||||
content_disposition = set(rv.headers['Content-Disposition'].split('; '))
|
||||
assert content_disposition == set((
|
||||
'attachment',
|
||||
'filename="Nandu/pinguino.txt"',
|
||||
"filename*=UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt"
|
||||
))
|
||||
@pytest.mark.usefixtures('req_ctx')
|
||||
@pytest.mark.parametrize(('filename', 'ascii', 'utf8'), (
|
||||
('index.html', 'index.html', False),
|
||||
(u'Ñandú/pingüino.txt', '"Nandu/pinguino.txt"',
|
||||
'%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt'),
|
||||
(u'Vögel.txt', 'Vogel.txt', 'V%C3%B6gel.txt'),
|
||||
))
|
||||
def test_attachment_filename_encoding(self, filename, ascii, utf8):
|
||||
rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=filename)
|
||||
rv.close()
|
||||
content_disposition = rv.headers['Content-Disposition']
|
||||
assert 'filename=%s' % ascii in content_disposition
|
||||
if utf8:
|
||||
assert "filename*=UTF-8''" + utf8 in content_disposition
|
||||
else:
|
||||
assert "filename*=UTF-8''" not in content_disposition
|
||||
|
||||
def test_static_file(self, app, req_ctx):
|
||||
# default cache timeout is 12 hours
|
||||
|
|
Loading…
Reference in New Issue