pycompat.py
275 lines
| 7.9 KiB
| text/x-python
|
PythonLexer
/ mercurial / pycompat.py
|
r28818 | # pycompat.py - portability shim for python 3 | ||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
"""Mercurial portability shim for python 3. | ||||
This contains aliases to hide python version-specific details from the core. | ||||
""" | ||||
from __future__ import absolute_import | ||||
|
r30578 | import getopt | ||
|
r30302 | import os | ||
|
r29584 | import sys | ||
|
r30030 | ispy3 = (sys.version_info[0] >= 3) | ||
if not ispy3: | ||||
|
r29324 | import cPickle as pickle | ||
|
r29584 | import cStringIO as io | ||
|
r29455 | import httplib | ||
|
r29584 | import Queue as _queue | ||
|
r29433 | import SocketServer as socketserver | ||
|
r29584 | import urlparse | ||
|
r30327 | urlunquote = urlparse.unquote | ||
|
r29432 | import xmlrpclib | ||
|
r29584 | else: | ||
import http.client as httplib | ||||
import io | ||||
import pickle | ||||
import queue as _queue | ||||
import socketserver | ||||
import urllib.parse as urlparse | ||||
|
r30327 | urlunquote = urlparse.unquote_to_bytes | ||
|
r29432 | import xmlrpc.client as xmlrpclib | ||
|
r29431 | |||
|
r30030 | if ispy3: | ||
|
r29797 | import builtins | ||
|
r29799 | import functools | ||
|
r30119 | fsencode = os.fsencode | ||
|
r30300 | fsdecode = os.fsdecode | ||
|
r30302 | # A bytes version of os.name. | ||
osname = os.name.encode('ascii') | ||||
|
r30303 | ospathsep = os.pathsep.encode('ascii') | ||
ossep = os.sep.encode('ascii') | ||||
|
r30623 | osaltsep = os.altsep | ||
if osaltsep: | ||||
osaltsep = osaltsep.encode('ascii') | ||||
|
r30500 | # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which | ||
# returns bytes. | ||||
getcwd = os.getcwdb | ||||
|
r30334 | |||
|
r30472 | # TODO: .buffer might not exist if std streams were replaced; we'll need | ||
# a silly wrapper to make a bytes stream backed by a unicode one. | ||||
stdin = sys.stdin.buffer | ||||
stdout = sys.stdout.buffer | ||||
stderr = sys.stderr.buffer | ||||
|
r30334 | # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix, | ||
# we can use os.fsencode() to get back bytes argv. | ||||
# | ||||
# https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55 | ||||
# | ||||
# TODO: On Windows, the native argv is wchar_t, so we'll need a different | ||||
# workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior. | ||||
|
r30330 | sysargv = list(map(os.fsencode, sys.argv)) | ||
|
r29797 | |||
|
r30032 | def sysstr(s): | ||
"""Return a keyword str to be passed to Python functions such as | ||||
getattr() and str.encode() | ||||
This never raises UnicodeDecodeError. Non-ascii characters are | ||||
considered invalid and mapped to arbitrary but unique code points | ||||
such that 'sysstr(a) != sysstr(b)' for all 'a != b'. | ||||
""" | ||||
if isinstance(s, builtins.str): | ||||
return s | ||||
return s.decode(u'latin-1') | ||||
|
r29799 | def _wrapattrfunc(f): | ||
@functools.wraps(f) | ||||
def w(object, name, *args): | ||||
|
r30032 | return f(object, sysstr(name), *args) | ||
|
r29799 | return w | ||
|
r29800 | # these wrappers are automagically imported by hgloader | ||
|
r29799 | delattr = _wrapattrfunc(builtins.delattr) | ||
getattr = _wrapattrfunc(builtins.getattr) | ||||
hasattr = _wrapattrfunc(builtins.hasattr) | ||||
setattr = _wrapattrfunc(builtins.setattr) | ||||
|
r29800 | xrange = builtins.range | ||
|
r29799 | |||
|
r30578 | # getopt.getopt() on Python 3 deals with unicodes internally so we cannot | ||
# pass bytes there. Passing unicodes will result in unicodes as return | ||||
# values which we need to convert again to bytes. | ||||
def getoptb(args, shortlist, namelist): | ||||
args = [a.decode('latin-1') for a in args] | ||||
shortlist = shortlist.decode('latin-1') | ||||
namelist = [a.decode('latin-1') for a in namelist] | ||||
opts, args = getopt.getopt(args, shortlist, namelist) | ||||
opts = [(a[0].encode('latin-1'), a[1].encode('latin-1')) | ||||
for a in opts] | ||||
args = [a.encode('latin-1') for a in args] | ||||
return opts, args | ||||
|
r30579 | # keys of keyword arguments in Python need to be strings which are unicodes | ||
# Python 3. This function takes keyword arguments, convert the keys to str. | ||||
def strkwargs(dic): | ||||
dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems()) | ||||
return dic | ||||
# keys of keyword arguments need to be unicode while passing into | ||||
# a function. This function helps us to convert those keys back to bytes | ||||
# again as we need to deal with bytes. | ||||
def byteskwargs(dic): | ||||
dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems()) | ||||
return dic | ||||
|
r30032 | else: | ||
def sysstr(s): | ||||
return s | ||||
|
r30133 | # Partial backport from os.py in Python 3, which only accepts bytes. | ||
# In Python 2, our paths should only ever be bytes, a unicode path | ||||
# indicates a bug. | ||||
def fsencode(filename): | ||||
if isinstance(filename, str): | ||||
return filename | ||||
|
r30119 | else: | ||
|
r30133 | raise TypeError( | ||
"expect str, not %s" % type(filename).__name__) | ||||
|
r30119 | |||
|
r30300 | # In Python 2, fsdecode() has a very chance to receive bytes. So it's | ||
# better not to touch Python 2 part as it's already working fine. | ||||
def fsdecode(filename): | ||||
return filename | ||||
|
r30578 | def getoptb(args, shortlist, namelist): | ||
return getopt.getopt(args, shortlist, namelist) | ||||
|
r30579 | def strkwargs(dic): | ||
return dic | ||||
def byteskwargs(dic): | ||||
return dic | ||||
|
r30302 | osname = os.name | ||
|
r30303 | ospathsep = os.pathsep | ||
ossep = os.sep | ||||
|
r30623 | osaltsep = os.altsep | ||
|
r30472 | stdin = sys.stdin | ||
stdout = sys.stdout | ||||
stderr = sys.stderr | ||||
|
r30330 | sysargv = sys.argv | ||
|
r30500 | getcwd = os.getcwd | ||
|
r30302 | |||
|
r29584 | stringio = io.StringIO | ||
|
r28818 | empty = _queue.Empty | ||
queue = _queue.Queue | ||||
|
r28834 | |||
|
r28882 | class _pycompatstub(object): | ||
|
r29801 | def __init__(self): | ||
self._aliases = {} | ||||
|
r28882 | |||
|
r29801 | def _registeraliases(self, origin, items): | ||
"""Add items that will be populated at the first access""" | ||||
|
r30086 | items = map(sysstr, items) | ||
self._aliases.update( | ||||
(item.replace(sysstr('_'), sysstr('')).lower(), (origin, item)) | ||||
for item in items) | ||||
|
r28882 | |||
|
r29801 | def __getattr__(self, name): | ||
|
r28882 | try: | ||
|
r29801 | origin, item = self._aliases[name] | ||
except KeyError: | ||||
raise AttributeError(name) | ||||
self.__dict__[name] = obj = getattr(origin, item) | ||||
return obj | ||||
|
r28882 | |||
|
r29566 | httpserver = _pycompatstub() | ||
|
r28882 | urlreq = _pycompatstub() | ||
urlerr = _pycompatstub() | ||||
|
r30030 | if not ispy3: | ||
|
r29566 | import BaseHTTPServer | ||
import CGIHTTPServer | ||||
import SimpleHTTPServer | ||||
|
r28882 | import urllib2 | ||
import urllib | ||||
|
r29801 | urlreq._registeraliases(urllib, ( | ||
|
r28882 | "addclosehook", | ||
"addinfourl", | ||||
"ftpwrapper", | ||||
"pathname2url", | ||||
"quote", | ||||
"splitattr", | ||||
"splitpasswd", | ||||
"splitport", | ||||
"splituser", | ||||
"unquote", | ||||
"url2pathname", | ||||
"urlencode", | ||||
)) | ||||
|
r29801 | urlreq._registeraliases(urllib2, ( | ||
|
r28882 | "AbstractHTTPHandler", | ||
"BaseHandler", | ||||
"build_opener", | ||||
"FileHandler", | ||||
"FTPHandler", | ||||
"HTTPBasicAuthHandler", | ||||
"HTTPDigestAuthHandler", | ||||
"HTTPHandler", | ||||
"HTTPPasswordMgrWithDefaultRealm", | ||||
"HTTPSHandler", | ||||
"install_opener", | ||||
"ProxyHandler", | ||||
"Request", | ||||
"urlopen", | ||||
)) | ||||
|
r29801 | urlerr._registeraliases(urllib2, ( | ||
|
r28882 | "HTTPError", | ||
"URLError", | ||||
)) | ||||
|
r29801 | httpserver._registeraliases(BaseHTTPServer, ( | ||
|
r29566 | "HTTPServer", | ||
"BaseHTTPRequestHandler", | ||||
)) | ||||
|
r29801 | httpserver._registeraliases(SimpleHTTPServer, ( | ||
|
r29566 | "SimpleHTTPRequestHandler", | ||
)) | ||||
|
r29801 | httpserver._registeraliases(CGIHTTPServer, ( | ||
|
r29566 | "CGIHTTPRequestHandler", | ||
)) | ||||
|
r28882 | |||
|
r29801 | else: | ||
|
r28882 | import urllib.request | ||
|
r29801 | urlreq._registeraliases(urllib.request, ( | ||
|
r28882 | "AbstractHTTPHandler", | ||
"addclosehook", | ||||
"addinfourl", | ||||
"BaseHandler", | ||||
"build_opener", | ||||
"FileHandler", | ||||
"FTPHandler", | ||||
"ftpwrapper", | ||||
"HTTPHandler", | ||||
"HTTPSHandler", | ||||
"install_opener", | ||||
"pathname2url", | ||||
"HTTPBasicAuthHandler", | ||||
"HTTPDigestAuthHandler", | ||||
|
r29414 | "HTTPPasswordMgrWithDefaultRealm", | ||
|
r28882 | "ProxyHandler", | ||
"quote", | ||||
"Request", | ||||
"splitattr", | ||||
"splitpasswd", | ||||
"splitport", | ||||
"splituser", | ||||
"unquote", | ||||
"url2pathname", | ||||
"urlopen", | ||||
)) | ||||
import urllib.error | ||||
|
r29801 | urlerr._registeraliases(urllib.error, ( | ||
|
r28882 | "HTTPError", | ||
"URLError", | ||||
)) | ||||
|
r29566 | import http.server | ||
|
r29801 | httpserver._registeraliases(http.server, ( | ||
|
r29566 | "HTTPServer", | ||
"BaseHTTPRequestHandler", | ||||
"SimpleHTTPRequestHandler", | ||||
"CGIHTTPRequestHandler", | ||||
)) | ||||