summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2023-05-08 15:35:34 -0700
committerDavid Lord <davidism@gmail.com>2023-05-08 15:35:34 -0700
commitbe78c3ff24dd01df8079a415debbd876052c096f (patch)
treec83aaec6df36833f509fd9c80f77a8429f699ac8
parent825633eb783f49b0450b0e6866bc55985b154531 (diff)
parentab61d0dab5ffbf846f6ddf4e1386ca340012250e (diff)
downloadwerkzeug-be78c3ff24dd01df8079a415debbd876052c096f.tar.gz
Merge branch '2.3.x'
-rw-r--r--CHANGES.rst7
-rw-r--r--src/werkzeug/__init__.py2
-rw-r--r--src/werkzeug/datastructures/auth.py26
-rw-r--r--src/werkzeug/datastructures/headers.py12
-rw-r--r--src/werkzeug/formparser.py35
-rw-r--r--src/werkzeug/http.py40
-rw-r--r--src/werkzeug/routing/converters.py2
-rw-r--r--src/werkzeug/routing/map.py6
-rw-r--r--src/werkzeug/sansio/http.py8
-rw-r--r--src/werkzeug/sansio/request.py18
-rw-r--r--src/werkzeug/sansio/response.py2
-rw-r--r--src/werkzeug/security.py8
-rw-r--r--src/werkzeug/test.py18
-rw-r--r--src/werkzeug/urls.py137
-rw-r--r--src/werkzeug/utils.py8
-rw-r--r--src/werkzeug/wrappers/response.py5
-rw-r--r--src/werkzeug/wsgi.py24
-rw-r--r--tests/test_formparser.py4
-rw-r--r--tests/test_urls.py15
-rw-r--r--tests/test_utils.py60
-rw-r--r--tests/test_wsgi.py10
21 files changed, 228 insertions, 219 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 2d83ee59..72b27a26 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -3,10 +3,15 @@
Version 2.3.4
-------------
-Unreleased
+Released 2023-05-08
- ``Authorization.from_header`` and ``WWWAuthenticate.from_header`` detects tokens
that end with base64 padding (``=``). :issue:`2685`
+- Remove usage of ``warnings.catch_warnings``. :issue:`2690`
+- Remove ``max_form_parts`` restriction from standard form data parsing and only use
+ if for multipart content. :pr:`2694`
+- ``Response`` will avoid converting the ``Location`` header in some cases to preserve
+ invalid URL schemes like ``itms-services``. :issue:`2691`
Version 2.3.3
diff --git a/src/werkzeug/__init__.py b/src/werkzeug/__init__.py
index b3208b2f..828edcf2 100644
--- a/src/werkzeug/__init__.py
+++ b/src/werkzeug/__init__.py
@@ -3,4 +3,4 @@ from .test import Client as Client
from .wrappers import Request as Request
from .wrappers import Response as Response
-__version__ = "2.3.4.dev"
+__version__ = "2.3.4"
diff --git a/src/werkzeug/datastructures/auth.py b/src/werkzeug/datastructures/auth.py
index 0d216516..a3403259 100644
--- a/src/werkzeug/datastructures/auth.py
+++ b/src/werkzeug/datastructures/auth.py
@@ -150,10 +150,10 @@ def auth_property(name: str, doc: str | None = None) -> property:
special_realm = auth_property('special_realm')
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4.
+ Will be removed in Werkzeug 3.0.
"""
warnings.warn(
- "'auth_property' is deprecated and will be removed in Werkzeug 2.4.",
+ "'auth_property' is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -202,7 +202,7 @@ class WWWAuthenticate:
if auth_type is None:
warnings.warn(
"An auth type must be given as the first parameter. Assuming 'basic' is"
- " deprecated and will be removed in Werkzeug 2.4.",
+ " deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -264,10 +264,10 @@ class WWWAuthenticate:
"""Clear any existing data and set a ``Basic`` challenge.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Create and assign an instance instead.
+ Will be removed in Werkzeug 3.0. Create and assign an instance instead.
"""
warnings.warn(
- "The 'set_basic' method is deprecated and will be removed in Werkzeug 2.4."
+ "The 'set_basic' method is deprecated and will be removed in Werkzeug 3.0."
" Create and assign an instance instead."
)
self._type = "basic"
@@ -291,10 +291,10 @@ class WWWAuthenticate:
"""Clear any existing data and set a ``Digest`` challenge.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Create and assign an instance instead.
+ Will be removed in Werkzeug 3.0. Create and assign an instance instead.
"""
warnings.warn(
- "The 'set_digest' method is deprecated and will be removed in Werkzeug 2.4."
+ "The 'set_digest' method is deprecated and will be removed in Werkzeug 3.0."
" Create and assign an instance instead."
)
self._type = "digest"
@@ -415,11 +415,11 @@ class WWWAuthenticate:
"""The ``qop`` parameter as a set.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. It will become the same as other
+ Will be removed in Werkzeug 3.0. It will become the same as other
parameters, returning a string.
"""
warnings.warn(
- "The 'qop' property is deprecated and will be removed in Werkzeug 2.4."
+ "The 'qop' property is deprecated and will be removed in Werkzeug 3.0."
" It will become the same as other parameters, returning a string.",
DeprecationWarning,
stacklevel=2,
@@ -441,11 +441,11 @@ class WWWAuthenticate:
"""The ``stale`` parameter as a boolean.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. It will become the same as other
+ Will be removed in Werkzeug 3.0. It will become the same as other
parameters, returning a string.
"""
warnings.warn(
- "The 'stale' property is deprecated and will be removed in Werkzeug 2.4."
+ "The 'stale' property is deprecated and will be removed in Werkzeug 3.0."
" It will become the same as other parameters, returning a string.",
DeprecationWarning,
stacklevel=2,
@@ -467,7 +467,7 @@ class WWWAuthenticate:
if isinstance(value, bool):
warnings.warn(
"Setting the 'stale' property to a boolean is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -483,7 +483,7 @@ def _deprecated_dict_method(f): # type: ignore[no-untyped-def]
def wrapper(*args, **kwargs): # type: ignore[no-untyped-def]
warnings.warn(
"Treating 'Authorization' and 'WWWAuthenticate' as a dict is deprecated and"
- " will be removed in Werkzeug 2.4. Use the 'parameters' attribute instead.",
+ " will be removed in Werkzeug 3.0. Use the 'parameters' attribute instead.",
DeprecationWarning,
stacklevel=2,
)
diff --git a/src/werkzeug/datastructures/headers.py b/src/werkzeug/datastructures/headers.py
index 1f13d1c7..dc060c41 100644
--- a/src/werkzeug/datastructures/headers.py
+++ b/src/werkzeug/datastructures/headers.py
@@ -103,7 +103,7 @@ class Headers:
.. versionchanged:: 2.3
The ``as_bytes`` parameter is deprecated and will be removed
- in Werkzeug 2.4.
+ in Werkzeug 3.0.
.. versionchanged:: 0.9
The ``as_bytes`` parameter was added.
@@ -111,7 +111,7 @@ class Headers:
if as_bytes is not None:
warnings.warn(
"The 'as_bytes' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -143,7 +143,7 @@ class Headers:
.. versionchanged:: 2.3
The ``as_bytes`` parameter is deprecated and will be removed
- in Werkzeug 2.4.
+ in Werkzeug 3.0.
.. versionchanged:: 0.9
The ``as_bytes`` parameter was added.
@@ -151,7 +151,7 @@ class Headers:
if as_bytes is not None:
warnings.warn(
"The 'as_bytes' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -480,7 +480,7 @@ def _str_header_key(key: t.Any) -> str:
if not isinstance(key, str):
warnings.warn(
"Header keys must be strings. Passing other types is deprecated and will"
- " not be supported in Werkzeug 2.4.",
+ " not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -500,7 +500,7 @@ def _str_header_value(value: t.Any) -> str:
if isinstance(value, bytes):
warnings.warn(
"Passing bytes as a header value is deprecated and will not be supported in"
- " Werkzeug 2.4.",
+ " Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
diff --git a/src/werkzeug/formparser.py b/src/werkzeug/formparser.py
index 074ac542..dcedd339 100644
--- a/src/werkzeug/formparser.py
+++ b/src/werkzeug/formparser.py
@@ -105,8 +105,8 @@ def parse_form_data(
:param cls: an optional dict class to use. If this is not specified
or `None` the default :class:`MultiDict` is used.
:param silent: If set to False parsing errors will not be caught.
- :param max_form_parts: The maximum number of parts to be parsed. If this is
- exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
+ :param max_form_parts: The maximum number of multipart parts to be parsed. If this
+ is exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
:return: A tuple in the form ``(stream, form, files)``.
.. versionchanged:: 2.3
@@ -114,7 +114,7 @@ def parse_form_data(
.. versionchanged:: 2.3
The ``charset`` and ``errors`` parameters are deprecated and will be removed in
- Werkzeug 2.4.
+ Werkzeug 3.0.
.. versionadded:: 0.5.1
Added the ``silent`` parameter.
@@ -157,16 +157,16 @@ class FormDataParser:
:param cls: an optional dict class to use. If this is not specified
or `None` the default :class:`MultiDict` is used.
:param silent: If set to False parsing errors will not be caught.
- :param max_form_parts: The maximum number of parts to be parsed. If this is
- exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
+ :param max_form_parts: The maximum number of multipart parts to be parsed. If this
+ is exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
.. versionchanged:: 2.3
The ``charset`` and ``errors`` parameters are deprecated and will be removed in
- Werkzeug 2.4.
+ Werkzeug 3.0.
.. versionchanged:: 2.3
The ``parse_functions`` attribute and ``get_parse_func`` methods are deprecated
- and will be removed in Werkzeug 2.4.
+ and will be removed in Werkzeug 3.0.
.. versionchanged:: 2.2.3
Added the ``max_form_parts`` parameter.
@@ -194,7 +194,7 @@ class FormDataParser:
if charset is not None:
warnings.warn(
"The 'charset' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -206,7 +206,7 @@ class FormDataParser:
if errors is not None:
warnings.warn(
"The 'errors' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -234,7 +234,7 @@ class FormDataParser:
):
warnings.warn(
"The 'get_parse_func' method is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -246,7 +246,7 @@ class FormDataParser:
elif mimetype == "application/x-url-encoded":
warnings.warn(
"The 'application/x-url-encoded' mimetype is invalid, and will not be"
- " treated as 'application/x-www-form-urlencoded' in Werkzeug 2.4.",
+ " treated as 'application/x-www-form-urlencoded' in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -254,7 +254,7 @@ class FormDataParser:
elif mimetype in self.parse_functions:
warnings.warn(
"The 'parse_functions' attribute is deprecated and will be removed in"
- " Werkzeug 2.4. Override 'parse' instead.",
+ " Werkzeug 3.0. Override 'parse' instead.",
DeprecationWarning,
stacklevel=2,
)
@@ -297,7 +297,7 @@ class FormDataParser:
.. versionchanged:: 2.3
The ``application/x-url-encoded`` content type is deprecated and will not be
- treated as ``application/x-www-form-urlencoded`` in Werkzeug 2.4.
+ treated as ``application/x-www-form-urlencoded`` in Werkzeug 3.0.
"""
if mimetype == "multipart/form-data":
parse_func = self._parse_multipart
@@ -306,7 +306,7 @@ class FormDataParser:
elif mimetype == "application/x-url-encoded":
warnings.warn(
"The 'application/x-url-encoded' mimetype is invalid, and will not be"
- " treated as 'application/x-www-form-urlencoded' in Werkzeug 2.4.",
+ " treated as 'application/x-www-form-urlencoded' in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -314,7 +314,7 @@ class FormDataParser:
elif mimetype in self.parse_functions:
warnings.warn(
"The 'parse_functions' attribute is deprecated and will be removed in"
- " Werkzeug 2.4. Override 'parse' instead.",
+ " Werkzeug 3.0. Override 'parse' instead.",
DeprecationWarning,
stacklevel=2,
)
@@ -378,7 +378,6 @@ class FormDataParser:
keep_blank_values=True,
encoding=self.charset,
errors="werkzeug.url_quote",
- max_num_fields=self.max_form_parts,
)
except ValueError as e:
raise RequestEntityTooLarge() from e
@@ -408,7 +407,7 @@ class MultiPartParser:
if charset is not None:
warnings.warn(
"The 'charset' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -420,7 +419,7 @@ class MultiPartParser:
if errors is not None:
warnings.warn(
"The 'errors' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
diff --git a/src/werkzeug/http.py b/src/werkzeug/http.py
index d38ca9a5..08b5c538 100644
--- a/src/werkzeug/http.py
+++ b/src/werkzeug/http.py
@@ -153,16 +153,16 @@ def quote_header_value(
The value is quoted if it is the empty string.
.. versionchanged:: 2.3
- Passing bytes is deprecated and will not be supported in Werkzeug 2.4.
+ Passing bytes is deprecated and will not be supported in Werkzeug 3.0.
.. versionchanged:: 2.3
- The ``extra_chars`` parameter is deprecated and will be removed in Werkzeug 2.4.
+ The ``extra_chars`` parameter is deprecated and will be removed in Werkzeug 3.0.
.. versionadded:: 0.5
"""
if isinstance(value, bytes):
warnings.warn(
- "Passing bytes is deprecated and will not be supported in Werkzeug 2.4.",
+ "Passing bytes is deprecated and will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -171,7 +171,7 @@ def quote_header_value(
if extra_chars is not None:
warnings.warn(
"The 'extra_chars' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -203,12 +203,12 @@ def unquote_header_value(value: str, is_filename: bool | None = None) -> str:
:param value: The header value to unquote.
.. versionchanged:: 2.3
- The ``is_filename`` parameter is deprecated and will be removed in Werkzeug 2.4.
+ The ``is_filename`` parameter is deprecated and will be removed in Werkzeug 3.0.
"""
if is_filename is not None:
warnings.warn(
"The 'is_filename' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -298,7 +298,7 @@ def dump_header(
:param iterable: The items to create a header from.
.. versionchanged:: 2.3
- The ``allow_token`` parameter is deprecated and will be removed in Werkzeug 2.4.
+ The ``allow_token`` parameter is deprecated and will be removed in Werkzeug 3.0.
.. versionchanged:: 2.2.3
If a key ends with ``*``, its value will not be quoted.
@@ -306,7 +306,7 @@ def dump_header(
if allow_token is not None:
warnings.warn(
"'The 'allow_token' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -394,10 +394,10 @@ def parse_dict_header(value: str, cls: type[dict] | None = None) -> dict[str, st
Added support for ``key*=charset''value`` encoded items.
.. versionchanged:: 2.3
- Passing bytes is deprecated, support will be removed in Werkzeug 2.4.
+ Passing bytes is deprecated, support will be removed in Werkzeug 3.0.
.. versionchanged:: 2.3
- The ``cls`` argument is deprecated and will be removed in Werkzeug 2.4.
+ The ``cls`` argument is deprecated and will be removed in Werkzeug 3.0.
.. versionchanged:: 0.9
The ``cls`` argument was added.
@@ -406,7 +406,7 @@ def parse_dict_header(value: str, cls: type[dict] | None = None) -> dict[str, st
cls = dict
else:
warnings.warn(
- "The 'cls' parameter is deprecated and will be removed in Werkzeug 2.4.",
+ "The 'cls' parameter is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -415,7 +415,7 @@ def parse_dict_header(value: str, cls: type[dict] | None = None) -> dict[str, st
if isinstance(value, bytes):
warnings.warn(
- "Passing bytes is deprecated and will be removed in Werkzeug 2.4.",
+ "Passing bytes is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -823,7 +823,7 @@ def parse_authorization_header(
:return: a :class:`~werkzeug.datastructures.Authorization` object or `None`.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use :meth:`.Authorization.from_header` instead.
+ Will be removed in Werkzeug 3.0. Use :meth:`.Authorization.from_header` instead.
"""
from .datastructures import Authorization
@@ -850,7 +850,7 @@ def parse_www_authenticate_header(
:return: a :class:`~werkzeug.datastructures.WWWAuthenticate` object.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use :meth:`.WWWAuthenticate.from_header`
+ Will be removed in Werkzeug 3.0. Use :meth:`.WWWAuthenticate.from_header`
instead.
"""
from .datastructures.auth import WWWAuthenticate
@@ -1298,7 +1298,7 @@ def parse_cookie(
.. versionchanged:: 2.3
Passing bytes, and the ``charset`` and ``errors`` parameters, are deprecated and
- will be removed in Werkzeug 2.4.
+ will be removed in Werkzeug 3.0.
.. versionchanged:: 1.0
Returns a :class:`MultiDict` instead of a ``TypeConversionDict``.
@@ -1311,7 +1311,7 @@ def parse_cookie(
cookie = header.get("HTTP_COOKIE")
elif isinstance(header, bytes):
warnings.warn(
- "Passing bytes is deprecated and will not be supported in Werkzeug 2.4.",
+ "Passing bytes is deprecated and will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -1403,7 +1403,7 @@ def dump_cookie(
.. versionchanged:: 2.3
Passing bytes, and the ``charset`` parameter, are deprecated and will be removed
- in Werkzeug 2.4.
+ in Werkzeug 3.0.
.. versionchanged:: 1.0.0
The string ``'None'`` is accepted for ``samesite``.
@@ -1411,7 +1411,7 @@ def dump_cookie(
if charset is not None:
warnings.warn(
"The 'charset' parameter is deprecated and will be removed"
- " in Werkzeug 2.4.",
+ " in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -1421,7 +1421,7 @@ def dump_cookie(
if isinstance(key, bytes):
warnings.warn(
"The 'key' parameter must be a string. Bytes are deprecated"
- " and will not be supported in Werkzeug 2.4.",
+ " and will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -1430,7 +1430,7 @@ def dump_cookie(
if isinstance(value, bytes):
warnings.warn(
"The 'value' parameter must be a string. Bytes are"
- " deprecated and will not be supported in Werkzeug 2.4.",
+ " deprecated and will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
diff --git a/src/werkzeug/routing/converters.py b/src/werkzeug/routing/converters.py
index bf8d80f4..c59e2abc 100644
--- a/src/werkzeug/routing/converters.py
+++ b/src/werkzeug/routing/converters.py
@@ -45,7 +45,7 @@ class BaseConverter:
if isinstance(value, (bytes, bytearray)):
warnings.warn(
"Passing bytes as a URL value is deprecated and will not be supported"
- " in Werkzeug 2.4.",
+ " in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=7,
)
diff --git a/src/werkzeug/routing/map.py b/src/werkzeug/routing/map.py
index cdb935dd..022fd1e0 100644
--- a/src/werkzeug/routing/map.py
+++ b/src/werkzeug/routing/map.py
@@ -70,7 +70,7 @@ class Map:
.. versionchanged:: 2.3
The ``charset`` and ``encoding_errors`` parameters are deprecated and will be
- removed in Werkzeug 2.4.
+ removed in Werkzeug 3.0.
.. versionchanged:: 1.0
If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules will match.
@@ -117,7 +117,7 @@ class Map:
if charset is not None:
warnings.warn(
"The 'charset' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -129,7 +129,7 @@ class Map:
if encoding_errors is not None:
warnings.warn(
"The 'encoding_errors' parameter is deprecated and will be"
- " removed in Werkzeug 2.4.",
+ " removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
diff --git a/src/werkzeug/sansio/http.py b/src/werkzeug/sansio/http.py
index 8eaf7cf2..21a61972 100644
--- a/src/werkzeug/sansio/http.py
+++ b/src/werkzeug/sansio/http.py
@@ -140,7 +140,7 @@ def parse_cookie(
.. versionchanged:: 2.3
Passing bytes, and the ``charset`` and ``errors`` parameters, are deprecated and
- will be removed in Werkzeug 2.4.
+ will be removed in Werkzeug 3.0.
.. versionadded:: 2.2
"""
@@ -150,7 +150,7 @@ def parse_cookie(
if isinstance(cookie, bytes):
warnings.warn(
"The 'cookie' parameter must be a string. Passing bytes is deprecated and"
- " will not be supported in Werkzeug 2.4.",
+ " will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -158,7 +158,7 @@ def parse_cookie(
if charset is not None:
warnings.warn(
- "The 'charset' parameter is deprecated and will be removed in Werkzeug 2.4",
+ "The 'charset' parameter is deprecated and will be removed in Werkzeug 3.0",
DeprecationWarning,
stacklevel=2,
)
@@ -167,7 +167,7 @@ def parse_cookie(
if errors is not None:
warnings.warn(
- "The 'errors' parameter is deprecated and will be removed in Werkzeug 2.4",
+ "The 'errors' parameter is deprecated and will be removed in Werkzeug 3.0",
DeprecationWarning,
stacklevel=2,
)
diff --git a/src/werkzeug/sansio/request.py b/src/werkzeug/sansio/request.py
index fb3fcccb..0bcda90b 100644
--- a/src/werkzeug/sansio/request.py
+++ b/src/werkzeug/sansio/request.py
@@ -69,7 +69,7 @@ class Request:
"""The charset used to decode body, form, and cookie data. Defaults to UTF-8.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Request data must always be UTF-8.
+ Will be removed in Werkzeug 3.0. Request data must always be UTF-8.
"""
warnings.warn(
"The 'charset' attribute is deprecated and will not be used in Werkzeug"
@@ -98,11 +98,11 @@ class Request:
"""How errors when decoding bytes are handled. Defaults to "replace".
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4.
+ Will be removed in Werkzeug 3.0.
"""
warnings.warn(
"The 'encoding_errors' attribute is deprecated and will not be used in"
- " Werkzeug 2.4.",
+ " Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -112,7 +112,7 @@ class Request:
def encoding_errors(self, value: str) -> None:
warnings.warn(
"The 'encoding_errors' attribute is deprecated and will not be used in"
- " Werkzeug 2.4.",
+ " Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -126,13 +126,13 @@ class Request:
Defaults to the value of :attr:`charset`, which defaults to UTF-8.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Percent-encoded bytes must always be UTF-8.
+ Will be removed in Werkzeug 3.0. Percent-encoded bytes must always be UTF-8.
.. versionadded:: 0.6
"""
warnings.warn(
"The 'url_charset' attribute is deprecated and will not be used in"
- " Werkzeug 2.4. Percent-encoded bytes must always be UTF-8.",
+ " Werkzeug 3.0. Percent-encoded bytes must always be UTF-8.",
DeprecationWarning,
stacklevel=2,
)
@@ -142,7 +142,7 @@ class Request:
def url_charset(self, value: str) -> None:
warnings.warn(
"The 'url_charset' attribute is deprecated and will not be used in"
- " Werkzeug 2.4. Percent-encoded bytes must always be UTF-8.",
+ " Werkzeug 3.0. Percent-encoded bytes must always be UTF-8.",
DeprecationWarning,
stacklevel=2,
)
@@ -224,7 +224,7 @@ class Request:
if not isinstance(type(self).encoding_errors, property):
warnings.warn(
"The 'encoding_errors' attribute is deprecated and will not be used in"
- " Werkzeug 2.4.",
+ " Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -235,7 +235,7 @@ class Request:
if not isinstance(type(self).url_charset, property):
warnings.warn(
"The 'url_charset' attribute is deprecated and will not be used in"
- " Werkzeug 2.4. Percent-encoded bytes must always be UTF-8.",
+ " Werkzeug 3.0. Percent-encoded bytes must always be UTF-8.",
DeprecationWarning,
stacklevel=2,
)
diff --git a/src/werkzeug/sansio/response.py b/src/werkzeug/sansio/response.py
index 387a4ae7..d71839f6 100644
--- a/src/werkzeug/sansio/response.py
+++ b/src/werkzeug/sansio/response.py
@@ -91,7 +91,7 @@ class Response:
"""The charset used to encode body and cookie data. Defaults to UTF-8.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Response data must always be UTF-8.
+ Will be removed in Werkzeug 3.0. Response data must always be UTF-8.
"""
warnings.warn(
"The 'charset' attribute is deprecated and will not be used in Werkzeug"
diff --git a/src/werkzeug/security.py b/src/werkzeug/security.py
index 10f57cd5..282c4fd8 100644
--- a/src/werkzeug/security.py
+++ b/src/werkzeug/security.py
@@ -27,7 +27,7 @@ def _hash_internal(method: str, salt: str, password: str) -> tuple[str, str]:
if method == "plain":
warnings.warn(
"The 'plain' password method is deprecated and will be removed in"
- " Werkzeug 2.4. Migrate to the 'scrypt' method.",
+ " Werkzeug 3.0. Migrate to the 'scrypt' method.",
stacklevel=3,
)
return password, method
@@ -74,7 +74,7 @@ def _hash_internal(method: str, salt: str, password: str) -> tuple[str, str]:
else:
warnings.warn(
f"The '{method}' password method is deprecated and will be removed in"
- " Werkzeug 2.4. Migrate to the 'scrypt' method.",
+ " Werkzeug 3.0. Migrate to the 'scrypt' method.",
stacklevel=3,
)
return hmac.new(salt, password, method).hexdigest(), method
@@ -110,7 +110,7 @@ def generate_password_hash(
The default iterations for pbkdf2 was increased to 600,000.
.. versionchanged:: 2.3
- All plain hashes are deprecated and will not be supported in Werkzeug 2.4.
+ All plain hashes are deprecated and will not be supported in Werkzeug 3.0.
"""
salt = gen_salt(salt_length)
h, actual_method = _hash_internal(method, salt, password)
@@ -129,7 +129,7 @@ def check_password_hash(pwhash: str, password: str) -> bool:
:param password: The plaintext password.
.. versionchanged:: 2.3
- All plain hashes are deprecated and will not be supported in Werkzeug 2.4.
+ All plain hashes are deprecated and will not be supported in Werkzeug 3.0.
"""
try:
method, salt, hashval = pwhash.split("$", 2)
diff --git a/src/werkzeug/test.py b/src/werkzeug/test.py
index 012ec30b..f5b4f2ff 100644
--- a/src/werkzeug/test.py
+++ b/src/werkzeug/test.py
@@ -65,11 +65,11 @@ def stream_encode_multipart(
in a file descriptor.
.. versionchanged:: 2.3
- The ``charset`` parameter is deprecated and will be removed in Werkzeug 2.4
+ The ``charset`` parameter is deprecated and will be removed in Werkzeug 3.0
"""
if charset is not None:
warnings.warn(
- "The 'charset' parameter is deprecated and will be removed in Werkzeug 2.4",
+ "The 'charset' parameter is deprecated and will be removed in Werkzeug 3.0",
DeprecationWarning,
stacklevel=2,
)
@@ -163,7 +163,7 @@ def encode_multipart(
(``boundary``, ``data``) where data is bytes.
.. versionchanged:: 2.3
- The ``charset`` parameter is deprecated and will be removed in Werkzeug 2.4
+ The ``charset`` parameter is deprecated and will be removed in Werkzeug 3.0
"""
stream, length, boundary = stream_encode_multipart(
values, use_tempfile=False, boundary=boundary, charset=charset
@@ -259,7 +259,7 @@ class EnvironBuilder:
is a shortcut for ``Basic`` authorization.
.. versionchanged:: 2.3
- The ``charset`` parameter is deprecated and will be removed in Werkzeug 2.4
+ The ``charset`` parameter is deprecated and will be removed in Werkzeug 3.0
.. versionchanged:: 2.1
``CONTENT_TYPE`` and ``CONTENT_LENGTH`` are not duplicated as
@@ -342,7 +342,7 @@ class EnvironBuilder:
if charset is not None:
warnings.warn(
"The 'charset' parameter is deprecated and will be"
- " removed in Werkzeug 2.4",
+ " removed in Werkzeug 3.0",
DeprecationWarning,
stacklevel=2,
)
@@ -860,7 +860,7 @@ class Client:
def cookie_jar(self) -> t.Iterable[Cookie] | None:
warnings.warn(
"The 'cookie_jar' attribute is a private API and will be removed in"
- " Werkzeug 2.4. Use the 'get_cookie' method instead.",
+ " Werkzeug 3.0. Use the 'get_cookie' method instead.",
DeprecationWarning,
stacklevel=2,
)
@@ -927,7 +927,7 @@ class Client:
.. versionchanged:: 2.3
The first parameter ``server_name`` is deprecated and will be removed in
- Werkzeug 2.4. The first parameter is ``key``. Use the ``domain`` and
+ Werkzeug 3.0. The first parameter is ``key``. Use the ``domain`` and
``origin_only`` parameters instead.
"""
if self._cookies is None:
@@ -938,7 +938,7 @@ class Client:
if args:
warnings.warn(
"The first parameter 'server_name' is no longer used, and will be"
- " removed in Werkzeug 2.4. The positional parameters are 'key' and"
+ " removed in Werkzeug 3.0. The positional parameters are 'key' and"
" 'value'. Use the 'domain' and 'origin_only' parameters instead.",
DeprecationWarning,
stacklevel=2,
@@ -977,7 +977,7 @@ class Client:
.. versionchanged:: 2.3
The first parameter ``server_name`` is deprecated and will be removed in
- Werkzeug 2.4. The first parameter is ``key``. Use the ``domain`` parameter
+ Werkzeug 3.0. The first parameter is ``key``. Use the ``domain`` parameter
instead.
.. versionchanged:: 2.3
diff --git a/src/werkzeug/urls.py b/src/werkzeug/urls.py
index b0e62d0b..89ef2194 100644
--- a/src/werkzeug/urls.py
+++ b/src/werkzeug/urls.py
@@ -59,7 +59,7 @@ class BaseURL(_URLTuple):
"""Superclass of :py:class:`URL` and :py:class:`BytesURL`.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use the ``urllib.parse`` library instead.
+ Will be removed in Werkzeug 3.0. Use the ``urllib.parse`` library instead.
"""
__slots__ = ()
@@ -71,7 +71,7 @@ class BaseURL(_URLTuple):
def __new__(cls, *args: t.Any, **kwargs: t.Any) -> BaseURL:
warnings.warn(
f"'werkzeug.urls.{cls.__name__}' is deprecated and will be removed in"
- " Werkzeug 2.4. Use the 'urllib.parse' library instead.",
+ " Werkzeug 3.0. Use the 'urllib.parse' library instead.",
DeprecationWarning,
stacklevel=2,
)
@@ -358,7 +358,7 @@ class URL(BaseURL):
URL.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use the ``urllib.parse`` library instead.
+ Will be removed in Werkzeug 3.0. Use the ``urllib.parse`` library instead.
"""
__slots__ = ()
@@ -371,22 +371,20 @@ class URL(BaseURL):
"""Encodes the URL to a tuple made out of bytes. The charset is
only being used for the path, query and fragment.
"""
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'werkzeug", DeprecationWarning)
- return BytesURL(
- self.scheme.encode("ascii"),
- self.encode_netloc(),
- self.path.encode(charset, errors),
- self.query.encode(charset, errors),
- self.fragment.encode(charset, errors),
- )
+ return BytesURL(
+ self.scheme.encode("ascii"),
+ self.encode_netloc(),
+ self.path.encode(charset, errors),
+ self.query.encode(charset, errors),
+ self.fragment.encode(charset, errors),
+ )
class BytesURL(BaseURL):
"""Represents a parsed URL in bytes.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use the ``urllib.parse`` library instead.
+ Will be removed in Werkzeug 3.0. Use the ``urllib.parse`` library instead.
"""
__slots__ = ()
@@ -406,15 +404,13 @@ class BytesURL(BaseURL):
"""Decodes the URL to a tuple made out of strings. The charset is
only being used for the path, query and fragment.
"""
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'werkzeug", DeprecationWarning)
- return URL(
- self.scheme.decode("ascii"), # type: ignore
- self.decode_netloc(),
- self.path.decode(charset, errors), # type: ignore
- self.query.decode(charset, errors), # type: ignore
- self.fragment.decode(charset, errors), # type: ignore
- )
+ return URL(
+ self.scheme.decode("ascii"), # type: ignore
+ self.decode_netloc(),
+ self.path.decode(charset, errors), # type: ignore
+ self.query.decode(charset, errors), # type: ignore
+ self.fragment.decode(charset, errors), # type: ignore
+ )
_unquote_maps: dict[frozenset[int], dict[bytes, int]] = {frozenset(): _hextobyte}
@@ -504,10 +500,10 @@ def url_parse(
from the URL.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use ``urllib.parse.urlsplit`` instead.
+ Will be removed in Werkzeug 3.0. Use ``urllib.parse.urlsplit`` instead.
"""
warnings.warn(
- "'werkzeug.urls.url_parse' is deprecated and will be removed in Werkzeug 2.4."
+ "'werkzeug.urls.url_parse' is deprecated and will be removed in Werkzeug 3.0."
" Use 'urllib.parse.urlsplit' instead.",
DeprecationWarning,
stacklevel=2,
@@ -546,9 +542,7 @@ def url_parse(
result_type = URL if is_text_based else BytesURL
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'werkzeug", DeprecationWarning)
- return result_type(scheme, netloc, url, query, fragment)
+ return result_type(scheme, netloc, url, query, fragment)
def _make_fast_url_quote(
@@ -605,13 +599,13 @@ def url_quote(
:param unsafe: an optional sequence of unsafe characters.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use ``urllib.parse.quote`` instead.
+ Will be removed in Werkzeug 3.0. Use ``urllib.parse.quote`` instead.
.. versionadded:: 0.9.2
The `unsafe` parameter was added.
"""
warnings.warn(
- "'werkzeug.urls.url_quote' is deprecated and will be removed in Werkzeug 2.4."
+ "'werkzeug.urls.url_quote' is deprecated and will be removed in Werkzeug 3.0."
" Use 'urllib.parse.quote' instead.",
DeprecationWarning,
stacklevel=2,
@@ -646,7 +640,7 @@ def url_quote_plus(
:param safe: An optional sequence of safe characters.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use ``urllib.parse.quote_plus`` instead.
+ Will be removed in Werkzeug 3.0. Use ``urllib.parse.quote_plus`` instead.
"""
warnings.warn(
"'werkzeug.urls.url_quote_plus' is deprecated and will be removed in Werkzeug"
@@ -655,9 +649,7 @@ def url_quote_plus(
stacklevel=2,
)
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'werkzeug", DeprecationWarning)
- return url_quote(string, charset, errors, safe + " ", "+").replace(" ", "+")
+ return url_quote(string, charset, errors, safe + " ", "+").replace(" ", "+")
def url_unparse(components: tuple[str, str, str, str, str]) -> str:
@@ -668,10 +660,10 @@ def url_unparse(components: tuple[str, str, str, str, str]) -> str:
into a URL string.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use ``urllib.parse.urlunsplit`` instead.
+ Will be removed in Werkzeug 3.0. Use ``urllib.parse.urlunsplit`` instead.
"""
warnings.warn(
- "'werkzeug.urls.url_unparse' is deprecated and will be removed in Werkzeug 2.4."
+ "'werkzeug.urls.url_unparse' is deprecated and will be removed in Werkzeug 3.0."
" Use 'urllib.parse.urlunsplit' instead.",
DeprecationWarning,
stacklevel=2,
@@ -716,10 +708,10 @@ def url_unquote(
:param errors: the error handling for the charset decoding.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use ``urllib.parse.unquote`` instead.
+ Will be removed in Werkzeug 3.0. Use ``urllib.parse.unquote`` instead.
"""
warnings.warn(
- "'werkzeug.urls.url_unquote' is deprecated and will be removed in Werkzeug 2.4."
+ "'werkzeug.urls.url_unquote' is deprecated and will be removed in Werkzeug 3.0."
" Use 'urllib.parse.unquote' instead.",
DeprecationWarning,
stacklevel=2,
@@ -745,7 +737,7 @@ def url_unquote_plus(
:param errors: The error handling for the `charset` decoding.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use ``urllib.parse.unquote_plus`` instead.
+ Will be removed in Werkzeug 3.0. Use ``urllib.parse.unquote_plus`` instead.
"""
warnings.warn(
"'werkzeug.urls.url_unquote_plus' is deprecated and will be removed in Werkzeug"
@@ -759,9 +751,7 @@ def url_unquote_plus(
else:
s = s.replace(b"+", b" ")
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'werkzeug", DeprecationWarning)
- return url_unquote(s, charset, errors)
+ return url_unquote(s, charset, errors)
def url_fix(s: str, charset: str = "utf-8") -> str:
@@ -778,10 +768,10 @@ def url_fix(s: str, charset: str = "utf-8") -> str:
as a string.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4.
+ Will be removed in Werkzeug 3.0.
"""
warnings.warn(
- "'werkzeug.urls.url_fix' is deprecated and will be removed in Werkzeug 2.4.",
+ "'werkzeug.urls.url_fix' is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -795,13 +785,11 @@ def url_fix(s: str, charset: str = "utf-8") -> str:
if s.startswith("file://") and s[7:8].isalpha() and s[8:10] in (":/", "|/"):
s = f"file:///{s[7:]}"
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'werkzeug", DeprecationWarning)
- url = url_parse(s)
- path = url_quote(url.path, charset, safe="/%+$!*'(),")
- qs = url_quote_plus(url.query, charset, safe=":&%=+$!*'(),")
- anchor = url_quote_plus(url.fragment, charset, safe=":&%=+$!*'(),")
- return url_unparse((url.scheme, url.encode_netloc(), path, qs, anchor))
+ url = url_parse(s)
+ path = url_quote(url.path, charset, safe="/%+$!*'(),")
+ qs = url_quote_plus(url.query, charset, safe=":&%=+$!*'(),")
+ anchor = url_quote_plus(url.fragment, charset, safe=":&%=+$!*'(),")
+ return url_unparse((url.scheme, url.encode_netloc(), path, qs, anchor))
def _codec_error_url_quote(e: UnicodeError) -> tuple[str, int]:
@@ -868,7 +856,7 @@ def uri_to_iri(
.. versionchanged:: 2.3
Passing a tuple or bytes, and the ``charset`` and ``errors`` parameters, are
- deprecated and will be removed in Werkzeug 2.4.
+ deprecated and will be removed in Werkzeug 3.0.
.. versionchanged:: 2.3
Which characters remain quoted is specific to each part of the URL.
@@ -882,7 +870,7 @@ def uri_to_iri(
"""
if isinstance(uri, tuple):
warnings.warn(
- "Passing a tuple is deprecated and will not be supported in Werkzeug 2.4.",
+ "Passing a tuple is deprecated and will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -890,7 +878,7 @@ def uri_to_iri(
if isinstance(uri, bytes):
warnings.warn(
- "Passing bytes is deprecated and will not be supported in Werkzeug 2.4.",
+ "Passing bytes is deprecated and will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -899,7 +887,7 @@ def uri_to_iri(
if charset is not None:
warnings.warn(
"The 'charset' parameter is deprecated and will be removed"
- " in Werkzeug 2.4.",
+ " in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -908,7 +896,7 @@ def uri_to_iri(
if errors is not None:
warnings.warn(
- "The 'errors' parameter is deprecated and will be removed in Werkzeug 2.4.",
+ "The 'errors' parameter is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -960,7 +948,7 @@ def iri_to_uri(
.. versionchanged:: 2.3
Passing a tuple or bytes, and the ``charset`` and ``errors`` parameters, are
- deprecated and will be removed in Werkzeug 2.4.
+ deprecated and will be removed in Werkzeug 3.0.
.. versionchanged:: 2.3
Which characters remain unquoted is specific to each part of the URL.
@@ -980,7 +968,7 @@ def iri_to_uri(
"""
if isinstance(iri, tuple):
warnings.warn(
- "Passing a tuple is deprecated and will not be supported in Werkzeug 2.4.",
+ "Passing a tuple is deprecated and will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -988,7 +976,7 @@ def iri_to_uri(
if isinstance(iri, bytes):
warnings.warn(
- "Passing bytes is deprecated and will not be supported in Werkzeug 2.4.",
+ "Passing bytes is deprecated and will not be supported in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -997,7 +985,7 @@ def iri_to_uri(
if charset is not None:
warnings.warn(
"The 'charset' parameter is deprecated and will be removed"
- " in Werkzeug 2.4.",
+ " in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -1006,7 +994,7 @@ def iri_to_uri(
if errors is not None:
warnings.warn(
- "The 'errors' parameter is deprecated and will be removed in Werkzeug 2.4.",
+ "The 'errors' parameter is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -1016,7 +1004,7 @@ def iri_to_uri(
if safe_conversion is not None:
warnings.warn(
"The 'safe_conversion' parameter is deprecated and will be removed in"
- " Werkzeug 2.4.",
+ " Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -1065,6 +1053,27 @@ def iri_to_uri(
return urlunsplit((parts.scheme, netloc, path, query, fragment))
+def _invalid_iri_to_uri(iri: str) -> str:
+ """The URL scheme ``itms-services://`` must contain the ``//`` even though it does
+ not have a host component. There may be other invalid schemes as well. Currently,
+ responses will always call ``iri_to_uri`` on the redirect ``Location`` header, which
+ removes the ``//``. For now, if the IRI only contains ASCII and does not contain
+ spaces, pass it on as-is. In Werkzeug 3.0, this should become a
+ ``response.process_location`` flag.
+
+ :meta private:
+ """
+ try:
+ iri.encode("ascii")
+ except UnicodeError:
+ pass
+ else:
+ if len(iri.split(None, 1)) == 1:
+ return iri
+
+ return iri_to_uri(iri)
+
+
def url_decode(
s: t.AnyStr,
charset: str = "utf-8",
@@ -1084,7 +1093,7 @@ def url_decode(
:param cls: Container to hold result instead of :class:`MultiDict`.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4. Use ``urllib.parse.parse_qs`` instead.
+ Will be removed in Werkzeug 3.0. Use ``urllib.parse.parse_qs`` instead.
.. versionchanged:: 2.1
The ``decode_keys`` parameter was removed.
@@ -1171,9 +1180,7 @@ def url_decode_stream(
cls = MultiDict
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'make_chunk_iter", DeprecationWarning)
- return cls(decoder)
+ return cls(decoder)
def _url_decode_impl(
diff --git a/src/werkzeug/utils.py b/src/werkzeug/utils.py
index d0841d84..785ac28b 100644
--- a/src/werkzeug/utils.py
+++ b/src/werkzeug/utils.py
@@ -260,21 +260,17 @@ def redirect(
response. The default is :class:`werkzeug.wrappers.Response` if
unspecified.
"""
- from .urls import iri_to_uri
-
if Response is None:
from .wrappers import Response
- display_location = escape(location)
- location = iri_to_uri(location)
+ html_location = escape(location)
response = Response( # type: ignore[misc]
"<!doctype html>\n"
"<html lang=en>\n"
"<title>Redirecting...</title>\n"
"<h1>Redirecting...</h1>\n"
"<p>You should be redirected automatically to the target URL: "
- f'<a href="{escape(location)}">{display_location}</a>. If'
- " not, click the link.\n",
+ f'<a href="{html_location}">{html_location}</a>. If not, click the link.\n',
code,
mimetype="text/html",
)
diff --git a/src/werkzeug/wrappers/response.py b/src/werkzeug/wrappers/response.py
index d2f20091..c8488094 100644
--- a/src/werkzeug/wrappers/response.py
+++ b/src/werkzeug/wrappers/response.py
@@ -8,6 +8,7 @@ from urllib.parse import urljoin
from ..datastructures import Headers
from ..http import remove_entity_headers
from ..sansio.response import Response as _SansIOResponse
+from ..urls import _invalid_iri_to_uri
from ..urls import iri_to_uri
from ..utils import cached_property
from ..wsgi import ClosingIterator
@@ -478,11 +479,11 @@ class Response(_SansIOResponse):
elif ikey == "content-length":
content_length = value
- # make sure the location header is an absolute URL
if location is not None:
- location = iri_to_uri(location)
+ location = _invalid_iri_to_uri(location)
if self.autocorrect_location_header:
+ # Make the location header an absolute URL.
current_url = get_current_url(environ, strip_querystring=True)
current_url = iri_to_uri(current_url)
location = urljoin(current_url, location)
diff --git a/src/werkzeug/wsgi.py b/src/werkzeug/wsgi.py
index a3a2fbb5..6061e114 100644
--- a/src/werkzeug/wsgi.py
+++ b/src/werkzeug/wsgi.py
@@ -211,14 +211,14 @@ def get_path_info(
.. versionchanged:: 2.3
The ``charset`` and ``errors`` parameters are deprecated and will be removed in
- Werkzeug 2.4.
+ Werkzeug 3.0.
.. versionadded:: 0.9
"""
if charset is not ...:
warnings.warn(
"The 'charset' parameter is deprecated and will be removed"
- " in Werkzeug 2.4.",
+ " in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -230,7 +230,7 @@ def get_path_info(
if errors is not None:
warnings.warn(
- "The 'errors' parameter is deprecated and will be removed in Werkzeug 2.4",
+ "The 'errors' parameter is deprecated and will be removed in Werkzeug 3.0",
DeprecationWarning,
stacklevel=2,
)
@@ -462,7 +462,7 @@ def _make_chunk_iter(
) -> t.Iterator[bytes]:
"""Helper for the line and chunk iter functions."""
warnings.warn(
- "'_make_chunk_iter' is deprecated and will be removed in Werkzeug 2.4.",
+ "'_make_chunk_iter' is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
@@ -506,7 +506,7 @@ def make_line_iter(
over the input stream using this helper function.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4.
+ Will be removed in Werkzeug 3.0.
.. versionadded:: 0.11
added support for the `cap_at_buffer` parameter.
@@ -528,15 +528,13 @@ def make_line_iter(
of two however.
"""
warnings.warn(
- "'make_line_iter' is deprecated and will be removed in Werkzeug 2.4.",
+ "'make_line_iter' is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
_iter = _make_chunk_iter(stream, limit, buffer_size)
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'_make_chunk_iter", DeprecationWarning)
- first_item = next(_iter, "")
+ first_item = next(_iter, "")
if not first_item:
return
@@ -603,7 +601,7 @@ def make_chunk_iter(
supports arbitrary newline markers.
.. deprecated:: 2.3
- Will be removed in Werkzeug 2.4.
+ Will be removed in Werkzeug 3.0.
.. versionchanged:: 0.11
added support for the `cap_at_buffer` parameter.
@@ -625,15 +623,13 @@ def make_chunk_iter(
of two however.
"""
warnings.warn(
- "'make_chunk_iter' is deprecated and will be removed in Werkzeug 2.4.",
+ "'make_chunk_iter' is deprecated and will be removed in Werkzeug 3.0.",
DeprecationWarning,
stacklevel=2,
)
_iter = _make_chunk_iter(stream, limit, buffer_size)
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", "'_make_chunk_iter", DeprecationWarning)
- first_item = next(_iter, b"")
+ first_item = next(_iter, b"")
if not first_item:
return
diff --git a/tests/test_formparser.py b/tests/test_formparser.py
index f9b44d7c..1dcb167e 100644
--- a/tests/test_formparser.py
+++ b/tests/test_formparser.py
@@ -126,8 +126,8 @@ class TestFormParser:
r = Request.from_values(method="POST", data={"a": 1, "b": 2})
r.max_form_parts = 1
- with pytest.raises(RequestEntityTooLarge):
- r.form
+ assert r.form["a"] == "1"
+ assert r.form["b"] == "2"
def test_missing_multipart_boundary(self):
data = (
diff --git a/tests/test_urls.py b/tests/test_urls.py
index 8baf0ad6..56bca8e9 100644
--- a/tests/test_urls.py
+++ b/tests/test_urls.py
@@ -1,11 +1,15 @@
import io
+import warnings
import pytest
from werkzeug import urls
from werkzeug.datastructures import OrderedMultiDict
-pytestmark = [pytest.mark.filterwarnings("ignore:'werkzeug:DeprecationWarning")]
+pytestmark = [
+ pytest.mark.filterwarnings("ignore:'werkzeug:DeprecationWarning"),
+ pytest.mark.filterwarnings("ignore:'_?make_chunk_iter':DeprecationWarning"),
+]
def test_parsing():
@@ -382,3 +386,12 @@ def test_iri_to_uri_dont_quote_valid_code_points():
# [] are not valid URL code points according to WhatWG URL Standard
# https://url.spec.whatwg.org/#url-code-points
assert urls.iri_to_uri("/path[bracket]?(paren)") == "/path%5Bbracket%5D?(paren)"
+
+
+def test_url_parse_does_not_clear_warnings_registry(recwarn):
+ warnings.simplefilter("default")
+ warnings.simplefilter("ignore", DeprecationWarning)
+ for _ in range(2):
+ urls.url_parse("http://example.org/")
+ warnings.warn("test warning")
+ assert len(recwarn) == 1
diff --git a/tests/test_utils.py b/tests/test_utils.py
index ed8d8d03..b7f1bcb1 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import inspect
from datetime import datetime
@@ -9,48 +11,32 @@ from werkzeug.datastructures import Headers
from werkzeug.http import http_date
from werkzeug.http import parse_date
from werkzeug.test import Client
+from werkzeug.test import EnvironBuilder
from werkzeug.wrappers import Response
-def test_redirect():
- resp = utils.redirect("/füübär")
- assert resp.headers["Location"] == "/f%C3%BC%C3%BCb%C3%A4r"
- assert resp.status_code == 302
- assert resp.get_data() == (
- b"<!doctype html>\n"
- b"<html lang=en>\n"
- b"<title>Redirecting...</title>\n"
- b"<h1>Redirecting...</h1>\n"
- b"<p>You should be redirected automatically to the target URL: "
- b'<a href="/f%C3%BC%C3%BCb%C3%A4r">/f\xc3\xbc\xc3\xbcb\xc3\xa4r</a>. '
- b"If not, click the link.\n"
- )
+@pytest.mark.parametrize(
+ ("url", "code", "expect"),
+ [
+ ("http://example.com", None, "http://example.com"),
+ ("/füübär", 305, "/f%C3%BC%C3%BCb%C3%A4r"),
+ ("http://☃.example.com/", 307, "http://xn--n3h.example.com/"),
+ ("itms-services://?url=abc", None, "itms-services://?url=abc"),
+ ],
+)
+def test_redirect(url: str, code: int | None, expect: str) -> None:
+ environ = EnvironBuilder().get_environ()
- resp = utils.redirect("http://☃.net/", 307)
- assert resp.headers["Location"] == "http://xn--n3h.net/"
- assert resp.status_code == 307
- assert resp.get_data() == (
- b"<!doctype html>\n"
- b"<html lang=en>\n"
- b"<title>Redirecting...</title>\n"
- b"<h1>Redirecting...</h1>\n"
- b"<p>You should be redirected automatically to the target URL: "
- b'<a href="http://xn--n3h.net/">http://\xe2\x98\x83.net/</a>. '
- b"If not, click the link.\n"
- )
+ if code is None:
+ resp = utils.redirect(url)
+ assert resp.status_code == 302
+ else:
+ resp = utils.redirect(url, code)
+ assert resp.status_code == code
- resp = utils.redirect("http://example.com/", 305)
- assert resp.headers["Location"] == "http://example.com/"
- assert resp.status_code == 305
- assert resp.get_data() == (
- b"<!doctype html>\n"
- b"<html lang=en>\n"
- b"<title>Redirecting...</title>\n"
- b"<h1>Redirecting...</h1>\n"
- b"<p>You should be redirected automatically to the target URL: "
- b'<a href="http://example.com/">http://example.com/</a>. '
- b"If not, click the link.\n"
- )
+ assert resp.headers["Location"] == url
+ assert resp.get_wsgi_headers(environ)["Location"] == expect
+ assert resp.get_data(as_text=True).count(url) == 2
def test_redirect_xss():
diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py
index d8335e5a..5f37aca9 100644
--- a/tests/test_wsgi.py
+++ b/tests/test_wsgi.py
@@ -257,6 +257,7 @@ def test_get_current_url_invalid_utf8():
@pytest.mark.filterwarnings("ignore:'make_line_iter:DeprecationWarning")
+@pytest.mark.filterwarnings("ignore:'_make_chunk_iter:DeprecationWarning")
def test_multi_part_line_breaks():
data = b"abcdef\r\nghijkl\r\nmnopqrstuvwxyz\r\nABCDEFGHIJK"
test_stream = io.BytesIO(data)
@@ -279,6 +280,7 @@ def test_multi_part_line_breaks():
@pytest.mark.filterwarnings("ignore:'make_line_iter:DeprecationWarning")
+@pytest.mark.filterwarnings("ignore:'_make_chunk_iter:DeprecationWarning")
def test_multi_part_line_breaks_bytes():
data = b"abcdef\r\nghijkl\r\nmnopqrstuvwxyz\r\nABCDEFGHIJK"
test_stream = io.BytesIO(data)
@@ -301,6 +303,7 @@ def test_multi_part_line_breaks_bytes():
@pytest.mark.filterwarnings("ignore:'make_line_iter:DeprecationWarning")
+@pytest.mark.filterwarnings("ignore:'_make_chunk_iter:DeprecationWarning")
def test_multi_part_line_breaks_problematic():
data = b"abc\rdef\r\nghi"
for _ in range(1, 10):
@@ -310,13 +313,14 @@ def test_multi_part_line_breaks_problematic():
@pytest.mark.filterwarnings("ignore:'make_line_iter:DeprecationWarning")
+@pytest.mark.filterwarnings("ignore:'_make_chunk_iter:DeprecationWarning")
def test_iter_functions_support_iterators():
data = ["abcdef\r\nghi", "jkl\r\nmnopqrstuvwxyz\r", "\nABCDEFGHIJK"]
lines = list(wsgi.make_line_iter(data))
assert lines == ["abcdef\r\n", "ghijkl\r\n", "mnopqrstuvwxyz\r\n", "ABCDEFGHIJK"]
-@pytest.mark.filterwarnings("ignore:'make_chunk_iter:DeprecationWarning")
+@pytest.mark.filterwarnings("ignore:'_?make_chunk_iter:DeprecationWarning")
def test_make_chunk_iter():
data = [b"abcdefXghi", b"jklXmnopqrstuvwxyzX", b"ABCDEFGHIJK"]
rv = list(wsgi.make_chunk_iter(data, b"X"))
@@ -328,7 +332,7 @@ def test_make_chunk_iter():
assert rv == [b"abcdef", b"ghijkl", b"mnopqrstuvwxyz", b"ABCDEFGHIJK"]
-@pytest.mark.filterwarnings("ignore:'make_chunk_iter:DeprecationWarning")
+@pytest.mark.filterwarnings("ignore:'_?make_chunk_iter:DeprecationWarning")
def test_make_chunk_iter_bytes():
data = [b"abcdefXghi", b"jklXmnopqrstuvwxyzX", b"ABCDEFGHIJK"]
rv = list(wsgi.make_chunk_iter(data, "X"))
@@ -362,6 +366,7 @@ def test_make_chunk_iter_bytes():
@pytest.mark.filterwarnings("ignore:'make_line_iter:DeprecationWarning")
+@pytest.mark.filterwarnings("ignore:'_make_chunk_iter:DeprecationWarning")
def test_lines_longer_buffer_size():
data = b"1234567890\n1234567890\n"
for bufsize in range(1, 15):
@@ -372,6 +377,7 @@ def test_lines_longer_buffer_size():
@pytest.mark.filterwarnings("ignore:'make_line_iter:DeprecationWarning")
+@pytest.mark.filterwarnings("ignore:'_make_chunk_iter:DeprecationWarning")
def test_lines_longer_buffer_size_cap():
data = b"1234567890\n1234567890\n"
for bufsize in range(1, 15):