blob: 7214492eca9d88805ab95398e545bc380c1b4ef9 [file] [log] [blame]
Raymond Hettinger15f44ab2016-08-30 10:47:49 -07001"""Regression tests for what was in Python 2's "urllib" module"""
Brett Cannon74bfd702003-04-25 09:39:47 +00002
Jeremy Hylton1afc1692008-06-18 20:49:58 +00003import urllib.parse
4import urllib.request
guido@google.coma119df92011-03-29 11:41:02 -07005import urllib.error
Georg Brandl24420152008-05-26 16:32:26 +00006import http.client
Barry Warsaw820c1202008-06-12 04:06:45 +00007import email.message
Jeremy Hylton66dc8c52007-08-04 03:42:26 +00008import io
Brett Cannon74bfd702003-04-25 09:39:47 +00009import unittest
Benjamin Peterson3c2dca62014-06-07 15:08:04 -070010from unittest.mock import patch
Benjamin Petersonee8712c2008-05-20 21:35:26 +000011from test import support
Brett Cannon74bfd702003-04-25 09:39:47 +000012import os
Antoine Pitrou07df6552014-11-02 17:23:14 +010013try:
14 import ssl
15except ImportError:
16 ssl = None
Senthil Kumaran2d2ea1b2011-04-14 13:16:30 +080017import sys
Georg Brandl5a650a22005-08-26 08:51:34 +000018import tempfile
Senthil Kumaran277e9092013-04-10 20:51:19 -070019from nturl2path import url2pathname, pathname2url
Jeremy Hylton6102e292000-08-31 15:48:10 +000020
Senthil Kumaranc5c5a142012-01-14 19:09:04 +080021from base64 import b64encode
Georg Brandl2daf6ae2012-02-20 19:54:16 +010022import collections
Senthil Kumaranc5c5a142012-01-14 19:09:04 +080023
Senthil Kumaran8b081b72013-04-10 20:53:12 -070024
Brett Cannon74bfd702003-04-25 09:39:47 +000025def hexescape(char):
26 """Escape char as RFC 2396 specifies"""
27 hex_repr = hex(ord(char))[2:].upper()
28 if len(hex_repr) == 1:
29 hex_repr = "0%s" % hex_repr
30 return "%" + hex_repr
Jeremy Hylton6102e292000-08-31 15:48:10 +000031
Jeremy Hylton1afc1692008-06-18 20:49:58 +000032# Shortcut for testing FancyURLopener
33_urlopener = None
Senthil Kumaran277e9092013-04-10 20:51:19 -070034
35
Jeremy Hylton1afc1692008-06-18 20:49:58 +000036def urlopen(url, data=None, proxies=None):
37 """urlopen(url [, data]) -> open file-like object"""
38 global _urlopener
39 if proxies is not None:
40 opener = urllib.request.FancyURLopener(proxies=proxies)
41 elif not _urlopener:
Martin Pantera0370222016-02-04 06:01:35 +000042 opener = FancyURLopener()
Jeremy Hylton1afc1692008-06-18 20:49:58 +000043 _urlopener = opener
44 else:
45 opener = _urlopener
46 if data is None:
47 return opener.open(url)
48 else:
49 return opener.open(url, data)
50
Senthil Kumarance260142011-11-01 01:35:17 +080051
Martin Pantera0370222016-02-04 06:01:35 +000052def FancyURLopener():
53 with support.check_warnings(
54 ('FancyURLopener style of invoking requests is deprecated.',
55 DeprecationWarning)):
56 return urllib.request.FancyURLopener()
57
58
Serhiy Storchakaf54c3502014-09-06 21:41:39 +030059def fakehttp(fakedata):
60 class FakeSocket(io.BytesIO):
61 io_refs = 1
62
63 def sendall(self, data):
64 FakeHTTPConnection.buf = data
65
66 def makefile(self, *args, **kwds):
67 self.io_refs += 1
68 return self
69
70 def read(self, amt=None):
71 if self.closed:
72 return b""
73 return io.BytesIO.read(self, amt)
74
75 def readline(self, length=None):
76 if self.closed:
77 return b""
78 return io.BytesIO.readline(self, length)
79
80 def close(self):
81 self.io_refs -= 1
82 if self.io_refs == 0:
83 io.BytesIO.close(self)
84
85 class FakeHTTPConnection(http.client.HTTPConnection):
86
87 # buffer to store data for verification in urlopen tests.
88 buf = None
Serhiy Storchakaf54c3502014-09-06 21:41:39 +030089
90 def connect(self):
Martin Panterce6e0682016-05-16 01:07:13 +000091 self.sock = FakeSocket(self.fakedata)
92 type(self).fakesock = self.sock
93 FakeHTTPConnection.fakedata = fakedata
Serhiy Storchakaf54c3502014-09-06 21:41:39 +030094
95 return FakeHTTPConnection
96
97
Senthil Kumarance260142011-11-01 01:35:17 +080098class FakeHTTPMixin(object):
99 def fakehttp(self, fakedata):
Senthil Kumarance260142011-11-01 01:35:17 +0800100 self._connection_class = http.client.HTTPConnection
Serhiy Storchakaf54c3502014-09-06 21:41:39 +0300101 http.client.HTTPConnection = fakehttp(fakedata)
Senthil Kumarance260142011-11-01 01:35:17 +0800102
103 def unfakehttp(self):
104 http.client.HTTPConnection = self._connection_class
105
106
Benjamin Peterson3c2dca62014-06-07 15:08:04 -0700107class FakeFTPMixin(object):
108 def fakeftp(self):
109 class FakeFtpWrapper(object):
110 def __init__(self, user, passwd, host, port, dirs, timeout=None,
111 persistent=True):
112 pass
113
114 def retrfile(self, file, type):
115 return io.BytesIO(), 0
116
117 def close(self):
118 pass
119
120 self._ftpwrapper_class = urllib.request.ftpwrapper
121 urllib.request.ftpwrapper = FakeFtpWrapper
122
123 def unfakeftp(self):
124 urllib.request.ftpwrapper = self._ftpwrapper_class
125
126
Brett Cannon74bfd702003-04-25 09:39:47 +0000127class urlopen_FileTests(unittest.TestCase):
128 """Test urlopen() opening a temporary file.
Jeremy Hylton6102e292000-08-31 15:48:10 +0000129
Brett Cannon74bfd702003-04-25 09:39:47 +0000130 Try to test as much functionality as possible so as to cut down on reliance
Andrew M. Kuchlingf1a2f9e2004-06-29 13:07:53 +0000131 on connecting to the Net for testing.
Jeremy Hylton7ae51bf2000-09-14 16:59:07 +0000132
Brett Cannon74bfd702003-04-25 09:39:47 +0000133 """
Jeremy Hylton7ae51bf2000-09-14 16:59:07 +0000134
Brett Cannon74bfd702003-04-25 09:39:47 +0000135 def setUp(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000136 # Create a temp file to use for testing
137 self.text = bytes("test_urllib: %s\n" % self.__class__.__name__,
138 "ascii")
139 f = open(support.TESTFN, 'wb')
Brett Cannon74bfd702003-04-25 09:39:47 +0000140 try:
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000141 f.write(self.text)
Brett Cannon74bfd702003-04-25 09:39:47 +0000142 finally:
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000143 f.close()
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000144 self.pathname = support.TESTFN
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000145 self.returned_obj = urlopen("file:%s" % self.pathname)
Jeremy Hylton7ae51bf2000-09-14 16:59:07 +0000146
Brett Cannon74bfd702003-04-25 09:39:47 +0000147 def tearDown(self):
148 """Shut down the open object"""
149 self.returned_obj.close()
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000150 os.remove(support.TESTFN)
Jeremy Hylton7ae51bf2000-09-14 16:59:07 +0000151
Brett Cannon74bfd702003-04-25 09:39:47 +0000152 def test_interface(self):
153 # Make sure object returned by urlopen() has the specified methods
154 for attr in ("read", "readline", "readlines", "fileno",
Christian Heimes9bd667a2008-01-20 15:14:11 +0000155 "close", "info", "geturl", "getcode", "__iter__"):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000156 self.assertTrue(hasattr(self.returned_obj, attr),
Brett Cannon74bfd702003-04-25 09:39:47 +0000157 "object returned by urlopen() lacks %s attribute" %
158 attr)
Skip Montanaroe78b92a2001-01-20 20:22:30 +0000159
Brett Cannon74bfd702003-04-25 09:39:47 +0000160 def test_read(self):
161 self.assertEqual(self.text, self.returned_obj.read())
Skip Montanaro080c9972001-01-28 21:12:22 +0000162
Brett Cannon74bfd702003-04-25 09:39:47 +0000163 def test_readline(self):
164 self.assertEqual(self.text, self.returned_obj.readline())
Guido van Rossuma0982942007-07-10 08:30:03 +0000165 self.assertEqual(b'', self.returned_obj.readline(),
Brett Cannon74bfd702003-04-25 09:39:47 +0000166 "calling readline() after exhausting the file did not"
167 " return an empty string")
Skip Montanaro080c9972001-01-28 21:12:22 +0000168
Brett Cannon74bfd702003-04-25 09:39:47 +0000169 def test_readlines(self):
170 lines_list = self.returned_obj.readlines()
171 self.assertEqual(len(lines_list), 1,
172 "readlines() returned the wrong number of lines")
173 self.assertEqual(lines_list[0], self.text,
174 "readlines() returned improper text")
Skip Montanaro080c9972001-01-28 21:12:22 +0000175
Brett Cannon74bfd702003-04-25 09:39:47 +0000176 def test_fileno(self):
177 file_num = self.returned_obj.fileno()
Ezio Melottie9615932010-01-24 19:26:24 +0000178 self.assertIsInstance(file_num, int, "fileno() did not return an int")
Brett Cannon74bfd702003-04-25 09:39:47 +0000179 self.assertEqual(os.read(file_num, len(self.text)), self.text,
180 "Reading on the file descriptor returned by fileno() "
181 "did not return the expected text")
Skip Montanaroe78b92a2001-01-20 20:22:30 +0000182
Brett Cannon74bfd702003-04-25 09:39:47 +0000183 def test_close(self):
Senthil Kumarand91ffca2011-03-19 17:25:27 +0800184 # Test close() by calling it here and then having it be called again
Brett Cannon74bfd702003-04-25 09:39:47 +0000185 # by the tearDown() method for the test
186 self.returned_obj.close()
Skip Montanaro080c9972001-01-28 21:12:22 +0000187
Brett Cannon74bfd702003-04-25 09:39:47 +0000188 def test_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000189 self.assertIsInstance(self.returned_obj.info(), email.message.Message)
Skip Montanaroe78b92a2001-01-20 20:22:30 +0000190
Brett Cannon74bfd702003-04-25 09:39:47 +0000191 def test_geturl(self):
192 self.assertEqual(self.returned_obj.geturl(), self.pathname)
Skip Montanaro080c9972001-01-28 21:12:22 +0000193
Christian Heimes9bd667a2008-01-20 15:14:11 +0000194 def test_getcode(self):
Florent Xicluna419e3842010-08-08 16:16:07 +0000195 self.assertIsNone(self.returned_obj.getcode())
Christian Heimes9bd667a2008-01-20 15:14:11 +0000196
Brett Cannon74bfd702003-04-25 09:39:47 +0000197 def test_iter(self):
198 # Test iterator
199 # Don't need to count number of iterations since test would fail the
200 # instant it returned anything beyond the first line from the
Raymond Hettinger038018a2011-06-26 14:29:35 +0200201 # comparison.
202 # Use the iterator in the usual implicit way to test for ticket #4608.
203 for line in self.returned_obj:
Brett Cannon74bfd702003-04-25 09:39:47 +0000204 self.assertEqual(line, self.text)
Skip Montanaro080c9972001-01-28 21:12:22 +0000205
Senthil Kumaran3800ea92012-01-21 11:52:48 +0800206 def test_relativelocalfile(self):
207 self.assertRaises(ValueError,urllib.request.urlopen,'./' + self.pathname)
208
Senthil Kumaranefbd4ea2017-04-01 23:47:35 -0700209
Benjamin Peterson9bc93512008-09-22 22:10:59 +0000210class ProxyTests(unittest.TestCase):
211
212 def setUp(self):
Walter Dörwaldb525e182009-04-26 21:39:21 +0000213 # Records changes to env vars
214 self.env = support.EnvironmentVarGuard()
Benjamin Peterson46a99002010-01-09 18:45:30 +0000215 # Delete all proxy related env vars
Antoine Pitroub3a88b52010-10-14 18:31:39 +0000216 for k in list(os.environ):
Antoine Pitrou8c8f1ac2010-10-14 18:32:54 +0000217 if 'proxy' in k.lower():
Benjamin Peterson46a99002010-01-09 18:45:30 +0000218 self.env.unset(k)
Benjamin Peterson9bc93512008-09-22 22:10:59 +0000219
220 def tearDown(self):
Benjamin Peterson9bc93512008-09-22 22:10:59 +0000221 # Restore all proxy related env vars
Walter Dörwaldb525e182009-04-26 21:39:21 +0000222 self.env.__exit__()
223 del self.env
Benjamin Peterson9bc93512008-09-22 22:10:59 +0000224
225 def test_getproxies_environment_keep_no_proxies(self):
Walter Dörwaldb525e182009-04-26 21:39:21 +0000226 self.env.set('NO_PROXY', 'localhost')
227 proxies = urllib.request.getproxies_environment()
228 # getproxies_environment use lowered case truncated (no '_proxy') keys
Florent Xicluna419e3842010-08-08 16:16:07 +0000229 self.assertEqual('localhost', proxies['no'])
Senthil Kumaran89976f12011-08-06 12:27:40 +0800230 # List of no_proxies with space.
Senthil Kumarana7c0ff22016-04-25 08:16:23 -0700231 self.env.set('NO_PROXY', 'localhost, anotherdomain.com, newdomain.com:1234')
Senthil Kumaran89976f12011-08-06 12:27:40 +0800232 self.assertTrue(urllib.request.proxy_bypass_environment('anotherdomain.com'))
Senthil Kumarana7c0ff22016-04-25 08:16:23 -0700233 self.assertTrue(urllib.request.proxy_bypass_environment('anotherdomain.com:8888'))
234 self.assertTrue(urllib.request.proxy_bypass_environment('newdomain.com:1234'))
235
Senthil Kumaran4cbb23f2016-07-30 23:24:16 -0700236 def test_proxy_cgi_ignore(self):
237 try:
238 self.env.set('HTTP_PROXY', 'http://somewhere:3128')
239 proxies = urllib.request.getproxies_environment()
240 self.assertEqual('http://somewhere:3128', proxies['http'])
241 self.env.set('REQUEST_METHOD', 'GET')
242 proxies = urllib.request.getproxies_environment()
243 self.assertNotIn('http', proxies)
244 finally:
245 self.env.unset('REQUEST_METHOD')
246 self.env.unset('HTTP_PROXY')
247
Martin Panteraa279822016-04-30 01:03:40 +0000248 def test_proxy_bypass_environment_host_match(self):
249 bypass = urllib.request.proxy_bypass_environment
250 self.env.set('NO_PROXY',
Xiang Zhang959ff7f2017-01-09 11:47:55 +0800251 'localhost, anotherdomain.com, newdomain.com:1234, .d.o.t')
Martin Panteraa279822016-04-30 01:03:40 +0000252 self.assertTrue(bypass('localhost'))
253 self.assertTrue(bypass('LocalHost')) # MixedCase
254 self.assertTrue(bypass('LOCALHOST')) # UPPERCASE
255 self.assertTrue(bypass('newdomain.com:1234'))
Xiang Zhang959ff7f2017-01-09 11:47:55 +0800256 self.assertTrue(bypass('foo.d.o.t')) # issue 29142
Martin Panteraa279822016-04-30 01:03:40 +0000257 self.assertTrue(bypass('anotherdomain.com:8888'))
258 self.assertTrue(bypass('www.newdomain.com:1234'))
259 self.assertFalse(bypass('prelocalhost'))
260 self.assertFalse(bypass('newdomain.com')) # no port
261 self.assertFalse(bypass('newdomain.com:1235')) # wrong port
Senthil Kumarana7c0ff22016-04-25 08:16:23 -0700262
Senthil Kumaranefbd4ea2017-04-01 23:47:35 -0700263
Senthil Kumarana7c0ff22016-04-25 08:16:23 -0700264class ProxyTests_withOrderedEnv(unittest.TestCase):
265
266 def setUp(self):
267 # We need to test conditions, where variable order _is_ significant
268 self._saved_env = os.environ
269 # Monkey patch os.environ, start with empty fake environment
270 os.environ = collections.OrderedDict()
271
272 def tearDown(self):
273 os.environ = self._saved_env
274
275 def test_getproxies_environment_prefer_lowercase(self):
276 # Test lowercase preference with removal
277 os.environ['no_proxy'] = ''
278 os.environ['No_Proxy'] = 'localhost'
279 self.assertFalse(urllib.request.proxy_bypass_environment('localhost'))
280 self.assertFalse(urllib.request.proxy_bypass_environment('arbitrary'))
281 os.environ['http_proxy'] = ''
282 os.environ['HTTP_PROXY'] = 'http://somewhere:3128'
283 proxies = urllib.request.getproxies_environment()
284 self.assertEqual({}, proxies)
285 # Test lowercase preference of proxy bypass and correct matching including ports
286 os.environ['no_proxy'] = 'localhost, noproxy.com, my.proxy:1234'
287 os.environ['No_Proxy'] = 'xyz.com'
288 self.assertTrue(urllib.request.proxy_bypass_environment('localhost'))
289 self.assertTrue(urllib.request.proxy_bypass_environment('noproxy.com:5678'))
290 self.assertTrue(urllib.request.proxy_bypass_environment('my.proxy:1234'))
291 self.assertFalse(urllib.request.proxy_bypass_environment('my.proxy'))
292 self.assertFalse(urllib.request.proxy_bypass_environment('arbitrary'))
293 # Test lowercase preference with replacement
294 os.environ['http_proxy'] = 'http://somewhere:3128'
295 os.environ['Http_Proxy'] = 'http://somewhereelse:3128'
296 proxies = urllib.request.getproxies_environment()
297 self.assertEqual('http://somewhere:3128', proxies['http'])
Benjamin Peterson9bc93512008-09-22 22:10:59 +0000298
Senthil Kumaranefbd4ea2017-04-01 23:47:35 -0700299
Benjamin Peterson3c2dca62014-06-07 15:08:04 -0700300class urlopen_HttpTests(unittest.TestCase, FakeHTTPMixin, FakeFTPMixin):
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000301 """Test urlopen() opening a fake http connection."""
302
Antoine Pitrou988dbd72010-12-17 17:35:56 +0000303 def check_read(self, ver):
304 self.fakehttp(b"HTTP/" + ver + b" 200 OK\r\n\r\nHello!")
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000305 try:
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000306 fp = urlopen("http://python.org/")
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000307 self.assertEqual(fp.readline(), b"Hello!")
308 self.assertEqual(fp.readline(), b"")
Christian Heimes9bd667a2008-01-20 15:14:11 +0000309 self.assertEqual(fp.geturl(), 'http://python.org/')
310 self.assertEqual(fp.getcode(), 200)
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000311 finally:
312 self.unfakehttp()
313
Senthil Kumaran26430412011-04-13 07:01:19 +0800314 def test_url_fragment(self):
315 # Issue #11703: geturl() omits fragments in the original URL.
316 url = 'http://docs.python.org/library/urllib.html#OK'
Senthil Kumaranb17abb12011-04-13 07:22:29 +0800317 self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello!")
Senthil Kumaran26430412011-04-13 07:01:19 +0800318 try:
319 fp = urllib.request.urlopen(url)
320 self.assertEqual(fp.geturl(), url)
321 finally:
322 self.unfakehttp()
323
Senthil Kumarand91ffca2011-03-19 17:25:27 +0800324 def test_willclose(self):
325 self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello!")
Senthil Kumaranacbaa922011-03-20 05:30:16 +0800326 try:
327 resp = urlopen("http://www.python.org")
328 self.assertTrue(resp.fp.will_close)
329 finally:
330 self.unfakehttp()
Senthil Kumarand91ffca2011-03-19 17:25:27 +0800331
Xtreak2fc936e2019-05-01 17:29:49 +0530332 @unittest.skipUnless(ssl, "ssl module required")
Gregory P. Smithc4e671e2019-04-30 19:12:21 -0700333 def test_url_with_control_char_rejected(self):
334 for char_no in list(range(0, 0x21)) + [0x7f]:
335 char = chr(char_no)
336 schemeless_url = f"//localhost:7777/test{char}/"
337 self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")
338 try:
339 # We explicitly test urllib.request.urlopen() instead of the top
340 # level 'def urlopen()' function defined in this... (quite ugly)
341 # test suite. They use different url opening codepaths. Plain
342 # urlopen uses FancyURLOpener which goes via a codepath that
343 # calls urllib.parse.quote() on the URL which makes all of the
344 # above attempts at injection within the url _path_ safe.
345 escaped_char_repr = repr(char).replace('\\', r'\\')
Gregory P. Smithb7378d72019-05-01 16:39:21 -0400346 InvalidURL = http.client.InvalidURL
Gregory P. Smithc4e671e2019-04-30 19:12:21 -0700347 with self.assertRaisesRegex(
Gregory P. Smithb7378d72019-05-01 16:39:21 -0400348 InvalidURL, f"contain control.*{escaped_char_repr}"):
Gregory P. Smithc4e671e2019-04-30 19:12:21 -0700349 urllib.request.urlopen(f"http:{schemeless_url}")
350 with self.assertRaisesRegex(
Gregory P. Smithb7378d72019-05-01 16:39:21 -0400351 InvalidURL, f"contain control.*{escaped_char_repr}"):
Gregory P. Smithc4e671e2019-04-30 19:12:21 -0700352 urllib.request.urlopen(f"https:{schemeless_url}")
353 # This code path quotes the URL so there is no injection.
354 resp = urlopen(f"http:{schemeless_url}")
355 self.assertNotIn(char, resp.geturl())
356 finally:
357 self.unfakehttp()
358
Xtreak2fc936e2019-05-01 17:29:49 +0530359 @unittest.skipUnless(ssl, "ssl module required")
Gregory P. Smithc4e671e2019-04-30 19:12:21 -0700360 def test_url_with_newline_header_injection_rejected(self):
361 self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")
362 host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"
363 schemeless_url = "//" + host + ":8080/test/?test=a"
364 try:
365 # We explicitly test urllib.request.urlopen() instead of the top
366 # level 'def urlopen()' function defined in this... (quite ugly)
367 # test suite. They use different url opening codepaths. Plain
368 # urlopen uses FancyURLOpener which goes via a codepath that
369 # calls urllib.parse.quote() on the URL which makes all of the
370 # above attempts at injection within the url _path_ safe.
Gregory P. Smithb7378d72019-05-01 16:39:21 -0400371 InvalidURL = http.client.InvalidURL
Gregory P. Smithc4e671e2019-04-30 19:12:21 -0700372 with self.assertRaisesRegex(
Gregory P. Smithb7378d72019-05-01 16:39:21 -0400373 InvalidURL, r"contain control.*\\r.*(found at least . .)"):
Gregory P. Smithc4e671e2019-04-30 19:12:21 -0700374 urllib.request.urlopen(f"http:{schemeless_url}")
Gregory P. Smithb7378d72019-05-01 16:39:21 -0400375 with self.assertRaisesRegex(InvalidURL, r"contain control.*\\n"):
Gregory P. Smithc4e671e2019-04-30 19:12:21 -0700376 urllib.request.urlopen(f"https:{schemeless_url}")
377 # This code path quotes the URL so there is no injection.
378 resp = urlopen(f"http:{schemeless_url}")
379 self.assertNotIn(' ', resp.geturl())
380 self.assertNotIn('\r', resp.geturl())
381 self.assertNotIn('\n', resp.geturl())
382 finally:
383 self.unfakehttp()
384
Antoine Pitrou988dbd72010-12-17 17:35:56 +0000385 def test_read_0_9(self):
386 # "0.9" response accepted (but not "simple responses" without
387 # a status line)
388 self.check_read(b"0.9")
389
390 def test_read_1_0(self):
391 self.check_read(b"1.0")
392
393 def test_read_1_1(self):
394 self.check_read(b"1.1")
395
Christian Heimes57dddfb2008-01-02 18:30:52 +0000396 def test_read_bogus(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200397 # urlopen() should raise OSError for many error codes.
Christian Heimes57dddfb2008-01-02 18:30:52 +0000398 self.fakehttp(b'''HTTP/1.1 401 Authentication Required
399Date: Wed, 02 Jan 2008 03:03:54 GMT
400Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
401Connection: close
402Content-Type: text/html; charset=iso-8859-1
403''')
404 try:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200405 self.assertRaises(OSError, urlopen, "http://python.org/")
Christian Heimes57dddfb2008-01-02 18:30:52 +0000406 finally:
407 self.unfakehttp()
408
guido@google.coma119df92011-03-29 11:41:02 -0700409 def test_invalid_redirect(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200410 # urlopen() should raise OSError for many error codes.
guido@google.coma119df92011-03-29 11:41:02 -0700411 self.fakehttp(b'''HTTP/1.1 302 Found
412Date: Wed, 02 Jan 2008 03:03:54 GMT
413Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
414Location: file://guidocomputer.athome.com:/python/license
415Connection: close
416Content-Type: text/html; charset=iso-8859-1
417''')
418 try:
Martin Pantera0370222016-02-04 06:01:35 +0000419 msg = "Redirection to url 'file:"
420 with self.assertRaisesRegex(urllib.error.HTTPError, msg):
421 urlopen("http://python.org/")
guido@google.coma119df92011-03-29 11:41:02 -0700422 finally:
423 self.unfakehttp()
424
Martin Pantera0370222016-02-04 06:01:35 +0000425 def test_redirect_limit_independent(self):
426 # Ticket #12923: make sure independent requests each use their
427 # own retry limit.
428 for i in range(FancyURLopener().maxtries):
429 self.fakehttp(b'''HTTP/1.1 302 Found
430Location: file://guidocomputer.athome.com:/python/license
431Connection: close
432''')
433 try:
434 self.assertRaises(urllib.error.HTTPError, urlopen,
435 "http://something")
436 finally:
437 self.unfakehttp()
438
Guido van Rossumd8faa362007-04-27 19:54:29 +0000439 def test_empty_socket(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200440 # urlopen() raises OSError if the underlying socket does not send any
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000441 # data. (#1680230)
Christian Heimes57dddfb2008-01-02 18:30:52 +0000442 self.fakehttp(b'')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000443 try:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200444 self.assertRaises(OSError, urlopen, "http://something")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000445 finally:
446 self.unfakehttp()
447
Senthil Kumaranf5776862012-10-21 13:30:02 -0700448 def test_missing_localfile(self):
449 # Test for #10836
Senthil Kumaran3ebef362012-10-21 18:31:25 -0700450 with self.assertRaises(urllib.error.URLError) as e:
Senthil Kumaranf5776862012-10-21 13:30:02 -0700451 urlopen('file://localhost/a/file/which/doesnot/exists.py')
Senthil Kumaran3ebef362012-10-21 18:31:25 -0700452 self.assertTrue(e.exception.filename)
453 self.assertTrue(e.exception.reason)
454
455 def test_file_notexists(self):
456 fd, tmp_file = tempfile.mkstemp()
Senthil Kumaran3194d7c2012-10-23 09:40:53 -0700457 tmp_fileurl = 'file://localhost/' + tmp_file.replace(os.path.sep, '/')
Senthil Kumaranbf644c52012-10-23 11:07:02 -0700458 try:
459 self.assertTrue(os.path.exists(tmp_file))
460 with urlopen(tmp_fileurl) as fobj:
461 self.assertTrue(fobj)
462 finally:
463 os.close(fd)
464 os.unlink(tmp_file)
Senthil Kumaran3ebef362012-10-21 18:31:25 -0700465 self.assertFalse(os.path.exists(tmp_file))
466 with self.assertRaises(urllib.error.URLError):
467 urlopen(tmp_fileurl)
468
469 def test_ftp_nohost(self):
470 test_ftp_url = 'ftp:///path'
471 with self.assertRaises(urllib.error.URLError) as e:
472 urlopen(test_ftp_url)
473 self.assertFalse(e.exception.filename)
474 self.assertTrue(e.exception.reason)
475
476 def test_ftp_nonexisting(self):
477 with self.assertRaises(urllib.error.URLError) as e:
478 urlopen('ftp://localhost/a/file/which/doesnot/exists.py')
479 self.assertFalse(e.exception.filename)
480 self.assertTrue(e.exception.reason)
481
Benjamin Peterson3c2dca62014-06-07 15:08:04 -0700482 @patch.object(urllib.request, 'MAXFTPCACHE', 0)
483 def test_ftp_cache_pruning(self):
484 self.fakeftp()
485 try:
486 urllib.request.ftpcache['test'] = urllib.request.ftpwrapper('user', 'pass', 'localhost', 21, [])
487 urlopen('ftp://localhost')
488 finally:
489 self.unfakeftp()
490
Senthil Kumarande0eb242010-08-01 17:53:37 +0000491 def test_userpass_inurl(self):
Antoine Pitrou988dbd72010-12-17 17:35:56 +0000492 self.fakehttp(b"HTTP/1.0 200 OK\r\n\r\nHello!")
Senthil Kumarande0eb242010-08-01 17:53:37 +0000493 try:
494 fp = urlopen("http://user:pass@python.org/")
495 self.assertEqual(fp.readline(), b"Hello!")
496 self.assertEqual(fp.readline(), b"")
497 self.assertEqual(fp.geturl(), 'http://user:pass@python.org/')
498 self.assertEqual(fp.getcode(), 200)
499 finally:
500 self.unfakehttp()
501
Senthil Kumaranc5c5a142012-01-14 19:09:04 +0800502 def test_userpass_inurl_w_spaces(self):
503 self.fakehttp(b"HTTP/1.0 200 OK\r\n\r\nHello!")
504 try:
505 userpass = "a b:c d"
506 url = "http://{}@python.org/".format(userpass)
507 fakehttp_wrapper = http.client.HTTPConnection
508 authorization = ("Authorization: Basic %s\r\n" %
509 b64encode(userpass.encode("ASCII")).decode("ASCII"))
510 fp = urlopen(url)
511 # The authorization header must be in place
512 self.assertIn(authorization, fakehttp_wrapper.buf.decode("UTF-8"))
513 self.assertEqual(fp.readline(), b"Hello!")
514 self.assertEqual(fp.readline(), b"")
515 # the spaces are quoted in URL so no match
516 self.assertNotEqual(fp.geturl(), url)
517 self.assertEqual(fp.getcode(), 200)
518 finally:
519 self.unfakehttp()
520
Senthil Kumaran38b968b92012-03-14 13:43:53 -0700521 def test_URLopener_deprecation(self):
522 with support.check_warnings(('',DeprecationWarning)):
Senthil Kumaran3ebef362012-10-21 18:31:25 -0700523 urllib.request.URLopener()
Senthil Kumaran38b968b92012-03-14 13:43:53 -0700524
Antoine Pitrou07df6552014-11-02 17:23:14 +0100525 @unittest.skipUnless(ssl, "ssl module required")
Senthil Kumarana5c85b32014-09-19 15:23:30 +0800526 def test_cafile_and_context(self):
527 context = ssl.create_default_context()
Christian Heimesd0486372016-09-10 23:23:33 +0200528 with support.check_warnings(('', DeprecationWarning)):
529 with self.assertRaises(ValueError):
530 urllib.request.urlopen(
531 "https://localhost", cafile="/nonexistent/path", context=context
532 )
Senthil Kumarana5c85b32014-09-19 15:23:30 +0800533
Senthil Kumaranefbd4ea2017-04-01 23:47:35 -0700534
Antoine Pitroudf204be2012-11-24 17:59:08 +0100535class urlopen_DataTests(unittest.TestCase):
536 """Test urlopen() opening a data URL."""
537
538 def setUp(self):
539 # text containing URL special- and unicode-characters
540 self.text = "test data URLs :;,%=& \u00f6 \u00c4 "
541 # 2x1 pixel RGB PNG image with one black and one white pixel
542 self.image = (
543 b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x02\x00\x00\x00'
544 b'\x01\x08\x02\x00\x00\x00{@\xe8\xdd\x00\x00\x00\x01sRGB\x00\xae'
545 b'\xce\x1c\xe9\x00\x00\x00\x0fIDAT\x08\xd7c```\xf8\xff\xff?\x00'
546 b'\x06\x01\x02\xfe\no/\x1e\x00\x00\x00\x00IEND\xaeB`\x82')
547
548 self.text_url = (
549 "data:text/plain;charset=UTF-8,test%20data%20URLs%20%3A%3B%2C%25%3"
550 "D%26%20%C3%B6%20%C3%84%20")
551 self.text_url_base64 = (
552 "data:text/plain;charset=ISO-8859-1;base64,dGVzdCBkYXRhIFVSTHMgOjs"
553 "sJT0mIPYgxCA%3D")
554 # base64 encoded data URL that contains ignorable spaces,
555 # such as "\n", " ", "%0A", and "%20".
556 self.image_url = (
557 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAIAAAB7\n"
558 "QOjdAAAAAXNSR0IArs4c6QAAAA9JREFUCNdj%0AYGBg%2BP//PwAGAQL%2BCm8 "
559 "vHgAAAABJRU5ErkJggg%3D%3D%0A%20")
560
561 self.text_url_resp = urllib.request.urlopen(self.text_url)
562 self.text_url_base64_resp = urllib.request.urlopen(
563 self.text_url_base64)
564 self.image_url_resp = urllib.request.urlopen(self.image_url)
565
566 def test_interface(self):
567 # Make sure object returned by urlopen() has the specified methods
568 for attr in ("read", "readline", "readlines",
569 "close", "info", "geturl", "getcode", "__iter__"):
570 self.assertTrue(hasattr(self.text_url_resp, attr),
571 "object returned by urlopen() lacks %s attribute" %
572 attr)
573
574 def test_info(self):
575 self.assertIsInstance(self.text_url_resp.info(), email.message.Message)
576 self.assertEqual(self.text_url_base64_resp.info().get_params(),
577 [('text/plain', ''), ('charset', 'ISO-8859-1')])
578 self.assertEqual(self.image_url_resp.info()['content-length'],
579 str(len(self.image)))
580 self.assertEqual(urllib.request.urlopen("data:,").info().get_params(),
581 [('text/plain', ''), ('charset', 'US-ASCII')])
582
583 def test_geturl(self):
584 self.assertEqual(self.text_url_resp.geturl(), self.text_url)
585 self.assertEqual(self.text_url_base64_resp.geturl(),
586 self.text_url_base64)
587 self.assertEqual(self.image_url_resp.geturl(), self.image_url)
588
589 def test_read_text(self):
590 self.assertEqual(self.text_url_resp.read().decode(
591 dict(self.text_url_resp.info().get_params())['charset']), self.text)
592
593 def test_read_text_base64(self):
594 self.assertEqual(self.text_url_base64_resp.read().decode(
595 dict(self.text_url_base64_resp.info().get_params())['charset']),
596 self.text)
597
598 def test_read_image(self):
599 self.assertEqual(self.image_url_resp.read(), self.image)
600
601 def test_missing_comma(self):
602 self.assertRaises(ValueError,urllib.request.urlopen,'data:text/plain')
603
604 def test_invalid_base64_data(self):
605 # missing padding character
606 self.assertRaises(ValueError,urllib.request.urlopen,'data:;base64,Cg=')
607
Senthil Kumaranefbd4ea2017-04-01 23:47:35 -0700608
Brett Cannon19691362003-04-29 05:08:06 +0000609class urlretrieve_FileTests(unittest.TestCase):
Brett Cannon74bfd702003-04-25 09:39:47 +0000610 """Test urllib.urlretrieve() on local files"""
Skip Montanaro080c9972001-01-28 21:12:22 +0000611
Brett Cannon19691362003-04-29 05:08:06 +0000612 def setUp(self):
Georg Brandl5a650a22005-08-26 08:51:34 +0000613 # Create a list of temporary files. Each item in the list is a file
614 # name (absolute path or relative to the current working directory).
615 # All files in this list will be deleted in the tearDown method. Note,
616 # this only helps to makes sure temporary files get deleted, but it
617 # does nothing about trying to close files that may still be open. It
618 # is the responsibility of the developer to properly close files even
619 # when exceptional conditions occur.
620 self.tempFiles = []
621
Brett Cannon19691362003-04-29 05:08:06 +0000622 # Create a temporary file.
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000623 self.registerFileForCleanUp(support.TESTFN)
Guido van Rossuma0982942007-07-10 08:30:03 +0000624 self.text = b'testing urllib.urlretrieve'
Georg Brandl5a650a22005-08-26 08:51:34 +0000625 try:
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000626 FILE = open(support.TESTFN, 'wb')
Georg Brandl5a650a22005-08-26 08:51:34 +0000627 FILE.write(self.text)
628 FILE.close()
629 finally:
630 try: FILE.close()
631 except: pass
Brett Cannon19691362003-04-29 05:08:06 +0000632
633 def tearDown(self):
Georg Brandl5a650a22005-08-26 08:51:34 +0000634 # Delete the temporary files.
635 for each in self.tempFiles:
636 try: os.remove(each)
637 except: pass
638
639 def constructLocalFileUrl(self, filePath):
Victor Stinner6c6f8512010-08-07 10:09:35 +0000640 filePath = os.path.abspath(filePath)
641 try:
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000642 filePath.encode("utf-8")
Victor Stinner6c6f8512010-08-07 10:09:35 +0000643 except UnicodeEncodeError:
644 raise unittest.SkipTest("filePath is not encodable to utf8")
645 return "file://%s" % urllib.request.pathname2url(filePath)
Georg Brandl5a650a22005-08-26 08:51:34 +0000646
Guido van Rossum70d0dda2007-08-29 01:53:26 +0000647 def createNewTempFile(self, data=b""):
Georg Brandl5a650a22005-08-26 08:51:34 +0000648 """Creates a new temporary file containing the specified data,
649 registers the file for deletion during the test fixture tear down, and
650 returns the absolute path of the file."""
651
652 newFd, newFilePath = tempfile.mkstemp()
653 try:
654 self.registerFileForCleanUp(newFilePath)
655 newFile = os.fdopen(newFd, "wb")
656 newFile.write(data)
657 newFile.close()
658 finally:
659 try: newFile.close()
660 except: pass
661 return newFilePath
662
663 def registerFileForCleanUp(self, fileName):
664 self.tempFiles.append(fileName)
Brett Cannon19691362003-04-29 05:08:06 +0000665
666 def test_basic(self):
667 # Make sure that a local file just gets its own location returned and
668 # a headers value is returned.
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000669 result = urllib.request.urlretrieve("file:%s" % support.TESTFN)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000670 self.assertEqual(result[0], support.TESTFN)
Ezio Melottie9615932010-01-24 19:26:24 +0000671 self.assertIsInstance(result[1], email.message.Message,
Martin Panter7462b6492015-11-02 03:37:02 +0000672 "did not get an email.message.Message instance "
Ezio Melottie9615932010-01-24 19:26:24 +0000673 "as second returned value")
Brett Cannon19691362003-04-29 05:08:06 +0000674
675 def test_copy(self):
676 # Test that setting the filename argument works.
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000677 second_temp = "%s.2" % support.TESTFN
Georg Brandl5a650a22005-08-26 08:51:34 +0000678 self.registerFileForCleanUp(second_temp)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000679 result = urllib.request.urlretrieve(self.constructLocalFileUrl(
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000680 support.TESTFN), second_temp)
Brett Cannon19691362003-04-29 05:08:06 +0000681 self.assertEqual(second_temp, result[0])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000682 self.assertTrue(os.path.exists(second_temp), "copy of the file was not "
Brett Cannon19691362003-04-29 05:08:06 +0000683 "made")
Alex Martelli01c77c62006-08-24 02:58:11 +0000684 FILE = open(second_temp, 'rb')
Brett Cannon19691362003-04-29 05:08:06 +0000685 try:
686 text = FILE.read()
Brett Cannon19691362003-04-29 05:08:06 +0000687 FILE.close()
Georg Brandl5a650a22005-08-26 08:51:34 +0000688 finally:
689 try: FILE.close()
690 except: pass
Brett Cannon19691362003-04-29 05:08:06 +0000691 self.assertEqual(self.text, text)
692
693 def test_reporthook(self):
694 # Make sure that the reporthook works.
Senthil Kumarane24f96a2012-03-13 19:29:33 -0700695 def hooktester(block_count, block_read_size, file_size, count_holder=[0]):
696 self.assertIsInstance(block_count, int)
697 self.assertIsInstance(block_read_size, int)
698 self.assertIsInstance(file_size, int)
699 self.assertEqual(block_count, count_holder[0])
Brett Cannon19691362003-04-29 05:08:06 +0000700 count_holder[0] = count_holder[0] + 1
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000701 second_temp = "%s.2" % support.TESTFN
Georg Brandl5a650a22005-08-26 08:51:34 +0000702 self.registerFileForCleanUp(second_temp)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000703 urllib.request.urlretrieve(
704 self.constructLocalFileUrl(support.TESTFN),
Georg Brandl5a650a22005-08-26 08:51:34 +0000705 second_temp, hooktester)
706
707 def test_reporthook_0_bytes(self):
708 # Test on zero length file. Should call reporthook only 1 time.
709 report = []
Senthil Kumarane24f96a2012-03-13 19:29:33 -0700710 def hooktester(block_count, block_read_size, file_size, _report=report):
711 _report.append((block_count, block_read_size, file_size))
Georg Brandl5a650a22005-08-26 08:51:34 +0000712 srcFileName = self.createNewTempFile()
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000713 urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName),
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000714 support.TESTFN, hooktester)
Georg Brandl5a650a22005-08-26 08:51:34 +0000715 self.assertEqual(len(report), 1)
716 self.assertEqual(report[0][2], 0)
717
718 def test_reporthook_5_bytes(self):
719 # Test on 5 byte file. Should call reporthook only 2 times (once when
720 # the "network connection" is established and once when the block is
Senthil Kumarane24f96a2012-03-13 19:29:33 -0700721 # read).
Georg Brandl5a650a22005-08-26 08:51:34 +0000722 report = []
Senthil Kumarane24f96a2012-03-13 19:29:33 -0700723 def hooktester(block_count, block_read_size, file_size, _report=report):
724 _report.append((block_count, block_read_size, file_size))
Guido van Rossum70d0dda2007-08-29 01:53:26 +0000725 srcFileName = self.createNewTempFile(b"x" * 5)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000726 urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName),
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000727 support.TESTFN, hooktester)
Georg Brandl5a650a22005-08-26 08:51:34 +0000728 self.assertEqual(len(report), 2)
Gregory P. Smith6d9388f2012-11-10 15:12:55 -0800729 self.assertEqual(report[0][2], 5)
730 self.assertEqual(report[1][2], 5)
Georg Brandl5a650a22005-08-26 08:51:34 +0000731
732 def test_reporthook_8193_bytes(self):
733 # Test on 8193 byte file. Should call reporthook only 3 times (once
734 # when the "network connection" is established, once for the next 8192
735 # bytes, and once for the last byte).
736 report = []
Senthil Kumarane24f96a2012-03-13 19:29:33 -0700737 def hooktester(block_count, block_read_size, file_size, _report=report):
738 _report.append((block_count, block_read_size, file_size))
Guido van Rossum70d0dda2007-08-29 01:53:26 +0000739 srcFileName = self.createNewTempFile(b"x" * 8193)
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000740 urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName),
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000741 support.TESTFN, hooktester)
Georg Brandl5a650a22005-08-26 08:51:34 +0000742 self.assertEqual(len(report), 3)
Gregory P. Smith6d9388f2012-11-10 15:12:55 -0800743 self.assertEqual(report[0][2], 8193)
744 self.assertEqual(report[0][1], 8192)
Senthil Kumarane24f96a2012-03-13 19:29:33 -0700745 self.assertEqual(report[1][1], 8192)
Gregory P. Smith6d9388f2012-11-10 15:12:55 -0800746 self.assertEqual(report[2][1], 8192)
Skip Montanaro080c9972001-01-28 21:12:22 +0000747
Senthil Kumarance260142011-11-01 01:35:17 +0800748
749class urlretrieve_HttpTests(unittest.TestCase, FakeHTTPMixin):
750 """Test urllib.urlretrieve() using fake http connections"""
751
752 def test_short_content_raises_ContentTooShortError(self):
753 self.fakehttp(b'''HTTP/1.1 200 OK
754Date: Wed, 02 Jan 2008 03:03:54 GMT
755Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
756Connection: close
757Content-Length: 100
758Content-Type: text/html; charset=iso-8859-1
759
760FF
761''')
762
763 def _reporthook(par1, par2, par3):
764 pass
765
766 with self.assertRaises(urllib.error.ContentTooShortError):
767 try:
Stéphane Wirtela40681d2019-02-22 14:45:36 +0100768 urllib.request.urlretrieve(support.TEST_HTTP_URL,
Senthil Kumarance260142011-11-01 01:35:17 +0800769 reporthook=_reporthook)
770 finally:
771 self.unfakehttp()
772
773 def test_short_content_raises_ContentTooShortError_without_reporthook(self):
774 self.fakehttp(b'''HTTP/1.1 200 OK
775Date: Wed, 02 Jan 2008 03:03:54 GMT
776Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
777Connection: close
778Content-Length: 100
779Content-Type: text/html; charset=iso-8859-1
780
781FF
782''')
783 with self.assertRaises(urllib.error.ContentTooShortError):
784 try:
Stéphane Wirtela40681d2019-02-22 14:45:36 +0100785 urllib.request.urlretrieve(support.TEST_HTTP_URL)
Senthil Kumarance260142011-11-01 01:35:17 +0800786 finally:
787 self.unfakehttp()
788
789
Brett Cannon74bfd702003-04-25 09:39:47 +0000790class QuotingTests(unittest.TestCase):
R David Murray44b548d2016-09-08 13:59:53 -0400791 r"""Tests for urllib.quote() and urllib.quote_plus()
Tim Petersc2659cf2003-05-12 20:19:37 +0000792
Ratnadeep Debnath21024f02017-02-25 14:30:28 +0530793 According to RFC 3986 (Uniform Resource Identifiers), to escape a
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000794 character you write it as '%' + <2 character US-ASCII hex value>.
795 The Python code of ``'%' + hex(ord(<character>))[2:]`` escapes a
796 character properly. Case does not matter on the hex letters.
Brett Cannon74bfd702003-04-25 09:39:47 +0000797
798 The various character sets specified are:
Tim Petersc2659cf2003-05-12 20:19:37 +0000799
Brett Cannon74bfd702003-04-25 09:39:47 +0000800 Reserved characters : ";/?:@&=+$,"
801 Have special meaning in URIs and must be escaped if not being used for
802 their special meaning
803 Data characters : letters, digits, and "-_.!~*'()"
804 Unreserved and do not need to be escaped; can be, though, if desired
805 Control characters : 0x00 - 0x1F, 0x7F
806 Have no use in URIs so must be escaped
807 space : 0x20
808 Must be escaped
809 Delimiters : '<>#%"'
810 Must be escaped
811 Unwise : "{}|\^[]`"
812 Must be escaped
Tim Petersc2659cf2003-05-12 20:19:37 +0000813
Brett Cannon74bfd702003-04-25 09:39:47 +0000814 """
815
816 def test_never_quote(self):
817 # Make sure quote() does not quote letters, digits, and "_,.-"
818 do_not_quote = '' .join(["ABCDEFGHIJKLMNOPQRSTUVWXYZ",
819 "abcdefghijklmnopqrstuvwxyz",
820 "0123456789",
Ratnadeep Debnath21024f02017-02-25 14:30:28 +0530821 "_.-~"])
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000822 result = urllib.parse.quote(do_not_quote)
Brett Cannon74bfd702003-04-25 09:39:47 +0000823 self.assertEqual(do_not_quote, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000824 "using quote(): %r != %r" % (do_not_quote, result))
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000825 result = urllib.parse.quote_plus(do_not_quote)
Brett Cannon74bfd702003-04-25 09:39:47 +0000826 self.assertEqual(do_not_quote, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000827 "using quote_plus(): %r != %r" % (do_not_quote, result))
Brett Cannon74bfd702003-04-25 09:39:47 +0000828
829 def test_default_safe(self):
830 # Test '/' is default value for 'safe' parameter
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000831 self.assertEqual(urllib.parse.quote.__defaults__[0], '/')
Brett Cannon74bfd702003-04-25 09:39:47 +0000832
833 def test_safe(self):
834 # Test setting 'safe' parameter does what it should do
835 quote_by_default = "<>"
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000836 result = urllib.parse.quote(quote_by_default, safe=quote_by_default)
Brett Cannon74bfd702003-04-25 09:39:47 +0000837 self.assertEqual(quote_by_default, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000838 "using quote(): %r != %r" % (quote_by_default, result))
Jeremy Hylton1ef7c6b2009-03-26 16:57:30 +0000839 result = urllib.parse.quote_plus(quote_by_default,
840 safe=quote_by_default)
Brett Cannon74bfd702003-04-25 09:39:47 +0000841 self.assertEqual(quote_by_default, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000842 "using quote_plus(): %r != %r" %
Brett Cannon74bfd702003-04-25 09:39:47 +0000843 (quote_by_default, result))
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000844 # Safe expressed as bytes rather than str
845 result = urllib.parse.quote(quote_by_default, safe=b"<>")
846 self.assertEqual(quote_by_default, result,
847 "using quote(): %r != %r" % (quote_by_default, result))
848 # "Safe" non-ASCII characters should have no effect
849 # (Since URIs are not allowed to have non-ASCII characters)
850 result = urllib.parse.quote("a\xfcb", encoding="latin-1", safe="\xfc")
851 expect = urllib.parse.quote("a\xfcb", encoding="latin-1", safe="")
852 self.assertEqual(expect, result,
853 "using quote(): %r != %r" %
854 (expect, result))
855 # Same as above, but using a bytes rather than str
856 result = urllib.parse.quote("a\xfcb", encoding="latin-1", safe=b"\xfc")
857 expect = urllib.parse.quote("a\xfcb", encoding="latin-1", safe="")
858 self.assertEqual(expect, result,
859 "using quote(): %r != %r" %
860 (expect, result))
Brett Cannon74bfd702003-04-25 09:39:47 +0000861
862 def test_default_quoting(self):
863 # Make sure all characters that should be quoted are by default sans
864 # space (separate test for that).
865 should_quote = [chr(num) for num in range(32)] # For 0x00 - 0x1F
R David Murray44b548d2016-09-08 13:59:53 -0400866 should_quote.append(r'<>#%"{}|\^[]`')
Brett Cannon74bfd702003-04-25 09:39:47 +0000867 should_quote.append(chr(127)) # For 0x7F
868 should_quote = ''.join(should_quote)
869 for char in should_quote:
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000870 result = urllib.parse.quote(char)
Brett Cannon74bfd702003-04-25 09:39:47 +0000871 self.assertEqual(hexescape(char), result,
Jeremy Hylton1ef7c6b2009-03-26 16:57:30 +0000872 "using quote(): "
873 "%s should be escaped to %s, not %s" %
Brett Cannon74bfd702003-04-25 09:39:47 +0000874 (char, hexescape(char), result))
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000875 result = urllib.parse.quote_plus(char)
Brett Cannon74bfd702003-04-25 09:39:47 +0000876 self.assertEqual(hexescape(char), result,
877 "using quote_plus(): "
Tim Petersc2659cf2003-05-12 20:19:37 +0000878 "%s should be escapes to %s, not %s" %
Brett Cannon74bfd702003-04-25 09:39:47 +0000879 (char, hexescape(char), result))
880 del should_quote
881 partial_quote = "ab[]cd"
882 expected = "ab%5B%5Dcd"
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000883 result = urllib.parse.quote(partial_quote)
Brett Cannon74bfd702003-04-25 09:39:47 +0000884 self.assertEqual(expected, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000885 "using quote(): %r != %r" % (expected, result))
Senthil Kumaran305a68e2011-09-13 06:40:27 +0800886 result = urllib.parse.quote_plus(partial_quote)
Brett Cannon74bfd702003-04-25 09:39:47 +0000887 self.assertEqual(expected, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000888 "using quote_plus(): %r != %r" % (expected, result))
Brett Cannon74bfd702003-04-25 09:39:47 +0000889
890 def test_quoting_space(self):
891 # Make sure quote() and quote_plus() handle spaces as specified in
892 # their unique way
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000893 result = urllib.parse.quote(' ')
Brett Cannon74bfd702003-04-25 09:39:47 +0000894 self.assertEqual(result, hexescape(' '),
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000895 "using quote(): %r != %r" % (result, hexescape(' ')))
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000896 result = urllib.parse.quote_plus(' ')
Brett Cannon74bfd702003-04-25 09:39:47 +0000897 self.assertEqual(result, '+',
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000898 "using quote_plus(): %r != +" % result)
Brett Cannon74bfd702003-04-25 09:39:47 +0000899 given = "a b cd e f"
900 expect = given.replace(' ', hexescape(' '))
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000901 result = urllib.parse.quote(given)
Brett Cannon74bfd702003-04-25 09:39:47 +0000902 self.assertEqual(expect, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000903 "using quote(): %r != %r" % (expect, result))
Brett Cannon74bfd702003-04-25 09:39:47 +0000904 expect = given.replace(' ', '+')
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000905 result = urllib.parse.quote_plus(given)
Brett Cannon74bfd702003-04-25 09:39:47 +0000906 self.assertEqual(expect, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000907 "using quote_plus(): %r != %r" % (expect, result))
Brett Cannon74bfd702003-04-25 09:39:47 +0000908
Raymond Hettinger2bdec7b2005-09-10 14:30:09 +0000909 def test_quoting_plus(self):
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000910 self.assertEqual(urllib.parse.quote_plus('alpha+beta gamma'),
Raymond Hettinger2bdec7b2005-09-10 14:30:09 +0000911 'alpha%2Bbeta+gamma')
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000912 self.assertEqual(urllib.parse.quote_plus('alpha+beta gamma', '+'),
Raymond Hettinger2bdec7b2005-09-10 14:30:09 +0000913 'alpha+beta+gamma')
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000914 # Test with bytes
915 self.assertEqual(urllib.parse.quote_plus(b'alpha+beta gamma'),
916 'alpha%2Bbeta+gamma')
917 # Test with safe bytes
918 self.assertEqual(urllib.parse.quote_plus('alpha+beta gamma', b'+'),
919 'alpha+beta+gamma')
920
921 def test_quote_bytes(self):
922 # Bytes should quote directly to percent-encoded values
923 given = b"\xa2\xd8ab\xff"
924 expect = "%A2%D8ab%FF"
925 result = urllib.parse.quote(given)
926 self.assertEqual(expect, result,
927 "using quote(): %r != %r" % (expect, result))
928 # Encoding argument should raise type error on bytes input
929 self.assertRaises(TypeError, urllib.parse.quote, given,
930 encoding="latin-1")
931 # quote_from_bytes should work the same
932 result = urllib.parse.quote_from_bytes(given)
933 self.assertEqual(expect, result,
934 "using quote_from_bytes(): %r != %r"
935 % (expect, result))
936
937 def test_quote_with_unicode(self):
938 # Characters in Latin-1 range, encoded by default in UTF-8
939 given = "\xa2\xd8ab\xff"
940 expect = "%C2%A2%C3%98ab%C3%BF"
941 result = urllib.parse.quote(given)
942 self.assertEqual(expect, result,
943 "using quote(): %r != %r" % (expect, result))
944 # Characters in Latin-1 range, encoded by with None (default)
945 result = urllib.parse.quote(given, encoding=None, errors=None)
946 self.assertEqual(expect, result,
947 "using quote(): %r != %r" % (expect, result))
948 # Characters in Latin-1 range, encoded with Latin-1
949 given = "\xa2\xd8ab\xff"
950 expect = "%A2%D8ab%FF"
951 result = urllib.parse.quote(given, encoding="latin-1")
952 self.assertEqual(expect, result,
953 "using quote(): %r != %r" % (expect, result))
954 # Characters in BMP, encoded by default in UTF-8
955 given = "\u6f22\u5b57" # "Kanji"
956 expect = "%E6%BC%A2%E5%AD%97"
957 result = urllib.parse.quote(given)
958 self.assertEqual(expect, result,
959 "using quote(): %r != %r" % (expect, result))
960 # Characters in BMP, encoded with Latin-1
961 given = "\u6f22\u5b57"
962 self.assertRaises(UnicodeEncodeError, urllib.parse.quote, given,
963 encoding="latin-1")
964 # Characters in BMP, encoded with Latin-1, with replace error handling
965 given = "\u6f22\u5b57"
966 expect = "%3F%3F" # "??"
967 result = urllib.parse.quote(given, encoding="latin-1",
968 errors="replace")
969 self.assertEqual(expect, result,
970 "using quote(): %r != %r" % (expect, result))
971 # Characters in BMP, Latin-1, with xmlcharref error handling
972 given = "\u6f22\u5b57"
973 expect = "%26%2328450%3B%26%2323383%3B" # "&#28450;&#23383;"
974 result = urllib.parse.quote(given, encoding="latin-1",
975 errors="xmlcharrefreplace")
976 self.assertEqual(expect, result,
977 "using quote(): %r != %r" % (expect, result))
Raymond Hettinger2bdec7b2005-09-10 14:30:09 +0000978
Georg Brandlfaf41492009-05-26 18:31:11 +0000979 def test_quote_plus_with_unicode(self):
980 # Encoding (latin-1) test for quote_plus
981 given = "\xa2\xd8 \xff"
982 expect = "%A2%D8+%FF"
983 result = urllib.parse.quote_plus(given, encoding="latin-1")
984 self.assertEqual(expect, result,
985 "using quote_plus(): %r != %r" % (expect, result))
986 # Errors test for quote_plus
987 given = "ab\u6f22\u5b57 cd"
988 expect = "ab%3F%3F+cd"
989 result = urllib.parse.quote_plus(given, encoding="latin-1",
990 errors="replace")
991 self.assertEqual(expect, result,
992 "using quote_plus(): %r != %r" % (expect, result))
993
Senthil Kumarand496c4c2010-07-30 19:34:36 +0000994
Brett Cannon74bfd702003-04-25 09:39:47 +0000995class UnquotingTests(unittest.TestCase):
996 """Tests for unquote() and unquote_plus()
Tim Petersc2659cf2003-05-12 20:19:37 +0000997
Brett Cannon74bfd702003-04-25 09:39:47 +0000998 See the doc string for quoting_Tests for details on quoting and such.
999
1000 """
1001
1002 def test_unquoting(self):
1003 # Make sure unquoting of all ASCII values works
1004 escape_list = []
1005 for num in range(128):
1006 given = hexescape(chr(num))
1007 expect = chr(num)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001008 result = urllib.parse.unquote(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001009 self.assertEqual(expect, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001010 "using unquote(): %r != %r" % (expect, result))
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001011 result = urllib.parse.unquote_plus(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001012 self.assertEqual(expect, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001013 "using unquote_plus(): %r != %r" %
Brett Cannon74bfd702003-04-25 09:39:47 +00001014 (expect, result))
1015 escape_list.append(given)
1016 escape_string = ''.join(escape_list)
1017 del escape_list
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001018 result = urllib.parse.unquote(escape_string)
Brett Cannon74bfd702003-04-25 09:39:47 +00001019 self.assertEqual(result.count('%'), 1,
Brett Cannon74bfd702003-04-25 09:39:47 +00001020 "using unquote(): not all characters escaped: "
1021 "%s" % result)
Georg Brandl604ef372010-07-31 08:20:02 +00001022 self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, None)
1023 self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, ())
Florent Xicluna62829dc2010-08-14 20:51:58 +00001024 with support.check_warnings(('', BytesWarning), quiet=True):
1025 self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, b'')
Brett Cannon74bfd702003-04-25 09:39:47 +00001026
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001027 def test_unquoting_badpercent(self):
1028 # Test unquoting on bad percent-escapes
1029 given = '%xab'
1030 expect = given
1031 result = urllib.parse.unquote(given)
1032 self.assertEqual(expect, result, "using unquote(): %r != %r"
1033 % (expect, result))
1034 given = '%x'
1035 expect = given
1036 result = urllib.parse.unquote(given)
1037 self.assertEqual(expect, result, "using unquote(): %r != %r"
1038 % (expect, result))
1039 given = '%'
1040 expect = given
1041 result = urllib.parse.unquote(given)
1042 self.assertEqual(expect, result, "using unquote(): %r != %r"
1043 % (expect, result))
1044 # unquote_to_bytes
1045 given = '%xab'
1046 expect = bytes(given, 'ascii')
1047 result = urllib.parse.unquote_to_bytes(given)
1048 self.assertEqual(expect, result, "using unquote_to_bytes(): %r != %r"
1049 % (expect, result))
1050 given = '%x'
1051 expect = bytes(given, 'ascii')
1052 result = urllib.parse.unquote_to_bytes(given)
1053 self.assertEqual(expect, result, "using unquote_to_bytes(): %r != %r"
1054 % (expect, result))
1055 given = '%'
1056 expect = bytes(given, 'ascii')
1057 result = urllib.parse.unquote_to_bytes(given)
1058 self.assertEqual(expect, result, "using unquote_to_bytes(): %r != %r"
1059 % (expect, result))
Georg Brandl604ef372010-07-31 08:20:02 +00001060 self.assertRaises((TypeError, AttributeError), urllib.parse.unquote_to_bytes, None)
1061 self.assertRaises((TypeError, AttributeError), urllib.parse.unquote_to_bytes, ())
Senthil Kumaran79e17f62010-07-19 18:17:19 +00001062
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001063 def test_unquoting_mixed_case(self):
1064 # Test unquoting on mixed-case hex digits in the percent-escapes
1065 given = '%Ab%eA'
1066 expect = b'\xab\xea'
1067 result = urllib.parse.unquote_to_bytes(given)
1068 self.assertEqual(expect, result,
1069 "using unquote_to_bytes(): %r != %r"
1070 % (expect, result))
1071
Brett Cannon74bfd702003-04-25 09:39:47 +00001072 def test_unquoting_parts(self):
1073 # Make sure unquoting works when have non-quoted characters
1074 # interspersed
1075 given = 'ab%sd' % hexescape('c')
1076 expect = "abcd"
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001077 result = urllib.parse.unquote(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001078 self.assertEqual(expect, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001079 "using quote(): %r != %r" % (expect, result))
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001080 result = urllib.parse.unquote_plus(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001081 self.assertEqual(expect, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001082 "using unquote_plus(): %r != %r" % (expect, result))
Tim Petersc2659cf2003-05-12 20:19:37 +00001083
Brett Cannon74bfd702003-04-25 09:39:47 +00001084 def test_unquoting_plus(self):
1085 # Test difference between unquote() and unquote_plus()
1086 given = "are+there+spaces..."
1087 expect = given
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001088 result = urllib.parse.unquote(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001089 self.assertEqual(expect, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001090 "using unquote(): %r != %r" % (expect, result))
Brett Cannon74bfd702003-04-25 09:39:47 +00001091 expect = given.replace('+', ' ')
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001092 result = urllib.parse.unquote_plus(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001093 self.assertEqual(expect, result,
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001094 "using unquote_plus(): %r != %r" % (expect, result))
1095
1096 def test_unquote_to_bytes(self):
1097 given = 'br%C3%BCckner_sapporo_20050930.doc'
1098 expect = b'br\xc3\xbcckner_sapporo_20050930.doc'
1099 result = urllib.parse.unquote_to_bytes(given)
1100 self.assertEqual(expect, result,
1101 "using unquote_to_bytes(): %r != %r"
1102 % (expect, result))
1103 # Test on a string with unescaped non-ASCII characters
1104 # (Technically an invalid URI; expect those characters to be UTF-8
1105 # encoded).
1106 result = urllib.parse.unquote_to_bytes("\u6f22%C3%BC")
1107 expect = b'\xe6\xbc\xa2\xc3\xbc' # UTF-8 for "\u6f22\u00fc"
1108 self.assertEqual(expect, result,
1109 "using unquote_to_bytes(): %r != %r"
1110 % (expect, result))
1111 # Test with a bytes as input
1112 given = b'%A2%D8ab%FF'
1113 expect = b'\xa2\xd8ab\xff'
1114 result = urllib.parse.unquote_to_bytes(given)
1115 self.assertEqual(expect, result,
1116 "using unquote_to_bytes(): %r != %r"
1117 % (expect, result))
1118 # Test with a bytes as input, with unescaped non-ASCII bytes
1119 # (Technically an invalid URI; expect those bytes to be preserved)
1120 given = b'%A2\xd8ab%FF'
1121 expect = b'\xa2\xd8ab\xff'
1122 result = urllib.parse.unquote_to_bytes(given)
1123 self.assertEqual(expect, result,
1124 "using unquote_to_bytes(): %r != %r"
1125 % (expect, result))
Brett Cannon74bfd702003-04-25 09:39:47 +00001126
Raymond Hettinger4b0f20d2005-10-15 16:41:53 +00001127 def test_unquote_with_unicode(self):
Guido van Rossum52dbbb92008-08-18 21:44:30 +00001128 # Characters in the Latin-1 range, encoded with UTF-8
1129 given = 'br%C3%BCckner_sapporo_20050930.doc'
1130 expect = 'br\u00fcckner_sapporo_20050930.doc'
1131 result = urllib.parse.unquote(given)
1132 self.assertEqual(expect, result,
1133 "using unquote(): %r != %r" % (expect, result))
1134 # Characters in the Latin-1 range, encoded with None (default)
1135 result = urllib.parse.unquote(given, encoding=None, errors=None)
1136 self.assertEqual(expect, result,
1137 "using unquote(): %r != %r" % (expect, result))
1138
1139 # Characters in the Latin-1 range, encoded with Latin-1
1140 result = urllib.parse.unquote('br%FCckner_sapporo_20050930.doc',
1141 encoding="latin-1")
1142 expect = 'br\u00fcckner_sapporo_20050930.doc'
1143 self.assertEqual(expect, result,
1144 "using unquote(): %r != %r" % (expect, result))
1145
1146 # Characters in BMP, encoded with UTF-8
1147 given = "%E6%BC%A2%E5%AD%97"
1148 expect = "\u6f22\u5b57" # "Kanji"
1149 result = urllib.parse.unquote(given)
1150 self.assertEqual(expect, result,
1151 "using unquote(): %r != %r" % (expect, result))
1152
1153 # Decode with UTF-8, invalid sequence
1154 given = "%F3%B1"
1155 expect = "\ufffd" # Replacement character
1156 result = urllib.parse.unquote(given)
1157 self.assertEqual(expect, result,
1158 "using unquote(): %r != %r" % (expect, result))
1159
1160 # Decode with UTF-8, invalid sequence, replace errors
1161 result = urllib.parse.unquote(given, errors="replace")
1162 self.assertEqual(expect, result,
1163 "using unquote(): %r != %r" % (expect, result))
1164
1165 # Decode with UTF-8, invalid sequence, ignoring errors
1166 given = "%F3%B1"
1167 expect = ""
1168 result = urllib.parse.unquote(given, errors="ignore")
1169 self.assertEqual(expect, result,
1170 "using unquote(): %r != %r" % (expect, result))
1171
1172 # A mix of non-ASCII and percent-encoded characters, UTF-8
1173 result = urllib.parse.unquote("\u6f22%C3%BC")
1174 expect = '\u6f22\u00fc'
1175 self.assertEqual(expect, result,
1176 "using unquote(): %r != %r" % (expect, result))
1177
1178 # A mix of non-ASCII and percent-encoded characters, Latin-1
1179 # (Note, the string contains non-Latin-1-representable characters)
1180 result = urllib.parse.unquote("\u6f22%FC", encoding="latin-1")
1181 expect = '\u6f22\u00fc'
1182 self.assertEqual(expect, result,
1183 "using unquote(): %r != %r" % (expect, result))
Raymond Hettinger4b0f20d2005-10-15 16:41:53 +00001184
Brett Cannon74bfd702003-04-25 09:39:47 +00001185class urlencode_Tests(unittest.TestCase):
1186 """Tests for urlencode()"""
1187
1188 def help_inputtype(self, given, test_type):
1189 """Helper method for testing different input types.
Tim Petersc2659cf2003-05-12 20:19:37 +00001190
Brett Cannon74bfd702003-04-25 09:39:47 +00001191 'given' must lead to only the pairs:
1192 * 1st, 1
1193 * 2nd, 2
1194 * 3rd, 3
Tim Petersc2659cf2003-05-12 20:19:37 +00001195
Brett Cannon74bfd702003-04-25 09:39:47 +00001196 Test cannot assume anything about order. Docs make no guarantee and
1197 have possible dictionary input.
Tim Petersc2659cf2003-05-12 20:19:37 +00001198
Brett Cannon74bfd702003-04-25 09:39:47 +00001199 """
1200 expect_somewhere = ["1st=1", "2nd=2", "3rd=3"]
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001201 result = urllib.parse.urlencode(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001202 for expected in expect_somewhere:
Ezio Melottib58e0bd2010-01-23 15:40:09 +00001203 self.assertIn(expected, result,
Brett Cannon74bfd702003-04-25 09:39:47 +00001204 "testing %s: %s not found in %s" %
1205 (test_type, expected, result))
1206 self.assertEqual(result.count('&'), 2,
1207 "testing %s: expected 2 '&'s; got %s" %
1208 (test_type, result.count('&')))
1209 amp_location = result.index('&')
1210 on_amp_left = result[amp_location - 1]
1211 on_amp_right = result[amp_location + 1]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001212 self.assertTrue(on_amp_left.isdigit() and on_amp_right.isdigit(),
Brett Cannon74bfd702003-04-25 09:39:47 +00001213 "testing %s: '&' not located in proper place in %s" %
1214 (test_type, result))
1215 self.assertEqual(len(result), (5 * 3) + 2, #5 chars per thing and amps
1216 "testing %s: "
1217 "unexpected number of characters: %s != %s" %
1218 (test_type, len(result), (5 * 3) + 2))
1219
1220 def test_using_mapping(self):
1221 # Test passing in a mapping object as an argument.
1222 self.help_inputtype({"1st":'1', "2nd":'2', "3rd":'3'},
1223 "using dict as input type")
1224
1225 def test_using_sequence(self):
1226 # Test passing in a sequence of two-item sequences as an argument.
1227 self.help_inputtype([('1st', '1'), ('2nd', '2'), ('3rd', '3')],
1228 "using sequence of two-item tuples as input")
1229
1230 def test_quoting(self):
1231 # Make sure keys and values are quoted using quote_plus()
1232 given = {"&":"="}
1233 expect = "%s=%s" % (hexescape('&'), hexescape('='))
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001234 result = urllib.parse.urlencode(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001235 self.assertEqual(expect, result)
1236 given = {"key name":"A bunch of pluses"}
1237 expect = "key+name=A+bunch+of+pluses"
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001238 result = urllib.parse.urlencode(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001239 self.assertEqual(expect, result)
1240
1241 def test_doseq(self):
1242 # Test that passing True for 'doseq' parameter works correctly
1243 given = {'sequence':['1', '2', '3']}
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001244 expect = "sequence=%s" % urllib.parse.quote_plus(str(['1', '2', '3']))
1245 result = urllib.parse.urlencode(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001246 self.assertEqual(expect, result)
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001247 result = urllib.parse.urlencode(given, True)
Brett Cannon74bfd702003-04-25 09:39:47 +00001248 for value in given["sequence"]:
1249 expect = "sequence=%s" % value
Ezio Melottib58e0bd2010-01-23 15:40:09 +00001250 self.assertIn(expect, result)
Brett Cannon74bfd702003-04-25 09:39:47 +00001251 self.assertEqual(result.count('&'), 2,
1252 "Expected 2 '&'s, got %s" % result.count('&'))
1253
Jeremy Hylton1ef7c6b2009-03-26 16:57:30 +00001254 def test_empty_sequence(self):
1255 self.assertEqual("", urllib.parse.urlencode({}))
1256 self.assertEqual("", urllib.parse.urlencode([]))
1257
1258 def test_nonstring_values(self):
1259 self.assertEqual("a=1", urllib.parse.urlencode({"a": 1}))
1260 self.assertEqual("a=None", urllib.parse.urlencode({"a": None}))
1261
1262 def test_nonstring_seq_values(self):
1263 self.assertEqual("a=1&a=2", urllib.parse.urlencode({"a": [1, 2]}, True))
1264 self.assertEqual("a=None&a=a",
1265 urllib.parse.urlencode({"a": [None, "a"]}, True))
Georg Brandl2daf6ae2012-02-20 19:54:16 +01001266 data = collections.OrderedDict([("a", 1), ("b", 1)])
Jeremy Hylton1ef7c6b2009-03-26 16:57:30 +00001267 self.assertEqual("a=a&a=b",
Georg Brandl2daf6ae2012-02-20 19:54:16 +01001268 urllib.parse.urlencode({"a": data}, True))
Jeremy Hylton1ef7c6b2009-03-26 16:57:30 +00001269
Senthil Kumarandf022da2010-07-03 17:48:22 +00001270 def test_urlencode_encoding(self):
1271 # ASCII encoding. Expect %3F with errors="replace'
1272 given = (('\u00a0', '\u00c1'),)
1273 expect = '%3F=%3F'
1274 result = urllib.parse.urlencode(given, encoding="ASCII", errors="replace")
1275 self.assertEqual(expect, result)
1276
1277 # Default is UTF-8 encoding.
1278 given = (('\u00a0', '\u00c1'),)
1279 expect = '%C2%A0=%C3%81'
1280 result = urllib.parse.urlencode(given)
1281 self.assertEqual(expect, result)
1282
1283 # Latin-1 encoding.
1284 given = (('\u00a0', '\u00c1'),)
1285 expect = '%A0=%C1'
1286 result = urllib.parse.urlencode(given, encoding="latin-1")
1287 self.assertEqual(expect, result)
1288
1289 def test_urlencode_encoding_doseq(self):
1290 # ASCII Encoding. Expect %3F with errors="replace'
1291 given = (('\u00a0', '\u00c1'),)
1292 expect = '%3F=%3F'
1293 result = urllib.parse.urlencode(given, doseq=True,
1294 encoding="ASCII", errors="replace")
1295 self.assertEqual(expect, result)
1296
1297 # ASCII Encoding. On a sequence of values.
1298 given = (("\u00a0", (1, "\u00c1")),)
1299 expect = '%3F=1&%3F=%3F'
1300 result = urllib.parse.urlencode(given, True,
1301 encoding="ASCII", errors="replace")
1302 self.assertEqual(expect, result)
1303
1304 # Utf-8
1305 given = (("\u00a0", "\u00c1"),)
1306 expect = '%C2%A0=%C3%81'
1307 result = urllib.parse.urlencode(given, True)
1308 self.assertEqual(expect, result)
1309
1310 given = (("\u00a0", (42, "\u00c1")),)
1311 expect = '%C2%A0=42&%C2%A0=%C3%81'
1312 result = urllib.parse.urlencode(given, True)
1313 self.assertEqual(expect, result)
1314
1315 # latin-1
1316 given = (("\u00a0", "\u00c1"),)
1317 expect = '%A0=%C1'
1318 result = urllib.parse.urlencode(given, True, encoding="latin-1")
1319 self.assertEqual(expect, result)
1320
1321 given = (("\u00a0", (42, "\u00c1")),)
1322 expect = '%A0=42&%A0=%C1'
1323 result = urllib.parse.urlencode(given, True, encoding="latin-1")
1324 self.assertEqual(expect, result)
1325
1326 def test_urlencode_bytes(self):
1327 given = ((b'\xa0\x24', b'\xc1\x24'),)
1328 expect = '%A0%24=%C1%24'
1329 result = urllib.parse.urlencode(given)
1330 self.assertEqual(expect, result)
1331 result = urllib.parse.urlencode(given, True)
1332 self.assertEqual(expect, result)
1333
1334 # Sequence of values
1335 given = ((b'\xa0\x24', (42, b'\xc1\x24')),)
1336 expect = '%A0%24=42&%A0%24=%C1%24'
1337 result = urllib.parse.urlencode(given, True)
1338 self.assertEqual(expect, result)
1339
1340 def test_urlencode_encoding_safe_parameter(self):
1341
1342 # Send '$' (\x24) as safe character
1343 # Default utf-8 encoding
1344
1345 given = ((b'\xa0\x24', b'\xc1\x24'),)
1346 result = urllib.parse.urlencode(given, safe=":$")
1347 expect = '%A0$=%C1$'
1348 self.assertEqual(expect, result)
1349
1350 given = ((b'\xa0\x24', b'\xc1\x24'),)
1351 result = urllib.parse.urlencode(given, doseq=True, safe=":$")
1352 expect = '%A0$=%C1$'
1353 self.assertEqual(expect, result)
1354
1355 # Safe parameter in sequence
1356 given = ((b'\xa0\x24', (b'\xc1\x24', 0xd, 42)),)
1357 expect = '%A0$=%C1$&%A0$=13&%A0$=42'
1358 result = urllib.parse.urlencode(given, True, safe=":$")
1359 self.assertEqual(expect, result)
1360
1361 # Test all above in latin-1 encoding
1362
1363 given = ((b'\xa0\x24', b'\xc1\x24'),)
1364 result = urllib.parse.urlencode(given, safe=":$",
1365 encoding="latin-1")
1366 expect = '%A0$=%C1$'
1367 self.assertEqual(expect, result)
1368
1369 given = ((b'\xa0\x24', b'\xc1\x24'),)
1370 expect = '%A0$=%C1$'
1371 result = urllib.parse.urlencode(given, doseq=True, safe=":$",
1372 encoding="latin-1")
1373
1374 given = ((b'\xa0\x24', (b'\xc1\x24', 0xd, 42)),)
1375 expect = '%A0$=%C1$&%A0$=13&%A0$=42'
1376 result = urllib.parse.urlencode(given, True, safe=":$",
1377 encoding="latin-1")
1378 self.assertEqual(expect, result)
1379
Brett Cannon74bfd702003-04-25 09:39:47 +00001380class Pathname_Tests(unittest.TestCase):
1381 """Test pathname2url() and url2pathname()"""
1382
1383 def test_basic(self):
1384 # Make sure simple tests pass
1385 expected_path = os.path.join("parts", "of", "a", "path")
1386 expected_url = "parts/of/a/path"
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001387 result = urllib.request.pathname2url(expected_path)
Brett Cannon74bfd702003-04-25 09:39:47 +00001388 self.assertEqual(expected_url, result,
1389 "pathname2url() failed; %s != %s" %
1390 (result, expected_url))
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001391 result = urllib.request.url2pathname(expected_url)
Brett Cannon74bfd702003-04-25 09:39:47 +00001392 self.assertEqual(expected_path, result,
1393 "url2pathame() failed; %s != %s" %
1394 (result, expected_path))
1395
1396 def test_quoting(self):
1397 # Test automatic quoting and unquoting works for pathnam2url() and
1398 # url2pathname() respectively
1399 given = os.path.join("needs", "quot=ing", "here")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001400 expect = "needs/%s/here" % urllib.parse.quote("quot=ing")
1401 result = urllib.request.pathname2url(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001402 self.assertEqual(expect, result,
1403 "pathname2url() failed; %s != %s" %
1404 (expect, result))
1405 expect = given
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001406 result = urllib.request.url2pathname(result)
Brett Cannon74bfd702003-04-25 09:39:47 +00001407 self.assertEqual(expect, result,
1408 "url2pathname() failed; %s != %s" %
1409 (expect, result))
1410 given = os.path.join("make sure", "using_quote")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001411 expect = "%s/using_quote" % urllib.parse.quote("make sure")
1412 result = urllib.request.pathname2url(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001413 self.assertEqual(expect, result,
1414 "pathname2url() failed; %s != %s" %
1415 (expect, result))
1416 given = "make+sure/using_unquote"
1417 expect = os.path.join("make+sure", "using_unquote")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001418 result = urllib.request.url2pathname(given)
Brett Cannon74bfd702003-04-25 09:39:47 +00001419 self.assertEqual(expect, result,
1420 "url2pathname() failed; %s != %s" %
1421 (expect, result))
Tim Petersc2659cf2003-05-12 20:19:37 +00001422
Senthil Kumaran2d2ea1b2011-04-14 13:16:30 +08001423 @unittest.skipUnless(sys.platform == 'win32',
1424 'test specific to the urllib.url2path function.')
1425 def test_ntpath(self):
1426 given = ('/C:/', '///C:/', '/C|//')
1427 expect = 'C:\\'
1428 for url in given:
1429 result = urllib.request.url2pathname(url)
1430 self.assertEqual(expect, result,
1431 'urllib.request..url2pathname() failed; %s != %s' %
1432 (expect, result))
1433 given = '///C|/path'
1434 expect = 'C:\\path'
1435 result = urllib.request.url2pathname(given)
1436 self.assertEqual(expect, result,
1437 'urllib.request.url2pathname() failed; %s != %s' %
1438 (expect, result))
1439
Senthil Kumaraneaaec272009-03-30 21:54:41 +00001440class Utility_Tests(unittest.TestCase):
1441 """Testcase to test the various utility functions in the urllib."""
1442
Senthil Kumaran1b7da512011-10-06 00:32:02 +08001443 def test_thishost(self):
1444 """Test the urllib.request.thishost utility function returns a tuple"""
1445 self.assertIsInstance(urllib.request.thishost(), tuple)
1446
Senthil Kumaran690ce9b2009-05-05 18:41:13 +00001447
1448class URLopener_Tests(unittest.TestCase):
1449 """Testcase to test the open method of URLopener class."""
1450
1451 def test_quoted_open(self):
1452 class DummyURLopener(urllib.request.URLopener):
1453 def open_spam(self, url):
1454 return url
Ezio Melotti79b99db2013-02-21 02:41:42 +02001455 with support.check_warnings(
1456 ('DummyURLopener style of invoking requests is deprecated.',
1457 DeprecationWarning)):
1458 self.assertEqual(DummyURLopener().open(
1459 'spam://example/ /'),'//example/%20/')
Senthil Kumaran690ce9b2009-05-05 18:41:13 +00001460
Ezio Melotti79b99db2013-02-21 02:41:42 +02001461 # test the safe characters are not quoted by urlopen
1462 self.assertEqual(DummyURLopener().open(
1463 "spam://c:|windows%/:=&?~#+!$,;'@()*[]|/path/"),
1464 "//c:|windows%/:=&?~#+!$,;'@()*[]|/path/")
Senthil Kumaran734f0592010-02-20 22:19:04 +00001465
Guido van Rossume7ba4952007-06-06 23:52:48 +00001466# Just commented them out.
1467# Can't really tell why keep failing in windows and sparc.
Ezio Melotti13925002011-03-16 11:05:33 +02001468# Everywhere else they work ok, but on those machines, sometimes
Guido van Rossume7ba4952007-06-06 23:52:48 +00001469# fail in one of the tests, sometimes in other. I have a linux, and
1470# the tests go ok.
Ezio Melotti85a86292013-08-17 16:57:41 +03001471# If anybody has one of the problematic environments, please help!
Guido van Rossume7ba4952007-06-06 23:52:48 +00001472# . Facundo
1473#
1474# def server(evt):
Georg Brandlf78e02b2008-06-10 17:40:04 +00001475# import socket, time
Guido van Rossume7ba4952007-06-06 23:52:48 +00001476# serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1477# serv.settimeout(3)
1478# serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1479# serv.bind(("", 9093))
Charles-François Natali6e204602014-07-23 19:28:13 +01001480# serv.listen()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001481# try:
1482# conn, addr = serv.accept()
1483# conn.send("1 Hola mundo\n")
1484# cantdata = 0
1485# while cantdata < 13:
1486# data = conn.recv(13-cantdata)
1487# cantdata += len(data)
1488# time.sleep(.3)
1489# conn.send("2 No more lines\n")
1490# conn.close()
1491# except socket.timeout:
1492# pass
1493# finally:
1494# serv.close()
1495# evt.set()
1496#
1497# class FTPWrapperTests(unittest.TestCase):
1498#
1499# def setUp(self):
Georg Brandlf78e02b2008-06-10 17:40:04 +00001500# import ftplib, time, threading
Guido van Rossume7ba4952007-06-06 23:52:48 +00001501# ftplib.FTP.port = 9093
1502# self.evt = threading.Event()
1503# threading.Thread(target=server, args=(self.evt,)).start()
1504# time.sleep(.1)
1505#
1506# def tearDown(self):
1507# self.evt.wait()
1508#
1509# def testBasic(self):
1510# # connects
1511# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
Georg Brandlf78e02b2008-06-10 17:40:04 +00001512# ftp.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001513#
1514# def testTimeoutNone(self):
Georg Brandlf78e02b2008-06-10 17:40:04 +00001515# # global default timeout is ignored
1516# import socket
Serhiy Storchaka25d8aea2014-02-08 14:50:08 +02001517# self.assertIsNone(socket.getdefaulttimeout())
Guido van Rossume7ba4952007-06-06 23:52:48 +00001518# socket.setdefaulttimeout(30)
1519# try:
1520# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
1521# finally:
Georg Brandlf78e02b2008-06-10 17:40:04 +00001522# socket.setdefaulttimeout(None)
Guido van Rossume7ba4952007-06-06 23:52:48 +00001523# self.assertEqual(ftp.ftp.sock.gettimeout(), 30)
Georg Brandlf78e02b2008-06-10 17:40:04 +00001524# ftp.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001525#
Georg Brandlf78e02b2008-06-10 17:40:04 +00001526# def testTimeoutDefault(self):
1527# # global default timeout is used
1528# import socket
Serhiy Storchaka25d8aea2014-02-08 14:50:08 +02001529# self.assertIsNone(socket.getdefaulttimeout())
Georg Brandlf78e02b2008-06-10 17:40:04 +00001530# socket.setdefaulttimeout(30)
1531# try:
1532# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
1533# finally:
1534# socket.setdefaulttimeout(None)
1535# self.assertEqual(ftp.ftp.sock.gettimeout(), 30)
1536# ftp.close()
1537#
1538# def testTimeoutValue(self):
1539# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [],
1540# timeout=30)
1541# self.assertEqual(ftp.ftp.sock.gettimeout(), 30)
1542# ftp.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001543
Senthil Kumaran8b081b72013-04-10 20:53:12 -07001544
Senthil Kumarande49d642011-10-16 23:54:44 +08001545class RequestTests(unittest.TestCase):
1546 """Unit tests for urllib.request.Request."""
1547
1548 def test_default_values(self):
1549 Request = urllib.request.Request
1550 request = Request("http://www.python.org")
1551 self.assertEqual(request.get_method(), 'GET')
1552 request = Request("http://www.python.org", {})
1553 self.assertEqual(request.get_method(), 'POST')
1554
1555 def test_with_method_arg(self):
1556 Request = urllib.request.Request
1557 request = Request("http://www.python.org", method='HEAD')
1558 self.assertEqual(request.method, 'HEAD')
1559 self.assertEqual(request.get_method(), 'HEAD')
1560 request = Request("http://www.python.org", {}, method='HEAD')
1561 self.assertEqual(request.method, 'HEAD')
1562 self.assertEqual(request.get_method(), 'HEAD')
1563 request = Request("http://www.python.org", method='GET')
1564 self.assertEqual(request.get_method(), 'GET')
1565 request.method = 'HEAD'
1566 self.assertEqual(request.get_method(), 'HEAD')
Skip Montanaro080c9972001-01-28 21:12:22 +00001567
1568
Senthil Kumaran277e9092013-04-10 20:51:19 -07001569class URL2PathNameTests(unittest.TestCase):
Brett Cannon74bfd702003-04-25 09:39:47 +00001570
Senthil Kumaran277e9092013-04-10 20:51:19 -07001571 def test_converting_drive_letter(self):
1572 self.assertEqual(url2pathname("///C|"), 'C:')
1573 self.assertEqual(url2pathname("///C:"), 'C:')
1574 self.assertEqual(url2pathname("///C|/"), 'C:\\')
Brett Cannon74bfd702003-04-25 09:39:47 +00001575
Senthil Kumaran277e9092013-04-10 20:51:19 -07001576 def test_converting_when_no_drive_letter(self):
1577 # cannot end a raw string in \
1578 self.assertEqual(url2pathname("///C/test/"), r'\\\C\test' '\\')
1579 self.assertEqual(url2pathname("////C/test/"), r'\\C\test' '\\')
1580
1581 def test_simple_compare(self):
1582 self.assertEqual(url2pathname("///C|/foo/bar/spam.foo"),
1583 r'C:\foo\bar\spam.foo')
1584
1585 def test_non_ascii_drive_letter(self):
1586 self.assertRaises(IOError, url2pathname, "///\u00e8|/")
1587
1588 def test_roundtrip_url2pathname(self):
1589 list_of_paths = ['C:',
1590 r'\\\C\test\\',
1591 r'C:\foo\bar\spam.foo'
1592 ]
1593 for path in list_of_paths:
Senthil Kumaranc7e09802013-04-10 20:54:23 -07001594 self.assertEqual(url2pathname(pathname2url(path)), path)
Senthil Kumaran277e9092013-04-10 20:51:19 -07001595
1596class PathName2URLTests(unittest.TestCase):
1597
1598 def test_converting_drive_letter(self):
1599 self.assertEqual(pathname2url("C:"), '///C:')
1600 self.assertEqual(pathname2url("C:\\"), '///C:')
1601
1602 def test_converting_when_no_drive_letter(self):
1603 self.assertEqual(pathname2url(r"\\\folder\test" "\\"),
1604 '/////folder/test/')
1605 self.assertEqual(pathname2url(r"\\folder\test" "\\"),
1606 '////folder/test/')
1607 self.assertEqual(pathname2url(r"\folder\test" "\\"),
1608 '/folder/test/')
1609
1610 def test_simple_compare(self):
1611 self.assertEqual(pathname2url(r'C:\foo\bar\spam.foo'),
1612 "///C:/foo/bar/spam.foo" )
1613
1614 def test_long_drive_letter(self):
1615 self.assertRaises(IOError, pathname2url, "XX:\\")
1616
1617 def test_roundtrip_pathname2url(self):
1618 list_of_paths = ['///C:',
1619 '/////folder/test/',
1620 '///C:/foo/bar/spam.foo']
1621 for path in list_of_paths:
Senthil Kumaranc7e09802013-04-10 20:54:23 -07001622 self.assertEqual(pathname2url(url2pathname(path)), path)
Brett Cannon74bfd702003-04-25 09:39:47 +00001623
1624if __name__ == '__main__':
Senthil Kumaran277e9092013-04-10 20:51:19 -07001625 unittest.main()