changeset 28883:032c4c2f802a

pycompat: switch to util.urlreq/util.urlerr for py3 compat
author timeless <timeless@mozdev.org>
date Wed, 06 Apr 2016 23:22:12 +0000
parents 800ec7c048b0
children 75309badb485
files hgext/acl.py hgext/convert/subversion.py hgext/largefiles/proto.py hgext/largefiles/remotestore.py mercurial/bundle2.py mercurial/byterange.py mercurial/exchange.py mercurial/hgweb/protocol.py mercurial/hgweb/server.py mercurial/httpconnection.py mercurial/httppeer.py mercurial/keepalive.py mercurial/localrepo.py mercurial/statichttprepo.py mercurial/templatefilters.py mercurial/url.py mercurial/util.py mercurial/wireproto.py tests/test-check-py3-compat.t tests/test-hgweb-auth.py
diffstat 20 files changed, 189 insertions(+), 154 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/acl.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/hgext/acl.py	Wed Apr 06 23:22:12 2016 +0000
@@ -194,7 +194,6 @@
 from __future__ import absolute_import
 
 import getpass
-import urllib
 
 from mercurial.i18n import _
 from mercurial import (
@@ -203,6 +202,8 @@
     util,
 )
 
+urlreq = util.urlreq
+
 # Note for extension authors: ONLY specify testedwith = 'internal' for
 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
 # be specifying the version(s) of Mercurial they are tested with, or
@@ -287,7 +288,7 @@
     if source == 'serve' and 'url' in kwargs:
         url = kwargs['url'].split(':')
         if url[0] == 'remote' and url[1].startswith('http'):
-            user = urllib.unquote(url[3])
+            user = urlreq.unquote(url[3])
 
     if user is None:
         user = getpass.getuser()
--- a/hgext/convert/subversion.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/hgext/convert/subversion.py	Wed Apr 06 23:22:12 2016 +0000
@@ -8,8 +8,6 @@
 import re
 import sys
 import tempfile
-import urllib
-import urllib2
 import xml.dom.minidom
 
 from mercurial import (
@@ -25,6 +23,8 @@
 
 stringio = util.stringio
 propertycache = util.propertycache
+urlerr = util.urlerr
+urlreq = util.urlreq
 
 commandline = common.commandline
 commit = common.commit
@@ -94,7 +94,7 @@
     # so we can extend it safely with new components. The "safe"
     # characters were taken from the "svn_uri__char_validity" table in
     # libsvn_subr/path.c.
-    return urllib.quote(s, "!$&'()*+,-./:=@_~")
+    return urlreq.quote(s, "!$&'()*+,-./:=@_~")
 
 def geturl(path):
     try:
@@ -233,10 +233,10 @@
 # for the svn-specific "not found" XML.
 def httpcheck(ui, path, proto):
     try:
-        opener = urllib2.build_opener()
+        opener = urlreq.buildopener()
         rsp = opener.open('%s://%s/!svn/ver/0/.svn' % (proto, path))
         data = rsp.read()
-    except urllib2.HTTPError as inst:
+    except urlerr.httperror as inst:
         if inst.code != 404:
             # Except for 404 we cannot know for sure this is not an svn repo
             ui.warn(_('svn: cannot probe remote repository, assume it could '
@@ -245,7 +245,7 @@
             return True
         data = inst.fp.read()
     except Exception:
-        # Could be urllib2.URLError if the URL is invalid or anything else.
+        # Could be urlerr.urlerror if the URL is invalid or anything else.
         return False
     return '<m:human-readable errcode="160013">' in data
 
@@ -260,7 +260,7 @@
             if (os.name == 'nt' and path[:1] == '/' and path[1:2].isalpha()
                 and path[2:6].lower() == '%3a/'):
                 path = path[:2] + ':/' + path[6:]
-            path = urllib.url2pathname(path)
+            path = urlreq.url2pathname(path)
     except ValueError:
         proto = 'file'
         path = os.path.abspath(url)
@@ -330,7 +330,7 @@
             self.baseurl = svn.ra.get_repos_root(self.ra)
             # Module is either empty or a repository path starting with
             # a slash and not ending with a slash.
-            self.module = urllib.unquote(self.url[len(self.baseurl):])
+            self.module = urlreq.unquote(self.url[len(self.baseurl):])
             self.prevmodule = None
             self.rootmodule = self.module
             self.commits = {}
--- a/hgext/largefiles/proto.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/hgext/largefiles/proto.py	Wed Apr 06 23:22:12 2016 +0000
@@ -4,12 +4,14 @@
 # GNU General Public License version 2 or any later version.
 
 import os
-import urllib2
 import re
 
 from mercurial import error, httppeer, util, wireproto
 from mercurial.i18n import _
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 import lfutil
 
 LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.'
@@ -140,7 +142,7 @@
             yield result, f
             try:
                 yield int(f.value)
-            except (ValueError, urllib2.HTTPError):
+            except (ValueError, urlerr.httperror):
                 # If the server returns anything but an integer followed by a
                 # newline, newline, it's not speaking our language; if we get
                 # an HTTP error, we can't be sure the largefile is present;
--- a/hgext/largefiles/remotestore.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/hgext/largefiles/remotestore.py	Wed Apr 06 23:22:12 2016 +0000
@@ -6,11 +6,12 @@
 
 '''remote largefile store; the base class for wirestore'''
 
-import urllib2
-
 from mercurial import util, wireproto, error
 from mercurial.i18n import _
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 import lfutil
 import basestore
 
@@ -49,11 +50,11 @@
     def _getfile(self, tmpfile, filename, hash):
         try:
             chunks = self._get(hash)
-        except urllib2.HTTPError as e:
+        except urlerr.httperror as e:
             # 401s get converted to error.Aborts; everything else is fine being
             # turned into a StoreError
             raise basestore.StoreError(filename, hash, self.url, str(e))
-        except urllib2.URLError as e:
+        except urlerr.urlerror as e:
             # This usually indicates a connection problem, so don't
             # keep trying with the other files... they will probably
             # all fail too.
--- a/mercurial/bundle2.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/bundle2.py	Wed Apr 06 23:22:12 2016 +0000
@@ -152,7 +152,6 @@
 import string
 import struct
 import sys
-import urllib
 
 from .i18n import _
 from . import (
@@ -165,6 +164,9 @@
     util,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 _pack = struct.pack
 _unpack = struct.unpack
 
@@ -457,8 +459,8 @@
         else:
             key, vals = line.split('=', 1)
             vals = vals.split(',')
-        key = urllib.unquote(key)
-        vals = [urllib.unquote(v) for v in vals]
+        key = urlreq.unquote(key)
+        vals = [urlreq.unquote(v) for v in vals]
         caps[key] = vals
     return caps
 
@@ -467,8 +469,8 @@
     chunks = []
     for ca in sorted(caps):
         vals = caps[ca]
-        ca = urllib.quote(ca)
-        vals = [urllib.quote(v) for v in vals]
+        ca = urlreq.quote(ca)
+        vals = [urlreq.quote(v) for v in vals]
         if vals:
             ca = "%s=%s" % (ca, ','.join(vals))
         chunks.append(ca)
@@ -570,9 +572,9 @@
         """return a encoded version of all stream parameters"""
         blocks = []
         for par, value in self._params:
-            par = urllib.quote(par)
+            par = urlreq.quote(par)
             if value is not None:
-                value = urllib.quote(value)
+                value = urlreq.quote(value)
                 par = '%s=%s' % (par, value)
             blocks.append(par)
         return ' '.join(blocks)
@@ -691,7 +693,7 @@
         params = {}
         for p in paramsblock.split(' '):
             p = p.split('=', 1)
-            p = [urllib.unquote(i) for i in p]
+            p = [urlreq.unquote(i) for i in p]
             if len(p) < 2:
                 p.append(None)
             self._processparam(*p)
@@ -1269,7 +1271,7 @@
     raw = remote.capable('bundle2')
     if not raw and raw != '':
         return {}
-    capsblob = urllib.unquote(remote.capable('bundle2'))
+    capsblob = urlreq.unquote(remote.capable('bundle2'))
     return decodecaps(capsblob)
 
 def obsmarkersversion(caps):
--- a/mercurial/byterange.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/byterange.py	Wed Apr 06 23:22:12 2016 +0000
@@ -26,22 +26,27 @@
 import re
 import socket
 import stat
-import urllib
-import urllib2
+
+from . import (
+    util,
+)
+
+urlerr = util.urlerr
+urlreq = util.urlreq
 
-addclosehook = urllib.addclosehook
-addinfourl = urllib.addinfourl
-splitattr = urllib.splitattr
-splitpasswd = urllib.splitpasswd
-splitport = urllib.splitport
-splituser = urllib.splituser
-unquote = urllib.unquote
+addclosehook = urlreq.addclosehook
+addinfourl = urlreq.addinfourl
+splitattr = urlreq.splitattr
+splitpasswd = urlreq.splitpasswd
+splitport = urlreq.splitport
+splituser = urlreq.splituser
+unquote = urlreq.unquote
 
 class RangeError(IOError):
     """Error raised when an unsatisfiable range is requested."""
     pass
 
-class HTTPRangeHandler(urllib2.BaseHandler):
+class HTTPRangeHandler(urlreq.basehandler):
     """Handler that enables HTTP Range headers.
 
     This was extremely simple. The Range header is a HTTP feature to
@@ -54,20 +59,20 @@
         import byterange
 
         range_handler = range.HTTPRangeHandler()
-        opener = urllib2.build_opener(range_handler)
+        opener = urlreq.buildopener(range_handler)
 
         # install it
-        urllib2.install_opener(opener)
+        urlreq.installopener(opener)
 
         # create Request and set Range header
-        req = urllib2.Request('http://www.python.org/')
+        req = urlreq.request('http://www.python.org/')
         req.header['Range'] = 'bytes=30-50'
-        f = urllib2.urlopen(req)
+        f = urlreq.urlopen(req)
     """
 
     def http_error_206(self, req, fp, code, msg, hdrs):
         # 206 Partial Content Response
-        r = urllib.addinfourl(fp, hdrs, req.get_full_url())
+        r = urlreq.addinfourl(fp, hdrs, req.get_full_url())
         r.code = code
         r.msg = msg
         return r
@@ -204,7 +209,7 @@
                 raise RangeError('Requested Range Not Satisfiable')
             pos += bufsize
 
-class FileRangeHandler(urllib2.FileHandler):
+class FileRangeHandler(urlreq.filehandler):
     """FileHandler subclass that adds Range support.
     This class handles Range headers exactly like an HTTP
     server would.
@@ -212,15 +217,15 @@
     def open_local_file(self, req):
         host = req.get_host()
         file = req.get_selector()
-        localfile = urllib.url2pathname(file)
+        localfile = urlreq.url2pathname(file)
         stats = os.stat(localfile)
         size = stats[stat.ST_SIZE]
         modified = email.Utils.formatdate(stats[stat.ST_MTIME])
         mtype = mimetypes.guess_type(file)[0]
         if host:
-            host, port = urllib.splitport(host)
+            host, port = urlreq.splitport(host)
             if port or socket.gethostbyname(host) not in self.get_names():
-                raise urllib2.URLError('file not on local host')
+                raise urlerr.urlerror('file not on local host')
         fo = open(localfile,'rb')
         brange = req.headers.get('Range', None)
         brange = range_header_to_tuple(brange)
@@ -236,7 +241,7 @@
         headers = email.message_from_string(
             'Content-Type: %s\nContent-Length: %d\nLast-Modified: %s\n' %
             (mtype or 'text/plain', size, modified))
-        return urllib.addinfourl(fo, headers, 'file:'+file)
+        return urlreq.addinfourl(fo, headers, 'file:'+file)
 
 
 # FTP Range Support
@@ -246,7 +251,7 @@
 # follows:
 # -- range support modifications start/end here
 
-class FTPRangeHandler(urllib2.FTPHandler):
+class FTPRangeHandler(urlreq.ftphandler):
     def ftp_open(self, req):
         host = req.get_host()
         if not host:
@@ -270,7 +275,7 @@
         try:
             host = socket.gethostbyname(host)
         except socket.error as msg:
-            raise urllib2.URLError(msg)
+            raise urlerr.urlerror(msg)
         path, attrs = splitattr(req.get_selector())
         dirs = path.split('/')
         dirs = map(unquote, dirs)
@@ -334,7 +339,7 @@
         fw = ftpwrapper(user, passwd, host, port, dirs)
         return fw
 
-class ftpwrapper(urllib.ftpwrapper):
+class ftpwrapper(urlreq.ftpwrapper):
     # range support note:
     # this ftpwrapper code is copied directly from
     # urllib. The only enhancement is to add the rest
--- a/mercurial/exchange.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/exchange.py	Wed Apr 06 23:22:12 2016 +0000
@@ -8,8 +8,6 @@
 from __future__ import absolute_import
 
 import errno
-import urllib
-import urllib2
 
 from .i18n import _
 from .node import (
@@ -35,6 +33,9 @@
     util,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 # Maps bundle compression human names to internal representation.
 _bundlespeccompressions = {'none': None,
                            'bzip2': 'BZ',
@@ -97,8 +98,8 @@
                       'missing "=" in parameter: %s') % p)
 
             key, value = p.split('=', 1)
-            key = urllib.unquote(key)
-            value = urllib.unquote(value)
+            key = urlreq.unquote(key)
+            value = urlreq.unquote(value)
             params[key] = value
 
         return version, params
@@ -236,7 +237,7 @@
     elif isinstance(b, streamclone.streamcloneapplier):
         requirements = streamclone.readbundle1header(fh)[2]
         params = 'requirements=%s' % ','.join(sorted(requirements))
-        return 'none-packed1;%s' % urllib.quote(params)
+        return 'none-packed1;%s' % urlreq.quote(params)
     else:
         raise error.Abort(_('unknown bundle type: %s') % b)
 
@@ -1465,7 +1466,7 @@
     """return a set with appropriate options to use bundle20 during getbundle"""
     caps = set(['HG20'])
     capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
-    caps.add('bundle2=' + urllib.quote(capsblob))
+    caps.add('bundle2=' + urlreq.quote(capsblob))
     return caps
 
 # List of names of steps to perform for a bundle2 for getbundle, order matters.
@@ -1531,7 +1532,7 @@
     b2caps = {}
     for bcaps in bundlecaps:
         if bcaps.startswith('bundle2='):
-            blob = urllib.unquote(bcaps[len('bundle2='):])
+            blob = urlreq.unquote(bcaps[len('bundle2='):])
             b2caps.update(bundle2.decodecaps(blob))
     bundler = bundle2.bundle20(repo.ui, b2caps)
 
@@ -1800,8 +1801,8 @@
         attrs = {'URL': fields[0]}
         for rawattr in fields[1:]:
             key, value = rawattr.split('=', 1)
-            key = urllib.unquote(key)
-            value = urllib.unquote(value)
+            key = urlreq.unquote(key)
+            value = urlreq.unquote(value)
             attrs[key] = value
 
             # Parse BUNDLESPEC into components. This makes client-side
@@ -1917,9 +1918,9 @@
                     cg.apply(repo, 'clonebundles', url)
                 tr.close()
                 return True
-            except urllib2.HTTPError as e:
+            except urlerr.httperror as e:
                 ui.warn(_('HTTP error fetching bundle: %s\n') % str(e))
-            except urllib2.URLError as e:
+            except urlerr.urlerror as e:
                 ui.warn(_('error fetching bundle: %s\n') % e.reason[1])
 
             return False
--- a/mercurial/hgweb/protocol.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/hgweb/protocol.py	Wed Apr 06 23:22:12 2016 +0000
@@ -8,7 +8,6 @@
 from __future__ import absolute_import
 
 import cgi
-import urllib
 import zlib
 
 from .common import (
@@ -21,6 +20,9 @@
 )
 stringio = util.stringio
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 HGTYPE = 'application/mercurial-0.1'
 HGERRTYPE = 'application/hg-error'
 
@@ -82,8 +84,8 @@
     def _client(self):
         return 'remote:%s:%s:%s' % (
             self.req.env.get('wsgi.url_scheme') or 'http',
-            urllib.quote(self.req.env.get('REMOTE_HOST', '')),
-            urllib.quote(self.req.env.get('REMOTE_USER', '')))
+            urlreq.quote(self.req.env.get('REMOTE_HOST', '')),
+            urlreq.quote(self.req.env.get('REMOTE_USER', '')))
 
 def iscmd(cmd):
     return cmd in wireproto.commands
--- a/mercurial/hgweb/server.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/hgweb/server.py	Wed Apr 06 23:22:12 2016 +0000
@@ -15,7 +15,6 @@
 import socket
 import sys
 import traceback
-import urllib
 
 from ..i18n import _
 
@@ -24,6 +23,9 @@
     util,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 from . import (
     common,
 )
@@ -38,7 +40,7 @@
         path, query = uri.split('?', 1)
     else:
         path, query = uri, ''
-    return urllib.unquote(path), query
+    return urlreq.unquote(path), query
 
 class _error_logger(object):
     def __init__(self, handler):
--- a/mercurial/httpconnection.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/httpconnection.py	Wed Apr 06 23:22:12 2016 +0000
@@ -13,8 +13,6 @@
 import logging
 import os
 import socket
-import urllib
-import urllib2
 
 from .i18n import _
 from . import (
@@ -23,6 +21,9 @@
     util,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 # moved here from url.py to avoid a cycle
 class httpsendfile(object):
     """This is a wrapper around the objects returned by python's "open".
@@ -123,10 +124,10 @@
 # Subclass BOTH of these because otherwise urllib2 "helpfully"
 # reinserts them since it notices we don't include any subclasses of
 # them.
-class http2handler(urllib2.HTTPHandler, urllib2.HTTPSHandler):
+class http2handler(urlreq.httphandler, urlreq.httpshandler):
     def __init__(self, ui, pwmgr):
         global _configuredlogging
-        urllib2.AbstractHTTPHandler.__init__(self)
+        urlreq.abstracthttphandler.__init__(self)
         self.ui = ui
         self.pwmgr = pwmgr
         self._connections = {}
@@ -187,7 +188,7 @@
             proxy = None
 
         if not host:
-            raise urllib2.URLError('no host given')
+            raise urlerr.urlerror('no host given')
 
         connkey = use_ssl, host, proxy
         allconns = self._connections.get(connkey, [])
@@ -217,13 +218,13 @@
             h.request(req.get_method(), path, req.data, headers)
             r = h.getresponse()
         except socket.error as err: # XXX what error?
-            raise urllib2.URLError(err)
+            raise urlerr.urlerror(err)
 
         # Pick apart the HTTPResponse object to get the addinfourl
         # object initialized properly.
         r.recv = r.read
 
-        resp = urllib.addinfourl(r, r.headers, req.get_full_url())
+        resp = urlreq.addinfourl(r, r.headers, req.get_full_url())
         resp.code = r.status
         resp.msg = r.reason
         return resp
--- a/mercurial/httppeer.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/httppeer.py	Wed Apr 06 23:22:12 2016 +0000
@@ -13,8 +13,6 @@
 import os
 import socket
 import tempfile
-import urllib
-import urllib2
 import zlib
 
 from .i18n import _
@@ -29,6 +27,9 @@
     wireproto,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 def zgenerator(f):
     zd = zlib.decompressobj()
     try:
@@ -59,7 +60,7 @@
         self.ui.debug('using %s\n' % self._url)
 
         self.urlopener = url.opener(ui, authinfo)
-        self.requestbuilder = urllib2.Request
+        self.requestbuilder = urlreq.request
 
     def __del__(self):
         if self.urlopener:
@@ -105,7 +106,7 @@
         # object rather than a basestring
         canmungedata = not data or isinstance(data, basestring)
         if postargsok and canmungedata:
-            strargs = urllib.urlencode(sorted(args.items()))
+            strargs = urlreq.urlencode(sorted(args.items()))
             if strargs:
                 if not data:
                     data = strargs
@@ -119,7 +120,7 @@
                     headersize = int(httpheader.split(',', 1)[0])
             if headersize > 0:
                 # The headers can typically carry more data than the URL.
-                encargs = urllib.urlencode(sorted(args.items()))
+                encargs = urlreq.urlencode(sorted(args.items()))
                 headerfmt = 'X-HgArg-%s'
                 contentlen = headersize - len(headerfmt % '000' + ': \r\n')
                 headernum = 0
@@ -132,7 +133,7 @@
                 headers['Vary'] = ','.join(varyheaders)
             else:
                 q += sorted(args.items())
-        qs = '?%s' % urllib.urlencode(q)
+        qs = '?%s' % urlreq.urlencode(q)
         cu = "%s%s" % (self._url, qs)
         size = 0
         if util.safehasattr(data, 'length'):
@@ -150,7 +151,7 @@
             req.add_unredirected_header('Content-Length', '%d' % size)
         try:
             resp = self.urlopener.open(req)
-        except urllib2.HTTPError as inst:
+        except urlerr.httperror as inst:
             if inst.code == 401:
                 raise error.Abort(_('authorization failed'))
             raise
--- a/mercurial/keepalive.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/keepalive.py	Wed Apr 06 23:22:12 2016 +0000
@@ -28,10 +28,10 @@
 >>> import urllib2
 >>> from keepalive import HTTPHandler
 >>> keepalive_handler = HTTPHandler()
->>> opener = urllib2.build_opener(keepalive_handler)
->>> urllib2.install_opener(opener)
+>>> opener = urlreq.buildopener(keepalive_handler)
+>>> urlreq.installopener(opener)
 >>>
->>> fo = urllib2.urlopen('http://www.python.org')
+>>> fo = urlreq.urlopen('http://www.python.org')
 
 If a connection to a given host is requested, and all of the existing
 connections are still in use, another connection will be opened.  If
@@ -114,7 +114,13 @@
 import socket
 import sys
 import thread
-import urllib2
+
+from . import (
+    util,
+)
+
+urlerr = util.urlerr
+urlreq = util.urlreq
 
 DEBUG = None
 
@@ -227,7 +233,7 @@
     def do_open(self, http_class, req):
         host = req.get_host()
         if not host:
-            raise urllib2.URLError('no host given')
+            raise urlerr.urlerror('no host given')
 
         try:
             h = self._cm.get_ready_conn(host)
@@ -254,7 +260,7 @@
                 self._start_transaction(h, req)
                 r = h.getresponse()
         except (socket.error, httplib.HTTPException) as err:
-            raise urllib2.URLError(err)
+            raise urlerr.urlerror(err)
 
         # if not a persistent connection, don't try to reuse it
         if r.will_close:
@@ -346,14 +352,14 @@
             else:
                 h.putrequest('GET', req.get_selector(), **skipheaders)
         except socket.error as err:
-            raise urllib2.URLError(err)
+            raise urlerr.urlerror(err)
         for k, v in headers.items():
             h.putheader(k, v)
         h.endheaders()
         if req.has_data():
             h.send(data)
 
-class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler):
+class HTTPHandler(KeepAliveHandler, urlreq.httphandler):
     pass
 
 class HTTPResponse(httplib.HTTPResponse):
@@ -593,14 +599,14 @@
     global HANDLE_ERRORS
     orig = HANDLE_ERRORS
     keepalive_handler = HTTPHandler()
-    opener = urllib2.build_opener(keepalive_handler)
-    urllib2.install_opener(opener)
+    opener = urlreq.buildopener(keepalive_handler)
+    urlreq.installopener(opener)
     pos = {0: 'off', 1: 'on'}
     for i in (0, 1):
         print("  fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i))
         HANDLE_ERRORS = i
         try:
-            fo = urllib2.urlopen(url)
+            fo = urlreq.urlopen(url)
             fo.read()
             fo.close()
             try:
@@ -623,25 +629,25 @@
     format = '%25s: %s'
 
     # first fetch the file with the normal http handler
-    opener = urllib2.build_opener()
-    urllib2.install_opener(opener)
-    fo = urllib2.urlopen(url)
+    opener = urlreq.buildopener()
+    urlreq.installopener(opener)
+    fo = urlreq.urlopen(url)
     foo = fo.read()
     fo.close()
     m = md5(foo)
     print(format % ('normal urllib', m.hexdigest()))
 
     # now install the keepalive handler and try again
-    opener = urllib2.build_opener(HTTPHandler())
-    urllib2.install_opener(opener)
+    opener = urlreq.buildopener(HTTPHandler())
+    urlreq.installopener(opener)
 
-    fo = urllib2.urlopen(url)
+    fo = urlreq.urlopen(url)
     foo = fo.read()
     fo.close()
     m = md5(foo)
     print(format % ('keepalive read', m.hexdigest()))
 
-    fo = urllib2.urlopen(url)
+    fo = urlreq.urlopen(url)
     foo = ''
     while True:
         f = fo.readline()
@@ -657,15 +663,15 @@
 
     sys.stdout.write('  first using the normal urllib handlers')
     # first use normal opener
-    opener = urllib2.build_opener()
-    urllib2.install_opener(opener)
+    opener = urlreq.buildopener()
+    urlreq.installopener(opener)
     t1 = fetch(N, url)
     print('  TIME: %.3f s' % t1)
 
     sys.stdout.write('  now using the keepalive handler       ')
     # now install the keepalive handler and try again
-    opener = urllib2.build_opener(HTTPHandler())
-    urllib2.install_opener(opener)
+    opener = urlreq.buildopener(HTTPHandler())
+    urlreq.installopener(opener)
     t2 = fetch(N, url)
     print('  TIME: %.3f s' % t2)
     print('  improvement factor: %.2f' % (t1 / t2))
@@ -677,7 +683,7 @@
     for i in range(N):
         if delay and i > 0:
             time.sleep(delay)
-        fo = urllib2.urlopen(url)
+        fo = urlreq.urlopen(url)
         foo = fo.read()
         fo.close()
         lens.append(len(foo))
@@ -700,7 +706,7 @@
         info = warning = error = debug
     DEBUG = FakeLogger()
     print("  fetching the file to establish a connection")
-    fo = urllib2.urlopen(url)
+    fo = urlreq.urlopen(url)
     data1 = fo.read()
     fo.close()
 
@@ -714,7 +720,7 @@
     sys.stderr.write('\r')
 
     print("  fetching the file a second time")
-    fo = urllib2.urlopen(url)
+    fo = urlreq.urlopen(url)
     data2 = fo.read()
     fo.close()
 
--- a/mercurial/localrepo.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/localrepo.py	Wed Apr 06 23:22:12 2016 +0000
@@ -12,7 +12,6 @@
 import os
 import random
 import time
-import urllib
 import weakref
 
 from .i18n import _
@@ -59,6 +58,8 @@
 
 release = lockmod.release
 propertycache = util.propertycache
+urlerr = util.urlerr
+urlreq = util.urlreq
 filecache = scmutil.filecache
 
 class repofilecache(filecache):
@@ -366,7 +367,7 @@
         if self.ui.configbool('experimental', 'bundle2-advertise', True):
             caps = set(caps)
             capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
-            caps.add('bundle2=' + urllib.quote(capsblob))
+            caps.add('bundle2=' + urlreq.quote(capsblob))
         return caps
 
     def _applyopenerreqs(self):
--- a/mercurial/statichttprepo.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/statichttprepo.py	Wed Apr 06 23:22:12 2016 +0000
@@ -11,8 +11,6 @@
 
 import errno
 import os
-import urllib
-import urllib2
 
 from .i18n import _
 from . import (
@@ -28,6 +26,9 @@
     util,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 class httprangereader(object):
     def __init__(self, url, opener):
         # we assume opener has HTTPRangeHandler
@@ -45,7 +46,7 @@
     def seek(self, pos):
         self.pos = pos
     def read(self, bytes=None):
-        req = urllib2.Request(self.url)
+        req = urlreq.request(self.url)
         end = ''
         if bytes:
             end = self.pos + bytes - 1
@@ -56,10 +57,10 @@
             f = self.opener.open(req)
             data = f.read()
             code = f.code
-        except urllib2.HTTPError as inst:
+        except urlerr.httperror as inst:
             num = inst.code == 404 and errno.ENOENT or None
             raise IOError(num, inst)
-        except urllib2.URLError as inst:
+        except urlerr.urlerror as inst:
             raise IOError(None, inst.reason[1])
 
         if code == 200:
@@ -92,7 +93,7 @@
         def __call__(self, path, mode='r', *args, **kw):
             if mode not in ('r', 'rb'):
                 raise IOError('Permission denied')
-            f = "/".join((self.base, urllib.quote(path)))
+            f = "/".join((self.base, urlreq.quote(path)))
             return httprangereader(f, urlopener)
 
         def join(self, path):
--- a/mercurial/templatefilters.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/templatefilters.py	Wed Apr 06 23:22:12 2016 +0000
@@ -11,7 +11,6 @@
 import os
 import re
 import time
-import urllib
 
 from . import (
     encoding,
@@ -22,6 +21,9 @@
     util,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 # filters are callables like:
 #   fn(obj)
 # with:
@@ -299,7 +301,7 @@
     Forward slashes are escaped twice to prevent web servers from prematurely
     unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
     """
-    return urllib.quote(text, safe='/@').replace('/', '%252F')
+    return urlreq.quote(text, safe='/@').replace('/', '%252F')
 
 @templatefilter('rfc3339date')
 def rfc3339date(text):
@@ -384,7 +386,7 @@
     """Any text. Escapes all "special" characters. For example,
     "foo bar" becomes "foo%20bar".
     """
-    return urllib.quote(text)
+    return urlreq.quote(text)
 
 @templatefilter('user')
 def userfilter(text):
--- a/mercurial/url.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/url.py	Wed Apr 06 23:22:12 2016 +0000
@@ -13,8 +13,6 @@
 import httplib
 import os
 import socket
-import urllib
-import urllib2
 
 from .i18n import _
 from . import (
@@ -26,13 +24,16 @@
 )
 stringio = util.stringio
 
-class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
+urlerr = util.urlerr
+urlreq = util.urlreq
+
+class passwordmgr(urlreq.httppasswordmgrwithdefaultrealm):
     def __init__(self, ui):
-        urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
+        urlreq.httppasswordmgrwithdefaultrealm.__init__(self)
         self.ui = ui
 
     def find_user_password(self, realm, authuri):
-        authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
+        authinfo = urlreq.httppasswordmgrwithdefaultrealm.find_user_password(
             self, realm, authuri)
         user, passwd = authinfo
         if user and passwd:
@@ -72,10 +73,10 @@
         self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
 
     def find_stored_password(self, authuri):
-        return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
+        return urlreq.httppasswordmgrwithdefaultrealm.find_user_password(
             self, None, authuri)
 
-class proxyhandler(urllib2.ProxyHandler):
+class proxyhandler(urlreq.proxyhandler):
     def __init__(self, ui):
         proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
         # XXX proxyauthinfo = None
@@ -121,7 +122,7 @@
                 except OSError:
                     pass
 
-        urllib2.ProxyHandler.__init__(self, proxies)
+        urlreq.proxyhandler.__init__(self, proxies)
         self.ui = ui
 
     def proxy_open(self, req, proxy, type_):
@@ -134,7 +135,7 @@
             if e.startswith('.') and host.endswith(e[1:]):
                 return None
 
-        return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
+        return urlreq.proxyhandler.proxy_open(self, req, proxy, type_)
 
 def _gen_sendfile(orgsend):
     def _sendfile(self, data):
@@ -148,7 +149,7 @@
             orgsend(self, data)
     return _sendfile
 
-has_https = util.safehasattr(urllib2, 'HTTPSHandler')
+has_https = util.safehasattr(urlreq, 'httpshandler')
 if has_https:
     try:
         _create_connection = socket.create_connection
@@ -357,10 +358,10 @@
                 **sslutil.sslkwargs(self.ui, host))
             sslutil.validator(self.ui, host)(self.sock)
 
-    class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
+    class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler):
         def __init__(self, ui):
             keepalive.KeepAliveHandler.__init__(self)
-            urllib2.HTTPSHandler.__init__(self)
+            urlreq.httpshandler.__init__(self)
             self.ui = ui
             self.pwmgr = passwordmgr(self.ui)
 
@@ -403,9 +404,9 @@
             conn.ui = self.ui
             return conn
 
-class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
+class httpdigestauthhandler(urlreq.httpdigestauthhandler):
     def __init__(self, *args, **kwargs):
-        urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
+        urlreq.httpdigestauthhandler.__init__(self, *args, **kwargs)
         self.retried_req = None
 
     def reset_retry_count(self):
@@ -419,13 +420,13 @@
         if req is not self.retried_req:
             self.retried_req = req
             self.retried = 0
-        return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
+        return urlreq.httpdigestauthhandler.http_error_auth_reqed(
                     self, auth_header, host, req, headers)
 
-class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
+class httpbasicauthhandler(urlreq.httpbasicauthhandler):
     def __init__(self, *args, **kwargs):
         self.auth = None
-        urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
+        urlreq.httpbasicauthhandler.__init__(self, *args, **kwargs)
         self.retried_req = None
 
     def http_request(self, request):
@@ -451,7 +452,7 @@
         if req is not self.retried_req:
             self.retried_req = req
             self.retried = 0
-        return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
+        return urlreq.httpbasicauthhandler.http_error_auth_reqed(
                         self, auth_header, host, req, headers)
 
     def retry_http_basic_auth(self, host, req, realm):
@@ -494,7 +495,7 @@
     handlers.extend((httpbasicauthhandler(passmgr),
                      httpdigestauthhandler(passmgr)))
     handlers.extend([h(ui, passmgr) for h in handlerfuncs])
-    opener = urllib2.build_opener(*handlers)
+    opener = urlreq.buildopener(*handlers)
 
     # 1.0 here is the _protocol_ version
     opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
@@ -508,6 +509,6 @@
         url_, authinfo = u.authinfo()
     else:
         path = util.normpath(os.path.abspath(url_))
-        url_ = 'file://' + urllib.pathname2url(path)
+        url_ = 'file://' + urlreq.pathname2url(path)
         authinfo = None
     return opener(ui, authinfo).open(url_, data)
--- a/mercurial/util.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/util.py	Wed Apr 06 23:22:12 2016 +0000
@@ -34,7 +34,6 @@
 import textwrap
 import time
 import traceback
-import urllib
 import zlib
 
 from . import (
@@ -50,11 +49,15 @@
     'empty',
     'queue',
     'urlerr',
-    'urlreq',
+    # we do import urlreq, but we do it outside the loop
+    #'urlreq',
     'stringio',
 ):
     globals()[attr] = getattr(pycompat, attr)
 
+# This line is to make pyflakes happy:
+urlreq = pycompat.urlreq
+
 if os.name == 'nt':
     from . import windows as platform
 else:
@@ -2388,30 +2391,30 @@
             if hasdriveletter(self.path):
                 s += '/'
         if self.user:
-            s += urllib.quote(self.user, safe=self._safechars)
+            s += urlreq.quote(self.user, safe=self._safechars)
         if self.passwd:
-            s += ':' + urllib.quote(self.passwd, safe=self._safechars)
+            s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
         if self.user or self.passwd:
             s += '@'
         if self.host:
             if not (self.host.startswith('[') and self.host.endswith(']')):
-                s += urllib.quote(self.host)
+                s += urlreq.quote(self.host)
             else:
                 s += self.host
         if self.port:
-            s += ':' + urllib.quote(self.port)
+            s += ':' + urlreq.quote(self.port)
         if self.host:
             s += '/'
         if self.path:
             # TODO: similar to the query string, we should not unescape the
             # path when we store it, the path might contain '%2f' = '/',
             # which we should *not* escape.
-            s += urllib.quote(self.path, safe=self._safepchars)
+            s += urlreq.quote(self.path, safe=self._safepchars)
         if self.query:
             # we store the query in escaped form.
             s += '?' + self.query
         if self.fragment is not None:
-            s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
+            s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
         return s
 
     def authinfo(self):
--- a/mercurial/wireproto.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/mercurial/wireproto.py	Wed Apr 06 23:22:12 2016 +0000
@@ -11,7 +11,6 @@
 import os
 import sys
 import tempfile
-import urllib
 
 from .i18n import _
 from .node import (
@@ -31,6 +30,9 @@
     util,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 bundle2required = _(
     'incompatible Mercurial client; bundle2 required\n'
     '(see https://www.mercurial-scm.org/wiki/IncompatibleClient)\n')
@@ -287,7 +289,7 @@
             branchmap = {}
             for branchpart in d.splitlines():
                 branchname, branchheads = branchpart.split(' ', 1)
-                branchname = encoding.tolocal(urllib.unquote(branchname))
+                branchname = encoding.tolocal(urlreq.unquote(branchname))
                 branchheads = decodelist(branchheads)
                 branchmap[branchname] = branchheads
             yield branchmap
@@ -632,7 +634,7 @@
     branchmap = repo.branchmap()
     heads = []
     for branch, nodes in branchmap.iteritems():
-        branchname = urllib.quote(encoding.fromlocal(branch))
+        branchname = urlreq.quote(encoding.fromlocal(branch))
         branchnodes = encodelist(nodes)
         heads.append('%s %s' % (branchname, branchnodes))
     return '\n'.join(heads)
@@ -684,7 +686,7 @@
             caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
     if repo.ui.configbool('experimental', 'bundle2-advertise', True):
         capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
-        caps.append('bundle2=' + urllib.quote(capsblob))
+        caps.append('bundle2=' + urlreq.quote(capsblob))
     caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
     caps.append(
         'httpheader=%d' % repo.ui.configint('server', 'maxhttpheaderlen', 1024))
--- a/tests/test-check-py3-compat.t	Thu Apr 07 00:05:48 2016 +0000
+++ b/tests/test-check-py3-compat.t	Wed Apr 06 23:22:12 2016 +0000
@@ -113,8 +113,8 @@
   hgext/largefiles/lfutil.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   hgext/largefiles/localstore.py: error importing module: <ImportError> No module named 'lfutil' (line *) (glob)
   hgext/largefiles/overrides.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
-  hgext/largefiles/proto.py: error importing module: <ImportError> No module named 'urllib2' (line *) (glob)
-  hgext/largefiles/remotestore.py: error importing module: <ImportError> No module named 'urllib2' (line *) (glob)
+  hgext/largefiles/proto.py: error importing: <ImportError> No module named 'httplib' (error at httppeer.py:*) (glob)
+  hgext/largefiles/remotestore.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob)
   hgext/largefiles/reposetup.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   hgext/largefiles/uisetup.py: error importing module: <SyntaxError> invalid syntax (archival.py, line *) (line *) (glob)
   hgext/largefiles/wirestore.py: error importing module: <ImportError> No module named 'lfutil' (line *) (glob)
@@ -135,7 +135,6 @@
   mercurial/branchmap.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/bundle*.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
   mercurial/bundlerepo.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
-  mercurial/byterange.py: error importing module: <ImportError> No module named 'urllib2' (line *) (glob)
   mercurial/changegroup.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/changelog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/cmdutil.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
@@ -147,7 +146,7 @@
   mercurial/dirstate.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/discovery.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/dispatch.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
-  mercurial/exchange.py: error importing module: <ImportError> No module named 'urllib2' (line *) (glob)
+  mercurial/exchange.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
   mercurial/extensions.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/filelog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/filemerge.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
@@ -167,7 +166,7 @@
   mercurial/hgweb/wsgicgi.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
   mercurial/hook.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/httpclient/_readers.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
-  mercurial/httpconnection.py: error importing module: <ImportError> No module named 'urllib2' (line *) (glob)
+  mercurial/httpconnection.py: error importing: <ImportError> No module named 'httplib' (error at __init__.py:*) (glob)
   mercurial/httppeer.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
   mercurial/keepalive.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
   mercurial/localrepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
@@ -186,7 +185,7 @@
   mercurial/simplemerge.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/sshpeer.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob)
   mercurial/sshserver.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
-  mercurial/statichttprepo.py: error importing module: <ImportError> No module named 'urllib2' (line *) (glob)
+  mercurial/statichttprepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/store.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/streamclone.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
   mercurial/subrepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob)
--- a/tests/test-hgweb-auth.py	Thu Apr 07 00:05:48 2016 +0000
+++ b/tests/test-hgweb-auth.py	Wed Apr 06 23:22:12 2016 +0000
@@ -1,7 +1,6 @@
 from __future__ import absolute_import, print_function
 
 from mercurial import demandimport; demandimport.enable()
-import urllib2
 from mercurial import (
     error,
     ui as uimod,
@@ -9,6 +8,9 @@
     util,
 )
 
+urlerr = util.urlerr
+urlreq = util.urlreq
+
 class myui(uimod.ui):
     def interactive(self):
         return False
@@ -104,7 +106,7 @@
 
 def testauthinfo(fullurl, authurl):
     print('URIs:', fullurl, authurl)
-    pm = urllib2.HTTPPasswordMgrWithDefaultRealm()
+    pm = urlreq.httppasswordmgrwithdefaultrealm()
     pm.add_password(*util.url(fullurl).authinfo()[1])
     print(pm.find_user_password('test', authurl))