Merge branch '2.2.x'

This commit is contained in:
David Lord 2023-04-12 10:57:53 -07:00
commit fa0ceb62f2
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
4 changed files with 47 additions and 58 deletions

View File

@ -11,7 +11,6 @@ from werkzeug.test import Client
from werkzeug.wrappers import Request as BaseRequest
from .cli import ScriptInfo
from .globals import _cv_request
from .sessions import SessionMixin
if t.TYPE_CHECKING: # pragma: no cover
@ -137,40 +136,45 @@ class FlaskClient(Client):
:meth:`~flask.Flask.test_request_context` which are directly
passed through.
"""
if self.cookie_jar is None:
raise RuntimeError(
"Session transactions only make sense with cookies enabled."
# new cookie interface for Werkzeug >= 2.3
cookie_storage = self._cookies if hasattr(self, "_cookies") else self.cookie_jar
if cookie_storage is None:
raise TypeError(
"Cookies are disabled. Create a client with 'use_cookies=True'."
)
app = self.application
environ_overrides = kwargs.setdefault("environ_overrides", {})
self.cookie_jar.inject_wsgi(environ_overrides)
outer_reqctx = _cv_request.get(None)
with app.test_request_context(*args, **kwargs) as c:
session_interface = app.session_interface
sess = session_interface.open_session(app, c.request)
ctx = app.test_request_context(*args, **kwargs)
if hasattr(self, "_add_cookies_to_wsgi"):
self._add_cookies_to_wsgi(ctx.request.environ)
else:
self.cookie_jar.inject_wsgi(ctx.request.environ) # type: ignore[union-attr]
with ctx:
sess = app.session_interface.open_session(app, ctx.request)
if sess is None:
raise RuntimeError(
"Session backend did not open a session. Check the configuration"
)
raise RuntimeError("Session backend did not open a session.")
# Since we have to open a new request context for the session
# handling we want to make sure that we hide out own context
# from the caller. By pushing the original request context
# (or None) on top of this and popping it we get exactly that
# behavior. It's important to not use the push and pop
# methods of the actual request context object since that would
# mean that cleanup handlers are called
token = _cv_request.set(outer_reqctx) # type: ignore[arg-type]
try:
yield sess
finally:
_cv_request.reset(token)
resp = app.response_class()
if not session_interface.is_null_session(sess):
session_interface.save_session(app, sess, resp)
headers = resp.get_wsgi_headers(c.request.environ)
self.cookie_jar.extract_wsgi(c.request.environ, headers)
if app.session_interface.is_null_session(sess):
return
with ctx:
app.session_interface.save_session(app, sess, resp)
if hasattr(self, "_update_cookies_from_response"):
self._update_cookies_from_response(
ctx.request.host.partition(":")[0], resp.headers.getlist("Set-Cookie")
)
else:
self.cookie_jar.extract_wsgi( # type: ignore[union-attr]
ctx.request.environ, resp.headers
)
def _copy_environ(self, other):
out = {**self.environ_base, **other}

View File

@ -260,8 +260,9 @@ def test_session_using_server_name(app, client):
return "Hello World"
rv = client.get("/", "http://example.com/")
assert "domain=.example.com" in rv.headers["set-cookie"].lower()
assert "httponly" in rv.headers["set-cookie"].lower()
cookie = rv.headers["set-cookie"].lower()
# or condition for Werkzeug < 2.3
assert "domain=example.com" in cookie or "domain=.example.com" in cookie
def test_session_using_server_name_and_port(app, client):
@ -273,8 +274,9 @@ def test_session_using_server_name_and_port(app, client):
return "Hello World"
rv = client.get("/", "http://example.com:8080/")
assert "domain=.example.com" in rv.headers["set-cookie"].lower()
assert "httponly" in rv.headers["set-cookie"].lower()
cookie = rv.headers["set-cookie"].lower()
# or condition for Werkzeug < 2.3
assert "domain=example.com" in cookie or "domain=.example.com" in cookie
def test_session_using_server_name_port_and_path(app, client):
@ -336,7 +338,8 @@ def test_session_using_session_settings(app, client):
rv = client.get("/", "http://www.example.com:8080/test/")
cookie = rv.headers["set-cookie"].lower()
assert "domain=.example.com" in cookie
# or condition for Werkzeug < 2.3
assert "domain=example.com" in cookie or "domain=.example.com" in cookie
assert "path=/" in cookie
assert "secure" in cookie
assert "httponly" not in cookie
@ -345,7 +348,8 @@ def test_session_using_session_settings(app, client):
rv = client.get("/clear", "http://www.example.com:8080/test/")
cookie = rv.headers["set-cookie"].lower()
assert "session=;" in cookie
assert "domain=.example.com" in cookie
# or condition for Werkzeug < 2.3
assert "domain=example.com" in cookie or "domain=.example.com" in cookie
assert "path=/" in cookie
assert "secure" in cookie
assert "samesite" in cookie

View File

@ -267,25 +267,6 @@ def _has_encoding(name):
return False
@pytest.mark.skipif(
not _has_encoding("euc-kr"), reason="The euc-kr encoding is required."
)
def test_modified_url_encoding(app, client):
class ModifiedRequest(flask.Request):
url_charset = "euc-kr"
app.request_class = ModifiedRequest
app.url_map.charset = "euc-kr"
@app.route("/")
def index():
return flask.request.args["foo"]
rv = client.get("/", query_string={"foo": "정상처리"}, charset="euc-kr")
assert rv.status_code == 200
assert rv.get_data(as_text=True) == "정상처리"
def test_json_key_sorting(app, client):
app.debug = True
assert app.json.sort_keys

View File

@ -206,10 +206,10 @@ def test_session_transactions_keep_context(app, client, req_ctx):
def test_session_transaction_needs_cookies(app):
c = app.test_client(use_cookies=False)
with pytest.raises(RuntimeError) as e:
with pytest.raises(TypeError, match="Cookies are disabled."):
with c.session_transaction():
pass
assert "cookies" in str(e.value)
def test_test_client_context_binding(app, client):