##// END OF EJS Templates
typing: add trivial type hints to mercurial/ui.py...
Matt Harbison -
r50692:0449fb77 default
parent child Browse files
Show More
@@ -20,10 +20,15 b' import sys'
20 import traceback
20 import traceback
21
21
22 from typing import (
22 from typing import (
23 Any,
24 Callable,
23 Dict,
25 Dict,
24 List,
26 List,
27 NoReturn,
25 Optional,
28 Optional,
26 Tuple,
29 Tuple,
30 Type,
31 TypeVar,
27 Union,
32 Union,
28 cast,
33 cast,
29 )
34 )
@@ -57,21 +62,23 b' from .utils import ('
57 urlutil,
62 urlutil,
58 )
63 )
59
64
65 _ConfigItems = Dict[Tuple[bytes, bytes], object] # {(section, name) : value}
60 # The **opts args of the various write() methods can be basically anything, but
66 # 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
67 # 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.
68 # handful of known types that are used.
63 _MsgOpts = Union[bytes, bool, List["_PromptChoice"]]
69 _MsgOpts = Union[bytes, bool, List["_PromptChoice"]]
64 _PromptChoice = Tuple[bytes, bytes]
70 _PromptChoice = Tuple[bytes, bytes]
71 _Tui = TypeVar('_Tui', bound="ui")
65
72
66 urlreq = util.urlreq
73 urlreq = util.urlreq
67
74
68 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
75 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
69 _keepalnum = b''.join(
76 _keepalnum: bytes = b''.join(
70 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
77 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
71 )
78 )
72
79
73 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
80 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
74 tweakrc = b"""
81 tweakrc: bytes = b"""
75 [ui]
82 [ui]
76 # The rollback command is dangerous. As a rule, don't use it.
83 # The rollback command is dangerous. As a rule, don't use it.
77 rollback = False
84 rollback = False
@@ -98,7 +105,7 b' showfunc = 1'
98 word-diff = 1
105 word-diff = 1
99 """
106 """
100
107
101 samplehgrcs = {
108 samplehgrcs: Dict[bytes, bytes] = {
102 b'user': b"""# example user config (see 'hg help config' for more info)
109 b'user': b"""# example user config (see 'hg help config' for more info)
103 [ui]
110 [ui]
104 # name and email, e.g.
111 # name and email, e.g.
@@ -187,7 +194,7 b' def _maybebytesurl(maybestr):'
187 class httppasswordmgrdbproxy:
194 class httppasswordmgrdbproxy:
188 """Delays loading urllib2 until it's needed."""
195 """Delays loading urllib2 until it's needed."""
189
196
190 def __init__(self):
197 def __init__(self) -> None:
191 self._mgr = None
198 self._mgr = None
192
199
193 def _get_mgr(self):
200 def _get_mgr(self):
@@ -210,7 +217,7 b' class httppasswordmgrdbproxy:'
210 )
217 )
211
218
212
219
213 def _catchterm(*args):
220 def _catchterm(*args) -> NoReturn:
214 raise error.SignalInterrupt
221 raise error.SignalInterrupt
215
222
216
223
@@ -219,11 +226,11 b' def _catchterm(*args):'
219 _unset = object()
226 _unset = object()
220
227
221 # _reqexithandlers: callbacks run at the end of a request
228 # _reqexithandlers: callbacks run at the end of a request
222 _reqexithandlers = []
229 _reqexithandlers: List = []
223
230
224
231
225 class ui:
232 class ui:
226 def __init__(self, src=None):
233 def __init__(self, src: Optional["ui"] = None) -> None:
227 """Create a fresh new ui object if no src given
234 """Create a fresh new ui object if no src given
228
235
229 Use uimod.ui.load() to create a ui which knows global and user configs.
236 Use uimod.ui.load() to create a ui which knows global and user configs.
@@ -318,13 +325,13 b' class ui:'
318 if k in self.environ:
325 if k in self.environ:
319 self._exportableenviron[k] = self.environ[k]
326 self._exportableenviron[k] = self.environ[k]
320
327
321 def _new_source(self):
328 def _new_source(self) -> None:
322 self._ocfg.new_source()
329 self._ocfg.new_source()
323 self._tcfg.new_source()
330 self._tcfg.new_source()
324 self._ucfg.new_source()
331 self._ucfg.new_source()
325
332
326 @classmethod
333 @classmethod
327 def load(cls):
334 def load(cls: Type[_Tui]) -> _Tui:
328 """Create a ui and load global and user configs"""
335 """Create a ui and load global and user configs"""
329 u = cls()
336 u = cls()
330 # we always trust global config files and environment variables
337 # we always trust global config files and environment variables
@@ -350,7 +357,7 b' class ui:'
350 u._new_source() # anything after that is a different level
357 u._new_source() # anything after that is a different level
351 return u
358 return u
352
359
353 def _maybetweakdefaults(self):
360 def _maybetweakdefaults(self) -> None:
354 if not self.configbool(b'ui', b'tweakdefaults'):
361 if not self.configbool(b'ui', b'tweakdefaults'):
355 return
362 return
356 if self._tweaked or self.plain(b'tweakdefaults'):
363 if self._tweaked or self.plain(b'tweakdefaults'):
@@ -370,17 +377,17 b' class ui:'
370 if not self.hasconfig(section, name):
377 if not self.hasconfig(section, name):
371 self.setconfig(section, name, value, b"<tweakdefaults>")
378 self.setconfig(section, name, value, b"<tweakdefaults>")
372
379
373 def copy(self):
380 def copy(self: _Tui) -> _Tui:
374 return self.__class__(self)
381 return self.__class__(self)
375
382
376 def resetstate(self):
383 def resetstate(self) -> None:
377 """Clear internal state that shouldn't persist across commands"""
384 """Clear internal state that shouldn't persist across commands"""
378 if self._progbar:
385 if self._progbar:
379 self._progbar.resetstate() # reset last-print time of progress bar
386 self._progbar.resetstate() # reset last-print time of progress bar
380 self.httppasswordmgrdb = httppasswordmgrdbproxy()
387 self.httppasswordmgrdb = httppasswordmgrdbproxy()
381
388
382 @contextlib.contextmanager
389 @contextlib.contextmanager
383 def timeblockedsection(self, key):
390 def timeblockedsection(self, key: bytes):
384 # this is open-coded below - search for timeblockedsection to find them
391 # this is open-coded below - search for timeblockedsection to find them
385 starttime = util.timer()
392 starttime = util.timer()
386 try:
393 try:
@@ -425,10 +432,10 b' class ui:'
425 finally:
432 finally:
426 self._uninterruptible = False
433 self._uninterruptible = False
427
434
428 def formatter(self, topic, opts):
435 def formatter(self, topic: bytes, opts):
429 return formatter.formatter(self, self, topic, opts)
436 return formatter.formatter(self, self, topic, opts)
430
437
431 def _trusted(self, fp, f):
438 def _trusted(self, fp, f: bytes) -> bool:
432 st = util.fstat(fp)
439 st = util.fstat(fp)
433 if util.isowner(st):
440 if util.isowner(st):
434 return True
441 return True
@@ -454,7 +461,7 b' class ui:'
454
461
455 def read_resource_config(
462 def read_resource_config(
456 self, name, root=None, trust=False, sections=None, remap=None
463 self, name, root=None, trust=False, sections=None, remap=None
457 ):
464 ) -> None:
458 try:
465 try:
459 fp = resourceutil.open_resource(name[0], name[1])
466 fp = resourceutil.open_resource(name[0], name[1])
460 except IOError:
467 except IOError:
@@ -468,7 +475,7 b' class ui:'
468
475
469 def readconfig(
476 def readconfig(
470 self, filename, root=None, trust=False, sections=None, remap=None
477 self, filename, root=None, trust=False, sections=None, remap=None
471 ):
478 ) -> None:
472 try:
479 try:
473 fp = open(filename, 'rb')
480 fp = open(filename, 'rb')
474 except IOError:
481 except IOError:
@@ -480,7 +487,7 b' class ui:'
480
487
481 def _readconfig(
488 def _readconfig(
482 self, filename, fp, root=None, trust=False, sections=None, remap=None
489 self, filename, fp, root=None, trust=False, sections=None, remap=None
483 ):
490 ) -> None:
484 with fp:
491 with fp:
485 cfg = config.config()
492 cfg = config.config()
486 trusted = sections or trust or self._trusted(fp, filename)
493 trusted = sections or trust or self._trusted(fp, filename)
@@ -496,7 +503,9 b' class ui:'
496
503
497 self._applyconfig(cfg, trusted, root)
504 self._applyconfig(cfg, trusted, root)
498
505
499 def applyconfig(self, configitems, source=b"", root=None):
506 def applyconfig(
507 self, configitems: _ConfigItems, source=b"", root=None
508 ) -> None:
500 """Add configitems from a non-file source. Unlike with ``setconfig()``,
509 """Add configitems from a non-file source. Unlike with ``setconfig()``,
501 they can be overridden by subsequent config file reads. The items are
510 they can be overridden by subsequent config file reads. The items are
502 in the same format as ``configoverride()``, namely a dict of the
511 in the same format as ``configoverride()``, namely a dict of the
@@ -512,7 +521,7 b' class ui:'
512
521
513 self._applyconfig(cfg, True, root)
522 self._applyconfig(cfg, True, root)
514
523
515 def _applyconfig(self, cfg, trusted, root):
524 def _applyconfig(self, cfg, trusted, root) -> None:
516 if self.plain():
525 if self.plain():
517 for k in (
526 for k in (
518 b'debug',
527 b'debug',
@@ -555,7 +564,7 b' class ui:'
555 root = os.path.expanduser(b'~')
564 root = os.path.expanduser(b'~')
556 self.fixconfig(root=root)
565 self.fixconfig(root=root)
557
566
558 def fixconfig(self, root=None, section=None):
567 def fixconfig(self, root=None, section=None) -> None:
559 if section in (None, b'paths'):
568 if section in (None, b'paths'):
560 # expand vars and ~
569 # expand vars and ~
561 # translate paths relative to root (or home) into absolute paths
570 # translate paths relative to root (or home) into absolute paths
@@ -618,12 +627,12 b' class ui:'
618 self._ucfg.backup(section, item),
627 self._ucfg.backup(section, item),
619 )
628 )
620
629
621 def restoreconfig(self, data):
630 def restoreconfig(self, data) -> None:
622 self._ocfg.restore(data[0])
631 self._ocfg.restore(data[0])
623 self._tcfg.restore(data[1])
632 self._tcfg.restore(data[1])
624 self._ucfg.restore(data[2])
633 self._ucfg.restore(data[2])
625
634
626 def setconfig(self, section, name, value, source=b''):
635 def setconfig(self, section, name, value, source=b'') -> None:
627 for cfg in (self._ocfg, self._tcfg, self._ucfg):
636 for cfg in (self._ocfg, self._tcfg, self._ucfg):
628 cfg.set(section, name, value, source)
637 cfg.set(section, name, value, source)
629 self.fixconfig(section=section)
638 self.fixconfig(section=section)
@@ -1009,7 +1018,7 b' class ui:'
1009 for name, value in self.configitems(section, untrusted):
1018 for name, value in self.configitems(section, untrusted):
1010 yield section, name, value
1019 yield section, name, value
1011
1020
1012 def plain(self, feature=None):
1021 def plain(self, feature: Optional[bytes] = None) -> bool:
1013 """is plain mode active?
1022 """is plain mode active?
1014
1023
1015 Plain mode means that all configuration variables which affect
1024 Plain mode means that all configuration variables which affect
@@ -1083,7 +1092,7 b' class ui:'
1083 )
1092 )
1084 return user
1093 return user
1085
1094
1086 def shortuser(self, user):
1095 def shortuser(self, user: bytes) -> bytes:
1087 """Return a short representation of a user name or email address."""
1096 """Return a short representation of a user name or email address."""
1088 if not self.verbose:
1097 if not self.verbose:
1089 user = stringutil.shortuser(user)
1098 user = stringutil.shortuser(user)
@@ -1161,14 +1170,18 b' class ui:'
1161 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1170 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1162
1171
1163 @contextlib.contextmanager
1172 @contextlib.contextmanager
1164 def silent(self, error=False, subproc=False, labeled=False):
1173 def silent(
1174 self, error: bool = False, subproc: bool = False, labeled: bool = False
1175 ):
1165 self.pushbuffer(error=error, subproc=subproc, labeled=labeled)
1176 self.pushbuffer(error=error, subproc=subproc, labeled=labeled)
1166 try:
1177 try:
1167 yield
1178 yield
1168 finally:
1179 finally:
1169 self.popbuffer()
1180 self.popbuffer()
1170
1181
1171 def pushbuffer(self, error=False, subproc=False, labeled=False):
1182 def pushbuffer(
1183 self, error: bool = False, subproc: bool = False, labeled: bool = False
1184 ) -> None:
1172 """install a buffer to capture standard output of the ui object
1185 """install a buffer to capture standard output of the ui object
1173
1186
1174 If error is True, the error output will be captured too.
1187 If error is True, the error output will be captured too.
@@ -1187,7 +1200,7 b' class ui:'
1187 self._bufferstates.append((error, subproc, labeled))
1200 self._bufferstates.append((error, subproc, labeled))
1188 self._bufferapplylabels = labeled
1201 self._bufferapplylabels = labeled
1189
1202
1190 def popbuffer(self):
1203 def popbuffer(self) -> bytes:
1191 '''pop the last buffer and return the buffered output'''
1204 '''pop the last buffer and return the buffered output'''
1192 self._bufferstates.pop()
1205 self._bufferstates.pop()
1193 if self._bufferstates:
1206 if self._bufferstates:
@@ -1197,20 +1210,20 b' class ui:'
1197
1210
1198 return b"".join(self._buffers.pop())
1211 return b"".join(self._buffers.pop())
1199
1212
1200 def _isbuffered(self, dest):
1213 def _isbuffered(self, dest) -> bool:
1201 if dest is self._fout:
1214 if dest is self._fout:
1202 return bool(self._buffers)
1215 return bool(self._buffers)
1203 if dest is self._ferr:
1216 if dest is self._ferr:
1204 return bool(self._bufferstates and self._bufferstates[-1][0])
1217 return bool(self._bufferstates and self._bufferstates[-1][0])
1205 return False
1218 return False
1206
1219
1207 def canwritewithoutlabels(self):
1220 def canwritewithoutlabels(self) -> bool:
1208 '''check if write skips the label'''
1221 '''check if write skips the label'''
1209 if self._buffers and not self._bufferapplylabels:
1222 if self._buffers and not self._bufferapplylabels:
1210 return True
1223 return True
1211 return self._colormode is None
1224 return self._colormode is None
1212
1225
1213 def canbatchlabeledwrites(self):
1226 def canbatchlabeledwrites(self) -> bool:
1214 '''check if write calls with labels are batchable'''
1227 '''check if write calls with labels are batchable'''
1215 # Windows color printing is special, see ``write``.
1228 # Windows color printing is special, see ``write``.
1216 return self._colormode != b'win32'
1229 return self._colormode != b'win32'
@@ -1369,7 +1382,7 b' class ui:'
1369 util.timer() - starttime
1382 util.timer() - starttime
1370 ) * 1000
1383 ) * 1000
1371
1384
1372 def _isatty(self, fh):
1385 def _isatty(self, fh) -> bool:
1373 if self.configbool(b'ui', b'nontty'):
1386 if self.configbool(b'ui', b'nontty'):
1374 return False
1387 return False
1375 return procutil.isatty(fh)
1388 return procutil.isatty(fh)
@@ -1407,10 +1420,10 b' class ui:'
1407 finally:
1420 finally:
1408 self.restorefinout(fin, fout)
1421 self.restorefinout(fin, fout)
1409
1422
1410 def disablepager(self):
1423 def disablepager(self) -> None:
1411 self._disablepager = True
1424 self._disablepager = True
1412
1425
1413 def pager(self, command):
1426 def pager(self, command: bytes) -> None:
1414 """Start a pager for subsequent command output.
1427 """Start a pager for subsequent command output.
1415
1428
1416 Commands which produce a long stream of output should call
1429 Commands which produce a long stream of output should call
@@ -1491,7 +1504,7 b' class ui:'
1491 # warning about a missing pager command.
1504 # warning about a missing pager command.
1492 self.disablepager()
1505 self.disablepager()
1493
1506
1494 def _runpager(self, command, env=None):
1507 def _runpager(self, command: bytes, env=None) -> bool:
1495 """Actually start the pager and set up file descriptors.
1508 """Actually start the pager and set up file descriptors.
1496
1509
1497 This is separate in part so that extensions (like chg) can
1510 This is separate in part so that extensions (like chg) can
@@ -1571,7 +1584,7 b' class ui:'
1571 self._exithandlers.append((func, args, kwargs))
1584 self._exithandlers.append((func, args, kwargs))
1572 return func
1585 return func
1573
1586
1574 def interface(self, feature):
1587 def interface(self, feature: bytes) -> bytes:
1575 """what interface to use for interactive console features?
1588 """what interface to use for interactive console features?
1576
1589
1577 The interface is controlled by the value of `ui.interface` but also by
1590 The interface is controlled by the value of `ui.interface` but also by
@@ -1626,12 +1639,12 b' class ui:'
1626 defaultinterface = b"text"
1639 defaultinterface = b"text"
1627 i = self.config(b"ui", b"interface")
1640 i = self.config(b"ui", b"interface")
1628 if i in alldefaults:
1641 if i in alldefaults:
1629 defaultinterface = i
1642 defaultinterface = cast(bytes, i) # cast to help pytype
1630
1643
1631 choseninterface = defaultinterface
1644 choseninterface: bytes = defaultinterface
1632 f = self.config(b"ui", b"interface.%s" % feature)
1645 f = self.config(b"ui", b"interface.%s" % feature)
1633 if f in availableinterfaces:
1646 if f in availableinterfaces:
1634 choseninterface = f
1647 choseninterface = cast(bytes, f) # cast to help pytype
1635
1648
1636 if i is not None and defaultinterface != i:
1649 if i is not None and defaultinterface != i:
1637 if f is not None:
1650 if f is not None:
@@ -1671,7 +1684,7 b' class ui:'
1671
1684
1672 return i
1685 return i
1673
1686
1674 def termwidth(self):
1687 def termwidth(self) -> int:
1675 """how wide is the terminal in columns?"""
1688 """how wide is the terminal in columns?"""
1676 if b'COLUMNS' in encoding.environ:
1689 if b'COLUMNS' in encoding.environ:
1677 try:
1690 try:
@@ -1918,14 +1931,14 b' class ui:'
1918
1931
1919 def edit(
1932 def edit(
1920 self,
1933 self,
1921 text,
1934 text: bytes,
1922 user,
1935 user: bytes,
1923 extra=None,
1936 extra: Optional[Dict[bytes, Any]] = None, # TODO: value type of bytes?
1924 editform=None,
1937 editform=None,
1925 pending=None,
1938 pending=None,
1926 repopath=None,
1939 repopath: Optional[bytes] = None,
1927 action=None,
1940 action: Optional[bytes] = None,
1928 ):
1941 ) -> bytes:
1929 if action is None:
1942 if action is None:
1930 self.develwarn(
1943 self.develwarn(
1931 b'action is None but will soon be a required '
1944 b'action is None but will soon be a required '
@@ -1994,13 +2007,13 b' class ui:'
1994
2007
1995 def system(
2008 def system(
1996 self,
2009 self,
1997 cmd,
2010 cmd: bytes,
1998 environ=None,
2011 environ=None,
1999 cwd=None,
2012 cwd: Optional[bytes] = None,
2000 onerr=None,
2013 onerr: Optional[Callable[[bytes], Exception]] = None,
2001 errprefix=None,
2014 errprefix: Optional[bytes] = None,
2002 blockedtag=None,
2015 blockedtag: Optional[bytes] = None,
2003 ):
2016 ) -> int:
2004 """execute shell command with appropriate output stream. command
2017 """execute shell command with appropriate output stream. command
2005 output will be redirected if fout is not stdout.
2018 output will be redirected if fout is not stdout.
2006
2019
@@ -2027,12 +2040,12 b' class ui:'
2027 raise onerr(errmsg)
2040 raise onerr(errmsg)
2028 return rc
2041 return rc
2029
2042
2030 def _runsystem(self, cmd, environ, cwd, out):
2043 def _runsystem(self, cmd: bytes, environ, cwd: Optional[bytes], out) -> int:
2031 """actually execute the given shell command (can be overridden by
2044 """actually execute the given shell command (can be overridden by
2032 extensions like chg)"""
2045 extensions like chg)"""
2033 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
2046 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
2034
2047
2035 def traceback(self, exc=None, force=False):
2048 def traceback(self, exc=None, force: bool = False):
2036 """print exception traceback if traceback printing enabled or forced.
2049 """print exception traceback if traceback printing enabled or forced.
2037 only to call in exception handler. returns true if traceback
2050 only to call in exception handler. returns true if traceback
2038 printed."""
2051 printed."""
@@ -2130,7 +2143,7 b' class ui:'
2130 """Returns a logger of the given name; or None if not registered"""
2143 """Returns a logger of the given name; or None if not registered"""
2131 return self._loggers.get(name)
2144 return self._loggers.get(name)
2132
2145
2133 def setlogger(self, name, logger):
2146 def setlogger(self, name, logger) -> None:
2134 """Install logger which can be identified later by the given name
2147 """Install logger which can be identified later by the given name
2135
2148
2136 More than one loggers can be registered. Use extension or module
2149 More than one loggers can be registered. Use extension or module
@@ -2138,7 +2151,7 b' class ui:'
2138 """
2151 """
2139 self._loggers[name] = logger
2152 self._loggers[name] = logger
2140
2153
2141 def log(self, event, msgfmt, *msgargs, **opts):
2154 def log(self, event, msgfmt, *msgargs, **opts) -> None:
2142 """hook for logging facility extensions
2155 """hook for logging facility extensions
2143
2156
2144 event should be a readily-identifiable subsystem, which will
2157 event should be a readily-identifiable subsystem, which will
@@ -2239,7 +2252,7 b' class ui:'
2239 return self._exportableenviron
2252 return self._exportableenviron
2240
2253
2241 @contextlib.contextmanager
2254 @contextlib.contextmanager
2242 def configoverride(self, overrides, source=b""):
2255 def configoverride(self, overrides: _ConfigItems, source: bytes = b""):
2243 """Context manager for temporary config overrides
2256 """Context manager for temporary config overrides
2244 `overrides` must be a dict of the following structure:
2257 `overrides` must be a dict of the following structure:
2245 {(section, name) : value}"""
2258 {(section, name) : value}"""
@@ -2257,7 +2270,7 b' class ui:'
2257 if (b'ui', b'quiet') in overrides:
2270 if (b'ui', b'quiet') in overrides:
2258 self.fixconfig(section=b'ui')
2271 self.fixconfig(section=b'ui')
2259
2272
2260 def estimatememory(self):
2273 def estimatememory(self) -> Optional[int]:
2261 """Provide an estimate for the available system memory in Bytes.
2274 """Provide an estimate for the available system memory in Bytes.
2262
2275
2263 This can be overriden via ui.available-memory. It returns None, if
2276 This can be overriden via ui.available-memory. It returns None, if
@@ -2292,7 +2305,7 b' def haveprogbar() -> bool:'
2292 return _progresssingleton is not None
2305 return _progresssingleton is not None
2293
2306
2294
2307
2295 def _selectmsgdests(ui):
2308 def _selectmsgdests(ui: ui):
2296 name = ui.config(b'ui', b'message-output')
2309 name = ui.config(b'ui', b'message-output')
2297 if name == b'channel':
2310 if name == b'channel':
2298 if ui.fmsg:
2311 if ui.fmsg:
General Comments 0
You need to be logged in to leave comments. Login now