Skip to content

Commit c83d003

Browse files
bpo-19094: Raise TypeError in urljoin(), urlparse(), and urlsplit() for inappropriate types
1 parent 983c746 commit c83d003

File tree

5 files changed

+73
-3
lines changed

5 files changed

+73
-3
lines changed

Doc/library/urllib.parse.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ or on combining URL components into a URL string.
220220
.. versionchanged:: 3.15
221221
Added the *missing_as_none* parameter.
222222

223+
.. versionchanged:: 3.15
224+
Values for ``url`` and ``scheme`` other than strings or bytes raise
225+
:exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to be
226+
changed to :exc:`TypeError` in future versions of Python).
227+
223228
.. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser
224229

225230

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

323+
.. versionchanged:: 3.15
324+
Values for ``qs`` and ``separator`` other than strings or bytes raise
325+
:exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to be
326+
changed to :exc:`TypeError` in future versions of Python).
327+
318328

319329
.. function:: urlunsplit(parts)
320330
urlunsplit(parts, *, keep_empty)
@@ -374,6 +384,11 @@ or on combining URL components into a URL string.
374384
.. versionchanged:: 3.15
375385
Added the *keep_empty* parameter.
376386

387+
.. versionchanged:: 3.15
388+
Items in ``parts`` other than strings or bytes raise
389+
:exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to be
390+
changed to :exc:`TypeError` in future versions of Python).
391+
377392

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

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

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

435+
.. versionchanged:: 3.15
436+
Values for ``base`` and ``url`` other than strings or bytes raise
437+
:exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to be
438+
changed to :exc:`TypeError` in future versions of Python).
439+
420440

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

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

470+
.. versionchanged:: 3.15
471+
Values other than strings or bytes raise
472+
:exc:`TypeError` if true or :exc:`DeprecationWarning` if false (to be
473+
changed to :exc:`TypeError` in future versions of Python).
474+
450475
.. function:: unwrap(url)
451476

452477
Extract the url from a wrapped URL (that is, a string formatted as

Doc/whatsnew/3.15.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,12 @@ New deprecations
17611761
:func:`issubclass`, but warnings were not previously emitted if it was
17621762
merely imported or accessed from the :mod:`!typing` module.
17631763

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

17651771
* ``__version__``
17661772

Lib/test/test_urlparse.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,14 @@ def test_mixed_types_rejected(self):
12551255
with self.assertRaisesRegex(TypeError, "Cannot mix str"):
12561256
urllib.parse.urljoin(b"http://python.org", "http://python.org")
12571257

1258+
def test_non_string_true_values_rejected(self):
1259+
# True values raise informative TypeErrors
1260+
msg = "Expected a string or bytes object: got <class "
1261+
with self.assertRaisesRegex(TypeError, msg):
1262+
urllib.parse.urlsplit(1, b'http')
1263+
12581264
def _check_result_type(self, str_type, str_args):
1265+
num_args = len(str_type._fields)
12591266
bytes_type = str_type._encoded_counterpart
12601267
self.assertIs(bytes_type._decoded_counterpart, str_type)
12611268
bytes_args = tuple_encode(str_args)
@@ -1987,6 +1994,27 @@ def test_to_bytes_deprecation(self):
19871994
self.assertEqual(str(cm.warning),
19881995
'urllib.parse.to_bytes() is deprecated as of 3.8')
19891996

1997+
def test_falsey_deprecation(self):
1998+
pattern = (
1999+
"Providing false values other than strings or bytes to urllib.parse "
2000+
"is deprecated: got <class "
2001+
)
2002+
cases = [
2003+
(urllib.parse.urljoin, ['http://www.python.org', []]),
2004+
(urllib.parse.urljoin, [[], b'docs']),
2005+
(urllib.parse.urlparse, [b'www.python.org', None]),
2006+
(urllib.parse.urlparse, [{}, '']),
2007+
(urllib.parse.urlsplit, [0, b'http']),
2008+
(urllib.parse.urlsplit, [b'http://www.python.org', None]),
2009+
(urllib.parse.urldefrag, [{}]),
2010+
(urllib.parse.urlunparse, [[None, b'www.python.org', None, None, None, None]]),
2011+
(urllib.parse.urlunsplit, [['http', 0, '', '', '']]),
2012+
]
2013+
for callable, args in cases:
2014+
with self.subTest(callable=callable):
2015+
with self.assertWarnsRegex(DeprecationWarning, pattern):
2016+
callable(*args)
2017+
19902018

19912019
def str_encode(s):
19922020
if s is None:

Lib/urllib/parse.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ def _coerce_args(*args):
131131
else:
132132
if isinstance(arg, str) != str_input:
133133
raise TypeError("Cannot mix str and non-str arguments")
134+
if not hasattr(arg, 'decode'):
135+
if arg:
136+
raise TypeError(f"Expected a string or bytes object: got {type(arg)}")
137+
else:
138+
warnings.warn(
139+
f"Providing false values other than strings or bytes "
140+
f"to urllib.parse is deprecated: got {type(arg)}",
141+
DeprecationWarning, stacklevel=3)
134142
if str_input is None:
135143
for arg in args:
136144
if arg is not None:
@@ -697,12 +705,12 @@ def _urlunsplit(scheme, netloc, url, query, fragment):
697705
def urljoin(base, url, allow_fragments=True):
698706
"""Join a base URL and a possibly relative URL to form an absolute
699707
interpretation of the latter."""
708+
base, url, _coerce_result = _coerce_args(base, url)
700709
if not base:
701-
return url
710+
return _coerce_result(url)
702711
if not url:
703-
return base
712+
return _coerce_result(base)
704713

705-
base, url, _coerce_result = _coerce_args(base, url)
706714
bscheme, bnetloc, bpath, bquery, bfragment = \
707715
_urlsplit(base, None, allow_fragments)
708716
scheme, netloc, path, query, fragment = \
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Providing anything but a string or bytes object to :mod:`urllib.parse`
2+
functions expecting strings or bytes now raises :exc:`DeprecationWarning`
3+
if the value tests false, or :exc:`TypeError` if it tests true.

0 commit comments

Comments
 (0)