Mercurial > hg
comparison mercurial/ui.py @ 49795:f1e820cda2f5
typing: add type hints related to message output in mercurial/ui.py
This will shake loose some bytes vs str issues in the doc checker.
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Sat, 10 Dec 2022 14:44:46 -0500 |
parents | 25fe689a4a64 |
children | 0449fb7729d7 |
comparison
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 """ |