##// END OF EJS Templates
auto-upgrade: introduce a way to auto-upgrade to/from dirstate-v2...
auto-upgrade: introduce a way to auto-upgrade to/from dirstate-v2 This is similar to what we introduced for `share-safe`, but apply to the tracked-hint feature. Differential Revision: https://phab.mercurial-scm.org/D12614

File last commit:

r49851:e45c3927 default
r50090:411d591e default
Show More
pycompat.py
425 lines | 11.4 KiB | text/x-python | PythonLexer
timeless
pycompat: add empty and queue to handle py3 divergence...
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.
"""
Gregory Szorc
pycompat: remove first not ispy3 block...
r49724 import builtins
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 import codecs
Gregory Szorc
pycompat: remove first not ispy3 block...
r49724 import concurrent.futures as futures
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 import functools
Pulkit Goyal
py3: make a bytes version of getopt.getopt()...
r30578 import getopt
Gregory Szorc
pycompat: remove first not ispy3 block...
r49724 import http.client as httplib
import http.cookiejar as cookielib
Augie Fackler
py3: introduce and use pycompat.getargspec...
r36196 import inspect
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 import io
Gregory Szorc
py3: define and use json.loads polyfill...
r43697 import json
Pulkit Goyal
py3: add a bytes version of os.name...
r30302 import os
Gregory Szorc
pycompat: remove first not ispy3 block...
r49724 import queue
Pulkit Goyal
py3: have a bytes version of shlex.split()...
r30678 import shlex
Gregory Szorc
pycompat: remove first not ispy3 block...
r49724 import socketserver
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 import struct
Pulkit Goyal
pycompat: make pycompat demandimport friendly...
r29584 import sys
Yuya Nishihara
py3: wrap tempfile.mkstemp() to use bytes path...
r38182 import tempfile
Gregory Szorc
pycompat: remove first not ispy3 block...
r49724 import xmlrpc.client as xmlrpclib
Pulkit Goyal
pycompat: make pycompat demandimport friendly...
r29584
Gregory Szorc
pycompat: remove large Python 2 block...
r49726
Augie Fackler
formatting: blacken the codebase...
r43346 ispy3 = sys.version_info[0] >= 3
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 ispypy = '__pypy__' in sys.builtin_module_names
Yuya Nishihara
typing: consolidate "if not globals():" trick...
r44212 TYPE_CHECKING = False
if not globals(): # hide this from non-pytype users
import typing
TYPE_CHECKING = typing.TYPE_CHECKING
Yuya Nishihara
pycompat: provide 'ispy3' constant...
r30030
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
pycompat: remove first not ispy3 block...
r49724 def future_set_exception_info(f, exc_info):
f.set_exception(exc_info[0])
Pulkit Goyal
py3: conditionalize the urlparse import...
r29431
Augie Fackler
py3: paper over differences in future exception handling...
r37687
Gregory Szorc
pycompat: remove first not ispy3 block...
r49724 FileNotFoundError = builtins.FileNotFoundError
template: FileNotFoundError is actually a built in exception...
r48665
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
pycompat: introduce identity function as a compat stub...
r31774 def identity(a):
return a
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
pycompat: move rapply() from util...
r38594 def _rapply(f, xs):
if xs is None:
# assume None means non-value of optional data
return xs
if isinstance(xs, (list, set, tuple)):
return type(xs)(_rapply(f, x) for x in xs)
if isinstance(xs, dict):
return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
return f(xs)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
pycompat: move rapply() from util...
r38594 def rapply(f, xs):
"""Apply function recursively to every item preserving the data structure
>>> def f(x):
... return 'f(%s)' % x
>>> rapply(f, None) is None
True
>>> rapply(f, 'a')
'f(a)'
>>> rapply(f, {'a'}) == {'f(a)'}
True
>>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
>>> xs = [object()]
>>> rapply(identity, xs) is xs
True
"""
if f is identity:
# fast path mainly for py2
return xs
return _rapply(f, xs)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
pycompat: remove check for Python >= 3.6...
r49814 if os.name == r'nt':
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 # MBCS (or ANSI) filesystem encoding must be used as before.
# Otherwise non-ASCII filenames in existing repositories would be
# corrupted.
# This must be set once prior to any fsencode/fsdecode calls.
sys._enablelegacywindowsfsencoding() # pytype: disable=module-attr
Yuya Nishihara
py3: enable legacy fs encoding to fix filename compatibility on Windows...
r43613
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 fsencode = os.fsencode
fsdecode = os.fsdecode
oscurdir = os.curdir.encode('ascii')
oslinesep = os.linesep.encode('ascii')
osname = os.name.encode('ascii')
ospathsep = os.pathsep.encode('ascii')
ospardir = os.pardir.encode('ascii')
ossep = os.sep.encode('ascii')
osaltsep = os.altsep
if osaltsep:
osaltsep = osaltsep.encode('ascii')
osdevnull = os.devnull.encode('ascii')
Matt Harbison
py3: rename pycompat.getcwd() to encoding.getcwd() (API)...
r39843
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 sysplatform = sys.platform.encode('ascii')
sysexecutable = sys.executable
if sysexecutable:
sysexecutable = os.fsencode(sysexecutable)
Yuya Nishihara
pycompat: name maplist() and ziplist() for better traceback message
r36952
Gregory Szorc
pycompat: remove large Python 2 block...
r49726
def maplist(*args):
return list(map(*args))
Yuya Nishihara
pycompat: name maplist() and ziplist() for better traceback message
r36952
Gregory Szorc
pycompat: remove large Python 2 block...
r49726
def rangelist(*args):
return list(range(*args))
Yuya Nishihara
annotate: do not construct attr.s object per line while computing history...
r37082
Gregory Szorc
pycompat: remove large Python 2 block...
r49726
def ziplist(*args):
return list(zip(*args))
Yuya Nishihara
pycompat: name maplist() and ziplist() for better traceback message
r36952
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 rawinput = input
getargspec = inspect.getfullargspec
Yuya Nishihara
py3: document why os.fsencode() can be used to get back bytes argv...
r30334
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 long = int
Matt Harbison
cbor: teach the encoder to handle python `long` type for Windows...
r39490
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 if getattr(sys, 'argv', None) is not None:
# On POSIX, the char** argv array is converted to Python str using
# Py_DecodeLocale(). The inverse of this is Py_EncodeLocale(), which
# isn't directly callable from Python code. In practice, os.fsencode()
# can be used instead (this is recommended by Python's documentation
# for sys.argv).
#
# On Windows, the wchar_t **argv is passed into the interpreter as-is.
# Like POSIX, we need to emulate what Py_EncodeLocale() would do. But
# there's an additional wrinkle. What we really want to access is the
# ANSI codepage representation of the arguments, as this is what
# `int main()` would receive if Python 3 didn't define `int wmain()`
# (this is how Python 2 worked). To get that, we encode with the mbcs
# encoding, which will pass CP_ACP to the underlying Windows API to
# produce bytes.
if os.name == r'nt':
sysargv = [a.encode("mbcs", "ignore") for a in sys.argv]
else:
sysargv = [fsencode(a) for a in sys.argv]
Yuya Nishihara
py3: move xrange alias next to import lines...
r29797
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 bytechr = struct.Struct('>B').pack
byterepr = b'%r'.__mod__
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 class bytestr(bytes):
"""A bytes which mostly acts as a Python 2 str
Yuya Nishihara
pycompat: try __bytes__() to convert object to bytestr...
r32450
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
('', 'foo', 'ascii', '1')
>>> s = bytestr(b'foo')
>>> assert s is bytestr(s)
__bytes__() should be called if provided:
Yuya Nishihara
pycompat: try __bytes__() to convert object to bytestr...
r32450
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 >>> class bytesable:
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 ... def __bytes__(self):
... return b'bytes'
>>> bytestr(bytesable())
'bytes'
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 There's no implicit conversion from non-ascii str as its encoding is
unknown:
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
Traceback (most recent call last):
...
UnicodeEncodeError: ...
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 Comparison between bytestr and bytes should work:
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 >>> 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'
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 Sliced elements should be bytes, not integer:
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 >>> 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'])
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 As bytestr type isn't propagated across operations, you need to cast
bytes to bytestr explicitly:
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 >>> s = bytestr(b'foo').upper()
>>> t = bytestr(s)
>>> s[0], t[0]
(70, b'F')
Matt Harbison
typing: add a fake `__init__()` to bytestr to distract pytype...
r48819
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 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
"""
Matt Harbison
typing: add a fake `__init__()` to bytestr to distract pytype...
r48819
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 # Trick pytype into not demanding Iterable[int] be passed to __new__(),
# since the appropriate bytes format is done internally.
#
# https://github.com/google/pytype/issues/500
if TYPE_CHECKING:
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def __init__(self, s=b''):
pass
def __new__(cls, s=b''):
if isinstance(s, bytestr):
Yuya Nishihara
pycompat: add bytestr wrapper which mostly acts as a Python 2 str...
r31439 return s
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 if not isinstance(
s, (bytes, bytearray)
) and not hasattr( # hasattr-py3-only
s, u'__bytes__'
):
s = str(s).encode('ascii')
return bytes.__new__(cls, s)
Yuya Nishihara
py3: always drop b'' prefix from repr() of bytestr...
r35921
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def __getitem__(self, key):
s = bytes.__getitem__(self, key)
if not isinstance(s, bytes):
s = bytechr(s)
Yuya Nishihara
py3: drop b'' from repr() of smartset...
r35922 return s
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def __iter__(self):
return iterbytestr(bytes.__iter__(self))
def __repr__(self):
return bytes.__repr__(self)[1:] # drop b''
Yuya Nishihara
py3: have registrar process docstrings in bytes...
r31820
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def iterbytestr(s):
"""Iterate bytes as if it were a str object of Python 2"""
return map(bytechr, s)
Yuya Nishihara
py3: have registrar process docstrings in bytes...
r31820
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def maybebytestr(s):
"""Promote bytes to bytestr"""
if isinstance(s, bytes):
return bytestr(s)
return s
Yuya Nishihara
pycompat: extract function that converts attribute or encoding name to str...
r30032
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 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)).
"""
if isinstance(s, bytes):
return s
return s.encode('utf-8')
Yuya Nishihara
pycompat: extract function that converts attribute or encoding name to str...
r30032
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 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('latin-1')
Pulkit Goyal
py3: add a new strurl() which will convert a bytes url to str
r32859
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def strurl(url):
"""Converts a bytes url back to str"""
if isinstance(url, bytes):
return url.decode('ascii')
return url
Pulkit Goyal
py3: add a new bytesurl() to convert a str url into bytes
r32860
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def bytesurl(url):
"""Converts a str url to bytes by encoding in ascii"""
if isinstance(url, str):
return url.encode('ascii')
return url
Yuya Nishihara
pycompat: extract helper to raise exception with traceback...
r32186
Gregory Szorc
pycompat: remove large Python 2 block...
r49726
def raisewithtb(exc, tb):
"""Raise exception with the given traceback"""
raise exc.with_traceback(tb)
Yuya Nishihara
py3: convert __doc__ back to bytes in help.py...
r32615
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def getdoc(obj):
"""Get docstring as bytes; may be None so gettext() won't confuse it
with _('')"""
doc = getattr(obj, '__doc__', None)
if doc is None:
return doc
return sysbytes(doc)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
pycompat: remove large Python 2 block...
r49726
def _wrapattrfunc(f):
@functools.wraps(f)
def w(object, name, *args):
return f(object, sysstr(name), *args)
Yuya Nishihara
py3: provide (del|get|has|set)attr wrappers that accepts bytes...
r29799
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 return w
Yuya Nishihara
py3: provide (del|get|has|set)attr wrappers that accepts bytes...
r29799
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 # these wrappers are automagically imported by hgloader
delattr = _wrapattrfunc(builtins.delattr)
getattr = _wrapattrfunc(builtins.getattr)
hasattr = _wrapattrfunc(builtins.hasattr)
setattr = _wrapattrfunc(builtins.setattr)
xrange = builtins.range
unicode = str
Pulkit Goyal
py3: add pycompat.open and replace open() calls...
r31149
Gregory Szorc
pycompat: remove large Python 2 block...
r49726
def open(name, mode=b'r', buffering=-1, encoding=None):
return builtins.open(name, sysstr(mode), buffering, encoding)
Yuya Nishihara
util: make safehasattr() a pycompat function...
r37117
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 safehasattr = _wrapattrfunc(builtins.hasattr)
def _getoptbwrapper(orig, args, shortlist, namelist):
"""
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.
"""
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 = orig(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
Pulkit Goyal
py3: make a bytes version of getopt.getopt()...
r30578
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def strkwargs(dic):
"""
Converts the keys of a python dictonary to str i.e. unicodes so that
they can be passed as keyword arguments as dictionaries with bytes keys
can't be passed as keyword arguments to functions on Python 3.
"""
dic = {k.decode('latin-1'): v for k, v in dic.items()}
return dic
Pulkit Goyal
py3: utility functions to convert keys of kwargs to bytes/unicodes...
r30579
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 def byteskwargs(dic):
"""
Converts keys of python dictionaries to bytes as they were converted to
str to pass that dictonary as a keyword argument on Python 3.
"""
dic = {k.encode('latin-1'): v for k, v in dic.items()}
return dic
Pulkit Goyal
py3: have a bytes version of shlex.split()...
r30678
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 # TODO: handle shlex.shlex().
def shlexsplit(s, comments=False, posix=True):
"""
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.
"""
ret = shlex.split(s.decode('latin-1'), comments, posix)
return [a.encode('latin-1') for a in ret]
Floris Bruynooghe
patchbomb: protect email addresses from shell...
r43289
Gregory Szorc
py3: define and use json.loads polyfill...
r43697
Gregory Szorc
pycompat: remove large Python 2 block...
r49726 iteritems = lambda x: x.items()
itervalues = lambda x: x.values()
Gregory Szorc
pycompat: remove json.loads polyfill for Python 3.5...
r49815 json_loads = json.loads
Jun Wu
selectors2: do not use platform.system()...
r34640
Matt Harbison
py3: byteify strings in pycompat...
r39678 isjython = sysplatform.startswith(b'java')
Jun Wu
pycompat: define operating system constants...
r34645
rdamazio@google.com
pycompat: adding Linux detection and fixing Mac...
r40563 isdarwin = sysplatform.startswith(b'darwin')
islinux = sysplatform.startswith(b'linux')
Matt Harbison
py3: byteify strings in pycompat...
r39678 isposix = osname == b'posix'
iswindows = osname == b'nt'
Yuya Nishihara
fancyopts: use getopt.gnu_getopt()...
r35226
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
fancyopts: use getopt.gnu_getopt()...
r35226 def getoptb(args, shortlist, namelist):
return _getoptbwrapper(getopt.getopt, args, shortlist, namelist)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
fancyopts: use getopt.gnu_getopt()...
r35226 def gnugetoptb(args, shortlist, namelist):
return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist)
Yuya Nishihara
py3: wrap tempfile.mkstemp() to use bytes path...
r38182
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
py3: wrap tempfile.mkdtemp() to use bytes path...
r38183 def mkdtemp(suffix=b'', prefix=b'tmp', dir=None):
return tempfile.mkdtemp(suffix, prefix, dir)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
py3: wrap tempfile.mkstemp() to use bytes path...
r38182 # text=True is not supported; use util.from/tonativeeol() instead
def mkstemp(suffix=b'', prefix=b'tmp', dir=None):
return tempfile.mkstemp(suffix, prefix, dir)
Yuya Nishihara
py3: wrap tempfile.NamedTemporaryFile() to return bytes fp.name...
r38184
Augie Fackler
formatting: blacken the codebase...
r43346
pycompat: add an entry for unnamedtmpfile...
r46319 # TemporaryFile does not support an "encoding=" argument on python2.
# This wrapper file are always open in byte mode.
def unnamedtempfile(mode=None, *args, **kwargs):
if mode is None:
Matt Harbison
pycompat: fix a bytes vs str issue in `unnamedtempfile()`...
r47385 mode = 'w+b'
pycompat: add an entry for unnamedtmpfile...
r46319 else:
mode = sysstr(mode)
assert 'b' in mode
return tempfile.TemporaryFile(mode, *args, **kwargs)
pycompat: update comment about unnamedtempfile...
r46318 # NamedTemporaryFile does not support an "encoding=" argument on python2.
# This wrapper file are always open in byte mode.
Augie Fackler
formatting: blacken the codebase...
r43346 def namedtempfile(
mode=b'w+b', bufsize=-1, suffix=b'', prefix=b'tmp', dir=None, delete=True
):
Yuya Nishihara
py3: wrap tempfile.NamedTemporaryFile() to return bytes fp.name...
r38184 mode = sysstr(mode)
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 assert 'b' in mode
Augie Fackler
formatting: blacken the codebase...
r43346 return tempfile.NamedTemporaryFile(
mode, bufsize, suffix=suffix, prefix=prefix, dir=dir, delete=delete
)