diff --git a/mercurial/utils/stringutil.py b/mercurial/utils/stringutil.py --- a/mercurial/utils/stringutil.py +++ b/mercurial/utils/stringutil.py @@ -14,6 +14,11 @@ import re as remod import textwrap import types +from typing import ( + Optional, + overload, +) + from ..i18n import _ from ..thirdparty import attr @@ -30,6 +35,16 @@ from .. import ( regexbytesescapemap = {i: (b'\\' + i) for i in _respecial} +@overload +def reescape(pat: bytes) -> bytes: + ... + + +@overload +def reescape(pat: str) -> str: + ... + + def reescape(pat): """Drop-in replacement for re.escape.""" # NOTE: it is intentional that this works on unicodes and not @@ -45,12 +60,12 @@ def reescape(pat): return pat.encode('latin1') -def pprint(o, bprefix=False, indent=0, level=0): +def pprint(o, bprefix: bool = False, indent: int = 0, level: int = 0) -> bytes: """Pretty print an object.""" return b''.join(pprintgen(o, bprefix=bprefix, indent=indent, level=level)) -def pprintgen(o, bprefix=False, indent=0, level=0): +def pprintgen(o, bprefix: bool = False, indent: int = 0, level: int = 0): """Pretty print an object to a generator of atoms. ``bprefix`` is a flag influencing whether bytestrings are preferred with @@ -250,7 +265,7 @@ def pprintgen(o, bprefix=False, indent=0 yield pycompat.byterepr(o) -def prettyrepr(o): +def prettyrepr(o) -> bytes: """Pretty print a representation of a possibly-nested object""" lines = [] rs = pycompat.byterepr(o) @@ -281,7 +296,7 @@ def prettyrepr(o): return b'\n'.join(b' ' * l + s for l, s in lines) -def buildrepr(r): +def buildrepr(r) -> bytes: """Format an optional printable representation from unexpanded bits ======== ================================= @@ -305,12 +320,12 @@ def buildrepr(r): return pprint(r) -def binary(s): +def binary(s: bytes) -> bool: """return true if a string is binary data""" return bool(s and b'\0' in s) -def _splitpattern(pattern): +def _splitpattern(pattern: bytes): if pattern.startswith(b're:'): return b're', pattern[3:] elif pattern.startswith(b'literal:'): @@ -318,7 +333,7 @@ def _splitpattern(pattern): return b'literal', pattern -def stringmatcher(pattern, casesensitive=True): +def stringmatcher(pattern: bytes, casesensitive: bool = True): """ accepts a string, possibly starting with 're:' or 'literal:' prefix. returns the matcher name, pattern, and matcher function. @@ -379,7 +394,7 @@ def stringmatcher(pattern, casesensitive raise error.ProgrammingError(b'unhandled pattern kind: %s' % kind) -def substringregexp(pattern, flags=0): +def substringregexp(pattern: bytes, flags: int = 0): """Build a regexp object from a string pattern possibly starting with 're:' or 'literal:' prefix. @@ -431,7 +446,7 @@ def substringregexp(pattern, flags=0): raise error.ProgrammingError(b'unhandled pattern kind: %s' % kind) -def shortuser(user): +def shortuser(user: bytes) -> bytes: """Return a short representation of a user name or email address.""" f = user.find(b'@') if f >= 0: @@ -448,7 +463,7 @@ def shortuser(user): return user -def emailuser(user): +def emailuser(user: bytes) -> bytes: """Return the user portion of an email address.""" f = user.find(b'@') if f >= 0: @@ -459,7 +474,7 @@ def emailuser(user): return user -def email(author): +def email(author: bytes) -> bytes: '''get email of author.''' r = author.find(b'>') if r == -1: @@ -467,7 +482,7 @@ def email(author): return author[author.find(b'<') + 1 : r] -def person(author): +def person(author: bytes) -> bytes: """Returns the name before an email address, interpreting it as per RFC 5322 @@ -612,7 +627,7 @@ def parsemailmap(mailmapcontent): return mailmap -def mapname(mailmap, author): +def mapname(mailmap, author: bytes) -> bytes: """Returns the author field according to the mailmap cache, or the original author field. @@ -663,7 +678,7 @@ def mapname(mailmap, author): _correctauthorformat = remod.compile(br'^[^<]+\s<[^<>]+@[^<>]+>$') -def isauthorwellformed(author): +def isauthorwellformed(author: bytes) -> bool: """Return True if the author field is well formed (ie "Contributor Name ") @@ -685,7 +700,7 @@ def isauthorwellformed(author): return _correctauthorformat.match(author) is not None -def firstline(text): +def firstline(text: bytes) -> bytes: """Return the first line of the input""" # Try to avoid running splitlines() on the whole string i = text.find(b'\n') @@ -697,12 +712,13 @@ def firstline(text): return b'' -def ellipsis(text, maxlength=400): +def ellipsis(text: bytes, maxlength: int = 400) -> bytes: """Trim string to at most maxlength (default: 400) columns in display.""" return encoding.trim(text, maxlength, ellipsis=b'...') -def escapestr(s): +def escapestr(s: bytes) -> bytes: + # "bytes" is also a typing shortcut for bytes, bytearray, and memoryview if isinstance(s, memoryview): s = bytes(s) # call underlying function of s.encode('string_escape') directly for @@ -710,7 +726,7 @@ def escapestr(s): return codecs.escape_encode(s)[0] # pytype: disable=module-attr -def unescapestr(s): +def unescapestr(s: bytes) -> bytes: return codecs.escape_decode(s)[0] # pytype: disable=module-attr @@ -724,7 +740,7 @@ def forcebytestr(obj): return pycompat.bytestr(encoding.strtolocal(str(obj))) -def uirepr(s): +def uirepr(s: bytes) -> bytes: # Avoid double backslash in Windows path repr() return pycompat.byterepr(pycompat.bytestr(s)).replace(b'\\\\', b'\\') @@ -838,7 +854,9 @@ def _MBTextWrapper(**kwargs): return tw(**kwargs) -def wrap(line, width, initindent=b'', hangindent=b''): +def wrap( + line: bytes, width: int, initindent: bytes = b'', hangindent: bytes = b'' +) -> bytes: maxindent = max(len(hangindent), len(initindent)) if width <= maxindent: # adjust for weird terminal size @@ -875,7 +893,7 @@ def wrap(line, width, initindent=b'', ha } -def parsebool(s): +def parsebool(s: bytes) -> Optional[bool]: """Parse s into a boolean. If s is not a valid boolean, returns None. @@ -883,7 +901,8 @@ def parsebool(s): return _booleans.get(s.lower(), None) -def parselist(value): +# TODO: make arg mandatory (and fix code below?) +def parselist(value: Optional[bytes]): """parse a configuration value as a list of comma/space separated strings >>> parselist(b'this,is "a small" ,test') @@ -973,7 +992,7 @@ def parselist(value): return result or [] -def evalpythonliteral(s): +def evalpythonliteral(s: bytes): """Evaluate a string containing a Python literal expression""" # We could backport our tokenizer hack to rewrite '' to u'' if we want return ast.literal_eval(s.decode('latin1'))