mercurial/ui.py
changeset 49795 f1e820cda2f5
parent 49794 25fe689a4a64
child 49797 0449fb7729d7
equal deleted inserted replaced
49794:25fe689a4a64 49795:f1e820cda2f5
    18 import subprocess
    18 import subprocess
    19 import sys
    19 import sys
    20 import traceback
    20 import traceback
    21 
    21 
    22 from typing import (
    22 from typing import (
       
    23     Dict,
       
    24     List,
    23     Optional,
    25     Optional,
       
    26     Tuple,
       
    27     Union,
       
    28     cast,
    24 )
    29 )
    25 
    30 
    26 from .i18n import _
    31 from .i18n import _
    27 from .node import hex
    32 from .node import hex
    28 from .pycompat import (
    33 from .pycompat import (
    49     procutil,
    54     procutil,
    50     resourceutil,
    55     resourceutil,
    51     stringutil,
    56     stringutil,
    52     urlutil,
    57     urlutil,
    53 )
    58 )
       
    59 
       
    60 # The **opts args of the various write() methods can be basically anything, but
       
    61 # there's no way to express it as "anything but str".  So type it to be the
       
    62 # handful of known types that are used.
       
    63 _MsgOpts = Union[bytes, bool, List["_PromptChoice"]]
       
    64 _PromptChoice = Tuple[bytes, bytes]
    54 
    65 
    55 urlreq = util.urlreq
    66 urlreq = util.urlreq
    56 
    67 
    57 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
    68 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
    58 _keepalnum = b''.join(
    69 _keepalnum = b''.join(
  1202     def canbatchlabeledwrites(self):
  1213     def canbatchlabeledwrites(self):
  1203         '''check if write calls with labels are batchable'''
  1214         '''check if write calls with labels are batchable'''
  1204         # Windows color printing is special, see ``write``.
  1215         # Windows color printing is special, see ``write``.
  1205         return self._colormode != b'win32'
  1216         return self._colormode != b'win32'
  1206 
  1217 
  1207     def write(self, *args, **opts):
  1218     def write(self, *args: bytes, **opts: _MsgOpts) -> None:
  1208         """write args to output
  1219         """write args to output
  1209 
  1220 
  1210         By default, this method simply writes to the buffer or stdout.
  1221         By default, this method simply writes to the buffer or stdout.
  1211         Color mode can be set on the UI class to have the output decorated
  1222         Color mode can be set on the UI class to have the output decorated
  1212         with color modifier before being written to stdout.
  1223         with color modifier before being written to stdout.
  1260         finally:
  1271         finally:
  1261             self._blockedtimes[b'stdio_blocked'] += (
  1272             self._blockedtimes[b'stdio_blocked'] += (
  1262                 util.timer() - starttime
  1273                 util.timer() - starttime
  1263             ) * 1000
  1274             ) * 1000
  1264 
  1275 
  1265     def write_err(self, *args, **opts):
  1276     def write_err(self, *args: bytes, **opts: _MsgOpts) -> None:
  1266         self._write(self._ferr, *args, **opts)
  1277         self._write(self._ferr, *args, **opts)
  1267 
  1278 
  1268     def _write(self, dest, *args, **opts):
  1279     def _write(self, dest, *args: bytes, **opts: _MsgOpts) -> None:
  1269         # update write() as well if you touch this code
  1280         # update write() as well if you touch this code
  1270         if self._isbuffered(dest):
  1281         if self._isbuffered(dest):
  1271             label = opts.get('label', b'')
  1282             label = opts.get('label', b'')
  1272             if label and self._bufferapplylabels:
  1283             if label and self._bufferapplylabels:
  1273                 self._buffers[-1].extend(self.label(a, label) for a in args)
  1284                 self._buffers[-1].extend(self.label(a, label) for a in args)
  1274             else:
  1285             else:
  1275                 self._buffers[-1].extend(args)
  1286                 self._buffers[-1].extend(args)
  1276         else:
  1287         else:
  1277             self._writenobuf(dest, *args, **opts)
  1288             self._writenobuf(dest, *args, **opts)
  1278 
  1289 
  1279     def _writenobuf(self, dest, *args, **opts):
  1290     def _writenobuf(self, dest, *args: bytes, **opts: _MsgOpts) -> None:
  1280         # update write() as well if you touch this code
  1291         # update write() as well if you touch this code
  1281         if not opts.get('keepprogressbar', False):
  1292         if not opts.get('keepprogressbar', False):
  1282             self._progclear()
  1293             self._progclear()
  1283         msg = b''.join(args)
  1294         msg = b''.join(args)
  1284 
  1295 
  1316         finally:
  1327         finally:
  1317             self._blockedtimes[b'stdio_blocked'] += (
  1328             self._blockedtimes[b'stdio_blocked'] += (
  1318                 util.timer() - starttime
  1329                 util.timer() - starttime
  1319             ) * 1000
  1330             ) * 1000
  1320 
  1331 
  1321     def _writemsg(self, dest, *args, **opts):
  1332     def _writemsg(self, dest, *args: bytes, **opts: _MsgOpts) -> None:
  1322         timestamp = self.showtimestamp and opts.get('type') in {
  1333         timestamp = self.showtimestamp and opts.get('type') in {
  1323             b'debug',
  1334             b'debug',
  1324             b'error',
  1335             b'error',
  1325             b'note',
  1336             b'note',
  1326             b'status',
  1337             b'status',
  1333             ) + args
  1344             ) + args
  1334         _writemsgwith(self._write, dest, *args, **opts)
  1345         _writemsgwith(self._write, dest, *args, **opts)
  1335         if timestamp:
  1346         if timestamp:
  1336             dest.flush()
  1347             dest.flush()
  1337 
  1348 
  1338     def _writemsgnobuf(self, dest, *args, **opts):
  1349     def _writemsgnobuf(self, dest, *args: bytes, **opts: _MsgOpts) -> None:
  1339         _writemsgwith(self._writenobuf, dest, *args, **opts)
  1350         _writemsgwith(self._writenobuf, dest, *args, **opts)
  1340 
  1351 
  1341     def flush(self):
  1352     def flush(self) -> None:
  1342         # opencode timeblockedsection because this is a critical path
  1353         # opencode timeblockedsection because this is a critical path
  1343         starttime = util.timer()
  1354         starttime = util.timer()
  1344         try:
  1355         try:
  1345             try:
  1356             try:
  1346                 self._fout.flush()
  1357                 self._fout.flush()
  1695             # usually those are non-interactive
  1706             # usually those are non-interactive
  1696             return self._isatty(self._fout)
  1707             return self._isatty(self._fout)
  1697 
  1708 
  1698         return i
  1709         return i
  1699 
  1710 
  1700     def _readline(self, prompt=b' ', promptopts=None):
  1711     def _readline(
       
  1712         self,
       
  1713         prompt: bytes = b' ',
       
  1714         promptopts: Optional[Dict[str, _MsgOpts]] = None,
       
  1715     ) -> bytes:
  1701         # Replacing stdin/stdout temporarily is a hard problem on Python 3
  1716         # Replacing stdin/stdout temporarily is a hard problem on Python 3
  1702         # because they have to be text streams with *no buffering*. Instead,
  1717         # because they have to be text streams with *no buffering*. Instead,
  1703         # we use rawinput() only if call_readline() will be invoked by
  1718         # we use rawinput() only if call_readline() will be invoked by
  1704         # PyOS_Readline(), so no I/O will be made at Python layer.
  1719         # PyOS_Readline(), so no I/O will be made at Python layer.
  1705         usereadline = (
  1720         usereadline = (
  1777             return r
  1792             return r
  1778         except EOFError:
  1793         except EOFError:
  1779             raise error.ResponseExpected()
  1794             raise error.ResponseExpected()
  1780 
  1795 
  1781     @staticmethod
  1796     @staticmethod
  1782     def extractchoices(prompt):
  1797     def extractchoices(prompt: bytes) -> Tuple[bytes, List[_PromptChoice]]:
  1783         """Extract prompt message and list of choices from specified prompt.
  1798         """Extract prompt message and list of choices from specified prompt.
  1784 
  1799 
  1785         This returns tuple "(message, choices)", and "choices" is the
  1800         This returns tuple "(message, choices)", and "choices" is the
  1786         list of tuple "(response character, text without &)".
  1801         list of tuple "(response character, text without &)".
  1787 
  1802 
  1809             ampidx = s.index(b'&')
  1824             ampidx = s.index(b'&')
  1810             return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
  1825             return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
  1811 
  1826 
  1812         return (msg, [choicetuple(s) for s in choices])
  1827         return (msg, [choicetuple(s) for s in choices])
  1813 
  1828 
  1814     def promptchoice(self, prompt, default=0):
  1829     def promptchoice(self, prompt: bytes, default: int = 0) -> int:
  1815         """Prompt user with a message, read response, and ensure it matches
  1830         """Prompt user with a message, read response, and ensure it matches
  1816         one of the provided choices. The prompt is formatted as follows:
  1831         one of the provided choices. The prompt is formatted as follows:
  1817 
  1832 
  1818            "would you like fries with that (Yn)? $$ &Yes $$ &No"
  1833            "would you like fries with that (Yn)? $$ &Yes $$ &No"
  1819 
  1834 
  1829             if r.lower() in resps:
  1844             if r.lower() in resps:
  1830                 return resps.index(r.lower())
  1845                 return resps.index(r.lower())
  1831             # TODO: shouldn't it be a warning?
  1846             # TODO: shouldn't it be a warning?
  1832             self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
  1847             self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
  1833 
  1848 
  1834     def getpass(self, prompt=None, default=None):
  1849     def getpass(
       
  1850         self, prompt: Optional[bytes] = None, default: Optional[bytes] = None
       
  1851     ) -> Optional[bytes]:
  1835         if not self.interactive():
  1852         if not self.interactive():
  1836             return default
  1853             return default
  1837         try:
  1854         try:
  1838             self._writemsg(
  1855             self._writemsg(
  1839                 self._fmsgerr,
  1856                 self._fmsgerr,
  1852                 else:
  1869                 else:
  1853                     return util.get_password()
  1870                     return util.get_password()
  1854         except EOFError:
  1871         except EOFError:
  1855             raise error.ResponseExpected()
  1872             raise error.ResponseExpected()
  1856 
  1873 
  1857     def status(self, *msg, **opts):
  1874     def status(self, *msg: bytes, **opts: _MsgOpts) -> None:
  1858         """write status message to output (if ui.quiet is False)
  1875         """write status message to output (if ui.quiet is False)
  1859 
  1876 
  1860         This adds an output label of "ui.status".
  1877         This adds an output label of "ui.status".
  1861         """
  1878         """
  1862         if not self.quiet:
  1879         if not self.quiet:
  1863             self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
  1880             self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
  1864 
  1881 
  1865     def warn(self, *msg, **opts):
  1882     def warn(self, *msg: bytes, **opts: _MsgOpts) -> None:
  1866         """write warning message to output (stderr)
  1883         """write warning message to output (stderr)
  1867 
  1884 
  1868         This adds an output label of "ui.warning".
  1885         This adds an output label of "ui.warning".
  1869         """
  1886         """
  1870         self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
  1887         self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
  1871 
  1888 
  1872     def error(self, *msg, **opts):
  1889     def error(self, *msg: bytes, **opts: _MsgOpts) -> None:
  1873         """write error message to output (stderr)
  1890         """write error message to output (stderr)
  1874 
  1891 
  1875         This adds an output label of "ui.error".
  1892         This adds an output label of "ui.error".
  1876         """
  1893         """
  1877         self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
  1894         self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
  1878 
  1895 
  1879     def note(self, *msg, **opts):
  1896     def note(self, *msg: bytes, **opts: _MsgOpts) -> None:
  1880         """write note to output (if ui.verbose is True)
  1897         """write note to output (if ui.verbose is True)
  1881 
  1898 
  1882         This adds an output label of "ui.note".
  1899         This adds an output label of "ui.note".
  1883         """
  1900         """
  1884         if self.verbose:
  1901         if self.verbose:
  1885             self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
  1902             self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
  1886 
  1903 
  1887     def debug(self, *msg, **opts):
  1904     def debug(self, *msg: bytes, **opts: _MsgOpts) -> None:
  1888         """write debug message to output (if ui.debugflag is True)
  1905         """write debug message to output (if ui.debugflag is True)
  1889 
  1906 
  1890         This adds an output label of "ui.debug".
  1907         This adds an output label of "ui.debug".
  1891         """
  1908         """
  1892         if self.debugflag:
  1909         if self.debugflag:
  2146             for logger in activeloggers:
  2163             for logger in activeloggers:
  2147                 logger.log(self, event, msg, opts)
  2164                 logger.log(self, event, msg, opts)
  2148         finally:
  2165         finally:
  2149             self._loggers = registeredloggers
  2166             self._loggers = registeredloggers
  2150 
  2167 
  2151     def label(self, msg, label):
  2168     def label(self, msg: bytes, label: bytes) -> bytes:
  2152         """style msg based on supplied label
  2169         """style msg based on supplied label
  2153 
  2170 
  2154         If some color mode is enabled, this will add the necessary control
  2171         If some color mode is enabled, this will add the necessary control
  2155         characters to apply such color. In addition, 'debug' color mode adds
  2172         characters to apply such color. In addition, 'debug' color mode adds
  2156         markup showing which label affects a piece of text.
  2173         markup showing which label affects a piece of text.
  2160         """
  2177         """
  2161         if self._colormode is not None:
  2178         if self._colormode is not None:
  2162             return color.colorlabel(self, msg, label)
  2179             return color.colorlabel(self, msg, label)
  2163         return msg
  2180         return msg
  2164 
  2181 
  2165     def develwarn(self, msg, stacklevel=1, config=None):
  2182     def develwarn(
       
  2183         self, msg: bytes, stacklevel: int = 1, config: Optional[bytes] = None
       
  2184     ) -> None:
  2166         """issue a developer warning message
  2185         """issue a developer warning message
  2167 
  2186 
  2168         Use 'stacklevel' to report the offender some layers further up in the
  2187         Use 'stacklevel' to report the offender some layers further up in the
  2169         stack.
  2188         stack.
  2170         """
  2189         """
  2192 
  2211 
  2193             # avoid cycles
  2212             # avoid cycles
  2194             del curframe
  2213             del curframe
  2195             del calframe
  2214             del calframe
  2196 
  2215 
  2197     def deprecwarn(self, msg, version, stacklevel=2):
  2216     def deprecwarn(
       
  2217         self, msg: bytes, version: bytes, stacklevel: int = 2
       
  2218     ) -> None:
  2198         """issue a deprecation warning
  2219         """issue a deprecation warning
  2199 
  2220 
  2200         - msg: message explaining what is deprecated and how to upgrade,
  2221         - msg: message explaining what is deprecated and how to upgrade,
  2201         - version: last version where the API will be supported,
  2222         - version: last version where the API will be supported,
  2202         """
  2223         """
  2285     if name == b'stderr':
  2306     if name == b'stderr':
  2286         return ui.ferr, ui.ferr
  2307         return ui.ferr, ui.ferr
  2287     raise error.Abort(b'invalid ui.message-output destination: %s' % name)
  2308     raise error.Abort(b'invalid ui.message-output destination: %s' % name)
  2288 
  2309 
  2289 
  2310 
  2290 def _writemsgwith(write, dest, *args, **opts):
  2311 def _writemsgwith(write, dest, *args: bytes, **opts: _MsgOpts) -> None:
  2291     """Write ui message with the given ui._write*() function
  2312     """Write ui message with the given ui._write*() function
  2292 
  2313 
  2293     The specified message type is translated to 'ui.<type>' label if the dest
  2314     The specified message type is translated to 'ui.<type>' label if the dest
  2294     isn't a structured channel, so that the message will be colorized.
  2315     isn't a structured channel, so that the message will be colorized.
  2295     """
  2316     """