diff -r 831be7dc260a Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py Sat Feb 23 07:53:56 2013 +0200 +++ b/Lib/test/test_urllib.py Sat Feb 23 18:50:30 2013 +0100 @@ -964,6 +964,33 @@ result = urllib.parse.urlencode(given) self.assertEqual(expect, result) + def test_quoting_uri(self): + uri = "http://localhost" + expect = "http://localhost/" + result = urllib.parse.quote_uri(uri) + self.assertEqual(expect, result) + + uri = "http://localhost/test.html" + expect = "http://localhost/test.html" + result = urllib.parse.quote_uri(uri) + self.assertEqual(expect, result) + + uri = "http://localhost:8080/åäö.html" + expect = "http://localhost:8080/%C3%A5%C3%A4%C3%B6.html" + result = urllib.parse.quote_uri(uri) + self.assertEqual(expect, result) + + uri = "http://slagvärket.örebro.se:8080/åäö.html" + expect = "http://xn--slagvrket-z2a.xn--rebro-iua.se:8080/%C3%A5%C3%A4%C3%B6.html" + result = urllib.parse.quote_uri(uri) + self.assertEqual(expect, result) + + uri = "http://auth:a@sd@slagvärket.örebro.se:8080/å@äö.html" + expect = "http://auth:a@sd@xn--slagvrket-z2a.xn--rebro-iua.se:8080/%C3%A5%40%C3%A4%C3%B6.html" + result = urllib.parse.quote_uri(uri) + self.assertEqual(expect, result) + + def test_doseq(self): # Test that passing True for 'doseq' parameter works correctly given = {'sequence':['1', '2', '3']} diff -r 831be7dc260a Lib/urllib/parse.py --- a/Lib/urllib/parse.py Sat Feb 23 07:53:56 2013 +0200 +++ b/Lib/urllib/parse.py Sat Feb 23 18:50:30 2013 +0100 @@ -981,3 +981,42 @@ match = _valueprog.match(attr) if match: return match.group(1, 2) return attr, None + +def quote_uri(uri): + if not "://" in uri: + raise ValueError("{!r} is not a valid URI".format(uri)) + proto, rest = uri.split("://",1) + # Splitting on / as to allow @ in passwords but dissallows / in passwords.. have to choose one + if "/" in rest: + rest,path = rest.split("/",1) + path = quote(path) + else: + path = "" + auth = "" + if "@" in rest: + items = rest.split("@") + rest = items.pop() + auth = "@".join(items) + auth += "@" # Add trailing @ so that assemply is smoother + if ":" in rest: + host,port = rest.split(":",1) + port = ":"+port # Add starting : so that assemply is smoother + else: + host = rest + port = "" + # Encode the hostname with punycode if needed + try: + host.encode("ascii") + except: + newhost = [] + hostparts = host.split(".") + for hostpart in hostparts: + try: + hostpart.encode("ascii") + newhost.append(hostpart) + except: + newhost.append("xn--"+hostpart.encode("punycode").decode("ascii")) + host = ".".join(newhost) + quoteduri = "{proto}://{auth}{host}{port}/{path}".format(**vars()) + return quoteduri +