pycompat.py
330 lines
| 10.1 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 | ||
|
r30678 | import shlex | ||
|
r29584 | import sys | ||
|
r30030 | ispy3 = (sys.version_info[0] >= 3) | ||
|
r33628 | ispypy = (r'__pypy__' in sys.builtin_module_names) | ||
|
r30030 | |||
if not ispy3: | ||||
|
r31934 | import cookielib | ||
|
r29324 | import cPickle as pickle | ||
|
r29455 | import httplib | ||
|
r29584 | import Queue as _queue | ||
|
r29433 | import SocketServer as socketserver | ||
|
r29432 | import xmlrpclib | ||
|
r29584 | else: | ||
|
r31942 | import http.cookiejar as cookielib | ||
|
r29584 | import http.client as httplib | ||
import pickle | ||||
import queue as _queue | ||||
import socketserver | ||||
|
r29432 | import xmlrpc.client as xmlrpclib | ||
|
r29431 | |||
|
r32865 | empty = _queue.Empty | ||
queue = _queue.Queue | ||||
|
r31774 | def identity(a): | ||
return a | ||||
|
r30030 | if ispy3: | ||
|
r29797 | import builtins | ||
|
r29799 | import functools | ||
|
r31372 | import io | ||
|
r31424 | import struct | ||
|
r31372 | |||
|
r30119 | fsencode = os.fsencode | ||
|
r30300 | fsdecode = os.fsdecode | ||
|
r31775 | oslinesep = os.linesep.encode('ascii') | ||
|
r30302 | 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 | ||||
|
r30624 | sysplatform = sys.platform.encode('ascii') | ||
|
r30668 | sysexecutable = sys.executable | ||
if sysexecutable: | ||||
sysexecutable = os.fsencode(sysexecutable) | ||||
|
r31359 | stringio = io.BytesIO | ||
|
r31501 | maplist = lambda *args: list(map(*args)) | ||
|
r35406 | ziplist = lambda *args: list(zip(*args)) | ||
|
r33853 | rawinput = input | ||
|
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. | ||||
|
r31277 | if getattr(sys, 'argv', None) is not None: | ||
sysargv = list(map(os.fsencode, sys.argv)) | ||||
|
r29797 | |||
|
r31424 | bytechr = struct.Struct('>B').pack | ||
|
r31253 | |||
|
r31439 | class bytestr(bytes): | ||
"""A bytes which mostly acts as a Python 2 str | ||||
>>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1) | ||||
(b'', b'foo', b'ascii', b'1') | ||||
>>> s = bytestr(b'foo') | ||||
>>> assert s is bytestr(s) | ||||
|
r32450 | __bytes__() should be called if provided: | ||
>>> class bytesable(object): | ||||
... def __bytes__(self): | ||||
... return b'bytes' | ||||
>>> bytestr(bytesable()) | ||||
b'bytes' | ||||
|
r31439 | There's no implicit conversion from non-ascii str as its encoding is | ||
unknown: | ||||
>>> bytestr(chr(0x80)) # doctest: +ELLIPSIS | ||||
Traceback (most recent call last): | ||||
... | ||||
UnicodeEncodeError: ... | ||||
Comparison between bytestr and bytes should work: | ||||
>>> assert bytestr(b'foo') == b'foo' | ||||
>>> assert b'foo' == bytestr(b'foo') | ||||
>>> assert b'f' in bytestr(b'foo') | ||||
>>> assert bytestr(b'f') in b'foo' | ||||
Sliced elements should be bytes, not integer: | ||||
>>> s[1], s[:2] | ||||
(b'o', b'fo') | ||||
>>> list(s), list(reversed(s)) | ||||
([b'f', b'o', b'o'], [b'o', b'o', b'f']) | ||||
As bytestr type isn't propagated across operations, you need to cast | ||||
bytes to bytestr explicitly: | ||||
>>> s = bytestr(b'foo').upper() | ||||
>>> t = bytestr(s) | ||||
>>> s[0], t[0] | ||||
(70, b'F') | ||||
Be careful to not pass a bytestr object to a function which expects | ||||
bytearray-like behavior. | ||||
>>> t = bytes(t) # cast to bytes | ||||
>>> assert type(t) is bytes | ||||
""" | ||||
def __new__(cls, s=b''): | ||||
if isinstance(s, bytestr): | ||||
return s | ||||
|
r32450 | if (not isinstance(s, (bytes, bytearray)) | ||
and not hasattr(s, u'__bytes__')): # hasattr-py3-only | ||||
|
r31439 | s = str(s).encode(u'ascii') | ||
return bytes.__new__(cls, s) | ||||
def __getitem__(self, key): | ||||
s = bytes.__getitem__(self, key) | ||||
if not isinstance(s, bytes): | ||||
s = bytechr(s) | ||||
return s | ||||
def __iter__(self): | ||||
return iterbytestr(bytes.__iter__(self)) | ||||
|
r31382 | def iterbytestr(s): | ||
"""Iterate bytes as if it were a str object of Python 2""" | ||||
|
r31425 | return map(bytechr, s) | ||
|
r31382 | |||
|
r31820 | def sysbytes(s): | ||
"""Convert an internal str (e.g. keyword, __doc__) back to bytes | ||||
This never raises UnicodeEncodeError, but only ASCII characters | ||||
can be round-trip by sysstr(sysbytes(s)). | ||||
""" | ||||
return s.encode(u'utf-8') | ||||
|
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') | ||||
|
r32859 | def strurl(url): | ||
"""Converts a bytes url back to str""" | ||||
return url.decode(u'ascii') | ||||
|
r32860 | def bytesurl(url): | ||
"""Converts a str url to bytes by encoding in ascii""" | ||||
return url.encode(u'ascii') | ||||
|
r32186 | def raisewithtb(exc, tb): | ||
"""Raise exception with the given traceback""" | ||||
raise exc.with_traceback(tb) | ||||
|
r32615 | def getdoc(obj): | ||
"""Get docstring as bytes; may be None so gettext() won't confuse it | ||||
with _('')""" | ||||
doc = getattr(obj, u'__doc__', None) | ||||
if doc is None: | ||||
return doc | ||||
return sysbytes(doc) | ||||
|
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 | ||
|
r31843 | unicode = str | ||
|
r29799 | |||
|
r31149 | def open(name, mode='r', buffering=-1): | ||
return builtins.open(name, sysstr(mode), buffering) | ||||
|
r35226 | def _getoptbwrapper(orig, args, shortlist, namelist): | ||
|
r32864 | """ | ||
Takes bytes arguments, converts them to unicode, pass them to | ||||
getopt.getopt(), convert the returned values back to bytes and then | ||||
return them for Python 3 compatibility as getopt.getopt() don't accepts | ||||
bytes on Python 3. | ||||
""" | ||||
|
r30578 | args = [a.decode('latin-1') for a in args] | ||
shortlist = shortlist.decode('latin-1') | ||||
namelist = [a.decode('latin-1') for a in namelist] | ||||
|
r35226 | opts, args = orig(args, shortlist, namelist) | ||
|
r30578 | 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 | def strkwargs(dic): | ||
|
r32864 | """ | ||
Converts the keys of a python dictonary to str i.e. unicodes so that | ||||
they can be passed as keyword arguments as dictonaries with bytes keys | ||||
can't be passed as keyword arguments to functions on Python 3. | ||||
""" | ||||
|
r30579 | dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems()) | ||
return dic | ||||
def byteskwargs(dic): | ||||
|
r32864 | """ | ||
Converts keys of python dictonaries to bytes as they were converted to | ||||
str to pass that dictonary as a keyword argument on Python 3. | ||||
""" | ||||
|
r30579 | dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems()) | ||
return dic | ||||
|
r30678 | # TODO: handle shlex.shlex(). | ||
def shlexsplit(s): | ||||
|
r32864 | """ | ||
Takes bytes argument, convert it to str i.e. unicodes, pass that into | ||||
shlex.split(), convert the returned value to bytes and return that for | ||||
Python 3 compatibility as shelx.split() don't accept bytes on Python 3. | ||||
""" | ||||
|
r30678 | ret = shlex.split(s.decode('latin-1')) | ||
return [a.encode('latin-1') for a in ret] | ||||
|
r30032 | else: | ||
|
r31372 | import cStringIO | ||
|
r31253 | bytechr = chr | ||
|
r31439 | bytestr = str | ||
|
r31382 | iterbytestr = iter | ||
|
r31820 | sysbytes = identity | ||
|
r31774 | sysstr = identity | ||
|
r32859 | strurl = identity | ||
|
r32860 | bytesurl = identity | ||
|
r30032 | |||
|
r32186 | # this can't be parsed on Python 3 | ||
exec('def raisewithtb(exc, tb):\n' | ||||
' raise exc, None, tb\n') | ||||
|
r30133 | def fsencode(filename): | ||
|
r32864 | """ | ||
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. | ||||
""" | ||||
|
r30133 | 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. | ||||
|
r31774 | fsdecode = identity | ||
|
r30300 | |||
|
r32615 | def getdoc(obj): | ||
return getattr(obj, '__doc__', None) | ||||
|
r35226 | def _getoptbwrapper(orig, args, shortlist, namelist): | ||
return orig(args, shortlist, namelist) | ||||
|
r30578 | |||
|
r31774 | strkwargs = identity | ||
byteskwargs = identity | ||||
|
r30579 | |||
|
r31775 | oslinesep = os.linesep | ||
|
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 | ||||
|
r31277 | if getattr(sys, 'argv', None) is not None: | ||
sysargv = sys.argv | ||||
|
r30624 | sysplatform = sys.platform | ||
|
r30500 | getcwd = os.getcwd | ||
|
r30668 | sysexecutable = sys.executable | ||
|
r30678 | shlexsplit = shlex.split | ||
|
r31372 | stringio = cStringIO.StringIO | ||
|
r31501 | maplist = map | ||
|
r35406 | ziplist = zip | ||
|
r33853 | rawinput = raw_input | ||
|
r34640 | |||
isjython = sysplatform.startswith('java') | ||||
|
r34645 | |||
isdarwin = sysplatform == 'darwin' | ||||
isposix = osname == 'posix' | ||||
iswindows = osname == 'nt' | ||||
|
r35226 | |||
def getoptb(args, shortlist, namelist): | ||||
return _getoptbwrapper(getopt.getopt, args, shortlist, namelist) | ||||
def gnugetoptb(args, shortlist, namelist): | ||||
return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist) | ||||