From 725f391399ffd4a59512d3933a78ceac715ba1a1 Mon Sep 17 00:00:00 2001 From: Mitchel Humpherys Date: Tue, 8 May 2018 09:06:58 -0700 Subject: [PATCH 1/7] docs: installation: Add link to Quickstart after install Once Flask is installed many users will want to proceed to the next section in the docs (Quickstart). Add a link to the end of the Install section for this. --- docs/installation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index 15b2f8f7..738eea74 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -142,6 +142,8 @@ update the code from the master branch: pip install -U https://github.com/pallets/flask/archive/master.tar.gz +Once you've installed Flask you can continue to :ref:`quickstart`. + .. _install-install-virtualenv: Install virtualenv From 335dcf8ec4da820879682b97c1166e0f44162560 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 8 May 2018 20:10:27 -0700 Subject: [PATCH 2/7] clarify the section navigation in installation --- docs/installation.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 738eea74..0014f136 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -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 `. + Living on the edge ~~~~~~~~~~~~~~~~~~ @@ -142,8 +145,6 @@ update the code from the master branch: pip install -U https://github.com/pallets/flask/archive/master.tar.gz -Once you've installed Flask you can continue to :ref:`quickstart`. - .. _install-install-virtualenv: Install virtualenv @@ -179,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 From 1c2a2cd0014693f1e1f894e498c2dc50dbb44f66 Mon Sep 17 00:00:00 2001 From: Daniel Opitz Date: Thu, 3 May 2018 02:01:49 +0200 Subject: [PATCH 3/7] Fixed LaTeX errors on documentation PDF generation --- docs/flaskstyle.sty | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/flaskstyle.sty b/docs/flaskstyle.sty index 8a3f75c3..cd53c898 100644 --- a/docs/flaskstyle.sty +++ b/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 From a913b4dafd1d85cfa1d8bafeea5417748de8d487 Mon Sep 17 00:00:00 2001 From: Briehan Lombaard Date: Wed, 16 May 2018 14:21:19 +0200 Subject: [PATCH 4/7] SSLContext was added in Python 2.7.9 --- flask/cli.py | 4 ++-- tests/test_cli.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index efc1733e..f7c603a1 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -670,7 +670,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 +687,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) diff --git a/tests/test_cli.py b/tests/test_cli.py index 387eeeba..ee038265 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -521,12 +521,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) From b51ab3ff2c4af7e592e649e0e97657154b79c11f Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 28 May 2018 06:26:27 -0700 Subject: [PATCH 5/7] encode filenames as ascii instead of latin-1 latin-1 is allowed by pep3333, but that breaks gunicorn --- CHANGES.rst | 6 ++++++ flask/helpers.py | 8 ++++++-- tests/test_helpers.py | 23 +++++++++++++++-------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index fd4e0533..a0a9cd28 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,12 @@ 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`_) + +.. _#2766: https://github.com/pallets/flask/issues/2766 + Version 1.0.2 ------------- diff --git a/flask/helpers.py b/flask/helpers.py index df0b91fc..7679a496 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -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: diff --git a/tests/test_helpers.py b/tests/test_helpers.py index b3535b28..ae1c0805 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -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 From b34c7174e75db5ab43ff3d76a1c7027126e8c9b3 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 29 May 2018 11:46:04 -0700 Subject: [PATCH 6/7] update test envs use pypy3 fix travis osx env cache pip on appveyor codecov on appveyor --- .appveyor.yml | 11 +++++++---- .travis.yml | 23 +++++++++++++---------- tox.ini | 2 +- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 3a7d2f63..11bb6d4b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -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' diff --git a/.travis.yml b/.travis.yml index 0ff95ecd..b3ba1e19 100644 --- a/.travis.yml +++ b/.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: diff --git a/tox.ini b/tox.ini index 03e53d90..546b9279 100644 --- a/tox.ini +++ b/tox.ini @@ -60,7 +60,7 @@ commands = coverage html [testenv:codecov] -passenv = CI TRAVIS TRAVIS_* +passenv = CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* deps = codecov skip_install = true commands = From 50227f0954e06853bbadf93d1c62dd03429b6711 Mon Sep 17 00:00:00 2001 From: ThiefMaster Date: Tue, 8 May 2018 18:05:55 +0200 Subject: [PATCH 7/7] Add option to not overwrite debug flag in cli This is mainly intended for custom CLIs that may load a config file which already sets the debug flag and does not make use of the `FLASK_*` env vars at all. --- CHANGES.rst | 3 +++ flask/cli.py | 23 ++++++++++++++--------- tests/test_cli.py | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index a0a9cd28..ef69bb99 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,8 +12,11 @@ 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 diff --git a/flask/cli.py b/flask/cli.py index f7c603a1..9f913d9c 100644 --- a/flask/cli.py +++ b/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') diff --git a/tests/test_cli.py b/tests/test_cli.py index ee038265..5f2fc48a 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -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."""