##// END OF EJS Templates
tests: import CPython's hostname matching tests...
Gregory Szorc -
r29451:676f4d0e stable
parent child Browse files
Show More
@@ -1,250 +1,422 b''
1 # coding=utf-8
1 from __future__ import absolute_import, print_function
2 from __future__ import absolute_import, print_function
2
3
3 import doctest
4 import doctest
4 import os
5 import os
5
6
6 def check(a, b):
7 def check(a, b):
7 if a != b:
8 if a != b:
8 print((a, b))
9 print((a, b))
9
10
10 def cert(cn):
11 def cert(cn):
11 return {'subject': ((('commonName', cn),),)}
12 return {'subject': ((('commonName', cn),),)}
12
13
13 from mercurial import (
14 from mercurial import (
14 sslutil,
15 sslutil,
15 )
16 )
16
17
17 _verifycert = sslutil._verifycert
18 _verifycert = sslutil._verifycert
18 # Test non-wildcard certificates
19 # Test non-wildcard certificates
19 check(_verifycert(cert('example.com'), 'example.com'),
20 check(_verifycert(cert('example.com'), 'example.com'),
20 None)
21 None)
21 check(_verifycert(cert('example.com'), 'www.example.com'),
22 check(_verifycert(cert('example.com'), 'www.example.com'),
22 'certificate is for example.com')
23 'certificate is for example.com')
23 check(_verifycert(cert('www.example.com'), 'example.com'),
24 check(_verifycert(cert('www.example.com'), 'example.com'),
24 'certificate is for www.example.com')
25 'certificate is for www.example.com')
25
26
26 # Test wildcard certificates
27 # Test wildcard certificates
27 check(_verifycert(cert('*.example.com'), 'www.example.com'),
28 check(_verifycert(cert('*.example.com'), 'www.example.com'),
28 None)
29 None)
29 check(_verifycert(cert('*.example.com'), 'example.com'),
30 check(_verifycert(cert('*.example.com'), 'example.com'),
30 'certificate is for *.example.com')
31 'certificate is for *.example.com')
31 check(_verifycert(cert('*.example.com'), 'w.w.example.com'),
32 check(_verifycert(cert('*.example.com'), 'w.w.example.com'),
32 'certificate is for *.example.com')
33 'certificate is for *.example.com')
33
34
34 # Test subjectAltName
35 # Test subjectAltName
35 san_cert = {'subject': ((('commonName', 'example.com'),),),
36 san_cert = {'subject': ((('commonName', 'example.com'),),),
36 'subjectAltName': (('DNS', '*.example.net'),
37 'subjectAltName': (('DNS', '*.example.net'),
37 ('DNS', 'example.net'))}
38 ('DNS', 'example.net'))}
38 check(_verifycert(san_cert, 'example.net'),
39 check(_verifycert(san_cert, 'example.net'),
39 None)
40 None)
40 check(_verifycert(san_cert, 'foo.example.net'),
41 check(_verifycert(san_cert, 'foo.example.net'),
41 None)
42 None)
42 # no fallback to subject commonName when subjectAltName has DNS
43 # no fallback to subject commonName when subjectAltName has DNS
43 check(_verifycert(san_cert, 'example.com'),
44 check(_verifycert(san_cert, 'example.com'),
44 'certificate is for *.example.net, example.net')
45 'certificate is for *.example.net, example.net')
45 # fallback to subject commonName when no DNS in subjectAltName
46 # fallback to subject commonName when no DNS in subjectAltName
46 san_cert = {'subject': ((('commonName', 'example.com'),),),
47 san_cert = {'subject': ((('commonName', 'example.com'),),),
47 'subjectAltName': (('IP Address', '8.8.8.8'),)}
48 'subjectAltName': (('IP Address', '8.8.8.8'),)}
48 check(_verifycert(san_cert, 'example.com'), None)
49 check(_verifycert(san_cert, 'example.com'), None)
49
50
50 # Avoid some pitfalls
51 # Avoid some pitfalls
51 check(_verifycert(cert('*.foo'), 'foo'),
52 check(_verifycert(cert('*.foo'), 'foo'),
52 'certificate is for *.foo')
53 'certificate is for *.foo')
53 check(_verifycert(cert('*o'), 'foo'),
54 check(_verifycert(cert('*o'), 'foo'),
54 'certificate is for *o')
55 'certificate is for *o')
55
56
56 check(_verifycert({'subject': ()},
57 check(_verifycert({'subject': ()},
57 'example.com'),
58 'example.com'),
58 'no commonName or subjectAltName found in certificate')
59 'no commonName or subjectAltName found in certificate')
59 check(_verifycert(None, 'example.com'),
60 check(_verifycert(None, 'example.com'),
60 'no certificate received')
61 'no certificate received')
61
62
62 # Unicode (IDN) certname isn't supported
63 # Unicode (IDN) certname isn't supported
63 check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'),
64 check(_verifycert(cert(u'\u4f8b.jp'), 'example.jp'),
64 'IDN in certificate not supported')
65 'IDN in certificate not supported')
65
66
67 # The following tests are from CPython's test_ssl.py.
68 check(_verifycert(cert('example.com'), 'example.com'), None)
69 check(_verifycert(cert('example.com'), 'ExAmple.cOm'), None)
70 check(_verifycert(cert('example.com'), 'www.example.com'),
71 'certificate is for example.com')
72 check(_verifycert(cert('example.com'), '.example.com'),
73 'certificate is for example.com')
74 check(_verifycert(cert('example.com'), 'example.org'),
75 'certificate is for example.com')
76 check(_verifycert(cert('example.com'), 'exampleXcom'),
77 'certificate is for example.com')
78 check(_verifycert(cert('*.a.com'), 'foo.a.com'), None)
79 check(_verifycert(cert('*.a.com'), 'bar.foo.a.com'),
80 'certificate is for *.a.com')
81 check(_verifycert(cert('*.a.com'), 'a.com'),
82 'certificate is for *.a.com')
83 check(_verifycert(cert('*.a.com'), 'Xa.com'),
84 'certificate is for *.a.com')
85 check(_verifycert(cert('*.a.com'), '.a.com'), None)
86
87 # only match one left-most wildcard
88 check(_verifycert(cert('f*.com'), 'foo.com'),
89 'certificate is for f*.com')
90 check(_verifycert(cert('f*.com'), 'f.com'),
91 'certificate is for f*.com')
92 check(_verifycert(cert('f*.com'), 'bar.com'),
93 'certificate is for f*.com')
94 check(_verifycert(cert('f*.com'), 'foo.a.com'),
95 'certificate is for f*.com')
96 check(_verifycert(cert('f*.com'), 'bar.foo.com'),
97 'certificate is for f*.com')
98
99 # NULL bytes are bad, CVE-2013-4073
100 check(_verifycert(cert('null.python.org\x00example.org'),
101 'null.python.org\x00example.org'), None)
102 check(_verifycert(cert('null.python.org\x00example.org'),
103 'example.org'),
104 'certificate is for null.python.org\x00example.org')
105 check(_verifycert(cert('null.python.org\x00example.org'),
106 'null.python.org'),
107 'certificate is for null.python.org\x00example.org')
108
109 # error cases with wildcards
110 check(_verifycert(cert('*.*.a.com'), 'bar.foo.a.com'),
111 'certificate is for *.*.a.com')
112 check(_verifycert(cert('*.*.a.com'), 'a.com'),
113 'certificate is for *.*.a.com')
114 check(_verifycert(cert('*.*.a.com'), 'Xa.com'),
115 'certificate is for *.*.a.com')
116 check(_verifycert(cert('*.*.a.com'), '.a.com'),
117 'certificate is for *.*.a.com')
118
119 check(_verifycert(cert('a.*.com'), 'a.foo.com'),
120 'certificate is for a.*.com')
121 check(_verifycert(cert('a.*.com'), 'a..com'),
122 'certificate is for a.*.com')
123 check(_verifycert(cert('a.*.com'), 'a.com'),
124 'certificate is for a.*.com')
125
126 # wildcard doesn't match IDNA prefix 'xn--'
127 idna = u'pΓΌthon.python.org'.encode('idna').decode('ascii')
128 check(_verifycert(cert(idna), idna), None)
129 check(_verifycert(cert('x*.python.org'), idna),
130 'certificate is for x*.python.org')
131 check(_verifycert(cert('xn--p*.python.org'), idna),
132 'certificate is for xn--p*.python.org')
133
134 # wildcard in first fragment and IDNA A-labels in sequent fragments
135 # are supported.
136 idna = u'www*.pythΓΆn.org'.encode('idna').decode('ascii')
137 check(_verifycert(cert(idna),
138 u'www.pythΓΆn.org'.encode('idna').decode('ascii')),
139 'certificate is for www*.xn--pythn-mua.org')
140 check(_verifycert(cert(idna),
141 u'www1.pythΓΆn.org'.encode('idna').decode('ascii')),
142 'certificate is for www*.xn--pythn-mua.org')
143 check(_verifycert(cert(idna),
144 u'ftp.pythΓΆn.org'.encode('idna').decode('ascii')),
145 'certificate is for www*.xn--pythn-mua.org')
146 check(_verifycert(cert(idna),
147 u'pythΓΆn.org'.encode('idna').decode('ascii')),
148 'certificate is for www*.xn--pythn-mua.org')
149
150 c = {
151 'notAfter': 'Jun 26 21:41:46 2011 GMT',
152 'subject': (((u'commonName', u'linuxfrz.org'),),),
153 'subjectAltName': (
154 ('DNS', 'linuxfr.org'),
155 ('DNS', 'linuxfr.com'),
156 ('othername', '<unsupported>'),
157 )
158 }
159 check(_verifycert(c, 'linuxfr.org'), None)
160 check(_verifycert(c, 'linuxfr.com'), None)
161 # Not a "DNS" entry
162 check(_verifycert(c, '<unsupported>'),
163 'certificate is for linuxfr.org, linuxfr.com')
164 # When there is a subjectAltName, commonName isn't used
165 check(_verifycert(c, 'linuxfrz.org'),
166 'certificate is for linuxfr.org, linuxfr.com')
167
168 # A pristine real-world example
169 c = {
170 'notAfter': 'Dec 18 23:59:59 2011 GMT',
171 'subject': (
172 ((u'countryName', u'US'),),
173 ((u'stateOrProvinceName', u'California'),),
174 ((u'localityName', u'Mountain View'),),
175 ((u'organizationName', u'Google Inc'),),
176 ((u'commonName', u'mail.google.com'),),
177 ),
178 }
179 check(_verifycert(c, 'mail.google.com'), None)
180 check(_verifycert(c, 'gmail.com'), 'certificate is for mail.google.com')
181
182 # Only commonName is considered
183 check(_verifycert(c, 'California'), 'certificate is for mail.google.com')
184
185 # Neither commonName nor subjectAltName
186 c = {
187 'notAfter': 'Dec 18 23:59:59 2011 GMT',
188 'subject': (
189 ((u'countryName', u'US'),),
190 ((u'stateOrProvinceName', u'California'),),
191 ((u'localityName', u'Mountain View'),),
192 ((u'organizationName', u'Google Inc'),),
193 ),
194 }
195 check(_verifycert(c, 'mail.google.com'),
196 'no commonName or subjectAltName found in certificate')
197
198 # No DNS entry in subjectAltName but a commonName
199 c = {
200 'notAfter': 'Dec 18 23:59:59 2099 GMT',
201 'subject': (
202 ((u'countryName', u'US'),),
203 ((u'stateOrProvinceName', u'California'),),
204 ((u'localityName', u'Mountain View'),),
205 ((u'commonName', u'mail.google.com'),),
206 ),
207 'subjectAltName': (('othername', 'blabla'),),
208 }
209 check(_verifycert(c, 'mail.google.com'), None)
210
211 # No DNS entry subjectAltName and no commonName
212 c = {
213 'notAfter': 'Dec 18 23:59:59 2099 GMT',
214 'subject': (
215 ((u'countryName', u'US'),),
216 ((u'stateOrProvinceName', u'California'),),
217 ((u'localityName', u'Mountain View'),),
218 ((u'organizationName', u'Google Inc'),),
219 ),
220 'subjectAltName': (('othername', 'blabla'),),
221 }
222 check(_verifycert(c, 'google.com'),
223 'no commonName or subjectAltName found in certificate')
224
225 # Empty cert / no cert
226 check(_verifycert(None, 'example.com'), 'no certificate received')
227 check(_verifycert({}, 'example.com'), 'no certificate received')
228
229 # avoid denials of service by refusing more than one
230 # wildcard per fragment.
231 check(_verifycert({'subject': (((u'commonName', u'a*b.com'),),)},
232 'axxb.com'), 'certificate is for a*b.com')
233 check(_verifycert({'subject': (((u'commonName', u'a*b.co*'),),)},
234 'axxb.com'), 'certificate is for a*b.co*')
235 check(_verifycert({'subject': (((u'commonName', u'a*b*.com'),),)},
236 'axxbxxc.com'), 'certificate is for a*b*.com')
237
66 def test_url():
238 def test_url():
67 """
239 """
68 >>> from mercurial.util import url
240 >>> from mercurial.util import url
69
241
70 This tests for edge cases in url.URL's parsing algorithm. Most of
242 This tests for edge cases in url.URL's parsing algorithm. Most of
71 these aren't useful for documentation purposes, so they aren't
243 these aren't useful for documentation purposes, so they aren't
72 part of the class's doc tests.
244 part of the class's doc tests.
73
245
74 Query strings and fragments:
246 Query strings and fragments:
75
247
76 >>> url('http://host/a?b#c')
248 >>> url('http://host/a?b#c')
77 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
249 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
78 >>> url('http://host/a?')
250 >>> url('http://host/a?')
79 <url scheme: 'http', host: 'host', path: 'a'>
251 <url scheme: 'http', host: 'host', path: 'a'>
80 >>> url('http://host/a#b#c')
252 >>> url('http://host/a#b#c')
81 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'>
253 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b#c'>
82 >>> url('http://host/a#b?c')
254 >>> url('http://host/a#b?c')
83 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'>
255 <url scheme: 'http', host: 'host', path: 'a', fragment: 'b?c'>
84 >>> url('http://host/?a#b')
256 >>> url('http://host/?a#b')
85 <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'>
257 <url scheme: 'http', host: 'host', path: '', query: 'a', fragment: 'b'>
86 >>> url('http://host/?a#b', parsequery=False)
258 >>> url('http://host/?a#b', parsequery=False)
87 <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'>
259 <url scheme: 'http', host: 'host', path: '?a', fragment: 'b'>
88 >>> url('http://host/?a#b', parsefragment=False)
260 >>> url('http://host/?a#b', parsefragment=False)
89 <url scheme: 'http', host: 'host', path: '', query: 'a#b'>
261 <url scheme: 'http', host: 'host', path: '', query: 'a#b'>
90 >>> url('http://host/?a#b', parsequery=False, parsefragment=False)
262 >>> url('http://host/?a#b', parsequery=False, parsefragment=False)
91 <url scheme: 'http', host: 'host', path: '?a#b'>
263 <url scheme: 'http', host: 'host', path: '?a#b'>
92
264
93 IPv6 addresses:
265 IPv6 addresses:
94
266
95 >>> url('ldap://[2001:db8::7]/c=GB?objectClass?one')
267 >>> url('ldap://[2001:db8::7]/c=GB?objectClass?one')
96 <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB',
268 <url scheme: 'ldap', host: '[2001:db8::7]', path: 'c=GB',
97 query: 'objectClass?one'>
269 query: 'objectClass?one'>
98 >>> url('ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one')
270 >>> url('ldap://joe:xxx@[2001:db8::7]:80/c=GB?objectClass?one')
99 <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]',
271 <url scheme: 'ldap', user: 'joe', passwd: 'xxx', host: '[2001:db8::7]',
100 port: '80', path: 'c=GB', query: 'objectClass?one'>
272 port: '80', path: 'c=GB', query: 'objectClass?one'>
101
273
102 Missing scheme, host, etc.:
274 Missing scheme, host, etc.:
103
275
104 >>> url('://192.0.2.16:80/')
276 >>> url('://192.0.2.16:80/')
105 <url path: '://192.0.2.16:80/'>
277 <url path: '://192.0.2.16:80/'>
106 >>> url('https://mercurial-scm.org')
278 >>> url('https://mercurial-scm.org')
107 <url scheme: 'https', host: 'mercurial-scm.org'>
279 <url scheme: 'https', host: 'mercurial-scm.org'>
108 >>> url('/foo')
280 >>> url('/foo')
109 <url path: '/foo'>
281 <url path: '/foo'>
110 >>> url('bundle:/foo')
282 >>> url('bundle:/foo')
111 <url scheme: 'bundle', path: '/foo'>
283 <url scheme: 'bundle', path: '/foo'>
112 >>> url('a?b#c')
284 >>> url('a?b#c')
113 <url path: 'a?b', fragment: 'c'>
285 <url path: 'a?b', fragment: 'c'>
114 >>> url('http://x.com?arg=/foo')
286 >>> url('http://x.com?arg=/foo')
115 <url scheme: 'http', host: 'x.com', query: 'arg=/foo'>
287 <url scheme: 'http', host: 'x.com', query: 'arg=/foo'>
116 >>> url('http://joe:xxx@/foo')
288 >>> url('http://joe:xxx@/foo')
117 <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'>
289 <url scheme: 'http', user: 'joe', passwd: 'xxx', path: 'foo'>
118
290
119 Just a scheme and a path:
291 Just a scheme and a path:
120
292
121 >>> url('mailto:John.Doe@example.com')
293 >>> url('mailto:John.Doe@example.com')
122 <url scheme: 'mailto', path: 'John.Doe@example.com'>
294 <url scheme: 'mailto', path: 'John.Doe@example.com'>
123 >>> url('a:b:c:d')
295 >>> url('a:b:c:d')
124 <url path: 'a:b:c:d'>
296 <url path: 'a:b:c:d'>
125 >>> url('aa:bb:cc:dd')
297 >>> url('aa:bb:cc:dd')
126 <url scheme: 'aa', path: 'bb:cc:dd'>
298 <url scheme: 'aa', path: 'bb:cc:dd'>
127
299
128 SSH examples:
300 SSH examples:
129
301
130 >>> url('ssh://joe@host//home/joe')
302 >>> url('ssh://joe@host//home/joe')
131 <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'>
303 <url scheme: 'ssh', user: 'joe', host: 'host', path: '/home/joe'>
132 >>> url('ssh://joe:xxx@host/src')
304 >>> url('ssh://joe:xxx@host/src')
133 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'>
305 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', path: 'src'>
134 >>> url('ssh://joe:xxx@host')
306 >>> url('ssh://joe:xxx@host')
135 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'>
307 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host'>
136 >>> url('ssh://joe@host')
308 >>> url('ssh://joe@host')
137 <url scheme: 'ssh', user: 'joe', host: 'host'>
309 <url scheme: 'ssh', user: 'joe', host: 'host'>
138 >>> url('ssh://host')
310 >>> url('ssh://host')
139 <url scheme: 'ssh', host: 'host'>
311 <url scheme: 'ssh', host: 'host'>
140 >>> url('ssh://')
312 >>> url('ssh://')
141 <url scheme: 'ssh'>
313 <url scheme: 'ssh'>
142 >>> url('ssh:')
314 >>> url('ssh:')
143 <url scheme: 'ssh'>
315 <url scheme: 'ssh'>
144
316
145 Non-numeric port:
317 Non-numeric port:
146
318
147 >>> url('http://example.com:dd')
319 >>> url('http://example.com:dd')
148 <url scheme: 'http', host: 'example.com', port: 'dd'>
320 <url scheme: 'http', host: 'example.com', port: 'dd'>
149 >>> url('ssh://joe:xxx@host:ssh/foo')
321 >>> url('ssh://joe:xxx@host:ssh/foo')
150 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh',
322 <url scheme: 'ssh', user: 'joe', passwd: 'xxx', host: 'host', port: 'ssh',
151 path: 'foo'>
323 path: 'foo'>
152
324
153 Bad authentication credentials:
325 Bad authentication credentials:
154
326
155 >>> url('http://joe@joeville:123@4:@host/a?b#c')
327 >>> url('http://joe@joeville:123@4:@host/a?b#c')
156 <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:',
328 <url scheme: 'http', user: 'joe@joeville', passwd: '123@4:',
157 host: 'host', path: 'a', query: 'b', fragment: 'c'>
329 host: 'host', path: 'a', query: 'b', fragment: 'c'>
158 >>> url('http://!*#?/@!*#?/:@host/a?b#c')
330 >>> url('http://!*#?/@!*#?/:@host/a?b#c')
159 <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'>
331 <url scheme: 'http', host: '!*', fragment: '?/@!*#?/:@host/a?b#c'>
160 >>> url('http://!*#?@!*#?:@host/a?b#c')
332 >>> url('http://!*#?@!*#?:@host/a?b#c')
161 <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'>
333 <url scheme: 'http', host: '!*', fragment: '?@!*#?:@host/a?b#c'>
162 >>> url('http://!*@:!*@@host/a?b#c')
334 >>> url('http://!*@:!*@@host/a?b#c')
163 <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host',
335 <url scheme: 'http', user: '!*@', passwd: '!*@', host: 'host',
164 path: 'a', query: 'b', fragment: 'c'>
336 path: 'a', query: 'b', fragment: 'c'>
165
337
166 File paths:
338 File paths:
167
339
168 >>> url('a/b/c/d.g.f')
340 >>> url('a/b/c/d.g.f')
169 <url path: 'a/b/c/d.g.f'>
341 <url path: 'a/b/c/d.g.f'>
170 >>> url('/x///z/y/')
342 >>> url('/x///z/y/')
171 <url path: '/x///z/y/'>
343 <url path: '/x///z/y/'>
172 >>> url('/foo:bar')
344 >>> url('/foo:bar')
173 <url path: '/foo:bar'>
345 <url path: '/foo:bar'>
174 >>> url('\\\\foo:bar')
346 >>> url('\\\\foo:bar')
175 <url path: '\\\\foo:bar'>
347 <url path: '\\\\foo:bar'>
176 >>> url('./foo:bar')
348 >>> url('./foo:bar')
177 <url path: './foo:bar'>
349 <url path: './foo:bar'>
178
350
179 Non-localhost file URL:
351 Non-localhost file URL:
180
352
181 >>> u = url('file://mercurial-scm.org/foo')
353 >>> u = url('file://mercurial-scm.org/foo')
182 Traceback (most recent call last):
354 Traceback (most recent call last):
183 File "<stdin>", line 1, in ?
355 File "<stdin>", line 1, in ?
184 Abort: file:// URLs can only refer to localhost
356 Abort: file:// URLs can only refer to localhost
185
357
186 Empty URL:
358 Empty URL:
187
359
188 >>> u = url('')
360 >>> u = url('')
189 >>> u
361 >>> u
190 <url path: ''>
362 <url path: ''>
191 >>> str(u)
363 >>> str(u)
192 ''
364 ''
193
365
194 Empty path with query string:
366 Empty path with query string:
195
367
196 >>> str(url('http://foo/?bar'))
368 >>> str(url('http://foo/?bar'))
197 'http://foo/?bar'
369 'http://foo/?bar'
198
370
199 Invalid path:
371 Invalid path:
200
372
201 >>> u = url('http://foo/bar')
373 >>> u = url('http://foo/bar')
202 >>> u.path = 'bar'
374 >>> u.path = 'bar'
203 >>> str(u)
375 >>> str(u)
204 'http://foo/bar'
376 'http://foo/bar'
205
377
206 >>> u = url('file:/foo/bar/baz')
378 >>> u = url('file:/foo/bar/baz')
207 >>> u
379 >>> u
208 <url scheme: 'file', path: '/foo/bar/baz'>
380 <url scheme: 'file', path: '/foo/bar/baz'>
209 >>> str(u)
381 >>> str(u)
210 'file:///foo/bar/baz'
382 'file:///foo/bar/baz'
211 >>> u.localpath()
383 >>> u.localpath()
212 '/foo/bar/baz'
384 '/foo/bar/baz'
213
385
214 >>> u = url('file:///foo/bar/baz')
386 >>> u = url('file:///foo/bar/baz')
215 >>> u
387 >>> u
216 <url scheme: 'file', path: '/foo/bar/baz'>
388 <url scheme: 'file', path: '/foo/bar/baz'>
217 >>> str(u)
389 >>> str(u)
218 'file:///foo/bar/baz'
390 'file:///foo/bar/baz'
219 >>> u.localpath()
391 >>> u.localpath()
220 '/foo/bar/baz'
392 '/foo/bar/baz'
221
393
222 >>> u = url('file:///f:oo/bar/baz')
394 >>> u = url('file:///f:oo/bar/baz')
223 >>> u
395 >>> u
224 <url scheme: 'file', path: 'f:oo/bar/baz'>
396 <url scheme: 'file', path: 'f:oo/bar/baz'>
225 >>> str(u)
397 >>> str(u)
226 'file:///f:oo/bar/baz'
398 'file:///f:oo/bar/baz'
227 >>> u.localpath()
399 >>> u.localpath()
228 'f:oo/bar/baz'
400 'f:oo/bar/baz'
229
401
230 >>> u = url('file://localhost/f:oo/bar/baz')
402 >>> u = url('file://localhost/f:oo/bar/baz')
231 >>> u
403 >>> u
232 <url scheme: 'file', host: 'localhost', path: 'f:oo/bar/baz'>
404 <url scheme: 'file', host: 'localhost', path: 'f:oo/bar/baz'>
233 >>> str(u)
405 >>> str(u)
234 'file://localhost/f:oo/bar/baz'
406 'file://localhost/f:oo/bar/baz'
235 >>> u.localpath()
407 >>> u.localpath()
236 'f:oo/bar/baz'
408 'f:oo/bar/baz'
237
409
238 >>> u = url('file:foo/bar/baz')
410 >>> u = url('file:foo/bar/baz')
239 >>> u
411 >>> u
240 <url scheme: 'file', path: 'foo/bar/baz'>
412 <url scheme: 'file', path: 'foo/bar/baz'>
241 >>> str(u)
413 >>> str(u)
242 'file:foo/bar/baz'
414 'file:foo/bar/baz'
243 >>> u.localpath()
415 >>> u.localpath()
244 'foo/bar/baz'
416 'foo/bar/baz'
245 """
417 """
246
418
247 if 'TERM' in os.environ:
419 if 'TERM' in os.environ:
248 del os.environ['TERM']
420 del os.environ['TERM']
249
421
250 doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)
422 doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)
General Comments 0
You need to be logged in to leave comments. Login now