diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2adf9a35..6b62df26 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -33,6 +33,8 @@ jobs: - {name: '3.7', python: '3.7', os: ubuntu-latest, tox: py37} - {name: '3.6', python: '3.6', os: ubuntu-latest, tox: py36} - {name: 'PyPy', python: 'pypy-3.7', os: ubuntu-latest, tox: pypy37} + - {name: 'Pallets Minimum Versions', python: '3.10', os: ubuntu-latest, tox: py-min} + - {name: 'Pallets Development Versions', python: '3.7', os: ubuntu-latest, tox: py-dev} - {name: Typing, python: '3.10', os: ubuntu-latest, tox: typing} steps: - uses: actions/checkout@v2 diff --git a/CHANGES.rst b/CHANGES.rst index 4fec7c54..98139ae0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,5 +1,16 @@ .. currentmodule:: flask +Version 2.0.3 +------------- + +Unreleased + +- The test client's ``as_tuple`` parameter is deprecated and will be + removed in Werkzeug 2.1. It is now also deprecated in Flask, to be + removed in Flask 2.1, while remaining compatible with both in + 2.0.x. Use ``response.request.environ`` instead. :pr:`4341` + + Version 2.0.2 ------------- diff --git a/requirements/dev.txt b/requirements/dev.txt index b87571ff..33493af3 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -38,7 +38,7 @@ filelock==3.3.2 # via # tox # virtualenv -greenlet==1.1.2 +greenlet==1.1.2 ; python_version < "3.11" # via -r requirements/tests.in identify==2.3.3 # via pre-commit diff --git a/requirements/tests-pallets-dev.in b/requirements/tests-pallets-dev.in new file mode 100644 index 00000000..dddbe48a --- /dev/null +++ b/requirements/tests-pallets-dev.in @@ -0,0 +1,5 @@ +https://github.com/pallets/werkzeug/archive/refs/heads/main.tar.gz +https://github.com/pallets/jinja/archive/refs/heads/main.tar.gz +https://github.com/pallets/markupsafe/archive/refs/heads/main.tar.gz +https://github.com/pallets/itsdangerous/archive/refs/heads/main.tar.gz +https://github.com/pallets/click/archive/refs/heads/main.tar.gz diff --git a/requirements/tests-pallets-dev.txt b/requirements/tests-pallets-dev.txt new file mode 100644 index 00000000..19894ed6 --- /dev/null +++ b/requirements/tests-pallets-dev.txt @@ -0,0 +1,18 @@ +# +# This file is autogenerated by pip-compile with python 3.10 +# To update, run: +# +# pip-compile requirements/tests-pallets-dev.in +# +click @ https://github.com/pallets/click/archive/refs/heads/main.tar.gz + # via -r requirements/tests-pallets-dev.in +itsdangerous @ https://github.com/pallets/itsdangerous/archive/refs/heads/main.tar.gz + # via -r requirements/tests-pallets-dev.in +jinja2 @ https://github.com/pallets/jinja/archive/refs/heads/main.tar.gz + # via -r requirements/tests-pallets-dev.in +markupsafe @ https://github.com/pallets/markupsafe/archive/refs/heads/main.tar.gz + # via + # -r requirements/tests-pallets-dev.in + # jinja2 +werkzeug @ https://github.com/pallets/werkzeug/archive/refs/heads/main.tar.gz + # via -r requirements/tests-pallets-dev.in diff --git a/requirements/tests-pallets-min.in b/requirements/tests-pallets-min.in new file mode 100644 index 00000000..6c8a55d9 --- /dev/null +++ b/requirements/tests-pallets-min.in @@ -0,0 +1,5 @@ +Werkzeug==2.0.0 +Jinja2==3.0.0 +MarkupSafe==2.0.0 +itsdangerous==2.0.0 +click==8.0.0 diff --git a/requirements/tests-pallets-min.txt b/requirements/tests-pallets-min.txt new file mode 100644 index 00000000..75b3524c --- /dev/null +++ b/requirements/tests-pallets-min.txt @@ -0,0 +1,18 @@ +# +# This file is autogenerated by pip-compile with python 3.10 +# To update, run: +# +# pip-compile requirements/tests-pallets-min.in +# +click==8.0.0 + # via -r requirements/tests-pallets-min.in +itsdangerous==2.0.0 + # via -r requirements/tests-pallets-min.in +jinja2==3.0.0 + # via -r requirements/tests-pallets-min.in +markupsafe==2.0.0 + # via + # -r requirements/tests-pallets-min.in + # jinja2 +werkzeug==2.0.0 + # via -r requirements/tests-pallets-min.in diff --git a/requirements/tests.in b/requirements/tests.in index 88fe5481..41936997 100644 --- a/requirements/tests.in +++ b/requirements/tests.in @@ -1,5 +1,5 @@ pytest asgiref blinker -greenlet +greenlet ; python_version < "3.11" python-dotenv diff --git a/requirements/tests.txt b/requirements/tests.txt index a86b6041..4e4247f5 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -10,7 +10,7 @@ attrs==21.2.0 # via pytest blinker==1.4 # via -r requirements/tests.in -greenlet==1.1.2 +greenlet==1.1.2 ; python_version < "3.11" # via -r requirements/tests.in iniconfig==1.1.1 # via pytest diff --git a/src/flask/testing.py b/src/flask/testing.py index fe3b846a..1b35cc7a 100644 --- a/src/flask/testing.py +++ b/src/flask/testing.py @@ -9,14 +9,15 @@ from werkzeug.test import Client from werkzeug.urls import url_parse from werkzeug.wrappers import Request as BaseRequest -from . import _request_ctx_stack from .cli import ScriptInfo +from .globals import _request_ctx_stack from .json import dumps as json_dumps from .sessions import SessionMixin if t.TYPE_CHECKING: + from werkzeug.test import TestResponse + from .app import Flask - from .wrappers import Response class EnvironBuilder(werkzeug.test.EnvironBuilder): @@ -171,14 +172,15 @@ class FlaskClient(Client): headers = resp.get_wsgi_headers(c.request.environ) self.cookie_jar.extract_wsgi(c.request.environ, headers) - def open( # type: ignore + def open( self, *args: t.Any, - as_tuple: bool = False, buffered: bool = False, follow_redirects: bool = False, **kwargs: t.Any, - ) -> "Response": + ) -> "TestResponse": + as_tuple = kwargs.pop("as_tuple", None) + # Same logic as super.open, but apply environ_base and preserve_context. request = None @@ -213,12 +215,28 @@ class FlaskClient(Client): finally: builder.close() - return super().open( # type: ignore - request, - as_tuple=as_tuple, - buffered=buffered, - follow_redirects=follow_redirects, - ) + if as_tuple is not None: + import warnings + + warnings.warn( + "'as_tuple' is deprecated and will be removed in" + " Werkzeug 2.1 and Flask 2.1. Use" + " 'response.request.environ' instead.", + DeprecationWarning, + stacklevel=3, + ) + return super().open( + request, + as_tuple=as_tuple, + buffered=buffered, + follow_redirects=follow_redirects, + ) + else: + return super().open( + request, + buffered=buffered, + follow_redirects=follow_redirects, + ) def __enter__(self) -> "FlaskClient": if self.preserve_context: @@ -272,7 +290,7 @@ class FlaskCliRunner(CliRunner): :return: a :class:`~click.testing.Result` object. """ if cli is None: - cli = self.app.cli + cli = self.app.cli # type: ignore if "obj" not in kwargs: kwargs["obj"] = ScriptInfo(create_app=lambda: self.app) diff --git a/tox.ini b/tox.ini index 6f6e804f..8eaa2336 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,9 @@ [tox] envlist = - py3{11,10,9,8,7,6},pypy37 + py3{11,10,9,8,7,6},pypy3{8,7} py39-click7 + py310-min + py37-dev style typing docs @@ -10,6 +12,8 @@ skip_missing_interpreters = true [testenv] deps = -r requirements/tests.txt + min: -r requirements/tests-pallets-min.txt + dev: -r requirements/tests-pallets-dev.txt click7: click<8