sapling/eden/scm/edenscm/mercurial/urllibcompat.py
Adam Simpkins 1a359987cd py3: update urllibcompat.quote() to operate on strings again
Summary:
D19652082 changed `urllibcompat.urlreq.quote()` to always return bytes.
This broke most of the code outside of the bundle2 module that was expecting
it to operate on strings.

This changes `quote()` to operate on strings again, since this is what the
Python 3 standard library does.  This updates `bundle2` to handle encoding and
decoding properly when parsing strings that go through `quote()` and
`unquote()`.

This also changes bundle2 to stop going through the `urllibcompat.urlreq`
wrapper class, since that wrapper class prevents type checking from being
performed on its members.

Reviewed By: quark-zju

Differential Revision: D19657765

fbshipit-source-id: 0611838213ec22221e0b8367e1f0d8a6dcfceb15
2020-01-30 23:22:02 -08:00

212 lines
5.2 KiB
Python

# Portions Copyright (c) Facebook, Inc. and its affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
# urllibcompat.py - adapters to ease using urllib2 on Py2 and urllib on Py3
#
# Copyright 2017 Google, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
import sys
from . import pycompat
_sysstr = pycompat.sysstr
class _pycompatstub(object):
def __init__(self):
self._aliases = {}
def _registeraliases(self, origin, items):
"""Add items that will be populated at the first access"""
items = list(map(_sysstr, items))
self._aliases.update(
(item.replace(_sysstr("_"), _sysstr("")).lower(), (origin, item))
for item in items
)
def _registeralias(self, origin, attr, name):
"""Alias ``origin``.``attr`` as ``name``"""
self._aliases[_sysstr(name)] = (origin, _sysstr(attr))
def __getattr__(self, name):
try:
origin, item = self._aliases[name]
except KeyError:
raise AttributeError(name)
self.__dict__[name] = obj = getattr(origin, item)
return obj
httpserver = _pycompatstub()
urlreq = _pycompatstub()
urlerr = _pycompatstub()
if sys.version_info[0] >= 3:
import urllib.parse
urlreq._registeraliases(
urllib.parse,
(
"splitattr",
"splitpasswd",
"splitport",
"splituser",
"urlparse",
"urlunparse",
),
)
urlreq._registeralias(urllib.parse, "unquote", "unquote")
import urllib.request
urlreq._registeraliases(
urllib.request,
(
"AbstractHTTPHandler",
"BaseHandler",
"build_opener",
"FileHandler",
"FTPHandler",
"ftpwrapper",
"HTTPHandler",
"HTTPSHandler",
"install_opener",
"pathname2url",
"HTTPBasicAuthHandler",
"HTTPDigestAuthHandler",
"HTTPPasswordMgrWithDefaultRealm",
"ProxyHandler",
"Request",
"url2pathname",
"urlopen",
),
)
import urllib.response
urlreq._registeraliases(urllib.response, ("addclosehook", "addinfourl"))
import urllib.error
urlerr._registeraliases(urllib.error, ("HTTPError", "URLError"))
import http.server
httpserver._registeraliases(
http.server,
(
"HTTPServer",
"BaseHTTPRequestHandler",
"SimpleHTTPRequestHandler",
"CGIHTTPRequestHandler",
),
)
# quote() and unquote() both operate on and return strings (not bytes)
quote = urllib.parse.quote
unquote = urllib.parse.unquote
# urllib.parse.urlencode() returns str. We use this function to make
# sure we return bytes.
def urlencode(query, doseq=False):
s = pycompat.encodeutf8(urllib.parse.urlencode(query, doseq=doseq))
return s
# pyre-fixme[16]: `_pycompatstub` has no attribute `quote`.
urlreq.quote = quote
# pyre-fixme[16]: `_pycompatstub` has no attribute `urlencode`.
urlreq.urlencode = urlencode
def getfullurl(req):
return req.full_url
def gethost(req):
return req.host
def getselector(req):
return req.selector
def getdata(req):
return req.data
def hasdata(req):
return req.data is not None
else:
import BaseHTTPServer
import CGIHTTPServer
import SimpleHTTPServer
import urllib2
import urllib
import urlparse
urlreq._registeraliases(
urllib,
(
"addclosehook",
"addinfourl",
"ftpwrapper",
"pathname2url",
"quote",
"splitattr",
"splitpasswd",
"splitport",
"splituser",
"unquote",
"url2pathname",
"urlencode",
),
)
urlreq._registeraliases(
urllib2,
(
"AbstractHTTPHandler",
"BaseHandler",
"build_opener",
"FileHandler",
"FTPHandler",
"HTTPBasicAuthHandler",
"HTTPDigestAuthHandler",
"HTTPHandler",
"HTTPPasswordMgrWithDefaultRealm",
"HTTPSHandler",
"install_opener",
"ProxyHandler",
"Request",
"urlopen",
),
)
urlreq._registeraliases(urlparse, ("urlparse", "urlunparse"))
urlerr._registeraliases(urllib2, ("HTTPError", "URLError"))
httpserver._registeraliases(
BaseHTTPServer, ("HTTPServer", "BaseHTTPRequestHandler")
)
httpserver._registeraliases(SimpleHTTPServer, ("SimpleHTTPRequestHandler",))
httpserver._registeraliases(CGIHTTPServer, ("CGIHTTPRequestHandler",))
def gethost(req):
return req.get_host()
def getselector(req):
return req.get_selector()
def getfullurl(req):
return req.get_full_url()
def getdata(req):
return req.get_data()
def hasdata(req):
return req.has_data()
quote = urllib.quote
unquote = urllib.unquote