diff --git a/mercurial/pycompat.py b/mercurial/pycompat.py --- a/mercurial/pycompat.py +++ b/mercurial/pycompat.py @@ -29,12 +29,22 @@ import tempfile import xmlrpc.client as xmlrpclib from typing import ( + Any, + AnyStr, + BinaryIO, + Dict, Iterable, Iterator, List, + Mapping, + NoReturn, Optional, + Sequence, + Tuple, Type, TypeVar, + cast, + overload, ) ispy3 = sys.version_info[0] >= 3 @@ -46,6 +56,8 @@ if not globals(): # hide this from non- TYPE_CHECKING = typing.TYPE_CHECKING +_GetOptResult = Tuple[List[Tuple[bytes, bytes]], List[bytes]] +_T0 = TypeVar('_T0') _Tbytestr = TypeVar('_Tbytestr', bound='bytestr') @@ -56,7 +68,7 @@ def future_set_exception_info(f, exc_inf FileNotFoundError = builtins.FileNotFoundError -def identity(a): +def identity(a: _T0) -> _T0: return a @@ -250,6 +262,17 @@ def iterbytestr(s: Iterable[int]) -> Ite return map(bytechr, s) +if TYPE_CHECKING: + + @overload + def maybebytestr(s: bytes) -> bytestr: + ... + + @overload + def maybebytestr(s: _T0) -> _T0: + ... + + def maybebytestr(s): """Promote bytes to bytestr""" if isinstance(s, bytes): @@ -257,7 +280,7 @@ def maybebytestr(s): return s -def sysbytes(s): +def sysbytes(s: AnyStr) -> bytes: """Convert an internal str (e.g. keyword, __doc__) back to bytes This never raises UnicodeEncodeError, but only ASCII characters @@ -268,7 +291,7 @@ def sysbytes(s): return s.encode('utf-8') -def sysstr(s): +def sysstr(s: AnyStr) -> str: """Return a keyword str to be passed to Python functions such as getattr() and str.encode() @@ -281,26 +304,26 @@ def sysstr(s): return s.decode('latin-1') -def strurl(url): +def strurl(url: AnyStr) -> str: """Converts a bytes url back to str""" if isinstance(url, bytes): return url.decode('ascii') return url -def bytesurl(url): +def bytesurl(url: AnyStr) -> bytes: """Converts a str url to bytes by encoding in ascii""" if isinstance(url, str): return url.encode('ascii') return url -def raisewithtb(exc, tb): +def raisewithtb(exc: BaseException, tb) -> NoReturn: """Raise exception with the given traceback""" raise exc.with_traceback(tb) -def getdoc(obj): +def getdoc(obj: object) -> Optional[bytes]: """Get docstring as bytes; may be None so gettext() won't confuse it with _('')""" doc = builtins.getattr(obj, '__doc__', None) @@ -326,14 +349,22 @@ xrange = builtins.range unicode = str -def open(name, mode=b'r', buffering=-1, encoding=None): +def open( + name, + mode: AnyStr = b'r', + buffering: int = -1, + encoding: Optional[str] = None, +) -> Any: + # TODO: assert binary mode, and cast result to BinaryIO? return builtins.open(name, sysstr(mode), buffering, encoding) safehasattr = _wrapattrfunc(builtins.hasattr) -def _getoptbwrapper(orig, args, shortlist, namelist): +def _getoptbwrapper( + orig, args: Sequence[bytes], shortlist: bytes, namelist: Sequence[bytes] +) -> _GetOptResult: """ Takes bytes arguments, converts them to unicode, pass them to getopt.getopt(), convert the returned values back to bytes and then @@ -349,7 +380,7 @@ def _getoptbwrapper(orig, args, shortlis return opts, args -def strkwargs(dic): +def strkwargs(dic: Mapping[bytes, _T0]) -> Dict[str, _T0]: """ 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 @@ -359,7 +390,7 @@ def strkwargs(dic): return dic -def byteskwargs(dic): +def byteskwargs(dic: Mapping[str, _T0]) -> Dict[bytes, _T0]: """ Converts keys of python dictionaries to bytes as they were converted to str to pass that dictonary as a keyword argument on Python 3. @@ -369,7 +400,9 @@ def byteskwargs(dic): # TODO: handle shlex.shlex(). -def shlexsplit(s, comments=False, posix=True): +def shlexsplit( + s: bytes, comments: bool = False, posix: bool = True +) -> List[bytes]: """ 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 @@ -392,38 +425,51 @@ isposix: bool = osname == b'posix' iswindows: bool = osname == b'nt' -def getoptb(args, shortlist, namelist): +def getoptb( + args: Sequence[bytes], shortlist: bytes, namelist: Sequence[bytes] +) -> _GetOptResult: return _getoptbwrapper(getopt.getopt, args, shortlist, namelist) -def gnugetoptb(args, shortlist, namelist): +def gnugetoptb( + args: Sequence[bytes], shortlist: bytes, namelist: Sequence[bytes] +) -> _GetOptResult: return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist) -def mkdtemp(suffix=b'', prefix=b'tmp', dir=None): +def mkdtemp( + suffix: bytes = b'', prefix: bytes = b'tmp', dir: Optional[bytes] = None +) -> bytes: return tempfile.mkdtemp(suffix, prefix, dir) # text=True is not supported; use util.from/tonativeeol() instead -def mkstemp(suffix=b'', prefix=b'tmp', dir=None): +def mkstemp( + suffix: bytes = b'', prefix: bytes = b'tmp', dir: Optional[bytes] = None +) -> Tuple[int, bytes]: return tempfile.mkstemp(suffix, prefix, dir) # TemporaryFile does not support an "encoding=" argument on python2. # This wrapper file are always open in byte mode. -def unnamedtempfile(mode=None, *args, **kwargs): +def unnamedtempfile(mode: Optional[bytes] = None, *args, **kwargs) -> BinaryIO: if mode is None: mode = 'w+b' else: mode = sysstr(mode) assert 'b' in mode - return tempfile.TemporaryFile(mode, *args, **kwargs) + return cast(BinaryIO, tempfile.TemporaryFile(mode, *args, **kwargs)) # NamedTemporaryFile does not support an "encoding=" argument on python2. # This wrapper file are always open in byte mode. def namedtempfile( - mode=b'w+b', bufsize=-1, suffix=b'', prefix=b'tmp', dir=None, delete=True + mode: bytes = b'w+b', + bufsize: int = -1, + suffix: bytes = b'', + prefix: bytes = b'tmp', + dir: Optional[bytes] = None, + delete: bool = True, ): mode = sysstr(mode) assert 'b' in mode