Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Doc/library/urllib.parse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ or on combining URL components into a URL string.
.. versionchanged:: 3.15
Added the *missing_as_none* parameter.

.. versionchanged:: 3.15
Values for ``url`` and ``scheme`` other than strings, bytes, or ``None``
raise :exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to
be changed to :exc:`TypeError` in future versions of Python).

.. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser


Expand Down Expand Up @@ -315,6 +320,11 @@ or on combining URL components into a URL string.
query parameter separator. This has been changed to allow only a single
separator key, with ``&`` as the default separator.

.. versionchanged:: 3.15
Values for ``qs`` and ``separator`` other than strings, bytes, or
``None`` raise :exc:`TypeError` if true or :exc:`DeprecationWarning` if
false (to be changed to :exc:`TypeError` in future versions of Python).


.. function:: urlunsplit(parts)
urlunsplit(parts, *, keep_empty)
Expand Down Expand Up @@ -374,6 +384,11 @@ or on combining URL components into a URL string.
.. versionchanged:: 3.15
Added the *keep_empty* parameter.

.. versionchanged:: 3.15
Items in ``parts`` other than strings, bytes, or ``None`` raise
:exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to be
changed to :exc:`TypeError` in future versions of Python).


.. function:: urljoin(base, url, allow_fragments=True)

Expand Down Expand Up @@ -417,6 +432,11 @@ or on combining URL components into a URL string.

Behavior updated to match the semantics defined in :rfc:`3986`.

.. versionchanged:: 3.15
Values for ``base`` and ``url`` other than strings, bytes, or ``None``
raise :exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to
be changed to :exc:`TypeError` in future versions of Python).


.. function:: urldefrag(url, *, missing_as_none=False)

Expand Down Expand Up @@ -447,6 +467,11 @@ or on combining URL components into a URL string.
.. versionchanged:: 3.15
Added the *missing_as_none* parameter.

.. versionchanged:: 3.15
Values other than other than strings, bytes, or ``None`` raise
:exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to be
changed to :exc:`TypeError` in future versions of Python).

.. function:: unwrap(url)

Extract the url from a wrapped URL (that is, a string formatted as
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,13 @@ New deprecations
:func:`issubclass`, but warnings were not previously emitted if it was
merely imported or accessed from the :mod:`!typing` module.

* :mod:`urllib`:

* Providing anything but a string, bytes object, or ``None`` to
:mod:`urllib.parse` functions expecting strings or bytes now raises
:exc:`DeprecationWarning` if the value tests false, or :exc:`TypeError` if
it tests true.
(Contributed by Jacob Walls in :issue:`19094`.)

* ``__version__``

Expand Down
24 changes: 24 additions & 0 deletions Lib/test/test_urlparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,14 @@ def test_mixed_types_rejected(self):
with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urljoin(b"http://python.org", "http://python.org")

def test_non_string_true_values_rejected(self):
# True values raise informative TypeErrors
msg = "Expected a string, bytes, or None: got <class "
with self.assertRaisesRegex(TypeError, msg):
urllib.parse.urlsplit(1, b'http')

def _check_result_type(self, str_type, str_args):
num_args = len(str_type._fields)
bytes_type = str_type._encoded_counterpart
self.assertIs(bytes_type._decoded_counterpart, str_type)
bytes_args = tuple_encode(str_args)
Expand Down Expand Up @@ -1987,6 +1994,23 @@ def test_to_bytes_deprecation(self):
self.assertEqual(str(cm.warning),
'urllib.parse.to_bytes() is deprecated as of 3.8')

def test_falsey_deprecation(self):
cases = [
(urllib.parse.urljoin, ['http://www.python.org', []]),
(urllib.parse.urljoin, [[], b'docs']),
(urllib.parse.urlparse, [b'www.python.org', {}]),
(urllib.parse.urlparse, [{}, '']),
(urllib.parse.urlsplit, [0, b'http']),
(urllib.parse.urlsplit, [b'http://www.python.org', 0]),
(urllib.parse.urldefrag, [{}]),
(urllib.parse.urlunparse, [[None, b'www.python.org', (), (), (), ()]]),
(urllib.parse.urlunsplit, [['http', 0, '', '', '']]),
]
for callable, args in cases:
with self.subTest(callable=callable.__name__, args=args):
with self.assertWarnsRegex(DeprecationWarning, "false values"):
callable(*args)


def str_encode(s):
if s is None:
Expand Down
14 changes: 11 additions & 3 deletions Lib/urllib/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,21 @@ def _coerce_args(*args):
# - noop for str inputs
# - encoding function otherwise
str_input = None
empty_values = {"", b"", None}
for arg in args:
if arg:
if str_input is None:
str_input = isinstance(arg, str)
else:
if isinstance(arg, str) != str_input:
raise TypeError("Cannot mix str and non-str arguments")
if arg is not None and str_input is False and not hasattr(arg, "decode"):
raise TypeError(f"Expected a string, bytes, or None: got {type(arg)}")
elif arg is not None and not isinstance(arg, str) and not hasattr(arg, "decode"):
warnings.warn(
f"Providing false values other than empty strings, bytes, or"
f"None to urllib.parse is deprecated: got {type(arg)}",
DeprecationWarning, stacklevel=3)
if str_input is None:
for arg in args:
if arg is not None:
Expand Down Expand Up @@ -697,12 +705,12 @@ def _urlunsplit(scheme, netloc, url, query, fragment):
def urljoin(base, url, allow_fragments=True):
"""Join a base URL and a possibly relative URL to form an absolute
interpretation of the latter."""
base, url, _coerce_result = _coerce_args(base, url)
if not base:
return url
return _coerce_result(url)
if not url:
return base
return _coerce_result(base)

base, url, _coerce_result = _coerce_args(base, url)
bscheme, bnetloc, bpath, bquery, bfragment = \
_urlsplit(base, None, allow_fragments)
scheme, netloc, path, query, fragment = \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Providing anything but a string or bytes object to :mod:`urllib.parse`
functions expecting strings or bytes now raises :exc:`DeprecationWarning`
if the value tests false, or :exc:`TypeError` if it tests true.
Loading