# HG changeset patch # User timeless # Date 2016-04-06 23:22:12 # Node ID 032c4c2f802a079ed47a4f95faa73b5df9a165fa # Parent 800ec7c048b06d3a056ed9fbdb6028f6993090bf pycompat: switch to util.urlreq/util.urlerr for py3 compat diff --git a/hgext/acl.py b/hgext/acl.py --- a/hgext/acl.py +++ b/hgext/acl.py @@ -194,7 +194,6 @@ 3) Deny access to a file to anyone but u from __future__ import absolute_import import getpass -import urllib from mercurial.i18n import _ from mercurial import ( @@ -203,6 +202,8 @@ from mercurial import ( 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 @@ def hook(ui, repo, hooktype, node=None, 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() diff --git a/hgext/convert/subversion.py b/hgext/convert/subversion.py --- a/hgext/convert/subversion.py +++ b/hgext/convert/subversion.py @@ -8,8 +8,6 @@ import os import re import sys import tempfile -import urllib -import urllib2 import xml.dom.minidom from mercurial import ( @@ -25,6 +23,8 @@ from . import common stringio = util.stringio propertycache = util.propertycache +urlerr = util.urlerr +urlreq = util.urlreq commandline = common.commandline commit = common.commit @@ -94,7 +94,7 @@ def quote(s): # 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 @@ def filecheck(ui, path, proto): # 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 @@ def httpcheck(ui, path, proto): 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 '' in data @@ -260,7 +260,7 @@ def issvnurl(ui, url): 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 @@ class svn_source(converter_source): 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 = {} diff --git a/hgext/largefiles/proto.py b/hgext/largefiles/proto.py --- a/hgext/largefiles/proto.py +++ b/hgext/largefiles/proto.py @@ -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 @@ def wirereposetup(ui, repo): 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; diff --git a/hgext/largefiles/remotestore.py b/hgext/largefiles/remotestore.py --- a/hgext/largefiles/remotestore.py +++ b/hgext/largefiles/remotestore.py @@ -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 @@ class remotestore(basestore.basestore): 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. diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -152,7 +152,6 @@ import re import string import struct import sys -import urllib from .i18n import _ from . import ( @@ -165,6 +164,9 @@ from . import ( util, ) +urlerr = util.urlerr +urlreq = util.urlreq + _pack = struct.pack _unpack = struct.unpack @@ -457,8 +459,8 @@ def decodecaps(blob): 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 @@ def encodecaps(caps): 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 @@ class bundle20(object): """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 @@ class unbundle20(unpackermixin): 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 @@ def bundle2caps(remote): 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): diff --git a/mercurial/byterange.py b/mercurial/byterange.py --- a/mercurial/byterange.py +++ b/mercurial/byterange.py @@ -26,22 +26,27 @@ import os 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 @@ class HTTPRangeHandler(urllib2.BaseHandl 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 @@ class RangeableFileObject(object): 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 @@ class FileRangeHandler(urllib2.FileHandl 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 @@ class FileRangeHandler(urllib2.FileHandl 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 @@ class FileRangeHandler(urllib2.FileHandl # 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 @@ class FTPRangeHandler(urllib2.FTPHandler 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 @@ class FTPRangeHandler(urllib2.FTPHandler 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 diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -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 @@ from . import ( util, ) +urlerr = util.urlerr +urlreq = util.urlreq + # Maps bundle compression human names to internal representation. _bundlespeccompressions = {'none': None, 'bzip2': 'BZ', @@ -97,8 +98,8 @@ def parsebundlespec(repo, spec, strict=T '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 @@ def getbundlespec(ui, fh): 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 @@ def caps20to10(repo): """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 @@ def getbundle(repo, source, heads=None, 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 @@ def parseclonebundlesmanifest(repo, s): 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 @@ def trypullbundlefromurl(ui, repo, url): 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 diff --git a/mercurial/hgweb/protocol.py b/mercurial/hgweb/protocol.py --- a/mercurial/hgweb/protocol.py +++ b/mercurial/hgweb/protocol.py @@ -8,7 +8,6 @@ from __future__ import absolute_import import cgi -import urllib import zlib from .common import ( @@ -21,6 +20,9 @@ from .. import ( ) stringio = util.stringio +urlerr = util.urlerr +urlreq = util.urlreq + HGTYPE = 'application/mercurial-0.1' HGERRTYPE = 'application/hg-error' @@ -82,8 +84,8 @@ class webproto(wireproto.abstractserverp 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 diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py +++ b/mercurial/hgweb/server.py @@ -15,7 +15,6 @@ import os import socket import sys import traceback -import urllib from ..i18n import _ @@ -24,6 +23,9 @@ from .. import ( util, ) +urlerr = util.urlerr +urlreq = util.urlreq + from . import ( common, ) @@ -38,7 +40,7 @@ def _splitURI(uri): 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): diff --git a/mercurial/httpconnection.py b/mercurial/httpconnection.py --- a/mercurial/httpconnection.py +++ b/mercurial/httpconnection.py @@ -13,8 +13,6 @@ from __future__ import absolute_import import logging import os import socket -import urllib -import urllib2 from .i18n import _ from . import ( @@ -23,6 +21,9 @@ from . import ( 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 @@ LOGFMT = '%(levelname)s:%(name)s:%(linen # 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 @@ class http2handler(urllib2.HTTPHandler, 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 @@ class http2handler(urllib2.HTTPHandler, 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 diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py --- a/mercurial/httppeer.py +++ b/mercurial/httppeer.py @@ -13,8 +13,6 @@ import httplib import os import socket import tempfile -import urllib -import urllib2 import zlib from .i18n import _ @@ -29,6 +27,9 @@ from . import ( wireproto, ) +urlerr = util.urlerr +urlreq = util.urlreq + def zgenerator(f): zd = zlib.decompressobj() try: @@ -59,7 +60,7 @@ class httppeer(wireproto.wirepeer): 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 @@ class httppeer(wireproto.wirepeer): # 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 @@ class httppeer(wireproto.wirepeer): 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 @@ class httppeer(wireproto.wirepeer): 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 @@ class httppeer(wireproto.wirepeer): 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 diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py --- a/mercurial/keepalive.py +++ b/mercurial/keepalive.py @@ -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 httplib import socket import sys import thread -import urllib2 + +from . import ( + util, +) + +urlerr = util.urlerr +urlreq = util.urlreq DEBUG = None @@ -227,7 +233,7 @@ class KeepAliveHandler(object): 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 @@ class KeepAliveHandler(object): 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 @@ class KeepAliveHandler(object): 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 @@ def error_handler(url): 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 @@ def continuity(url): 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 @@ def comp(N, url): 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 @@ def fetch(N, url, delay=0): 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 @@ def test_timeout(url): 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 @@ def test_timeout(url): sys.stderr.write('\r') print(" fetching the file a second time") - fo = urllib2.urlopen(url) + fo = urlreq.urlopen(url) data2 = fo.read() fo.close() diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -12,7 +12,6 @@ import inspect import os import random import time -import urllib import weakref from .i18n import _ @@ -59,6 +58,8 @@ from . import ( release = lockmod.release propertycache = util.propertycache +urlerr = util.urlerr +urlreq = util.urlreq filecache = scmutil.filecache class repofilecache(filecache): @@ -366,7 +367,7 @@ class localrepository(object): 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): diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py +++ b/mercurial/statichttprepo.py @@ -11,8 +11,6 @@ from __future__ import absolute_import import errno import os -import urllib -import urllib2 from .i18n import _ from . import ( @@ -28,6 +26,9 @@ from . import ( util, ) +urlerr = util.urlerr +urlreq = util.urlreq + class httprangereader(object): def __init__(self, url, opener): # we assume opener has HTTPRangeHandler @@ -45,7 +46,7 @@ class httprangereader(object): 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 @@ class httprangereader(object): 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 build_opener(ui, authinfo): 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): diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py --- a/mercurial/templatefilters.py +++ b/mercurial/templatefilters.py @@ -11,7 +11,6 @@ import cgi import os import re import time -import urllib from . import ( encoding, @@ -22,6 +21,9 @@ from . import ( util, ) +urlerr = util.urlerr +urlreq = util.urlreq + # filters are callables like: # fn(obj) # with: @@ -299,7 +301,7 @@ def revescape(text): 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 @@ def urlescape(text): """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): diff --git a/mercurial/url.py b/mercurial/url.py --- a/mercurial/url.py +++ b/mercurial/url.py @@ -13,8 +13,6 @@ import base64 import httplib import os import socket -import urllib -import urllib2 from .i18n import _ from . import ( @@ -26,13 +24,16 @@ from . import ( ) 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 @@ class passwordmgr(urllib2.HTTPPasswordMg 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 @@ class proxyhandler(urllib2.ProxyHandler) 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 @@ class proxyhandler(urllib2.ProxyHandler) 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 @@ def _gen_sendfile(orgsend): 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 @@ if has_https: **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 @@ if has_https: 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 @@ class httpdigestauthhandler(urllib2.HTTP 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 @@ class httpbasicauthhandler(urllib2.HTTPB 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 @@ def opener(ui, authinfo=None): 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 @@ def open(ui, url_, data=None): 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) diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -34,7 +34,6 @@ import tempfile import textwrap import time import traceback -import urllib import zlib from . import ( @@ -50,11 +49,15 @@ for attr in ( '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 @@ class url(object): 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): diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py --- a/mercurial/wireproto.py +++ b/mercurial/wireproto.py @@ -11,7 +11,6 @@ import itertools import os import sys import tempfile -import urllib from .i18n import _ from .node import ( @@ -31,6 +30,9 @@ from . import ( 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 @@ class wirepeer(peer.peerrepository): 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 @@ def branchmap(repo, proto): 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 @@ def _capabilities(repo, proto): 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)) diff --git a/tests/test-check-py3-compat.t b/tests/test-check-py3-compat.t --- a/tests/test-check-py3-compat.t +++ b/tests/test-check-py3-compat.t @@ -113,8 +113,8 @@ hgext/largefiles/lfutil.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/largefiles/localstore.py: error importing module: No module named 'lfutil' (line *) (glob) hgext/largefiles/overrides.py: error importing: invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob) - hgext/largefiles/proto.py: error importing module: No module named 'urllib2' (line *) (glob) - hgext/largefiles/remotestore.py: error importing module: No module named 'urllib2' (line *) (glob) + hgext/largefiles/proto.py: error importing: No module named 'httplib' (error at httppeer.py:*) (glob) + hgext/largefiles/remotestore.py: error importing: invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob) hgext/largefiles/reposetup.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/largefiles/uisetup.py: error importing module: invalid syntax (archival.py, line *) (line *) (glob) hgext/largefiles/wirestore.py: error importing module: No module named 'lfutil' (line *) (glob) @@ -135,7 +135,6 @@ mercurial/branchmap.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/bundle*.py: invalid syntax: invalid syntax (, line *) (glob) mercurial/bundlerepo.py: error importing module: invalid syntax (bundle*.py, line *) (line *) (glob) - mercurial/byterange.py: error importing module: No module named 'urllib2' (line *) (glob) mercurial/changegroup.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/changelog.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/cmdutil.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) @@ -147,7 +146,7 @@ mercurial/dirstate.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/discovery.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/dispatch.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - mercurial/exchange.py: error importing module: No module named 'urllib2' (line *) (glob) + mercurial/exchange.py: error importing module: invalid syntax (bundle*.py, line *) (line *) (glob) mercurial/extensions.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/filelog.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/filemerge.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) @@ -167,7 +166,7 @@ mercurial/hgweb/wsgicgi.py: error importing module: Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) mercurial/hook.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/httpclient/_readers.py: error importing module: No module named 'httplib' (line *) (glob) - mercurial/httpconnection.py: error importing module: No module named 'urllib2' (line *) (glob) + mercurial/httpconnection.py: error importing: No module named 'httplib' (error at __init__.py:*) (glob) mercurial/httppeer.py: error importing module: No module named 'httplib' (line *) (glob) mercurial/keepalive.py: error importing module: No module named 'httplib' (line *) (glob) mercurial/localrepo.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) @@ -186,7 +185,7 @@ mercurial/simplemerge.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/sshpeer.py: error importing: invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob) mercurial/sshserver.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - mercurial/statichttprepo.py: error importing module: No module named 'urllib2' (line *) (glob) + mercurial/statichttprepo.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/store.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/streamclone.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/subrepo.py: error importing: 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) diff --git a/tests/test-hgweb-auth.py b/tests/test-hgweb-auth.py --- a/tests/test-hgweb-auth.py +++ b/tests/test-hgweb-auth.py @@ -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 @@ from mercurial import ( util, ) +urlerr = util.urlerr +urlreq = util.urlreq + class myui(uimod.ui): def interactive(self): return False @@ -104,7 +106,7 @@ test({'x.prefix': 'http://example.org/fo 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))