1import sys 2import unicodedata 3import unittest 4import urllib.parse 5 6RFC1808_BASE = "http://a/b/c/d;p?q#f" 7RFC2396_BASE = "http://a/b/c/d;p?q" 8RFC3986_BASE = 'http://a/b/c/d;p?q' 9SIMPLE_BASE = 'http://a/b/c/d' 10 11# Each parse_qsl testcase is a two-tuple that contains 12# a string with the query and a list with the expected result. 13 14parse_qsl_test_cases = [ 15 ("", []), 16 ("&", []), 17 ("&&", []), 18 ("=", [('', '')]), 19 ("=a", [('', 'a')]), 20 ("a", [('a', '')]), 21 ("a=", [('a', '')]), 22 ("&a=b", [('a', 'b')]), 23 ("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]), 24 ("a=1&a=2", [('a', '1'), ('a', '2')]), 25 (b"", []), 26 (b"&", []), 27 (b"&&", []), 28 (b"=", [(b'', b'')]), 29 (b"=a", [(b'', b'a')]), 30 (b"a", [(b'a', b'')]), 31 (b"a=", [(b'a', b'')]), 32 (b"&a=b", [(b'a', b'b')]), 33 (b"a=a+b&b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), 34 (b"a=1&a=2", [(b'a', b'1'), (b'a', b'2')]), 35 (";a=b", [(';a', 'b')]), 36 ("a=a+b;b=b+c", [('a', 'a b;b=b c')]), 37 (b";a=b", [(b';a', b'b')]), 38 (b"a=a+b;b=b+c", [(b'a', b'a b;b=b c')]), 39] 40 41# Each parse_qs testcase is a two-tuple that contains 42# a string with the query and a dictionary with the expected result. 43 44parse_qs_test_cases = [ 45 ("", {}), 46 ("&", {}), 47 ("&&", {}), 48 ("=", {'': ['']}), 49 ("=a", {'': ['a']}), 50 ("a", {'a': ['']}), 51 ("a=", {'a': ['']}), 52 ("&a=b", {'a': ['b']}), 53 ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}), 54 ("a=1&a=2", {'a': ['1', '2']}), 55 (b"", {}), 56 (b"&", {}), 57 (b"&&", {}), 58 (b"=", {b'': [b'']}), 59 (b"=a", {b'': [b'a']}), 60 (b"a", {b'a': [b'']}), 61 (b"a=", {b'a': [b'']}), 62 (b"&a=b", {b'a': [b'b']}), 63 (b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), 64 (b"a=1&a=2", {b'a': [b'1', b'2']}), 65 (";a=b", {';a': ['b']}), 66 ("a=a+b;b=b+c", {'a': ['a b;b=b c']}), 67 (b";a=b", {b';a': [b'b']}), 68 (b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}), 69] 70 71class UrlParseTestCase(unittest.TestCase): 72 73 def checkRoundtrips(self, url, parsed, split): 74 result = urllib.parse.urlparse(url) 75 self.assertEqual(result, parsed) 76 t = (result.scheme, result.netloc, result.path, 77 result.params, result.query, result.fragment) 78 self.assertEqual(t, parsed) 79 # put it back together and it should be the same 80 result2 = urllib.parse.urlunparse(result) 81 self.assertEqual(result2, url) 82 self.assertEqual(result2, result.geturl()) 83 84 # the result of geturl() is a fixpoint; we can always parse it 85 # again to get the same result: 86 result3 = urllib.parse.urlparse(result.geturl()) 87 self.assertEqual(result3.geturl(), result.geturl()) 88 self.assertEqual(result3, result) 89 self.assertEqual(result3.scheme, result.scheme) 90 self.assertEqual(result3.netloc, result.netloc) 91 self.assertEqual(result3.path, result.path) 92 self.assertEqual(result3.params, result.params) 93 self.assertEqual(result3.query, result.query) 94 self.assertEqual(result3.fragment, result.fragment) 95 self.assertEqual(result3.username, result.username) 96 self.assertEqual(result3.password, result.password) 97 self.assertEqual(result3.hostname, result.hostname) 98 self.assertEqual(result3.port, result.port) 99 100 # check the roundtrip using urlsplit() as well 101 result = urllib.parse.urlsplit(url) 102 self.assertEqual(result, split) 103 t = (result.scheme, result.netloc, result.path, 104 result.query, result.fragment) 105 self.assertEqual(t, split) 106 result2 = urllib.parse.urlunsplit(result) 107 self.assertEqual(result2, url) 108 self.assertEqual(result2, result.geturl()) 109 110 # check the fixpoint property of re-parsing the result of geturl() 111 result3 = urllib.parse.urlsplit(result.geturl()) 112 self.assertEqual(result3.geturl(), result.geturl()) 113 self.assertEqual(result3, result) 114 self.assertEqual(result3.scheme, result.scheme) 115 self.assertEqual(result3.netloc, result.netloc) 116 self.assertEqual(result3.path, result.path) 117 self.assertEqual(result3.query, result.query) 118 self.assertEqual(result3.fragment, result.fragment) 119 self.assertEqual(result3.username, result.username) 120 self.assertEqual(result3.password, result.password) 121 self.assertEqual(result3.hostname, result.hostname) 122 self.assertEqual(result3.port, result.port) 123 124 def test_qsl(self): 125 for orig, expect in parse_qsl_test_cases: 126 result = urllib.parse.parse_qsl(orig, keep_blank_values=True) 127 self.assertEqual(result, expect, "Error parsing %r" % orig) 128 expect_without_blanks = [v for v in expect if len(v[1])] 129 result = urllib.parse.parse_qsl(orig, keep_blank_values=False) 130 self.assertEqual(result, expect_without_blanks, 131 "Error parsing %r" % orig) 132 133 def test_qs(self): 134 for orig, expect in parse_qs_test_cases: 135 result = urllib.parse.parse_qs(orig, keep_blank_values=True) 136 self.assertEqual(result, expect, "Error parsing %r" % orig) 137 expect_without_blanks = {v: expect[v] 138 for v in expect if len(expect[v][0])} 139 result = urllib.parse.parse_qs(orig, keep_blank_values=False) 140 self.assertEqual(result, expect_without_blanks, 141 "Error parsing %r" % orig) 142 143 def test_roundtrips(self): 144 str_cases = [ 145 ('file:///tmp/junk.txt', 146 ('file', '', '/tmp/junk.txt', '', '', ''), 147 ('file', '', '/tmp/junk.txt', '', '')), 148 ('imap://mail.python.org/mbox1', 149 ('imap', 'mail.python.org', '/mbox1', '', '', ''), 150 ('imap', 'mail.python.org', '/mbox1', '', '')), 151 ('mms://wms.sys.hinet.net/cts/Drama/09006251100.asf', 152 ('mms', 'wms.sys.hinet.net', '/cts/Drama/09006251100.asf', 153 '', '', ''), 154 ('mms', 'wms.sys.hinet.net', '/cts/Drama/09006251100.asf', 155 '', '')), 156 ('nfs://server/path/to/file.txt', 157 ('nfs', 'server', '/path/to/file.txt', '', '', ''), 158 ('nfs', 'server', '/path/to/file.txt', '', '')), 159 ('svn+ssh://svn.zope.org/repos/main/ZConfig/trunk/', 160 ('svn+ssh', 'svn.zope.org', '/repos/main/ZConfig/trunk/', 161 '', '', ''), 162 ('svn+ssh', 'svn.zope.org', '/repos/main/ZConfig/trunk/', 163 '', '')), 164 ('git+ssh://git@github.com/user/project.git', 165 ('git+ssh', 'git@github.com','/user/project.git', 166 '','',''), 167 ('git+ssh', 'git@github.com','/user/project.git', 168 '', '')), 169 ] 170 def _encode(t): 171 return (t[0].encode('ascii'), 172 tuple(x.encode('ascii') for x in t[1]), 173 tuple(x.encode('ascii') for x in t[2])) 174 bytes_cases = [_encode(x) for x in str_cases] 175 for url, parsed, split in str_cases + bytes_cases: 176 self.checkRoundtrips(url, parsed, split) 177 178 def test_http_roundtrips(self): 179 # urllib.parse.urlsplit treats 'http:' as an optimized special case, 180 # so we test both 'http:' and 'https:' in all the following. 181 # Three cheers for white box knowledge! 182 str_cases = [ 183 ('://www.python.org', 184 ('www.python.org', '', '', '', ''), 185 ('www.python.org', '', '', '')), 186 ('://www.python.org#abc', 187 ('www.python.org', '', '', '', 'abc'), 188 ('www.python.org', '', '', 'abc')), 189 ('://www.python.org?q=abc', 190 ('www.python.org', '', '', 'q=abc', ''), 191 ('www.python.org', '', 'q=abc', '')), 192 ('://www.python.org/#abc', 193 ('www.python.org', '/', '', '', 'abc'), 194 ('www.python.org', '/', '', 'abc')), 195 ('://a/b/c/d;p?q#f', 196 ('a', '/b/c/d', 'p', 'q', 'f'), 197 ('a', '/b/c/d;p', 'q', 'f')), 198 ] 199 def _encode(t): 200 return (t[0].encode('ascii'), 201 tuple(x.encode('ascii') for x in t[1]), 202 tuple(x.encode('ascii') for x in t[2])) 203 bytes_cases = [_encode(x) for x in str_cases] 204 str_schemes = ('http', 'https') 205 bytes_schemes = (b'http', b'https') 206 str_tests = str_schemes, str_cases 207 bytes_tests = bytes_schemes, bytes_cases 208 for schemes, test_cases in (str_tests, bytes_tests): 209 for scheme in schemes: 210 for url, parsed, split in test_cases: 211 url = scheme + url 212 parsed = (scheme,) + parsed 213 split = (scheme,) + split 214 self.checkRoundtrips(url, parsed, split) 215 216 def checkJoin(self, base, relurl, expected): 217 str_components = (base, relurl, expected) 218 self.assertEqual(urllib.parse.urljoin(base, relurl), expected) 219 bytes_components = baseb, relurlb, expectedb = [ 220 x.encode('ascii') for x in str_components] 221 self.assertEqual(urllib.parse.urljoin(baseb, relurlb), expectedb) 222 223 def test_unparse_parse(self): 224 str_cases = ['Python', './Python','x-newscheme://foo.com/stuff','x://y','x:/y','x:/','/',] 225 bytes_cases = [x.encode('ascii') for x in str_cases] 226 for u in str_cases + bytes_cases: 227 self.assertEqual(urllib.parse.urlunsplit(urllib.parse.urlsplit(u)), u) 228 self.assertEqual(urllib.parse.urlunparse(urllib.parse.urlparse(u)), u) 229 230 def test_RFC1808(self): 231 # "normal" cases from RFC 1808: 232 self.checkJoin(RFC1808_BASE, 'g:h', 'g:h') 233 self.checkJoin(RFC1808_BASE, 'g', 'http://a/b/c/g') 234 self.checkJoin(RFC1808_BASE, './g', 'http://a/b/c/g') 235 self.checkJoin(RFC1808_BASE, 'g/', 'http://a/b/c/g/') 236 self.checkJoin(RFC1808_BASE, '/g', 'http://a/g') 237 self.checkJoin(RFC1808_BASE, '//g', 'http://g') 238 self.checkJoin(RFC1808_BASE, 'g?y', 'http://a/b/c/g?y') 239 self.checkJoin(RFC1808_BASE, 'g?y/./x', 'http://a/b/c/g?y/./x') 240 self.checkJoin(RFC1808_BASE, '#s', 'http://a/b/c/d;p?q#s') 241 self.checkJoin(RFC1808_BASE, 'g#s', 'http://a/b/c/g#s') 242 self.checkJoin(RFC1808_BASE, 'g#s/./x', 'http://a/b/c/g#s/./x') 243 self.checkJoin(RFC1808_BASE, 'g?y#s', 'http://a/b/c/g?y#s') 244 self.checkJoin(RFC1808_BASE, 'g;x', 'http://a/b/c/g;x') 245 self.checkJoin(RFC1808_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s') 246 self.checkJoin(RFC1808_BASE, '.', 'http://a/b/c/') 247 self.checkJoin(RFC1808_BASE, './', 'http://a/b/c/') 248 self.checkJoin(RFC1808_BASE, '..', 'http://a/b/') 249 self.checkJoin(RFC1808_BASE, '../', 'http://a/b/') 250 self.checkJoin(RFC1808_BASE, '../g', 'http://a/b/g') 251 self.checkJoin(RFC1808_BASE, '../..', 'http://a/') 252 self.checkJoin(RFC1808_BASE, '../../', 'http://a/') 253 self.checkJoin(RFC1808_BASE, '../../g', 'http://a/g') 254 255 # "abnormal" cases from RFC 1808: 256 self.checkJoin(RFC1808_BASE, '', 'http://a/b/c/d;p?q#f') 257 self.checkJoin(RFC1808_BASE, 'g.', 'http://a/b/c/g.') 258 self.checkJoin(RFC1808_BASE, '.g', 'http://a/b/c/.g') 259 self.checkJoin(RFC1808_BASE, 'g..', 'http://a/b/c/g..') 260 self.checkJoin(RFC1808_BASE, '..g', 'http://a/b/c/..g') 261 self.checkJoin(RFC1808_BASE, './../g', 'http://a/b/g') 262 self.checkJoin(RFC1808_BASE, './g/.', 'http://a/b/c/g/') 263 self.checkJoin(RFC1808_BASE, 'g/./h', 'http://a/b/c/g/h') 264 self.checkJoin(RFC1808_BASE, 'g/../h', 'http://a/b/c/h') 265 266 # RFC 1808 and RFC 1630 disagree on these (according to RFC 1808), 267 # so we'll not actually run these tests (which expect 1808 behavior). 268 #self.checkJoin(RFC1808_BASE, 'http:g', 'http:g') 269 #self.checkJoin(RFC1808_BASE, 'http:', 'http:') 270 271 # XXX: The following tests are no longer compatible with RFC3986 272 # self.checkJoin(RFC1808_BASE, '../../../g', 'http://a/../g') 273 # self.checkJoin(RFC1808_BASE, '../../../../g', 'http://a/../../g') 274 # self.checkJoin(RFC1808_BASE, '/./g', 'http://a/./g') 275 # self.checkJoin(RFC1808_BASE, '/../g', 'http://a/../g') 276 277 278 def test_RFC2368(self): 279 # Issue 11467: path that starts with a number is not parsed correctly 280 self.assertEqual(urllib.parse.urlparse('mailto:1337@example.org'), 281 ('mailto', '', '1337@example.org', '', '', '')) 282 283 def test_RFC2396(self): 284 # cases from RFC 2396 285 286 self.checkJoin(RFC2396_BASE, 'g:h', 'g:h') 287 self.checkJoin(RFC2396_BASE, 'g', 'http://a/b/c/g') 288 self.checkJoin(RFC2396_BASE, './g', 'http://a/b/c/g') 289 self.checkJoin(RFC2396_BASE, 'g/', 'http://a/b/c/g/') 290 self.checkJoin(RFC2396_BASE, '/g', 'http://a/g') 291 self.checkJoin(RFC2396_BASE, '//g', 'http://g') 292 self.checkJoin(RFC2396_BASE, 'g?y', 'http://a/b/c/g?y') 293 self.checkJoin(RFC2396_BASE, '#s', 'http://a/b/c/d;p?q#s') 294 self.checkJoin(RFC2396_BASE, 'g#s', 'http://a/b/c/g#s') 295 self.checkJoin(RFC2396_BASE, 'g?y#s', 'http://a/b/c/g?y#s') 296 self.checkJoin(RFC2396_BASE, 'g;x', 'http://a/b/c/g;x') 297 self.checkJoin(RFC2396_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s') 298 self.checkJoin(RFC2396_BASE, '.', 'http://a/b/c/') 299 self.checkJoin(RFC2396_BASE, './', 'http://a/b/c/') 300 self.checkJoin(RFC2396_BASE, '..', 'http://a/b/') 301 self.checkJoin(RFC2396_BASE, '../', 'http://a/b/') 302 self.checkJoin(RFC2396_BASE, '../g', 'http://a/b/g') 303 self.checkJoin(RFC2396_BASE, '../..', 'http://a/') 304 self.checkJoin(RFC2396_BASE, '../../', 'http://a/') 305 self.checkJoin(RFC2396_BASE, '../../g', 'http://a/g') 306 self.checkJoin(RFC2396_BASE, '', RFC2396_BASE) 307 self.checkJoin(RFC2396_BASE, 'g.', 'http://a/b/c/g.') 308 self.checkJoin(RFC2396_BASE, '.g', 'http://a/b/c/.g') 309 self.checkJoin(RFC2396_BASE, 'g..', 'http://a/b/c/g..') 310 self.checkJoin(RFC2396_BASE, '..g', 'http://a/b/c/..g') 311 self.checkJoin(RFC2396_BASE, './../g', 'http://a/b/g') 312 self.checkJoin(RFC2396_BASE, './g/.', 'http://a/b/c/g/') 313 self.checkJoin(RFC2396_BASE, 'g/./h', 'http://a/b/c/g/h') 314 self.checkJoin(RFC2396_BASE, 'g/../h', 'http://a/b/c/h') 315 self.checkJoin(RFC2396_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y') 316 self.checkJoin(RFC2396_BASE, 'g;x=1/../y', 'http://a/b/c/y') 317 self.checkJoin(RFC2396_BASE, 'g?y/./x', 'http://a/b/c/g?y/./x') 318 self.checkJoin(RFC2396_BASE, 'g?y/../x', 'http://a/b/c/g?y/../x') 319 self.checkJoin(RFC2396_BASE, 'g#s/./x', 'http://a/b/c/g#s/./x') 320 self.checkJoin(RFC2396_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x') 321 322 # XXX: The following tests are no longer compatible with RFC3986 323 # self.checkJoin(RFC2396_BASE, '../../../g', 'http://a/../g') 324 # self.checkJoin(RFC2396_BASE, '../../../../g', 'http://a/../../g') 325 # self.checkJoin(RFC2396_BASE, '/./g', 'http://a/./g') 326 # self.checkJoin(RFC2396_BASE, '/../g', 'http://a/../g') 327 328 def test_RFC3986(self): 329 self.checkJoin(RFC3986_BASE, '?y','http://a/b/c/d;p?y') 330 self.checkJoin(RFC3986_BASE, ';x', 'http://a/b/c/;x') 331 self.checkJoin(RFC3986_BASE, 'g:h','g:h') 332 self.checkJoin(RFC3986_BASE, 'g','http://a/b/c/g') 333 self.checkJoin(RFC3986_BASE, './g','http://a/b/c/g') 334 self.checkJoin(RFC3986_BASE, 'g/','http://a/b/c/g/') 335 self.checkJoin(RFC3986_BASE, '/g','http://a/g') 336 self.checkJoin(RFC3986_BASE, '//g','http://g') 337 self.checkJoin(RFC3986_BASE, '?y','http://a/b/c/d;p?y') 338 self.checkJoin(RFC3986_BASE, 'g?y','http://a/b/c/g?y') 339 self.checkJoin(RFC3986_BASE, '#s','http://a/b/c/d;p?q#s') 340 self.checkJoin(RFC3986_BASE, 'g#s','http://a/b/c/g#s') 341 self.checkJoin(RFC3986_BASE, 'g?y#s','http://a/b/c/g?y#s') 342 self.checkJoin(RFC3986_BASE, ';x','http://a/b/c/;x') 343 self.checkJoin(RFC3986_BASE, 'g;x','http://a/b/c/g;x') 344 self.checkJoin(RFC3986_BASE, 'g;x?y#s','http://a/b/c/g;x?y#s') 345 self.checkJoin(RFC3986_BASE, '','http://a/b/c/d;p?q') 346 self.checkJoin(RFC3986_BASE, '.','http://a/b/c/') 347 self.checkJoin(RFC3986_BASE, './','http://a/b/c/') 348 self.checkJoin(RFC3986_BASE, '..','http://a/b/') 349 self.checkJoin(RFC3986_BASE, '../','http://a/b/') 350 self.checkJoin(RFC3986_BASE, '../g','http://a/b/g') 351 self.checkJoin(RFC3986_BASE, '../..','http://a/') 352 self.checkJoin(RFC3986_BASE, '../../','http://a/') 353 self.checkJoin(RFC3986_BASE, '../../g','http://a/g') 354 self.checkJoin(RFC3986_BASE, '../../../g', 'http://a/g') 355 356 # Abnormal Examples 357 358 # The 'abnormal scenarios' are incompatible with RFC2986 parsing 359 # Tests are here for reference. 360 361 self.checkJoin(RFC3986_BASE, '../../../g','http://a/g') 362 self.checkJoin(RFC3986_BASE, '../../../../g','http://a/g') 363 self.checkJoin(RFC3986_BASE, '/./g','http://a/g') 364 self.checkJoin(RFC3986_BASE, '/../g','http://a/g') 365 self.checkJoin(RFC3986_BASE, 'g.','http://a/b/c/g.') 366 self.checkJoin(RFC3986_BASE, '.g','http://a/b/c/.g') 367 self.checkJoin(RFC3986_BASE, 'g..','http://a/b/c/g..') 368 self.checkJoin(RFC3986_BASE, '..g','http://a/b/c/..g') 369 self.checkJoin(RFC3986_BASE, './../g','http://a/b/g') 370 self.checkJoin(RFC3986_BASE, './g/.','http://a/b/c/g/') 371 self.checkJoin(RFC3986_BASE, 'g/./h','http://a/b/c/g/h') 372 self.checkJoin(RFC3986_BASE, 'g/../h','http://a/b/c/h') 373 self.checkJoin(RFC3986_BASE, 'g;x=1/./y','http://a/b/c/g;x=1/y') 374 self.checkJoin(RFC3986_BASE, 'g;x=1/../y','http://a/b/c/y') 375 self.checkJoin(RFC3986_BASE, 'g?y/./x','http://a/b/c/g?y/./x') 376 self.checkJoin(RFC3986_BASE, 'g?y/../x','http://a/b/c/g?y/../x') 377 self.checkJoin(RFC3986_BASE, 'g#s/./x','http://a/b/c/g#s/./x') 378 self.checkJoin(RFC3986_BASE, 'g#s/../x','http://a/b/c/g#s/../x') 379 #self.checkJoin(RFC3986_BASE, 'http:g','http:g') # strict parser 380 self.checkJoin(RFC3986_BASE, 'http:g','http://a/b/c/g') #relaxed parser 381 382 # Test for issue9721 383 self.checkJoin('http://a/b/c/de', ';x','http://a/b/c/;x') 384 385 def test_urljoins(self): 386 self.checkJoin(SIMPLE_BASE, 'g:h','g:h') 387 self.checkJoin(SIMPLE_BASE, 'http:g','http://a/b/c/g') 388 self.checkJoin(SIMPLE_BASE, 'http:','http://a/b/c/d') 389 self.checkJoin(SIMPLE_BASE, 'g','http://a/b/c/g') 390 self.checkJoin(SIMPLE_BASE, './g','http://a/b/c/g') 391 self.checkJoin(SIMPLE_BASE, 'g/','http://a/b/c/g/') 392 self.checkJoin(SIMPLE_BASE, '/g','http://a/g') 393 self.checkJoin(SIMPLE_BASE, '//g','http://g') 394 self.checkJoin(SIMPLE_BASE, '?y','http://a/b/c/d?y') 395 self.checkJoin(SIMPLE_BASE, 'g?y','http://a/b/c/g?y') 396 self.checkJoin(SIMPLE_BASE, 'g?y/./x','http://a/b/c/g?y/./x') 397 self.checkJoin(SIMPLE_BASE, '.','http://a/b/c/') 398 self.checkJoin(SIMPLE_BASE, './','http://a/b/c/') 399 self.checkJoin(SIMPLE_BASE, '..','http://a/b/') 400 self.checkJoin(SIMPLE_BASE, '../','http://a/b/') 401 self.checkJoin(SIMPLE_BASE, '../g','http://a/b/g') 402 self.checkJoin(SIMPLE_BASE, '../..','http://a/') 403 self.checkJoin(SIMPLE_BASE, '../../g','http://a/g') 404 self.checkJoin(SIMPLE_BASE, './../g','http://a/b/g') 405 self.checkJoin(SIMPLE_BASE, './g/.','http://a/b/c/g/') 406 self.checkJoin(SIMPLE_BASE, 'g/./h','http://a/b/c/g/h') 407 self.checkJoin(SIMPLE_BASE, 'g/../h','http://a/b/c/h') 408 self.checkJoin(SIMPLE_BASE, 'http:g','http://a/b/c/g') 409 self.checkJoin(SIMPLE_BASE, 'http:','http://a/b/c/d') 410 self.checkJoin(SIMPLE_BASE, 'http:?y','http://a/b/c/d?y') 411 self.checkJoin(SIMPLE_BASE, 'http:g?y','http://a/b/c/g?y') 412 self.checkJoin(SIMPLE_BASE, 'http:g?y/./x','http://a/b/c/g?y/./x') 413 self.checkJoin('http:///', '..','http:///') 414 self.checkJoin('', 'http://a/b/c/g?y/./x','http://a/b/c/g?y/./x') 415 self.checkJoin('', 'http://a/./g', 'http://a/./g') 416 self.checkJoin('svn://pathtorepo/dir1', 'dir2', 'svn://pathtorepo/dir2') 417 self.checkJoin('svn+ssh://pathtorepo/dir1', 'dir2', 'svn+ssh://pathtorepo/dir2') 418 self.checkJoin('ws://a/b','g','ws://a/g') 419 self.checkJoin('wss://a/b','g','wss://a/g') 420 421 # XXX: The following tests are no longer compatible with RFC3986 422 # self.checkJoin(SIMPLE_BASE, '../../../g','http://a/../g') 423 # self.checkJoin(SIMPLE_BASE, '/./g','http://a/./g') 424 425 # test for issue22118 duplicate slashes 426 self.checkJoin(SIMPLE_BASE + '/', 'foo', SIMPLE_BASE + '/foo') 427 428 # Non-RFC-defined tests, covering variations of base and trailing 429 # slashes 430 self.checkJoin('http://a/b/c/d/e/', '../../f/g/', 'http://a/b/c/f/g/') 431 self.checkJoin('http://a/b/c/d/e', '../../f/g/', 'http://a/b/f/g/') 432 self.checkJoin('http://a/b/c/d/e/', '/../../f/g/', 'http://a/f/g/') 433 self.checkJoin('http://a/b/c/d/e', '/../../f/g/', 'http://a/f/g/') 434 self.checkJoin('http://a/b/c/d/e/', '../../f/g', 'http://a/b/c/f/g') 435 self.checkJoin('http://a/b/', '../../f/g/', 'http://a/f/g/') 436 437 # issue 23703: don't duplicate filename 438 self.checkJoin('a', 'b', 'b') 439 440 def test_RFC2732(self): 441 str_cases = [ 442 ('http://Test.python.org:5432/foo/', 'test.python.org', 5432), 443 ('http://12.34.56.78:5432/foo/', '12.34.56.78', 5432), 444 ('http://[::1]:5432/foo/', '::1', 5432), 445 ('http://[dead:beef::1]:5432/foo/', 'dead:beef::1', 5432), 446 ('http://[dead:beef::]:5432/foo/', 'dead:beef::', 5432), 447 ('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]:5432/foo/', 448 'dead:beef:cafe:5417:affe:8fa3:deaf:feed', 5432), 449 ('http://[::12.34.56.78]:5432/foo/', '::12.34.56.78', 5432), 450 ('http://[::ffff:12.34.56.78]:5432/foo/', 451 '::ffff:12.34.56.78', 5432), 452 ('http://Test.python.org/foo/', 'test.python.org', None), 453 ('http://12.34.56.78/foo/', '12.34.56.78', None), 454 ('http://[::1]/foo/', '::1', None), 455 ('http://[dead:beef::1]/foo/', 'dead:beef::1', None), 456 ('http://[dead:beef::]/foo/', 'dead:beef::', None), 457 ('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/', 458 'dead:beef:cafe:5417:affe:8fa3:deaf:feed', None), 459 ('http://[::12.34.56.78]/foo/', '::12.34.56.78', None), 460 ('http://[::ffff:12.34.56.78]/foo/', 461 '::ffff:12.34.56.78', None), 462 ('http://Test.python.org:/foo/', 'test.python.org', None), 463 ('http://12.34.56.78:/foo/', '12.34.56.78', None), 464 ('http://[::1]:/foo/', '::1', None), 465 ('http://[dead:beef::1]:/foo/', 'dead:beef::1', None), 466 ('http://[dead:beef::]:/foo/', 'dead:beef::', None), 467 ('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]:/foo/', 468 'dead:beef:cafe:5417:affe:8fa3:deaf:feed', None), 469 ('http://[::12.34.56.78]:/foo/', '::12.34.56.78', None), 470 ('http://[::ffff:12.34.56.78]:/foo/', 471 '::ffff:12.34.56.78', None), 472 ] 473 def _encode(t): 474 return t[0].encode('ascii'), t[1].encode('ascii'), t[2] 475 bytes_cases = [_encode(x) for x in str_cases] 476 for url, hostname, port in str_cases + bytes_cases: 477 urlparsed = urllib.parse.urlparse(url) 478 self.assertEqual((urlparsed.hostname, urlparsed.port) , (hostname, port)) 479 480 str_cases = [ 481 'http://::12.34.56.78]/', 482 'http://[::1/foo/', 483 'ftp://[::1/foo/bad]/bad', 484 'http://[::1/foo/bad]/bad', 485 'http://[::ffff:12.34.56.78'] 486 bytes_cases = [x.encode('ascii') for x in str_cases] 487 for invalid_url in str_cases + bytes_cases: 488 self.assertRaises(ValueError, urllib.parse.urlparse, invalid_url) 489 490 def test_urldefrag(self): 491 str_cases = [ 492 ('http://python.org#frag', 'http://python.org', 'frag'), 493 ('http://python.org', 'http://python.org', ''), 494 ('http://python.org/#frag', 'http://python.org/', 'frag'), 495 ('http://python.org/', 'http://python.org/', ''), 496 ('http://python.org/?q#frag', 'http://python.org/?q', 'frag'), 497 ('http://python.org/?q', 'http://python.org/?q', ''), 498 ('http://python.org/p#frag', 'http://python.org/p', 'frag'), 499 ('http://python.org/p?q', 'http://python.org/p?q', ''), 500 (RFC1808_BASE, 'http://a/b/c/d;p?q', 'f'), 501 (RFC2396_BASE, 'http://a/b/c/d;p?q', ''), 502 ] 503 def _encode(t): 504 return type(t)(x.encode('ascii') for x in t) 505 bytes_cases = [_encode(x) for x in str_cases] 506 for url, defrag, frag in str_cases + bytes_cases: 507 result = urllib.parse.urldefrag(url) 508 self.assertEqual(result.geturl(), url) 509 self.assertEqual(result, (defrag, frag)) 510 self.assertEqual(result.url, defrag) 511 self.assertEqual(result.fragment, frag) 512 513 def test_urlsplit_scoped_IPv6(self): 514 p = urllib.parse.urlsplit('http://[FE80::822a:a8ff:fe49:470c%tESt]:1234') 515 self.assertEqual(p.hostname, "fe80::822a:a8ff:fe49:470c%tESt") 516 self.assertEqual(p.netloc, '[FE80::822a:a8ff:fe49:470c%tESt]:1234') 517 518 p = urllib.parse.urlsplit(b'http://[FE80::822a:a8ff:fe49:470c%tESt]:1234') 519 self.assertEqual(p.hostname, b"fe80::822a:a8ff:fe49:470c%tESt") 520 self.assertEqual(p.netloc, b'[FE80::822a:a8ff:fe49:470c%tESt]:1234') 521 522 def test_urlsplit_attributes(self): 523 url = "HTTP://WWW.PYTHON.ORG/doc/#frag" 524 p = urllib.parse.urlsplit(url) 525 self.assertEqual(p.scheme, "http") 526 self.assertEqual(p.netloc, "WWW.PYTHON.ORG") 527 self.assertEqual(p.path, "/doc/") 528 self.assertEqual(p.query, "") 529 self.assertEqual(p.fragment, "frag") 530 self.assertEqual(p.username, None) 531 self.assertEqual(p.password, None) 532 self.assertEqual(p.hostname, "www.python.org") 533 self.assertEqual(p.port, None) 534 # geturl() won't return exactly the original URL in this case 535 # since the scheme is always case-normalized 536 # We handle this by ignoring the first 4 characters of the URL 537 self.assertEqual(p.geturl()[4:], url[4:]) 538 539 url = "http://User:Pass@www.python.org:080/doc/?query=yes#frag" 540 p = urllib.parse.urlsplit(url) 541 self.assertEqual(p.scheme, "http") 542 self.assertEqual(p.netloc, "User:Pass@www.python.org:080") 543 self.assertEqual(p.path, "/doc/") 544 self.assertEqual(p.query, "query=yes") 545 self.assertEqual(p.fragment, "frag") 546 self.assertEqual(p.username, "User") 547 self.assertEqual(p.password, "Pass") 548 self.assertEqual(p.hostname, "www.python.org") 549 self.assertEqual(p.port, 80) 550 self.assertEqual(p.geturl(), url) 551 552 # Addressing issue1698, which suggests Username can contain 553 # "@" characters. Though not RFC compliant, many ftp sites allow 554 # and request email addresses as usernames. 555 556 url = "http://User@example.com:Pass@www.python.org:080/doc/?query=yes#frag" 557 p = urllib.parse.urlsplit(url) 558 self.assertEqual(p.scheme, "http") 559 self.assertEqual(p.netloc, "User@example.com:Pass@www.python.org:080") 560 self.assertEqual(p.path, "/doc/") 561 self.assertEqual(p.query, "query=yes") 562 self.assertEqual(p.fragment, "frag") 563 self.assertEqual(p.username, "User@example.com") 564 self.assertEqual(p.password, "Pass") 565 self.assertEqual(p.hostname, "www.python.org") 566 self.assertEqual(p.port, 80) 567 self.assertEqual(p.geturl(), url) 568 569 # And check them all again, only with bytes this time 570 url = b"HTTP://WWW.PYTHON.ORG/doc/#frag" 571 p = urllib.parse.urlsplit(url) 572 self.assertEqual(p.scheme, b"http") 573 self.assertEqual(p.netloc, b"WWW.PYTHON.ORG") 574 self.assertEqual(p.path, b"/doc/") 575 self.assertEqual(p.query, b"") 576 self.assertEqual(p.fragment, b"frag") 577 self.assertEqual(p.username, None) 578 self.assertEqual(p.password, None) 579 self.assertEqual(p.hostname, b"www.python.org") 580 self.assertEqual(p.port, None) 581 self.assertEqual(p.geturl()[4:], url[4:]) 582 583 url = b"http://User:Pass@www.python.org:080/doc/?query=yes#frag" 584 p = urllib.parse.urlsplit(url) 585 self.assertEqual(p.scheme, b"http") 586 self.assertEqual(p.netloc, b"User:Pass@www.python.org:080") 587 self.assertEqual(p.path, b"/doc/") 588 self.assertEqual(p.query, b"query=yes") 589 self.assertEqual(p.fragment, b"frag") 590 self.assertEqual(p.username, b"User") 591 self.assertEqual(p.password, b"Pass") 592 self.assertEqual(p.hostname, b"www.python.org") 593 self.assertEqual(p.port, 80) 594 self.assertEqual(p.geturl(), url) 595 596 url = b"http://User@example.com:Pass@www.python.org:080/doc/?query=yes#frag" 597 p = urllib.parse.urlsplit(url) 598 self.assertEqual(p.scheme, b"http") 599 self.assertEqual(p.netloc, b"User@example.com:Pass@www.python.org:080") 600 self.assertEqual(p.path, b"/doc/") 601 self.assertEqual(p.query, b"query=yes") 602 self.assertEqual(p.fragment, b"frag") 603 self.assertEqual(p.username, b"User@example.com") 604 self.assertEqual(p.password, b"Pass") 605 self.assertEqual(p.hostname, b"www.python.org") 606 self.assertEqual(p.port, 80) 607 self.assertEqual(p.geturl(), url) 608 609 # Verify an illegal port raises ValueError 610 url = b"HTTP://WWW.PYTHON.ORG:65536/doc/#frag" 611 p = urllib.parse.urlsplit(url) 612 with self.assertRaisesRegex(ValueError, "out of range"): 613 p.port 614 615 def test_urlsplit_remove_unsafe_bytes(self): 616 # Remove ASCII tabs and newlines from input 617 url = "http\t://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment" 618 p = urllib.parse.urlsplit(url) 619 self.assertEqual(p.scheme, "http") 620 self.assertEqual(p.netloc, "www.python.org") 621 self.assertEqual(p.path, "/javascript:alert('msg')/") 622 self.assertEqual(p.query, "query=something") 623 self.assertEqual(p.fragment, "fragment") 624 self.assertEqual(p.username, None) 625 self.assertEqual(p.password, None) 626 self.assertEqual(p.hostname, "www.python.org") 627 self.assertEqual(p.port, None) 628 self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment") 629 630 # Remove ASCII tabs and newlines from input as bytes. 631 url = b"http\t://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment" 632 p = urllib.parse.urlsplit(url) 633 self.assertEqual(p.scheme, b"http") 634 self.assertEqual(p.netloc, b"www.python.org") 635 self.assertEqual(p.path, b"/javascript:alert('msg')/") 636 self.assertEqual(p.query, b"query=something") 637 self.assertEqual(p.fragment, b"fragment") 638 self.assertEqual(p.username, None) 639 self.assertEqual(p.password, None) 640 self.assertEqual(p.hostname, b"www.python.org") 641 self.assertEqual(p.port, None) 642 self.assertEqual(p.geturl(), b"http://www.python.org/javascript:alert('msg')/?query=something#fragment") 643 644 # with scheme as cache-key 645 url = "http://www.python.org/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment" 646 scheme = "ht\ntp" 647 for _ in range(2): 648 p = urllib.parse.urlsplit(url, scheme=scheme) 649 self.assertEqual(p.scheme, "http") 650 self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment") 651 652 def test_attributes_bad_port(self): 653 """Check handling of invalid ports.""" 654 for bytes in (False, True): 655 for parse in (urllib.parse.urlsplit, urllib.parse.urlparse): 656 for port in ("foo", "1.5", "-1", "0x10"): 657 with self.subTest(bytes=bytes, parse=parse, port=port): 658 netloc = "www.example.net:" + port 659 url = "http://" + netloc 660 if bytes: 661 netloc = netloc.encode("ascii") 662 url = url.encode("ascii") 663 p = parse(url) 664 self.assertEqual(p.netloc, netloc) 665 with self.assertRaises(ValueError): 666 p.port 667 668 def test_attributes_bad_scheme(self): 669 """Check handling of invalid schemes.""" 670 for bytes in (False, True): 671 for parse in (urllib.parse.urlsplit, urllib.parse.urlparse): 672 for scheme in (".", "+", "-", "0", "http&", "६http"): 673 with self.subTest(bytes=bytes, parse=parse, scheme=scheme): 674 url = scheme + "://www.example.net" 675 if bytes: 676 if url.isascii(): 677 url = url.encode("ascii") 678 else: 679 continue 680 p = parse(url) 681 if bytes: 682 self.assertEqual(p.scheme, b"") 683 else: 684 self.assertEqual(p.scheme, "") 685 686 def test_attributes_without_netloc(self): 687 # This example is straight from RFC 3261. It looks like it 688 # should allow the username, hostname, and port to be filled 689 # in, but doesn't. Since it's a URI and doesn't use the 690 # scheme://netloc syntax, the netloc and related attributes 691 # should be left empty. 692 uri = "sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15" 693 p = urllib.parse.urlsplit(uri) 694 self.assertEqual(p.netloc, "") 695 self.assertEqual(p.username, None) 696 self.assertEqual(p.password, None) 697 self.assertEqual(p.hostname, None) 698 self.assertEqual(p.port, None) 699 self.assertEqual(p.geturl(), uri) 700 701 p = urllib.parse.urlparse(uri) 702 self.assertEqual(p.netloc, "") 703 self.assertEqual(p.username, None) 704 self.assertEqual(p.password, None) 705 self.assertEqual(p.hostname, None) 706 self.assertEqual(p.port, None) 707 self.assertEqual(p.geturl(), uri) 708 709 # You guessed it, repeating the test with bytes input 710 uri = b"sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15" 711 p = urllib.parse.urlsplit(uri) 712 self.assertEqual(p.netloc, b"") 713 self.assertEqual(p.username, None) 714 self.assertEqual(p.password, None) 715 self.assertEqual(p.hostname, None) 716 self.assertEqual(p.port, None) 717 self.assertEqual(p.geturl(), uri) 718 719 p = urllib.parse.urlparse(uri) 720 self.assertEqual(p.netloc, b"") 721 self.assertEqual(p.username, None) 722 self.assertEqual(p.password, None) 723 self.assertEqual(p.hostname, None) 724 self.assertEqual(p.port, None) 725 self.assertEqual(p.geturl(), uri) 726 727 def test_noslash(self): 728 # Issue 1637: http://foo.com?query is legal 729 self.assertEqual(urllib.parse.urlparse("http://example.com?blahblah=/foo"), 730 ('http', 'example.com', '', '', 'blahblah=/foo', '')) 731 self.assertEqual(urllib.parse.urlparse(b"http://example.com?blahblah=/foo"), 732 (b'http', b'example.com', b'', b'', b'blahblah=/foo', b'')) 733 734 def test_withoutscheme(self): 735 # Test urlparse without scheme 736 # Issue 754016: urlparse goes wrong with IP:port without scheme 737 # RFC 1808 specifies that netloc should start with //, urlparse expects 738 # the same, otherwise it classifies the portion of url as path. 739 self.assertEqual(urllib.parse.urlparse("path"), 740 ('','','path','','','')) 741 self.assertEqual(urllib.parse.urlparse("//www.python.org:80"), 742 ('','www.python.org:80','','','','')) 743 self.assertEqual(urllib.parse.urlparse("http://www.python.org:80"), 744 ('http','www.python.org:80','','','','')) 745 # Repeat for bytes input 746 self.assertEqual(urllib.parse.urlparse(b"path"), 747 (b'',b'',b'path',b'',b'',b'')) 748 self.assertEqual(urllib.parse.urlparse(b"//www.python.org:80"), 749 (b'',b'www.python.org:80',b'',b'',b'',b'')) 750 self.assertEqual(urllib.parse.urlparse(b"http://www.python.org:80"), 751 (b'http',b'www.python.org:80',b'',b'',b'',b'')) 752 753 def test_portseparator(self): 754 # Issue 754016 makes changes for port separator ':' from scheme separator 755 self.assertEqual(urllib.parse.urlparse("http:80"), ('http','','80','','','')) 756 self.assertEqual(urllib.parse.urlparse("https:80"), ('https','','80','','','')) 757 self.assertEqual(urllib.parse.urlparse("path:80"), ('path','','80','','','')) 758 self.assertEqual(urllib.parse.urlparse("http:"),('http','','','','','')) 759 self.assertEqual(urllib.parse.urlparse("https:"),('https','','','','','')) 760 self.assertEqual(urllib.parse.urlparse("http://www.python.org:80"), 761 ('http','www.python.org:80','','','','')) 762 # As usual, need to check bytes input as well 763 self.assertEqual(urllib.parse.urlparse(b"http:80"), (b'http',b'',b'80',b'',b'',b'')) 764 self.assertEqual(urllib.parse.urlparse(b"https:80"), (b'https',b'',b'80',b'',b'',b'')) 765 self.assertEqual(urllib.parse.urlparse(b"path:80"), (b'path',b'',b'80',b'',b'',b'')) 766 self.assertEqual(urllib.parse.urlparse(b"http:"),(b'http',b'',b'',b'',b'',b'')) 767 self.assertEqual(urllib.parse.urlparse(b"https:"),(b'https',b'',b'',b'',b'',b'')) 768 self.assertEqual(urllib.parse.urlparse(b"http://www.python.org:80"), 769 (b'http',b'www.python.org:80',b'',b'',b'',b'')) 770 771 def test_usingsys(self): 772 # Issue 3314: sys module is used in the error 773 self.assertRaises(TypeError, urllib.parse.urlencode, "foo") 774 775 def test_anyscheme(self): 776 # Issue 7904: s3://foo.com/stuff has netloc "foo.com". 777 self.assertEqual(urllib.parse.urlparse("s3://foo.com/stuff"), 778 ('s3', 'foo.com', '/stuff', '', '', '')) 779 self.assertEqual(urllib.parse.urlparse("x-newscheme://foo.com/stuff"), 780 ('x-newscheme', 'foo.com', '/stuff', '', '', '')) 781 self.assertEqual(urllib.parse.urlparse("x-newscheme://foo.com/stuff?query#fragment"), 782 ('x-newscheme', 'foo.com', '/stuff', '', 'query', 'fragment')) 783 self.assertEqual(urllib.parse.urlparse("x-newscheme://foo.com/stuff?query"), 784 ('x-newscheme', 'foo.com', '/stuff', '', 'query', '')) 785 786 # And for bytes... 787 self.assertEqual(urllib.parse.urlparse(b"s3://foo.com/stuff"), 788 (b's3', b'foo.com', b'/stuff', b'', b'', b'')) 789 self.assertEqual(urllib.parse.urlparse(b"x-newscheme://foo.com/stuff"), 790 (b'x-newscheme', b'foo.com', b'/stuff', b'', b'', b'')) 791 self.assertEqual(urllib.parse.urlparse(b"x-newscheme://foo.com/stuff?query#fragment"), 792 (b'x-newscheme', b'foo.com', b'/stuff', b'', b'query', b'fragment')) 793 self.assertEqual(urllib.parse.urlparse(b"x-newscheme://foo.com/stuff?query"), 794 (b'x-newscheme', b'foo.com', b'/stuff', b'', b'query', b'')) 795 796 def test_default_scheme(self): 797 # Exercise the scheme parameter of urlparse() and urlsplit() 798 for func in (urllib.parse.urlparse, urllib.parse.urlsplit): 799 with self.subTest(function=func): 800 result = func("http://example.net/", "ftp") 801 self.assertEqual(result.scheme, "http") 802 result = func(b"http://example.net/", b"ftp") 803 self.assertEqual(result.scheme, b"http") 804 self.assertEqual(func("path", "ftp").scheme, "ftp") 805 self.assertEqual(func("path", scheme="ftp").scheme, "ftp") 806 self.assertEqual(func(b"path", scheme=b"ftp").scheme, b"ftp") 807 self.assertEqual(func("path").scheme, "") 808 self.assertEqual(func(b"path").scheme, b"") 809 self.assertEqual(func(b"path", "").scheme, b"") 810 811 def test_parse_fragments(self): 812 # Exercise the allow_fragments parameter of urlparse() and urlsplit() 813 tests = ( 814 ("http:#frag", "path", "frag"), 815 ("//example.net#frag", "path", "frag"), 816 ("index.html#frag", "path", "frag"), 817 (";a=b#frag", "params", "frag"), 818 ("?a=b#frag", "query", "frag"), 819 ("#frag", "path", "frag"), 820 ("abc#@frag", "path", "@frag"), 821 ("//abc#@frag", "path", "@frag"), 822 ("//abc:80#@frag", "path", "@frag"), 823 ("//abc#@frag:80", "path", "@frag:80"), 824 ) 825 for url, attr, expected_frag in tests: 826 for func in (urllib.parse.urlparse, urllib.parse.urlsplit): 827 if attr == "params" and func is urllib.parse.urlsplit: 828 attr = "path" 829 with self.subTest(url=url, function=func): 830 result = func(url, allow_fragments=False) 831 self.assertEqual(result.fragment, "") 832 self.assertTrue( 833 getattr(result, attr).endswith("#" + expected_frag)) 834 self.assertEqual(func(url, "", False).fragment, "") 835 836 result = func(url, allow_fragments=True) 837 self.assertEqual(result.fragment, expected_frag) 838 self.assertFalse( 839 getattr(result, attr).endswith(expected_frag)) 840 self.assertEqual(func(url, "", True).fragment, 841 expected_frag) 842 self.assertEqual(func(url).fragment, expected_frag) 843 844 def test_mixed_types_rejected(self): 845 # Several functions that process either strings or ASCII encoded bytes 846 # accept multiple arguments. Check they reject mixed type input 847 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 848 urllib.parse.urlparse("www.python.org", b"http") 849 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 850 urllib.parse.urlparse(b"www.python.org", "http") 851 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 852 urllib.parse.urlsplit("www.python.org", b"http") 853 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 854 urllib.parse.urlsplit(b"www.python.org", "http") 855 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 856 urllib.parse.urlunparse(( b"http", "www.python.org","","","","")) 857 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 858 urllib.parse.urlunparse(("http", b"www.python.org","","","","")) 859 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 860 urllib.parse.urlunsplit((b"http", "www.python.org","","","")) 861 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 862 urllib.parse.urlunsplit(("http", b"www.python.org","","","")) 863 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 864 urllib.parse.urljoin("http://python.org", b"http://python.org") 865 with self.assertRaisesRegex(TypeError, "Cannot mix str"): 866 urllib.parse.urljoin(b"http://python.org", "http://python.org") 867 868 def _check_result_type(self, str_type): 869 num_args = len(str_type._fields) 870 bytes_type = str_type._encoded_counterpart 871 self.assertIs(bytes_type._decoded_counterpart, str_type) 872 str_args = ('',) * num_args 873 bytes_args = (b'',) * num_args 874 str_result = str_type(*str_args) 875 bytes_result = bytes_type(*bytes_args) 876 encoding = 'ascii' 877 errors = 'strict' 878 self.assertEqual(str_result, str_args) 879 self.assertEqual(bytes_result.decode(), str_args) 880 self.assertEqual(bytes_result.decode(), str_result) 881 self.assertEqual(bytes_result.decode(encoding), str_args) 882 self.assertEqual(bytes_result.decode(encoding), str_result) 883 self.assertEqual(bytes_result.decode(encoding, errors), str_args) 884 self.assertEqual(bytes_result.decode(encoding, errors), str_result) 885 self.assertEqual(bytes_result, bytes_args) 886 self.assertEqual(str_result.encode(), bytes_args) 887 self.assertEqual(str_result.encode(), bytes_result) 888 self.assertEqual(str_result.encode(encoding), bytes_args) 889 self.assertEqual(str_result.encode(encoding), bytes_result) 890 self.assertEqual(str_result.encode(encoding, errors), bytes_args) 891 self.assertEqual(str_result.encode(encoding, errors), bytes_result) 892 893 def test_result_pairs(self): 894 # Check encoding and decoding between result pairs 895 result_types = [ 896 urllib.parse.DefragResult, 897 urllib.parse.SplitResult, 898 urllib.parse.ParseResult, 899 ] 900 for result_type in result_types: 901 self._check_result_type(result_type) 902 903 def test_parse_qs_encoding(self): 904 result = urllib.parse.parse_qs("key=\u0141%E9", encoding="latin-1") 905 self.assertEqual(result, {'key': ['\u0141\xE9']}) 906 result = urllib.parse.parse_qs("key=\u0141%C3%A9", encoding="utf-8") 907 self.assertEqual(result, {'key': ['\u0141\xE9']}) 908 result = urllib.parse.parse_qs("key=\u0141%C3%A9", encoding="ascii") 909 self.assertEqual(result, {'key': ['\u0141\ufffd\ufffd']}) 910 result = urllib.parse.parse_qs("key=\u0141%E9-", encoding="ascii") 911 self.assertEqual(result, {'key': ['\u0141\ufffd-']}) 912 result = urllib.parse.parse_qs("key=\u0141%E9-", encoding="ascii", 913 errors="ignore") 914 self.assertEqual(result, {'key': ['\u0141-']}) 915 916 def test_parse_qsl_encoding(self): 917 result = urllib.parse.parse_qsl("key=\u0141%E9", encoding="latin-1") 918 self.assertEqual(result, [('key', '\u0141\xE9')]) 919 result = urllib.parse.parse_qsl("key=\u0141%C3%A9", encoding="utf-8") 920 self.assertEqual(result, [('key', '\u0141\xE9')]) 921 result = urllib.parse.parse_qsl("key=\u0141%C3%A9", encoding="ascii") 922 self.assertEqual(result, [('key', '\u0141\ufffd\ufffd')]) 923 result = urllib.parse.parse_qsl("key=\u0141%E9-", encoding="ascii") 924 self.assertEqual(result, [('key', '\u0141\ufffd-')]) 925 result = urllib.parse.parse_qsl("key=\u0141%E9-", encoding="ascii", 926 errors="ignore") 927 self.assertEqual(result, [('key', '\u0141-')]) 928 929 def test_parse_qsl_max_num_fields(self): 930 with self.assertRaises(ValueError): 931 urllib.parse.parse_qs('&'.join(['a=a']*11), max_num_fields=10) 932 urllib.parse.parse_qs('&'.join(['a=a']*10), max_num_fields=10) 933 934 def test_parse_qs_separator(self): 935 parse_qs_semicolon_cases = [ 936 (";", {}), 937 (";;", {}), 938 (";a=b", {'a': ['b']}), 939 ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), 940 ("a=1;a=2", {'a': ['1', '2']}), 941 (b";", {}), 942 (b";;", {}), 943 (b";a=b", {b'a': [b'b']}), 944 (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), 945 (b"a=1;a=2", {b'a': [b'1', b'2']}), 946 ] 947 for orig, expect in parse_qs_semicolon_cases: 948 with self.subTest(f"Original: {orig!r}, Expected: {expect!r}"): 949 result = urllib.parse.parse_qs(orig, separator=';') 950 self.assertEqual(result, expect, "Error parsing %r" % orig) 951 result_bytes = urllib.parse.parse_qs(orig, separator=b';') 952 self.assertEqual(result_bytes, expect, "Error parsing %r" % orig) 953 954 955 def test_parse_qsl_separator(self): 956 parse_qsl_semicolon_cases = [ 957 (";", []), 958 (";;", []), 959 (";a=b", [('a', 'b')]), 960 ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]), 961 ("a=1;a=2", [('a', '1'), ('a', '2')]), 962 (b";", []), 963 (b";;", []), 964 (b";a=b", [(b'a', b'b')]), 965 (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), 966 (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]), 967 ] 968 for orig, expect in parse_qsl_semicolon_cases: 969 with self.subTest(f"Original: {orig!r}, Expected: {expect!r}"): 970 result = urllib.parse.parse_qsl(orig, separator=';') 971 self.assertEqual(result, expect, "Error parsing %r" % orig) 972 result_bytes = urllib.parse.parse_qsl(orig, separator=b';') 973 self.assertEqual(result_bytes, expect, "Error parsing %r" % orig) 974 975 976 def test_urlencode_sequences(self): 977 # Other tests incidentally urlencode things; test non-covered cases: 978 # Sequence and object values. 979 result = urllib.parse.urlencode({'a': [1, 2], 'b': (3, 4, 5)}, True) 980 # we cannot rely on ordering here 981 assert set(result.split('&')) == {'a=1', 'a=2', 'b=3', 'b=4', 'b=5'} 982 983 class Trivial: 984 def __str__(self): 985 return 'trivial' 986 987 result = urllib.parse.urlencode({'a': Trivial()}, True) 988 self.assertEqual(result, 'a=trivial') 989 990 def test_urlencode_quote_via(self): 991 result = urllib.parse.urlencode({'a': 'some value'}) 992 self.assertEqual(result, "a=some+value") 993 result = urllib.parse.urlencode({'a': 'some value/another'}, 994 quote_via=urllib.parse.quote) 995 self.assertEqual(result, "a=some%20value%2Fanother") 996 result = urllib.parse.urlencode({'a': 'some value/another'}, 997 safe='/', quote_via=urllib.parse.quote) 998 self.assertEqual(result, "a=some%20value/another") 999 1000 def test_quote_from_bytes(self): 1001 self.assertRaises(TypeError, urllib.parse.quote_from_bytes, 'foo') 1002 result = urllib.parse.quote_from_bytes(b'archaeological arcana') 1003 self.assertEqual(result, 'archaeological%20arcana') 1004 result = urllib.parse.quote_from_bytes(b'') 1005 self.assertEqual(result, '') 1006 1007 def test_unquote_to_bytes(self): 1008 result = urllib.parse.unquote_to_bytes('abc%20def') 1009 self.assertEqual(result, b'abc def') 1010 result = urllib.parse.unquote_to_bytes('') 1011 self.assertEqual(result, b'') 1012 1013 def test_quote_errors(self): 1014 self.assertRaises(TypeError, urllib.parse.quote, b'foo', 1015 encoding='utf-8') 1016 self.assertRaises(TypeError, urllib.parse.quote, b'foo', errors='strict') 1017 1018 def test_issue14072(self): 1019 p1 = urllib.parse.urlsplit('tel:+31-641044153') 1020 self.assertEqual(p1.scheme, 'tel') 1021 self.assertEqual(p1.path, '+31-641044153') 1022 p2 = urllib.parse.urlsplit('tel:+31641044153') 1023 self.assertEqual(p2.scheme, 'tel') 1024 self.assertEqual(p2.path, '+31641044153') 1025 # assert the behavior for urlparse 1026 p1 = urllib.parse.urlparse('tel:+31-641044153') 1027 self.assertEqual(p1.scheme, 'tel') 1028 self.assertEqual(p1.path, '+31-641044153') 1029 p2 = urllib.parse.urlparse('tel:+31641044153') 1030 self.assertEqual(p2.scheme, 'tel') 1031 self.assertEqual(p2.path, '+31641044153') 1032 1033 def test_port_casting_failure_message(self): 1034 message = "Port could not be cast to integer value as 'oracle'" 1035 p1 = urllib.parse.urlparse('http://Server=sde; Service=sde:oracle') 1036 with self.assertRaisesRegex(ValueError, message): 1037 p1.port 1038 1039 p2 = urllib.parse.urlsplit('http://Server=sde; Service=sde:oracle') 1040 with self.assertRaisesRegex(ValueError, message): 1041 p2.port 1042 1043 def test_telurl_params(self): 1044 p1 = urllib.parse.urlparse('tel:123-4;phone-context=+1-650-516') 1045 self.assertEqual(p1.scheme, 'tel') 1046 self.assertEqual(p1.path, '123-4') 1047 self.assertEqual(p1.params, 'phone-context=+1-650-516') 1048 1049 p1 = urllib.parse.urlparse('tel:+1-201-555-0123') 1050 self.assertEqual(p1.scheme, 'tel') 1051 self.assertEqual(p1.path, '+1-201-555-0123') 1052 self.assertEqual(p1.params, '') 1053 1054 p1 = urllib.parse.urlparse('tel:7042;phone-context=example.com') 1055 self.assertEqual(p1.scheme, 'tel') 1056 self.assertEqual(p1.path, '7042') 1057 self.assertEqual(p1.params, 'phone-context=example.com') 1058 1059 p1 = urllib.parse.urlparse('tel:863-1234;phone-context=+1-914-555') 1060 self.assertEqual(p1.scheme, 'tel') 1061 self.assertEqual(p1.path, '863-1234') 1062 self.assertEqual(p1.params, 'phone-context=+1-914-555') 1063 1064 def test_Quoter_repr(self): 1065 quoter = urllib.parse.Quoter(urllib.parse._ALWAYS_SAFE) 1066 self.assertIn('Quoter', repr(quoter)) 1067 1068 def test_all(self): 1069 expected = [] 1070 undocumented = { 1071 'splitattr', 'splithost', 'splitnport', 'splitpasswd', 1072 'splitport', 'splitquery', 'splittag', 'splittype', 'splituser', 1073 'splitvalue', 1074 'Quoter', 'ResultBase', 'clear_cache', 'to_bytes', 'unwrap', 1075 } 1076 for name in dir(urllib.parse): 1077 if name.startswith('_') or name in undocumented: 1078 continue 1079 object = getattr(urllib.parse, name) 1080 if getattr(object, '__module__', None) == 'urllib.parse': 1081 expected.append(name) 1082 self.assertCountEqual(urllib.parse.__all__, expected) 1083 1084 def test_urlsplit_normalization(self): 1085 # Certain characters should never occur in the netloc, 1086 # including under normalization. 1087 # Ensure that ALL of them are detected and cause an error 1088 illegal_chars = '/:#?@' 1089 hex_chars = {'{:04X}'.format(ord(c)) for c in illegal_chars} 1090 denorm_chars = [ 1091 c for c in map(chr, range(128, sys.maxunicode)) 1092 if (hex_chars & set(unicodedata.decomposition(c).split())) 1093 and c not in illegal_chars 1094 ] 1095 # Sanity check that we found at least one such character 1096 self.assertIn('\u2100', denorm_chars) 1097 self.assertIn('\uFF03', denorm_chars) 1098 1099 # bpo-36742: Verify port separators are ignored when they 1100 # existed prior to decomposition 1101 urllib.parse.urlsplit('http://\u30d5\u309a:80') 1102 with self.assertRaises(ValueError): 1103 urllib.parse.urlsplit('http://\u30d5\u309a\ufe1380') 1104 1105 for scheme in ["http", "https", "ftp"]: 1106 for netloc in ["netloc{}false.netloc", "n{}user@netloc"]: 1107 for c in denorm_chars: 1108 url = "{}://{}/path".format(scheme, netloc.format(c)) 1109 with self.subTest(url=url, char='{:04X}'.format(ord(c))): 1110 with self.assertRaises(ValueError): 1111 urllib.parse.urlsplit(url) 1112 1113class Utility_Tests(unittest.TestCase): 1114 """Testcase to test the various utility functions in the urllib.""" 1115 # In Python 2 this test class was in test_urllib. 1116 1117 def test_splittype(self): 1118 splittype = urllib.parse._splittype 1119 self.assertEqual(splittype('type:opaquestring'), ('type', 'opaquestring')) 1120 self.assertEqual(splittype('opaquestring'), (None, 'opaquestring')) 1121 self.assertEqual(splittype(':opaquestring'), (None, ':opaquestring')) 1122 self.assertEqual(splittype('type:'), ('type', '')) 1123 self.assertEqual(splittype('type:opaque:string'), ('type', 'opaque:string')) 1124 1125 def test_splithost(self): 1126 splithost = urllib.parse._splithost 1127 self.assertEqual(splithost('//www.example.org:80/foo/bar/baz.html'), 1128 ('www.example.org:80', '/foo/bar/baz.html')) 1129 self.assertEqual(splithost('//www.example.org:80'), 1130 ('www.example.org:80', '')) 1131 self.assertEqual(splithost('/foo/bar/baz.html'), 1132 (None, '/foo/bar/baz.html')) 1133 1134 # bpo-30500: # starts a fragment. 1135 self.assertEqual(splithost('//127.0.0.1#@host.com'), 1136 ('127.0.0.1', '/#@host.com')) 1137 self.assertEqual(splithost('//127.0.0.1#@host.com:80'), 1138 ('127.0.0.1', '/#@host.com:80')) 1139 self.assertEqual(splithost('//127.0.0.1:80#@host.com'), 1140 ('127.0.0.1:80', '/#@host.com')) 1141 1142 # Empty host is returned as empty string. 1143 self.assertEqual(splithost("///file"), 1144 ('', '/file')) 1145 1146 # Trailing semicolon, question mark and hash symbol are kept. 1147 self.assertEqual(splithost("//example.net/file;"), 1148 ('example.net', '/file;')) 1149 self.assertEqual(splithost("//example.net/file?"), 1150 ('example.net', '/file?')) 1151 self.assertEqual(splithost("//example.net/file#"), 1152 ('example.net', '/file#')) 1153 1154 def test_splituser(self): 1155 splituser = urllib.parse._splituser 1156 self.assertEqual(splituser('User:Pass@www.python.org:080'), 1157 ('User:Pass', 'www.python.org:080')) 1158 self.assertEqual(splituser('@www.python.org:080'), 1159 ('', 'www.python.org:080')) 1160 self.assertEqual(splituser('www.python.org:080'), 1161 (None, 'www.python.org:080')) 1162 self.assertEqual(splituser('User:Pass@'), 1163 ('User:Pass', '')) 1164 self.assertEqual(splituser('User@example.com:Pass@www.python.org:080'), 1165 ('User@example.com:Pass', 'www.python.org:080')) 1166 1167 def test_splitpasswd(self): 1168 # Some of the password examples are not sensible, but it is added to 1169 # confirming to RFC2617 and addressing issue4675. 1170 splitpasswd = urllib.parse._splitpasswd 1171 self.assertEqual(splitpasswd('user:ab'), ('user', 'ab')) 1172 self.assertEqual(splitpasswd('user:a\nb'), ('user', 'a\nb')) 1173 self.assertEqual(splitpasswd('user:a\tb'), ('user', 'a\tb')) 1174 self.assertEqual(splitpasswd('user:a\rb'), ('user', 'a\rb')) 1175 self.assertEqual(splitpasswd('user:a\fb'), ('user', 'a\fb')) 1176 self.assertEqual(splitpasswd('user:a\vb'), ('user', 'a\vb')) 1177 self.assertEqual(splitpasswd('user:a:b'), ('user', 'a:b')) 1178 self.assertEqual(splitpasswd('user:a b'), ('user', 'a b')) 1179 self.assertEqual(splitpasswd('user 2:ab'), ('user 2', 'ab')) 1180 self.assertEqual(splitpasswd('user+1:a+b'), ('user+1', 'a+b')) 1181 self.assertEqual(splitpasswd('user:'), ('user', '')) 1182 self.assertEqual(splitpasswd('user'), ('user', None)) 1183 self.assertEqual(splitpasswd(':ab'), ('', 'ab')) 1184 1185 def test_splitport(self): 1186 splitport = urllib.parse._splitport 1187 self.assertEqual(splitport('parrot:88'), ('parrot', '88')) 1188 self.assertEqual(splitport('parrot'), ('parrot', None)) 1189 self.assertEqual(splitport('parrot:'), ('parrot', None)) 1190 self.assertEqual(splitport('127.0.0.1'), ('127.0.0.1', None)) 1191 self.assertEqual(splitport('parrot:cheese'), ('parrot:cheese', None)) 1192 self.assertEqual(splitport('[::1]:88'), ('[::1]', '88')) 1193 self.assertEqual(splitport('[::1]'), ('[::1]', None)) 1194 self.assertEqual(splitport(':88'), ('', '88')) 1195 1196 def test_splitnport(self): 1197 splitnport = urllib.parse._splitnport 1198 self.assertEqual(splitnport('parrot:88'), ('parrot', 88)) 1199 self.assertEqual(splitnport('parrot'), ('parrot', -1)) 1200 self.assertEqual(splitnport('parrot', 55), ('parrot', 55)) 1201 self.assertEqual(splitnport('parrot:'), ('parrot', -1)) 1202 self.assertEqual(splitnport('parrot:', 55), ('parrot', 55)) 1203 self.assertEqual(splitnport('127.0.0.1'), ('127.0.0.1', -1)) 1204 self.assertEqual(splitnport('127.0.0.1', 55), ('127.0.0.1', 55)) 1205 self.assertEqual(splitnport('parrot:cheese'), ('parrot', None)) 1206 self.assertEqual(splitnport('parrot:cheese', 55), ('parrot', None)) 1207 1208 def test_splitquery(self): 1209 # Normal cases are exercised by other tests; ensure that we also 1210 # catch cases with no port specified (testcase ensuring coverage) 1211 splitquery = urllib.parse._splitquery 1212 self.assertEqual(splitquery('http://python.org/fake?foo=bar'), 1213 ('http://python.org/fake', 'foo=bar')) 1214 self.assertEqual(splitquery('http://python.org/fake?foo=bar?'), 1215 ('http://python.org/fake?foo=bar', '')) 1216 self.assertEqual(splitquery('http://python.org/fake'), 1217 ('http://python.org/fake', None)) 1218 self.assertEqual(splitquery('?foo=bar'), ('', 'foo=bar')) 1219 1220 def test_splittag(self): 1221 splittag = urllib.parse._splittag 1222 self.assertEqual(splittag('http://example.com?foo=bar#baz'), 1223 ('http://example.com?foo=bar', 'baz')) 1224 self.assertEqual(splittag('http://example.com?foo=bar#'), 1225 ('http://example.com?foo=bar', '')) 1226 self.assertEqual(splittag('#baz'), ('', 'baz')) 1227 self.assertEqual(splittag('http://example.com?foo=bar'), 1228 ('http://example.com?foo=bar', None)) 1229 self.assertEqual(splittag('http://example.com?foo=bar#baz#boo'), 1230 ('http://example.com?foo=bar#baz', 'boo')) 1231 1232 def test_splitattr(self): 1233 splitattr = urllib.parse._splitattr 1234 self.assertEqual(splitattr('/path;attr1=value1;attr2=value2'), 1235 ('/path', ['attr1=value1', 'attr2=value2'])) 1236 self.assertEqual(splitattr('/path;'), ('/path', [''])) 1237 self.assertEqual(splitattr(';attr1=value1;attr2=value2'), 1238 ('', ['attr1=value1', 'attr2=value2'])) 1239 self.assertEqual(splitattr('/path'), ('/path', [])) 1240 1241 def test_splitvalue(self): 1242 # Normal cases are exercised by other tests; test pathological cases 1243 # with no key/value pairs. (testcase ensuring coverage) 1244 splitvalue = urllib.parse._splitvalue 1245 self.assertEqual(splitvalue('foo=bar'), ('foo', 'bar')) 1246 self.assertEqual(splitvalue('foo='), ('foo', '')) 1247 self.assertEqual(splitvalue('=bar'), ('', 'bar')) 1248 self.assertEqual(splitvalue('foobar'), ('foobar', None)) 1249 self.assertEqual(splitvalue('foo=bar=baz'), ('foo', 'bar=baz')) 1250 1251 def test_to_bytes(self): 1252 result = urllib.parse._to_bytes('http://www.python.org') 1253 self.assertEqual(result, 'http://www.python.org') 1254 self.assertRaises(UnicodeError, urllib.parse._to_bytes, 1255 'http://www.python.org/medi\u00e6val') 1256 1257 def test_unwrap(self): 1258 for wrapped_url in ('<URL:scheme://host/path>', '<scheme://host/path>', 1259 'URL:scheme://host/path', 'scheme://host/path'): 1260 url = urllib.parse.unwrap(wrapped_url) 1261 self.assertEqual(url, 'scheme://host/path') 1262 1263 1264class DeprecationTest(unittest.TestCase): 1265 1266 def test_splittype_deprecation(self): 1267 with self.assertWarns(DeprecationWarning) as cm: 1268 urllib.parse.splittype('') 1269 self.assertEqual(str(cm.warning), 1270 'urllib.parse.splittype() is deprecated as of 3.8, ' 1271 'use urllib.parse.urlparse() instead') 1272 1273 def test_splithost_deprecation(self): 1274 with self.assertWarns(DeprecationWarning) as cm: 1275 urllib.parse.splithost('') 1276 self.assertEqual(str(cm.warning), 1277 'urllib.parse.splithost() is deprecated as of 3.8, ' 1278 'use urllib.parse.urlparse() instead') 1279 1280 def test_splituser_deprecation(self): 1281 with self.assertWarns(DeprecationWarning) as cm: 1282 urllib.parse.splituser('') 1283 self.assertEqual(str(cm.warning), 1284 'urllib.parse.splituser() is deprecated as of 3.8, ' 1285 'use urllib.parse.urlparse() instead') 1286 1287 def test_splitpasswd_deprecation(self): 1288 with self.assertWarns(DeprecationWarning) as cm: 1289 urllib.parse.splitpasswd('') 1290 self.assertEqual(str(cm.warning), 1291 'urllib.parse.splitpasswd() is deprecated as of 3.8, ' 1292 'use urllib.parse.urlparse() instead') 1293 1294 def test_splitport_deprecation(self): 1295 with self.assertWarns(DeprecationWarning) as cm: 1296 urllib.parse.splitport('') 1297 self.assertEqual(str(cm.warning), 1298 'urllib.parse.splitport() is deprecated as of 3.8, ' 1299 'use urllib.parse.urlparse() instead') 1300 1301 def test_splitnport_deprecation(self): 1302 with self.assertWarns(DeprecationWarning) as cm: 1303 urllib.parse.splitnport('') 1304 self.assertEqual(str(cm.warning), 1305 'urllib.parse.splitnport() is deprecated as of 3.8, ' 1306 'use urllib.parse.urlparse() instead') 1307 1308 def test_splitquery_deprecation(self): 1309 with self.assertWarns(DeprecationWarning) as cm: 1310 urllib.parse.splitquery('') 1311 self.assertEqual(str(cm.warning), 1312 'urllib.parse.splitquery() is deprecated as of 3.8, ' 1313 'use urllib.parse.urlparse() instead') 1314 1315 def test_splittag_deprecation(self): 1316 with self.assertWarns(DeprecationWarning) as cm: 1317 urllib.parse.splittag('') 1318 self.assertEqual(str(cm.warning), 1319 'urllib.parse.splittag() is deprecated as of 3.8, ' 1320 'use urllib.parse.urlparse() instead') 1321 1322 def test_splitattr_deprecation(self): 1323 with self.assertWarns(DeprecationWarning) as cm: 1324 urllib.parse.splitattr('') 1325 self.assertEqual(str(cm.warning), 1326 'urllib.parse.splitattr() is deprecated as of 3.8, ' 1327 'use urllib.parse.urlparse() instead') 1328 1329 def test_splitvalue_deprecation(self): 1330 with self.assertWarns(DeprecationWarning) as cm: 1331 urllib.parse.splitvalue('') 1332 self.assertEqual(str(cm.warning), 1333 'urllib.parse.splitvalue() is deprecated as of 3.8, ' 1334 'use urllib.parse.parse_qsl() instead') 1335 1336 def test_to_bytes_deprecation(self): 1337 with self.assertWarns(DeprecationWarning) as cm: 1338 urllib.parse.to_bytes('') 1339 self.assertEqual(str(cm.warning), 1340 'urllib.parse.to_bytes() is deprecated as of 3.8') 1341 1342 1343if __name__ == "__main__": 1344 unittest.main() 1345