blob: 6187dade5bd9bdc1406fd5531f2908876477d932 [file] [log] [blame]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001import unittest
2from test import test_support
3
Andrew M. Kuchling85064ff2004-07-10 19:46:40 +00004import os, socket
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00005import StringIO
Jeremy Hyltone3e61042001-05-09 15:50:25 +00006
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00007import urllib2
8from urllib2 import Request, OpenerDirector
Jeremy Hyltone3e61042001-05-09 15:50:25 +00009
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000010# XXX
11# Request
12# CacheFTPHandler (hard to write)
Thomas Wouters477c8d52006-05-27 19:21:47 +000013# parse_keqv_list, parse_http_list, HTTPDigestAuthHandler
Jeremy Hyltone3e61042001-05-09 15:50:25 +000014
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000015class TrivialTests(unittest.TestCase):
16 def test_trivial(self):
17 # A couple trivial tests
Guido van Rossume2ae77b2001-10-24 20:42:55 +000018
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000019 self.assertRaises(ValueError, urllib2.urlopen, 'bogus url')
Tim Peters861adac2001-07-16 20:49:49 +000020
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000021 # XXX Name hacking to get this to work on Windows.
22 fname = os.path.abspath(urllib2.__file__).replace('\\', '/')
23 if fname[1:2] == ":":
24 fname = fname[2:]
25 # And more hacking to get it to work on MacOS. This assumes
26 # urllib.pathname2url works, unfortunately...
27 if os.name == 'mac':
28 fname = '/' + fname.replace(':', '/')
29 elif os.name == 'riscos':
30 import string
31 fname = os.expand(fname)
32 fname = fname.translate(string.maketrans("/.", "./"))
33
34 file_url = "file://%s" % fname
35 f = urllib2.urlopen(file_url)
36
37 buf = f.read()
38 f.close()
Tim Petersf5f32b42005-07-17 23:16:17 +000039
Georg Brandle1b13d22005-08-24 22:20:32 +000040 def test_parse_http_list(self):
41 tests = [('a,b,c', ['a', 'b', 'c']),
42 ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']),
43 ('a, b, "c", "d", "e,f", g, h', ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']),
44 ('a="b\\"c", d="e\\,f", g="h\\\\i"', ['a="b"c"', 'd="e,f"', 'g="h\\i"'])]
45 for string, list in tests:
46 self.assertEquals(urllib2.parse_http_list(string), list)
47
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +000048
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000049def test_request_headers_dict():
50 """
51 The Request.headers dictionary is not a documented interface. It should
52 stay that way, because the complete set of headers are only accessible
53 through the .get_header(), .has_header(), .header_items() interface.
54 However, .headers pre-dates those methods, and so real code will be using
55 the dictionary.
56
57 The introduction in 2.4 of those methods was a mistake for the same reason:
58 code that previously saw all (urllib2 user)-provided headers in .headers
59 now sees only a subset (and the function interface is ugly and incomplete).
60 A better change would have been to replace .headers dict with a dict
61 subclass (or UserDict.DictMixin instance?) that preserved the .headers
62 interface and also provided access to the "unredirected" headers. It's
63 probably too late to fix that, though.
64
65
66 Check .capitalize() case normalization:
67
68 >>> url = "http://example.com"
69 >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"]
70 'blah'
71 >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"]
72 'blah'
73
74 Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError,
75 but that could be changed in future.
76
77 """
78
79def test_request_headers_methods():
80 """
81 Note the case normalization of header names here, to .capitalize()-case.
82 This should be preserved for backwards-compatibility. (In the HTTP case,
83 normalization to .title()-case is done by urllib2 before sending headers to
84 httplib).
85
86 >>> url = "http://example.com"
87 >>> r = Request(url, headers={"Spam-eggs": "blah"})
88 >>> r.has_header("Spam-eggs")
89 True
90 >>> r.header_items()
91 [('Spam-eggs', 'blah')]
92 >>> r.add_header("Foo-Bar", "baz")
Guido van Rossumcc2b0162007-02-11 06:12:03 +000093 >>> items = sorted(r.header_items())
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000094 >>> items
95 [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]
96
97 Note that e.g. r.has_header("spam-EggS") is currently False, and
98 r.get_header("spam-EggS") returns None, but that could be changed in
99 future.
100
101 >>> r.has_header("Not-there")
102 False
Guido van Rossum7131f842007-02-09 20:13:25 +0000103 >>> print(r.get_header("Not-there"))
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000104 None
105 >>> r.get_header("Not-there", "default")
106 'default'
107
108 """
109
110
Thomas Wouters477c8d52006-05-27 19:21:47 +0000111def test_password_manager(self):
112 """
113 >>> mgr = urllib2.HTTPPasswordMgr()
114 >>> add = mgr.add_password
115 >>> add("Some Realm", "http://example.com/", "joe", "password")
116 >>> add("Some Realm", "http://example.com/ni", "ni", "ni")
117 >>> add("c", "http://example.com/foo", "foo", "ni")
118 >>> add("c", "http://example.com/bar", "bar", "nini")
119 >>> add("b", "http://example.com/", "first", "blah")
120 >>> add("b", "http://example.com/", "second", "spam")
121 >>> add("a", "http://example.com", "1", "a")
122 >>> add("Some Realm", "http://c.example.com:3128", "3", "c")
123 >>> add("Some Realm", "d.example.com", "4", "d")
124 >>> add("Some Realm", "e.example.com:3128", "5", "e")
125
126 >>> mgr.find_user_password("Some Realm", "example.com")
127 ('joe', 'password')
128 >>> mgr.find_user_password("Some Realm", "http://example.com")
129 ('joe', 'password')
130 >>> mgr.find_user_password("Some Realm", "http://example.com/")
131 ('joe', 'password')
132 >>> mgr.find_user_password("Some Realm", "http://example.com/spam")
133 ('joe', 'password')
134 >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam")
135 ('joe', 'password')
136 >>> mgr.find_user_password("c", "http://example.com/foo")
137 ('foo', 'ni')
138 >>> mgr.find_user_password("c", "http://example.com/bar")
139 ('bar', 'nini')
140
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000141 Actually, this is really undefined ATM
142## Currently, we use the highest-level path where more than one match:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000143
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000144## >>> mgr.find_user_password("Some Realm", "http://example.com/ni")
145## ('joe', 'password')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000146
147 Use latest add_password() in case of conflict:
148
149 >>> mgr.find_user_password("b", "http://example.com/")
150 ('second', 'spam')
151
152 No special relationship between a.example.com and example.com:
153
154 >>> mgr.find_user_password("a", "http://example.com/")
155 ('1', 'a')
156 >>> mgr.find_user_password("a", "http://a.example.com/")
157 (None, None)
158
159 Ports:
160
161 >>> mgr.find_user_password("Some Realm", "c.example.com")
162 (None, None)
163 >>> mgr.find_user_password("Some Realm", "c.example.com:3128")
164 ('3', 'c')
165 >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128")
166 ('3', 'c')
167 >>> mgr.find_user_password("Some Realm", "d.example.com")
168 ('4', 'd')
169 >>> mgr.find_user_password("Some Realm", "e.example.com:3128")
170 ('5', 'e')
171
172 """
173 pass
174
175
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000176def test_password_manager_default_port(self):
177 """
178 >>> mgr = urllib2.HTTPPasswordMgr()
179 >>> add = mgr.add_password
180
181 The point to note here is that we can't guess the default port if there's
182 no scheme. This applies to both add_password and find_user_password.
183
184 >>> add("f", "http://g.example.com:80", "10", "j")
185 >>> add("g", "http://h.example.com", "11", "k")
186 >>> add("h", "i.example.com:80", "12", "l")
187 >>> add("i", "j.example.com", "13", "m")
188 >>> mgr.find_user_password("f", "g.example.com:100")
189 (None, None)
190 >>> mgr.find_user_password("f", "g.example.com:80")
191 ('10', 'j')
192 >>> mgr.find_user_password("f", "g.example.com")
193 (None, None)
194 >>> mgr.find_user_password("f", "http://g.example.com:100")
195 (None, None)
196 >>> mgr.find_user_password("f", "http://g.example.com:80")
197 ('10', 'j')
198 >>> mgr.find_user_password("f", "http://g.example.com")
199 ('10', 'j')
200 >>> mgr.find_user_password("g", "h.example.com")
201 ('11', 'k')
202 >>> mgr.find_user_password("g", "h.example.com:80")
203 ('11', 'k')
204 >>> mgr.find_user_password("g", "http://h.example.com:80")
205 ('11', 'k')
206 >>> mgr.find_user_password("h", "i.example.com")
207 (None, None)
208 >>> mgr.find_user_password("h", "i.example.com:80")
209 ('12', 'l')
210 >>> mgr.find_user_password("h", "http://i.example.com:80")
211 ('12', 'l')
212 >>> mgr.find_user_password("i", "j.example.com")
213 ('13', 'm')
214 >>> mgr.find_user_password("i", "j.example.com:80")
215 (None, None)
216 >>> mgr.find_user_password("i", "http://j.example.com")
217 ('13', 'm')
218 >>> mgr.find_user_password("i", "http://j.example.com:80")
219 (None, None)
220
221 """
222
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000223class MockOpener:
224 addheaders = []
225 def open(self, req, data=None):
226 self.req, self.data = req, data
227 def error(self, proto, *args):
228 self.proto, self.args = proto, args
229
230class MockFile:
231 def read(self, count=None): pass
232 def readline(self, count=None): pass
233 def close(self): pass
234
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000235class MockHeaders(dict):
236 def getheaders(self, name):
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000237 return list(self.values())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000238
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000239class MockResponse(StringIO.StringIO):
240 def __init__(self, code, msg, headers, data, url=None):
241 StringIO.StringIO.__init__(self, data)
242 self.code, self.msg, self.headers, self.url = code, msg, headers, url
243 def info(self):
244 return self.headers
245 def geturl(self):
246 return self.url
247
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000248class MockCookieJar:
249 def add_cookie_header(self, request):
250 self.ach_req = request
251 def extract_cookies(self, response, request):
252 self.ec_req, self.ec_r = request, response
253
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000254class FakeMethod:
255 def __init__(self, meth_name, action, handle):
256 self.meth_name = meth_name
257 self.handle = handle
258 self.action = action
259 def __call__(self, *args):
260 return self.handle(self.meth_name, self.action, *args)
261
262class MockHandler:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000263 # useful for testing handler machinery
264 # see add_ordered_mock_handlers() docstring
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000265 handler_order = 500
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000266 def __init__(self, methods):
267 self._define_methods(methods)
268 def _define_methods(self, methods):
269 for spec in methods:
270 if len(spec) == 2: name, action = spec
271 else: name, action = spec, None
272 meth = FakeMethod(name, action, self.handle)
273 setattr(self.__class__, name, meth)
274 def handle(self, fn_name, action, *args, **kwds):
275 self.parent.calls.append((self, fn_name, args, kwds))
276 if action is None:
277 return None
278 elif action == "return self":
279 return self
280 elif action == "return response":
281 res = MockResponse(200, "OK", {}, "")
282 return res
283 elif action == "return request":
284 return Request("http://blah/")
285 elif action.startswith("error"):
286 code = action[action.rfind(" ")+1:]
287 try:
288 code = int(code)
289 except ValueError:
290 pass
291 res = MockResponse(200, "OK", {}, "")
292 return self.parent.error("http", args[0], res, code, "", {})
293 elif action == "raise":
294 raise urllib2.URLError("blah")
295 assert False
296 def close(self): pass
297 def add_parent(self, parent):
298 self.parent = parent
299 self.parent.calls = []
300 def __lt__(self, other):
301 if not hasattr(other, "handler_order"):
302 # No handler_order, leave in original order. Yuck.
303 return True
304 return self.handler_order < other.handler_order
305
306def add_ordered_mock_handlers(opener, meth_spec):
307 """Create MockHandlers and add them to an OpenerDirector.
308
309 meth_spec: list of lists of tuples and strings defining methods to define
310 on handlers. eg:
311
312 [["http_error", "ftp_open"], ["http_open"]]
313
314 defines methods .http_error() and .ftp_open() on one handler, and
315 .http_open() on another. These methods just record their arguments and
316 return None. Using a tuple instead of a string causes the method to
317 perform some action (see MockHandler.handle()), eg:
318
319 [["http_error"], [("http_open", "return request")]]
320
321 defines .http_error() on one handler (which simply returns None), and
322 .http_open() on another handler, which returns a Request object.
323
324 """
325 handlers = []
326 count = 0
327 for meths in meth_spec:
328 class MockHandlerSubclass(MockHandler): pass
329 h = MockHandlerSubclass(meths)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000330 h.handler_order += count
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000331 h.add_parent(opener)
332 count = count + 1
333 handlers.append(h)
334 opener.add_handler(h)
335 return handlers
336
Thomas Wouters477c8d52006-05-27 19:21:47 +0000337def build_test_opener(*handler_instances):
338 opener = OpenerDirector()
339 for h in handler_instances:
340 opener.add_handler(h)
341 return opener
342
343class MockHTTPHandler(urllib2.BaseHandler):
344 # useful for testing redirections and auth
345 # sends supplied headers and code as first response
346 # sends 200 OK as second response
347 def __init__(self, code, headers):
348 self.code = code
349 self.headers = headers
350 self.reset()
351 def reset(self):
352 self._count = 0
353 self.requests = []
354 def http_open(self, req):
355 import mimetools, httplib, copy
356 from StringIO import StringIO
357 self.requests.append(copy.deepcopy(req))
358 if self._count == 0:
359 self._count = self._count + 1
360 name = httplib.responses[self.code]
361 msg = mimetools.Message(StringIO(self.headers))
362 return self.parent.error(
363 "http", req, MockFile(), self.code, name, msg)
364 else:
365 self.req = req
366 msg = mimetools.Message(StringIO("\r\n\r\n"))
367 return MockResponse(200, "OK", msg, "", req.get_full_url())
368
369class MockPasswordManager:
370 def add_password(self, realm, uri, user, password):
371 self.realm = realm
372 self.url = uri
373 self.user = user
374 self.password = password
375 def find_user_password(self, realm, authuri):
376 self.target_realm = realm
377 self.target_url = authuri
378 return self.user, self.password
379
380
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000381class OpenerDirectorTests(unittest.TestCase):
382
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000383 def test_badly_named_methods(self):
384 # test work-around for three methods that accidentally follow the
385 # naming conventions for handler methods
386 # (*_open() / *_request() / *_response())
387
388 # These used to call the accidentally-named methods, causing a
389 # TypeError in real code; here, returning self from these mock
390 # methods would either cause no exception, or AttributeError.
391
392 from urllib2 import URLError
393
394 o = OpenerDirector()
395 meth_spec = [
396 [("do_open", "return self"), ("proxy_open", "return self")],
397 [("redirect_request", "return self")],
398 ]
399 handlers = add_ordered_mock_handlers(o, meth_spec)
400 o.add_handler(urllib2.UnknownHandler())
401 for scheme in "do", "proxy", "redirect":
402 self.assertRaises(URLError, o.open, scheme+"://example.com/")
403
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000404 def test_handled(self):
405 # handler returning non-None means no more handlers will be called
406 o = OpenerDirector()
407 meth_spec = [
408 ["http_open", "ftp_open", "http_error_302"],
409 ["ftp_open"],
410 [("http_open", "return self")],
411 [("http_open", "return self")],
412 ]
413 handlers = add_ordered_mock_handlers(o, meth_spec)
414
415 req = Request("http://example.com/")
416 r = o.open(req)
417 # Second .http_open() gets called, third doesn't, since second returned
418 # non-None. Handlers without .http_open() never get any methods called
419 # on them.
420 # In fact, second mock handler defining .http_open() returns self
421 # (instead of response), which becomes the OpenerDirector's return
422 # value.
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000423 self.assertEqual(r, handlers[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000424 calls = [(handlers[0], "http_open"), (handlers[2], "http_open")]
425 for expected, got in zip(calls, o.calls):
426 handler, name, args, kwds = got
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000427 self.assertEqual((handler, name), expected)
428 self.assertEqual(args, (req,))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000429
430 def test_handler_order(self):
431 o = OpenerDirector()
432 handlers = []
433 for meths, handler_order in [
434 ([("http_open", "return self")], 500),
435 (["http_open"], 0),
436 ]:
437 class MockHandlerSubclass(MockHandler): pass
438 h = MockHandlerSubclass(meths)
439 h.handler_order = handler_order
440 handlers.append(h)
441 o.add_handler(h)
442
443 r = o.open("http://example.com/")
444 # handlers called in reverse order, thanks to their sort order
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000445 self.assertEqual(o.calls[0][0], handlers[1])
446 self.assertEqual(o.calls[1][0], handlers[0])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000447
448 def test_raise(self):
449 # raising URLError stops processing of request
450 o = OpenerDirector()
451 meth_spec = [
452 [("http_open", "raise")],
453 [("http_open", "return self")],
454 ]
455 handlers = add_ordered_mock_handlers(o, meth_spec)
456
457 req = Request("http://example.com/")
458 self.assertRaises(urllib2.URLError, o.open, req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000459 self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000460
461## def test_error(self):
462## # XXX this doesn't actually seem to be used in standard library,
463## # but should really be tested anyway...
464
465 def test_http_error(self):
466 # XXX http_error_default
467 # http errors are a special case
468 o = OpenerDirector()
469 meth_spec = [
470 [("http_open", "error 302")],
471 [("http_error_400", "raise"), "http_open"],
472 [("http_error_302", "return response"), "http_error_303",
473 "http_error"],
474 [("http_error_302")],
475 ]
476 handlers = add_ordered_mock_handlers(o, meth_spec)
477
478 class Unknown:
479 def __eq__(self, other): return True
480
481 req = Request("http://example.com/")
482 r = o.open(req)
483 assert len(o.calls) == 2
484 calls = [(handlers[0], "http_open", (req,)),
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000485 (handlers[2], "http_error_302",
486 (req, Unknown(), 302, "", {}))]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000487 for expected, got in zip(calls, o.calls):
488 handler, method_name, args = expected
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000489 self.assertEqual((handler, method_name), got[:2])
490 self.assertEqual(args, got[2])
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000491
492 def test_processors(self):
493 # *_request / *_response methods get called appropriately
494 o = OpenerDirector()
495 meth_spec = [
496 [("http_request", "return request"),
497 ("http_response", "return response")],
498 [("http_request", "return request"),
499 ("http_response", "return response")],
500 ]
501 handlers = add_ordered_mock_handlers(o, meth_spec)
502
503 req = Request("http://example.com/")
504 r = o.open(req)
505 # processor methods are called on *all* handlers that define them,
506 # not just the first handler that handles the request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000507 calls = [
508 (handlers[0], "http_request"), (handlers[1], "http_request"),
509 (handlers[0], "http_response"), (handlers[1], "http_response")]
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000510
511 for i, (handler, name, args, kwds) in enumerate(o.calls):
512 if i < 2:
513 # *_request
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000514 self.assertEqual((handler, name), calls[i])
515 self.assertEqual(len(args), 1)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000516 self.assert_(isinstance(args[0], Request))
517 else:
518 # *_response
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000519 self.assertEqual((handler, name), calls[i])
520 self.assertEqual(len(args), 2)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000521 self.assert_(isinstance(args[0], Request))
522 # response from opener.open is None, because there's no
523 # handler that defines http_open to handle it
524 self.assert_(args[1] is None or
525 isinstance(args[1], MockResponse))
526
527
Tim Peters58eb11c2004-01-18 20:29:55 +0000528def sanepathname2url(path):
529 import urllib
530 urlpath = urllib.pathname2url(path)
531 if os.name == "nt" and urlpath.startswith("///"):
532 urlpath = urlpath[2:]
533 # XXX don't ask me about the mac...
534 return urlpath
535
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000536class HandlerTests(unittest.TestCase):
537
538 def test_ftp(self):
539 class MockFTPWrapper:
540 def __init__(self, data): self.data = data
541 def retrfile(self, filename, filetype):
542 self.filename, self.filetype = filename, filetype
543 return StringIO.StringIO(self.data), len(self.data)
544
545 class NullFTPHandler(urllib2.FTPHandler):
546 def __init__(self, data): self.data = data
547 def connect_ftp(self, user, passwd, host, port, dirs):
548 self.user, self.passwd = user, passwd
549 self.host, self.port = host, port
550 self.dirs = dirs
551 self.ftpwrapper = MockFTPWrapper(self.data)
552 return self.ftpwrapper
553
554 import ftplib, socket
555 data = "rheum rhaponicum"
556 h = NullFTPHandler(data)
557 o = h.parent = MockOpener()
558
559 for url, host, port, type_, dirs, filename, mimetype in [
560 ("ftp://localhost/foo/bar/baz.html",
561 "localhost", ftplib.FTP_PORT, "I",
562 ["foo", "bar"], "baz.html", "text/html"),
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000563 ("ftp://localhost:80/foo/bar/",
564 "localhost", 80, "D",
565 ["foo", "bar"], "", None),
566 ("ftp://localhost/baz.gif;type=a",
567 "localhost", ftplib.FTP_PORT, "A",
568 [], "baz.gif", None), # XXX really this should guess image/gif
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000569 ]:
570 r = h.ftp_open(Request(url))
571 # ftp authentication not yet implemented by FTPHandler
572 self.assert_(h.user == h.passwd == "")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000573 self.assertEqual(h.host, socket.gethostbyname(host))
574 self.assertEqual(h.port, port)
575 self.assertEqual(h.dirs, dirs)
576 self.assertEqual(h.ftpwrapper.filename, filename)
577 self.assertEqual(h.ftpwrapper.filetype, type_)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000578 headers = r.info()
Kurt B. Kaiser3f7cb5d2004-07-11 17:14:13 +0000579 self.assertEqual(headers.get("Content-type"), mimetype)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000580 self.assertEqual(int(headers["Content-length"]), len(data))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000581
582 def test_file(self):
583 import time, rfc822, socket
584 h = urllib2.FileHandler()
585 o = h.parent = MockOpener()
586
Tim Peters58eb11c2004-01-18 20:29:55 +0000587 TESTFN = test_support.TESTFN
588 urlpath = sanepathname2url(os.path.abspath(TESTFN))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000589 towrite = "hello, world\n"
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000590 urls = [
Tim Peters58eb11c2004-01-18 20:29:55 +0000591 "file://localhost%s" % urlpath,
592 "file://%s" % urlpath,
593 "file://%s%s" % (socket.gethostbyname('localhost'), urlpath),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000594 ]
595 try:
596 localaddr = socket.gethostbyname(socket.gethostname())
597 except socket.gaierror:
598 localaddr = ''
599 if localaddr:
600 urls.append("file://%s%s" % (localaddr, urlpath))
601
602 for url in urls:
Tim Peters58eb11c2004-01-18 20:29:55 +0000603 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000604 try:
605 try:
606 f.write(towrite)
607 finally:
608 f.close()
609
610 r = h.file_open(Request(url))
611 try:
612 data = r.read()
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000613 headers = r.info()
614 newurl = r.geturl()
615 finally:
616 r.close()
Tim Peters58eb11c2004-01-18 20:29:55 +0000617 stats = os.stat(TESTFN)
618 modified = rfc822.formatdate(stats.st_mtime)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000619 finally:
620 os.remove(TESTFN)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000621 self.assertEqual(data, towrite)
622 self.assertEqual(headers["Content-type"], "text/plain")
623 self.assertEqual(headers["Content-length"], "13")
Tim Peters58eb11c2004-01-18 20:29:55 +0000624 self.assertEqual(headers["Last-modified"], modified)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000625
626 for url in [
Tim Peters58eb11c2004-01-18 20:29:55 +0000627 "file://localhost:80%s" % urlpath,
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000628# XXXX bug: these fail with socket.gaierror, should be URLError
629## "file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
630## os.getcwd(), TESTFN),
631## "file://somerandomhost.ontheinternet.com%s/%s" %
632## (os.getcwd(), TESTFN),
633 ]:
634 try:
Tim Peters58eb11c2004-01-18 20:29:55 +0000635 f = open(TESTFN, "wb")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000636 try:
637 f.write(towrite)
638 finally:
639 f.close()
640
641 self.assertRaises(urllib2.URLError,
642 h.file_open, Request(url))
643 finally:
644 os.remove(TESTFN)
645
646 h = urllib2.FileHandler()
647 o = h.parent = MockOpener()
648 # XXXX why does // mean ftp (and /// mean not ftp!), and where
649 # is file: scheme specified? I think this is really a bug, and
650 # what was intended was to distinguish between URLs like:
651 # file:/blah.txt (a file)
652 # file://localhost/blah.txt (a file)
653 # file:///blah.txt (a file)
654 # file://ftp.example.com/blah.txt (an ftp URL)
655 for url, ftp in [
656 ("file://ftp.example.com//foo.txt", True),
657 ("file://ftp.example.com///foo.txt", False),
658# XXXX bug: fails with OSError, should be URLError
659 ("file://ftp.example.com/foo.txt", False),
660 ]:
661 req = Request(url)
662 try:
663 h.file_open(req)
664 # XXXX remove OSError when bug fixed
665 except (urllib2.URLError, OSError):
666 self.assert_(not ftp)
667 else:
668 self.assert_(o.req is req)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000669 self.assertEqual(req.type, "ftp")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000670
671 def test_http(self):
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000672 class MockHTTPResponse:
673 def __init__(self, fp, msg, status, reason):
674 self.fp = fp
675 self.msg = msg
676 self.status = status
677 self.reason = reason
Jeremy Hylton5d9c3032004-08-07 17:40:50 +0000678 def read(self):
679 return ''
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000680 class MockHTTPClass:
681 def __init__(self):
682 self.req_headers = []
683 self.data = None
684 self.raise_on_endheaders = False
685 def __call__(self, host):
686 self.host = host
687 return self
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000688 def set_debuglevel(self, level):
689 self.level = level
690 def request(self, method, url, body=None, headers={}):
691 self.method = method
692 self.selector = url
693 self.req_headers += headers.items()
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000694 self.req_headers.sort()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000695 if body:
696 self.data = body
697 if self.raise_on_endheaders:
698 import socket
699 raise socket.error()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000700 def getresponse(self):
701 return MockHTTPResponse(MockFile(), {}, 200, "OK")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000702
703 h = urllib2.AbstractHTTPHandler()
704 o = h.parent = MockOpener()
705
706 url = "http://example.com/"
707 for method, data in [("GET", None), ("POST", "blah")]:
708 req = Request(url, data, {"Foo": "bar"})
709 req.add_unredirected_header("Spam", "eggs")
710 http = MockHTTPClass()
711 r = h.do_open(http, req)
712
713 # result attributes
714 r.read; r.readline # wrapped MockFile methods
715 r.info; r.geturl # addinfourl methods
716 r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply()
717 hdrs = r.info()
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000718 hdrs.get; hdrs.__contains__ # r.info() gives dict from .getreply()
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000719 self.assertEqual(r.geturl(), url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000720
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000721 self.assertEqual(http.host, "example.com")
722 self.assertEqual(http.level, 0)
723 self.assertEqual(http.method, method)
724 self.assertEqual(http.selector, "/")
725 self.assertEqual(http.req_headers,
Jeremy Hyltonb3ee6f92004-02-24 19:40:35 +0000726 [("Connection", "close"),
727 ("Foo", "bar"), ("Spam", "eggs")])
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000728 self.assertEqual(http.data, data)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000729
730 # check socket.error converted to URLError
731 http.raise_on_endheaders = True
732 self.assertRaises(urllib2.URLError, h.do_open, http, req)
733
734 # check adding of standard headers
735 o.addheaders = [("Spam", "eggs")]
736 for data in "", None: # POST, GET
737 req = Request("http://example.com/", data)
738 r = MockResponse(200, "OK", {}, "")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000739 newreq = h.do_request_(req)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000740 if data is None: # GET
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000741 self.assert_("Content-length" not in req.unredirected_hdrs)
742 self.assert_("Content-type" not in req.unredirected_hdrs)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000743 else: # POST
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000744 self.assertEqual(req.unredirected_hdrs["Content-length"], "0")
745 self.assertEqual(req.unredirected_hdrs["Content-type"],
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000746 "application/x-www-form-urlencoded")
747 # XXX the details of Host could be better tested
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000748 self.assertEqual(req.unredirected_hdrs["Host"], "example.com")
749 self.assertEqual(req.unredirected_hdrs["Spam"], "eggs")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000750
751 # don't clobber existing headers
752 req.add_unredirected_header("Content-length", "foo")
753 req.add_unredirected_header("Content-type", "bar")
754 req.add_unredirected_header("Host", "baz")
755 req.add_unredirected_header("Spam", "foo")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000756 newreq = h.do_request_(req)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000757 self.assertEqual(req.unredirected_hdrs["Content-length"], "foo")
758 self.assertEqual(req.unredirected_hdrs["Content-type"], "bar")
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000759 self.assertEqual(req.unredirected_hdrs["Host"], "baz")
760 self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000761
762 def test_errors(self):
763 h = urllib2.HTTPErrorProcessor()
764 o = h.parent = MockOpener()
765
766 url = "http://example.com/"
767 req = Request(url)
768 # 200 OK is passed through
769 r = MockResponse(200, "OK", {}, "", url)
770 newr = h.http_response(req, r)
771 self.assert_(r is newr)
772 self.assert_(not hasattr(o, "proto")) # o.error not called
773 # anything else calls o.error (and MockOpener returns None, here)
774 r = MockResponse(201, "Created", {}, "", url)
775 self.assert_(h.http_response(req, r) is None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000776 self.assertEqual(o.proto, "http") # o.error called
777 self.assertEqual(o.args, (req, r, 201, "Created", {}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000778
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000779 def test_cookies(self):
780 cj = MockCookieJar()
781 h = urllib2.HTTPCookieProcessor(cj)
782 o = h.parent = MockOpener()
783
784 req = Request("http://example.com/")
785 r = MockResponse(200, "OK", {}, "")
786 newreq = h.http_request(req)
787 self.assert_(cj.ach_req is req is newreq)
788 self.assertEquals(req.get_origin_req_host(), "example.com")
789 self.assert_(not req.is_unverifiable())
790 newr = h.http_response(req, r)
791 self.assert_(cj.ec_req is req)
792 self.assert_(cj.ec_r is r is newr)
793
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000794 def test_redirect(self):
795 from_url = "http://example.com/a.html"
796 to_url = "http://example.com/b.html"
797 h = urllib2.HTTPRedirectHandler()
798 o = h.parent = MockOpener()
799
800 # ordinary redirect behaviour
801 for code in 301, 302, 303, 307:
802 for data in None, "blah\nblah\n":
803 method = getattr(h, "http_error_%s" % code)
804 req = Request(from_url, data)
805 req.add_header("Nonsense", "viking=withhold")
806 req.add_unredirected_header("Spam", "spam")
807 try:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000808 method(req, MockFile(), code, "Blah",
809 MockHeaders({"location": to_url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000810 except urllib2.HTTPError:
811 # 307 in response to POST requires user OK
812 self.assert_(code == 307 and data is not None)
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000813 self.assertEqual(o.req.get_full_url(), to_url)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000814 try:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000815 self.assertEqual(o.req.get_method(), "GET")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000816 except AttributeError:
817 self.assert_(not o.req.has_data())
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000818 self.assertEqual(o.req.headers["Nonsense"],
819 "viking=withhold")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000820 self.assert_("Spam" not in o.req.headers)
821 self.assert_("Spam" not in o.req.unredirected_hdrs)
822
823 # loop detection
824 req = Request(from_url)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000825 def redirect(h, req, url=to_url):
826 h.http_error_302(req, MockFile(), 302, "Blah",
827 MockHeaders({"location": url}))
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000828 # Note that the *original* request shares the same record of
829 # redirections with the sub-requests caused by the redirections.
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000830
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000831 # detect infinite loop redirect of a URL to itself
832 req = Request(from_url, origin_req_host="example.com")
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000833 count = 0
834 try:
835 while 1:
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000836 redirect(h, req, "http://example.com/")
837 count = count + 1
838 except urllib2.HTTPError:
839 # don't stop until max_repeats, because cookies may introduce state
840 self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats)
841
842 # detect endless non-repeating chain of redirects
843 req = Request(from_url, origin_req_host="example.com")
844 count = 0
845 try:
846 while 1:
847 redirect(h, req, "http://example.com/%d" % count)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000848 count = count + 1
849 except urllib2.HTTPError:
Jeremy Hyltondf38ea92003-12-17 20:42:38 +0000850 self.assertEqual(count,
851 urllib2.HTTPRedirectHandler.max_redirections)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +0000852
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000853 def test_cookie_redirect(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000854 # cookies shouldn't leak into redirected requests
855 from cookielib import CookieJar
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000856
Neal Norwitz2a0c7802006-03-24 07:10:31 +0000857 from test.test_cookielib import interact_netscape
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000858
859 cj = CookieJar()
860 interact_netscape(cj, "http://www.example.com/", "spam=eggs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000861 hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n")
862 hdeh = urllib2.HTTPDefaultErrorHandler()
863 hrh = urllib2.HTTPRedirectHandler()
864 cp = urllib2.HTTPCookieProcessor(cj)
865 o = build_test_opener(hh, hdeh, hrh, cp)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000866 o.open("http://www.example.com/")
867 self.assert_(not hh.req.has_header("Cookie"))
868
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000869 def test_proxy(self):
870 o = OpenerDirector()
871 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
872 o.add_handler(ph)
873 meth_spec = [
874 [("http_open", "return response")]
875 ]
876 handlers = add_ordered_mock_handlers(o, meth_spec)
877
878 req = Request("http://acme.example.com/")
879 self.assertEqual(req.get_host(), "acme.example.com")
880 r = o.open(req)
881 self.assertEqual(req.get_host(), "proxy.example.com:3128")
882
883 self.assertEqual([(handlers[0], "http_open")],
884 [tup[0:2] for tup in o.calls])
885
Thomas Wouters477c8d52006-05-27 19:21:47 +0000886 def test_basic_auth(self):
887 opener = OpenerDirector()
888 password_manager = MockPasswordManager()
889 auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
890 realm = "ACME Widget Store"
891 http_handler = MockHTTPHandler(
892 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000893 opener.add_handler(auth_handler)
894 opener.add_handler(http_handler)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000895 self._test_basic_auth(opener, auth_handler, "Authorization",
896 realm, http_handler, password_manager,
897 "http://acme.example.com/protected",
898 "http://acme.example.com/protected",
899 )
900
901 def test_proxy_basic_auth(self):
902 opener = OpenerDirector()
903 ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
904 opener.add_handler(ph)
905 password_manager = MockPasswordManager()
906 auth_handler = urllib2.ProxyBasicAuthHandler(password_manager)
907 realm = "ACME Networks"
908 http_handler = MockHTTPHandler(
909 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000910 opener.add_handler(auth_handler)
911 opener.add_handler(http_handler)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000912 self._test_basic_auth(opener, auth_handler, "Proxy-authorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000913 realm, http_handler, password_manager,
914 "http://acme.example.com:3128/protected",
915 "proxy.example.com:3128",
916 )
917
918 def test_basic_and_digest_auth_handlers(self):
919 # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
920 # response (http://python.org/sf/1479302), where it should instead
921 # return None to allow another handler (especially
922 # HTTPBasicAuthHandler) to handle the response.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000923
924 # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must
925 # try digest first (since it's the strongest auth scheme), so we record
926 # order of calls here to check digest comes first:
927 class RecordingOpenerDirector(OpenerDirector):
928 def __init__(self):
929 OpenerDirector.__init__(self)
930 self.recorded = []
931 def record(self, info):
932 self.recorded.append(info)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000933 class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000934 def http_error_401(self, *args, **kwds):
935 self.parent.record("digest")
936 urllib2.HTTPDigestAuthHandler.http_error_401(self,
937 *args, **kwds)
938 class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
939 def http_error_401(self, *args, **kwds):
940 self.parent.record("basic")
941 urllib2.HTTPBasicAuthHandler.http_error_401(self,
942 *args, **kwds)
943
944 opener = RecordingOpenerDirector()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000945 password_manager = MockPasswordManager()
946 digest_handler = TestDigestAuthHandler(password_manager)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000947 basic_handler = TestBasicAuthHandler(password_manager)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000948 realm = "ACME Networks"
949 http_handler = MockHTTPHandler(
950 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm)
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000951 opener.add_handler(basic_handler)
952 opener.add_handler(digest_handler)
953 opener.add_handler(http_handler)
954
955 # check basic auth isn't blocked by digest handler failing
Thomas Wouters477c8d52006-05-27 19:21:47 +0000956 self._test_basic_auth(opener, basic_handler, "Authorization",
957 realm, http_handler, password_manager,
958 "http://acme.example.com/protected",
959 "http://acme.example.com/protected",
960 )
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000961 # check digest was tried before basic (twice, because
962 # _test_basic_auth called .open() twice)
963 self.assertEqual(opener.recorded, ["digest", "basic"]*2)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000964
965 def _test_basic_auth(self, opener, auth_handler, auth_header,
966 realm, http_handler, password_manager,
967 request_url, protected_url):
968 import base64, httplib
969 user, password = "wile", "coyote"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000970
971 # .add_password() fed through to password manager
972 auth_handler.add_password(realm, request_url, user, password)
973 self.assertEqual(realm, password_manager.realm)
974 self.assertEqual(request_url, password_manager.url)
975 self.assertEqual(user, password_manager.user)
976 self.assertEqual(password, password_manager.password)
977
978 r = opener.open(request_url)
979
980 # should have asked the password manager for the username/password
981 self.assertEqual(password_manager.target_realm, realm)
982 self.assertEqual(password_manager.target_url, protected_url)
983
984 # expect one request without authorization, then one with
985 self.assertEqual(len(http_handler.requests), 2)
986 self.assertFalse(http_handler.requests[0].has_header(auth_header))
987 userpass = '%s:%s' % (user, password)
988 auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip()
989 self.assertEqual(http_handler.requests[1].get_header(auth_header),
990 auth_hdr_value)
991
992 # if the password manager can't find a password, the handler won't
993 # handle the HTTP auth error
994 password_manager.user = password_manager.password = None
995 http_handler.reset()
996 r = opener.open(request_url)
997 self.assertEqual(len(http_handler.requests), 1)
998 self.assertFalse(http_handler.requests[0].has_header(auth_header))
999
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001000
1001class MiscTests(unittest.TestCase):
1002
1003 def test_build_opener(self):
1004 class MyHTTPHandler(urllib2.HTTPHandler): pass
1005 class FooHandler(urllib2.BaseHandler):
1006 def foo_open(self): pass
1007 class BarHandler(urllib2.BaseHandler):
1008 def bar_open(self): pass
1009
1010 build_opener = urllib2.build_opener
1011
1012 o = build_opener(FooHandler, BarHandler)
1013 self.opener_has_handler(o, FooHandler)
1014 self.opener_has_handler(o, BarHandler)
1015
1016 # can take a mix of classes and instances
1017 o = build_opener(FooHandler, BarHandler())
1018 self.opener_has_handler(o, FooHandler)
1019 self.opener_has_handler(o, BarHandler)
1020
1021 # subclasses of default handlers override default handlers
1022 o = build_opener(MyHTTPHandler)
1023 self.opener_has_handler(o, MyHTTPHandler)
1024
1025 # a particular case of overriding: default handlers can be passed
1026 # in explicitly
1027 o = build_opener()
1028 self.opener_has_handler(o, urllib2.HTTPHandler)
1029 o = build_opener(urllib2.HTTPHandler)
1030 self.opener_has_handler(o, urllib2.HTTPHandler)
1031 o = build_opener(urllib2.HTTPHandler())
1032 self.opener_has_handler(o, urllib2.HTTPHandler)
1033
1034 def opener_has_handler(self, opener, handler_class):
1035 for h in opener.handlers:
1036 if h.__class__ == handler_class:
1037 break
1038 else:
1039 self.assert_(False)
1040
1041
1042def test_main(verbose=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +00001043 from test import test_urllib2
1044 test_support.run_doctest(test_urllib2, verbose)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001045 test_support.run_doctest(urllib2, verbose)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001046 tests = (TrivialTests,
1047 OpenerDirectorTests,
1048 HandlerTests,
1049 MiscTests)
Andrew M. Kuchlingbd3200f2004-06-29 13:15:46 +00001050 test_support.run_unittest(*tests)
Jeremy Hyltonc1be59f2003-12-14 05:27:34 +00001051
1052if __name__ == "__main__":
1053 test_main(verbose=True)