mercurial/ui.py
changeset 43076 2372284d9457
parent 42672 51a2e3102db2
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    44 )
    44 )
    45 
    45 
    46 urlreq = util.urlreq
    46 urlreq = util.urlreq
    47 
    47 
    48 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
    48 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
    49 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
    49 _keepalnum = ''.join(
    50                      if not c.isalnum())
    50     c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
       
    51 )
    51 
    52 
    52 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
    53 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
    53 tweakrc = b"""
    54 tweakrc = b"""
    54 [ui]
    55 [ui]
    55 # The rollback command is dangerous. As a rule, don't use it.
    56 # The rollback command is dangerous. As a rule, don't use it.
    76 showfunc = 1
    77 showfunc = 1
    77 word-diff = 1
    78 word-diff = 1
    78 """
    79 """
    79 
    80 
    80 samplehgrcs = {
    81 samplehgrcs = {
    81     'user':
    82     'user': b"""# example user config (see 'hg help config' for more info)
    82 b"""# example user config (see 'hg help config' for more info)
       
    83 [ui]
    83 [ui]
    84 # name and email, e.g.
    84 # name and email, e.g.
    85 # username = Jane Doe <jdoe@example.com>
    85 # username = Jane Doe <jdoe@example.com>
    86 username =
    86 username =
    87 
    87 
   104 #
   104 #
   105 # histedit =
   105 # histedit =
   106 # rebase =
   106 # rebase =
   107 # uncommit =
   107 # uncommit =
   108 """,
   108 """,
   109 
   109     'cloned': b"""# example repository config (see 'hg help config' for more info)
   110     'cloned':
       
   111 b"""# example repository config (see 'hg help config' for more info)
       
   112 [paths]
   110 [paths]
   113 default = %s
   111 default = %s
   114 
   112 
   115 # path aliases to other clones of this repo in URLs or filesystem paths
   113 # path aliases to other clones of this repo in URLs or filesystem paths
   116 # (see 'hg help config.paths' for more info)
   114 # (see 'hg help config.paths' for more info)
   121 
   119 
   122 [ui]
   120 [ui]
   123 # name and email (local to this repository, optional), e.g.
   121 # name and email (local to this repository, optional), e.g.
   124 # username = Jane Doe <jdoe@example.com>
   122 # username = Jane Doe <jdoe@example.com>
   125 """,
   123 """,
   126 
   124     'local': b"""# example repository config (see 'hg help config' for more info)
   127     'local':
       
   128 b"""# example repository config (see 'hg help config' for more info)
       
   129 [paths]
   125 [paths]
   130 # path aliases to other clones of this repo in URLs or filesystem paths
   126 # path aliases to other clones of this repo in URLs or filesystem paths
   131 # (see 'hg help config.paths' for more info)
   127 # (see 'hg help config.paths' for more info)
   132 #
   128 #
   133 # default         = http://example.com/hg/example-repo
   129 # default         = http://example.com/hg/example-repo
   137 
   133 
   138 [ui]
   134 [ui]
   139 # name and email (local to this repository, optional), e.g.
   135 # name and email (local to this repository, optional), e.g.
   140 # username = Jane Doe <jdoe@example.com>
   136 # username = Jane Doe <jdoe@example.com>
   141 """,
   137 """,
   142 
   138     'global': b"""# example system-wide hg config (see 'hg help config' for more info)
   143     'global':
       
   144 b"""# example system-wide hg config (see 'hg help config' for more info)
       
   145 
   139 
   146 [ui]
   140 [ui]
   147 # uncomment to disable color in command output
   141 # uncomment to disable color in command output
   148 # (see 'hg help color' for details)
   142 # (see 'hg help color' for details)
   149 # color = never
   143 # color = never
   159 # blackbox =
   153 # blackbox =
   160 # churn =
   154 # churn =
   161 """,
   155 """,
   162 }
   156 }
   163 
   157 
       
   158 
   164 def _maybestrurl(maybebytes):
   159 def _maybestrurl(maybebytes):
   165     return pycompat.rapply(pycompat.strurl, maybebytes)
   160     return pycompat.rapply(pycompat.strurl, maybebytes)
   166 
   161 
       
   162 
   167 def _maybebytesurl(maybestr):
   163 def _maybebytesurl(maybestr):
   168     return pycompat.rapply(pycompat.bytesurl, maybestr)
   164     return pycompat.rapply(pycompat.bytesurl, maybestr)
   169 
   165 
       
   166 
   170 class httppasswordmgrdbproxy(object):
   167 class httppasswordmgrdbproxy(object):
   171     """Delays loading urllib2 until it's needed."""
   168     """Delays loading urllib2 until it's needed."""
       
   169 
   172     def __init__(self):
   170     def __init__(self):
   173         self._mgr = None
   171         self._mgr = None
   174 
   172 
   175     def _get_mgr(self):
   173     def _get_mgr(self):
   176         if self._mgr is None:
   174         if self._mgr is None:
   177             self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
   175             self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
   178         return self._mgr
   176         return self._mgr
   179 
   177 
   180     def add_password(self, realm, uris, user, passwd):
   178     def add_password(self, realm, uris, user, passwd):
   181         return self._get_mgr().add_password(
   179         return self._get_mgr().add_password(
   182             _maybestrurl(realm), _maybestrurl(uris),
   180             _maybestrurl(realm),
   183             _maybestrurl(user), _maybestrurl(passwd))
   181             _maybestrurl(uris),
       
   182             _maybestrurl(user),
       
   183             _maybestrurl(passwd),
       
   184         )
   184 
   185 
   185     def find_user_password(self, realm, uri):
   186     def find_user_password(self, realm, uri):
   186         mgr = self._get_mgr()
   187         mgr = self._get_mgr()
   187         return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
   188         return _maybebytesurl(
   188                                                      _maybestrurl(uri)))
   189             mgr.find_user_password(_maybestrurl(realm), _maybestrurl(uri))
       
   190         )
       
   191 
   189 
   192 
   190 def _catchterm(*args):
   193 def _catchterm(*args):
   191     raise error.SignalInterrupt
   194     raise error.SignalInterrupt
       
   195 
   192 
   196 
   193 # unique object used to detect no default value has been provided when
   197 # unique object used to detect no default value has been provided when
   194 # retrieving configuration value.
   198 # retrieving configuration value.
   195 _unset = object()
   199 _unset = object()
   196 
   200 
   197 # _reqexithandlers: callbacks run at the end of a request
   201 # _reqexithandlers: callbacks run at the end of a request
   198 _reqexithandlers = []
   202 _reqexithandlers = []
       
   203 
   199 
   204 
   200 class ui(object):
   205 class ui(object):
   201     def __init__(self, src=None):
   206     def __init__(self, src=None):
   202         """Create a fresh new ui object if no src given
   207         """Create a fresh new ui object if no src given
   203 
   208 
   214         # This exists to prevent an extra list lookup.
   219         # This exists to prevent an extra list lookup.
   215         self._bufferapplylabels = None
   220         self._bufferapplylabels = None
   216         self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
   221         self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
   217         self._reportuntrusted = True
   222         self._reportuntrusted = True
   218         self._knownconfig = configitems.coreitems
   223         self._knownconfig = configitems.coreitems
   219         self._ocfg = config.config() # overlay
   224         self._ocfg = config.config()  # overlay
   220         self._tcfg = config.config() # trusted
   225         self._tcfg = config.config()  # trusted
   221         self._ucfg = config.config() # untrusted
   226         self._ucfg = config.config()  # untrusted
   222         self._trustusers = set()
   227         self._trustusers = set()
   223         self._trustgroups = set()
   228         self._trustgroups = set()
   224         self.callhooks = True
   229         self.callhooks = True
   225         # Insecure server connections requested.
   230         # Insecure server connections requested.
   226         self.insecureconnections = False
   231         self.insecureconnections = False
   347         starttime = util.timer()
   352         starttime = util.timer()
   348         try:
   353         try:
   349             yield
   354             yield
   350         finally:
   355         finally:
   351             self._blockedtimes[key + '_blocked'] += (
   356             self._blockedtimes[key + '_blocked'] += (
   352                 (util.timer() - starttime) * 1000)
   357                 util.timer() - starttime
       
   358             ) * 1000
   353 
   359 
   354     @contextlib.contextmanager
   360     @contextlib.contextmanager
   355     def uninterruptible(self):
   361     def uninterruptible(self):
   356         """Mark an operation as unsafe.
   362         """Mark an operation as unsafe.
   357 
   363 
   359         few are risky (for example repair.strip). This context manager
   365         few are risky (for example repair.strip). This context manager
   360         lets you advise Mercurial that something risky is happening so
   366         lets you advise Mercurial that something risky is happening so
   361         that control-C etc can be blocked if desired.
   367         that control-C etc can be blocked if desired.
   362         """
   368         """
   363         enabled = self.configbool('experimental', 'nointerrupt')
   369         enabled = self.configbool('experimental', 'nointerrupt')
   364         if (enabled and
   370         if enabled and self.configbool(
   365             self.configbool('experimental', 'nointerrupt-interactiveonly')):
   371             'experimental', 'nointerrupt-interactiveonly'
       
   372         ):
   366             enabled = self.interactive()
   373             enabled = self.interactive()
   367         if self._uninterruptible or not enabled:
   374         if self._uninterruptible or not enabled:
   368             # if nointerrupt support is turned off, the process isn't
   375             # if nointerrupt support is turned off, the process isn't
   369             # interactive, or we're already in an uninterruptible
   376             # interactive, or we're already in an uninterruptible
   370             # block, do nothing.
   377             # block, do nothing.
   371             yield
   378             yield
   372             return
   379             return
       
   380 
   373         def warn():
   381         def warn():
   374             self.warn(_("shutting down cleanly\n"))
   382             self.warn(_("shutting down cleanly\n"))
   375             self.warn(
   383             self.warn(
   376                 _("press ^C again to terminate immediately (dangerous)\n"))
   384                 _("press ^C again to terminate immediately (dangerous)\n")
       
   385             )
   377             return True
   386             return True
       
   387 
   378         with procutil.uninterruptible(warn):
   388         with procutil.uninterruptible(warn):
   379             try:
   389             try:
   380                 self._uninterruptible = True
   390                 self._uninterruptible = True
   381                 yield
   391                 yield
   382             finally:
   392             finally:
   398         group = util.groupname(st.st_gid)
   408         group = util.groupname(st.st_gid)
   399         if user in tusers or group in tgroups or user == util.username():
   409         if user in tusers or group in tgroups or user == util.username():
   400             return True
   410             return True
   401 
   411 
   402         if self._reportuntrusted:
   412         if self._reportuntrusted:
   403             self.warn(_('not trusting file %s from untrusted '
   413             self.warn(
   404                         'user %s, group %s\n') % (f, user, group))
   414                 _('not trusting file %s from untrusted ' 'user %s, group %s\n')
       
   415                 % (f, user, group)
       
   416             )
   405         return False
   417         return False
   406 
   418 
   407     def readconfig(self, filename, root=None, trust=False,
   419     def readconfig(
   408                    sections=None, remap=None):
   420         self, filename, root=None, trust=False, sections=None, remap=None
       
   421     ):
   409         try:
   422         try:
   410             fp = open(filename, r'rb')
   423             fp = open(filename, r'rb')
   411         except IOError:
   424         except IOError:
   412             if not sections: # ignore unless we were looking for something
   425             if not sections:  # ignore unless we were looking for something
   413                 return
   426                 return
   414             raise
   427             raise
   415 
   428 
   416         cfg = config.config()
   429         cfg = config.config()
   417         trusted = sections or trust or self._trusted(fp, filename)
   430         trusted = sections or trust or self._trusted(fp, filename)
   423             if trusted:
   436             if trusted:
   424                 raise
   437                 raise
   425             self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst))
   438             self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst))
   426 
   439 
   427         if self.plain():
   440         if self.plain():
   428             for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
   441             for k in (
   429                       'logtemplate', 'message-output', 'statuscopies', 'style',
   442                 'debug',
   430                       'traceback', 'verbose'):
   443                 'fallbackencoding',
       
   444                 'quiet',
       
   445                 'slash',
       
   446                 'logtemplate',
       
   447                 'message-output',
       
   448                 'statuscopies',
       
   449                 'style',
       
   450                 'traceback',
       
   451                 'verbose',
       
   452             ):
   431                 if k in cfg['ui']:
   453                 if k in cfg['ui']:
   432                     del cfg['ui'][k]
   454                     del cfg['ui'][k]
   433             for k, v in cfg.items('defaults'):
   455             for k, v in cfg.items('defaults'):
   434                 del cfg['defaults'][k]
   456                 del cfg['defaults'][k]
   435             for k, v in cfg.items('commands'):
   457             for k, v in cfg.items('commands'):
   467                         continue
   489                         continue
   468                     if not p:
   490                     if not p:
   469                         continue
   491                         continue
   470                     if '%%' in p:
   492                     if '%%' in p:
   471                         s = self.configsource('paths', n) or 'none'
   493                         s = self.configsource('paths', n) or 'none'
   472                         self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
   494                         self.warn(
   473                                   % (n, p, s))
   495                             _("(deprecated '%%' in path %s=%s from %s)\n")
       
   496                             % (n, p, s)
       
   497                         )
   474                         p = p.replace('%%', '%')
   498                         p = p.replace('%%', '%')
   475                     p = util.expandpath(p)
   499                     p = util.expandpath(p)
   476                     if not util.hasscheme(p) and not os.path.isabs(p):
   500                     if not util.hasscheme(p) and not os.path.isabs(p):
   477                         p = os.path.normpath(os.path.join(root, p))
   501                         p = os.path.normpath(os.path.join(root, p))
   478                     c.set("paths", n, p)
   502                     c.set("paths", n, p)
   483             self.debugflag = self.configbool('ui', 'debug')
   507             self.debugflag = self.configbool('ui', 'debug')
   484             self.verbose = self.debugflag or self.configbool('ui', 'verbose')
   508             self.verbose = self.debugflag or self.configbool('ui', 'verbose')
   485             self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
   509             self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
   486             if self.verbose and self.quiet:
   510             if self.verbose and self.quiet:
   487                 self.quiet = self.verbose = False
   511                 self.quiet = self.verbose = False
   488             self._reportuntrusted = self.debugflag or self.configbool("ui",
   512             self._reportuntrusted = self.debugflag or self.configbool(
   489                 "report_untrusted")
   513                 "ui", "report_untrusted"
       
   514             )
   490             self.tracebackflag = self.configbool('ui', 'traceback')
   515             self.tracebackflag = self.configbool('ui', 'traceback')
   491             self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
   516             self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
   492 
   517 
   493         if section in (None, 'trusted'):
   518         if section in (None, 'trusted'):
   494             # update trust information
   519             # update trust information
   502             if tracked:
   527             if tracked:
   503                 logger = loggingutil.fileobjectlogger(self._ferr, tracked)
   528                 logger = loggingutil.fileobjectlogger(self._ferr, tracked)
   504                 self.setlogger(b'debug', logger)
   529                 self.setlogger(b'debug', logger)
   505 
   530 
   506     def backupconfig(self, section, item):
   531     def backupconfig(self, section, item):
   507         return (self._ocfg.backup(section, item),
   532         return (
   508                 self._tcfg.backup(section, item),
   533             self._ocfg.backup(section, item),
   509                 self._ucfg.backup(section, item),)
   534             self._tcfg.backup(section, item),
       
   535             self._ucfg.backup(section, item),
       
   536         )
       
   537 
   510     def restoreconfig(self, data):
   538     def restoreconfig(self, data):
   511         self._ocfg.restore(data[0])
   539         self._ocfg.restore(data[0])
   512         self._tcfg.restore(data[1])
   540         self._tcfg.restore(data[1])
   513         self._ucfg.restore(data[2])
   541         self._ucfg.restore(data[2])
   514 
   542 
   524     def configsource(self, section, name, untrusted=False):
   552     def configsource(self, section, name, untrusted=False):
   525         return self._data(untrusted).source(section, name)
   553         return self._data(untrusted).source(section, name)
   526 
   554 
   527     def config(self, section, name, default=_unset, untrusted=False):
   555     def config(self, section, name, default=_unset, untrusted=False):
   528         """return the plain string version of a config"""
   556         """return the plain string version of a config"""
   529         value = self._config(section, name, default=default,
   557         value = self._config(
   530                              untrusted=untrusted)
   558             section, name, default=default, untrusted=untrusted
       
   559         )
   531         if value is _unset:
   560         if value is _unset:
   532             return None
   561             return None
   533         return value
   562         return value
   534 
   563 
   535     def _config(self, section, name, default=_unset, untrusted=False):
   564     def _config(self, section, name, default=_unset, untrusted=False):
   542             if callable(item.default):
   571             if callable(item.default):
   543                 itemdefault = item.default()
   572                 itemdefault = item.default()
   544             else:
   573             else:
   545                 itemdefault = item.default
   574                 itemdefault = item.default
   546         else:
   575         else:
   547             msg = ("accessing unregistered config item: '%s.%s'")
   576             msg = "accessing unregistered config item: '%s.%s'"
   548             msg %= (section, name)
   577             msg %= (section, name)
   549             self.develwarn(msg, 2, 'warn-config-unknown')
   578             self.develwarn(msg, 2, 'warn-config-unknown')
   550 
   579 
   551         if default is _unset:
   580         if default is _unset:
   552             if item is None:
   581             if item is None:
   556                 msg = "config item requires an explicit default value: '%s.%s'"
   585                 msg = "config item requires an explicit default value: '%s.%s'"
   557                 msg %= (section, name)
   586                 msg %= (section, name)
   558                 self.develwarn(msg, 2, 'warn-config-default')
   587                 self.develwarn(msg, 2, 'warn-config-default')
   559             else:
   588             else:
   560                 value = itemdefault
   589                 value = itemdefault
   561         elif (item is not None
   590         elif (
   562               and item.default is not configitems.dynamicdefault
   591             item is not None
   563               and default != itemdefault):
   592             and item.default is not configitems.dynamicdefault
   564             msg = ("specifying a mismatched default value for a registered "
   593             and default != itemdefault
   565                    "config item: '%s.%s' '%s'")
   594         ):
       
   595             msg = (
       
   596                 "specifying a mismatched default value for a registered "
       
   597                 "config item: '%s.%s' '%s'"
       
   598             )
   566             msg %= (section, name, pycompat.bytestr(default))
   599             msg %= (section, name, pycompat.bytestr(default))
   567             self.develwarn(msg, 2, 'warn-config-default')
   600             self.develwarn(msg, 2, 'warn-config-default')
   568 
   601 
   569         for s, n in alternates:
   602         for s, n in alternates:
   570             candidate = self._data(untrusted).get(s, n, None)
   603             candidate = self._data(untrusted).get(s, n, None)
   574 
   607 
   575         if self.debugflag and not untrusted and self._reportuntrusted:
   608         if self.debugflag and not untrusted and self._reportuntrusted:
   576             for s, n in alternates:
   609             for s, n in alternates:
   577                 uvalue = self._ucfg.get(s, n)
   610                 uvalue = self._ucfg.get(s, n)
   578                 if uvalue is not None and uvalue != value:
   611                 if uvalue is not None and uvalue != value:
   579                     self.debug("ignoring untrusted configuration option "
   612                     self.debug(
   580                                "%s.%s = %s\n" % (s, n, uvalue))
   613                         "ignoring untrusted configuration option "
       
   614                         "%s.%s = %s\n" % (s, n, uvalue)
       
   615                     )
   581         return value
   616         return value
   582 
   617 
   583     def configsuboptions(self, section, name, default=_unset, untrusted=False):
   618     def configsuboptions(self, section, name, default=_unset, untrusted=False):
   584         """Get a config option and all sub-options.
   619         """Get a config option and all sub-options.
   585 
   620 
   594         data = self._data(untrusted)
   629         data = self._data(untrusted)
   595         sub = {}
   630         sub = {}
   596         prefix = '%s:' % name
   631         prefix = '%s:' % name
   597         for k, v in data.items(section):
   632         for k, v in data.items(section):
   598             if k.startswith(prefix):
   633             if k.startswith(prefix):
   599                 sub[k[len(prefix):]] = v
   634                 sub[k[len(prefix) :]] = v
   600 
   635 
   601         if self.debugflag and not untrusted and self._reportuntrusted:
   636         if self.debugflag and not untrusted and self._reportuntrusted:
   602             for k, v in sub.items():
   637             for k, v in sub.items():
   603                 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
   638                 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
   604                 if uvalue is not None and uvalue != v:
   639                 if uvalue is not None and uvalue != v:
   605                     self.debug('ignoring untrusted configuration option '
   640                     self.debug(
   606                                '%s:%s.%s = %s\n' % (section, name, k, uvalue))
   641                         'ignoring untrusted configuration option '
       
   642                         '%s:%s.%s = %s\n' % (section, name, k, uvalue)
       
   643                     )
   607 
   644 
   608         return main, sub
   645         return main, sub
   609 
   646 
   610     def configpath(self, section, name, default=_unset, untrusted=False):
   647     def configpath(self, section, name, default=_unset, untrusted=False):
   611         'get a path config item, expanded relative to repo root or config file'
   648         'get a path config item, expanded relative to repo root or config file'
   649             return default
   686             return default
   650         if isinstance(v, bool):
   687         if isinstance(v, bool):
   651             return v
   688             return v
   652         b = stringutil.parsebool(v)
   689         b = stringutil.parsebool(v)
   653         if b is None:
   690         if b is None:
   654             raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
   691             raise error.ConfigError(
   655                                     % (section, name, v))
   692                 _("%s.%s is not a boolean ('%s')") % (section, name, v)
       
   693             )
   656         return b
   694         return b
   657 
   695 
   658     def configwith(self, convert, section, name, default=_unset,
   696     def configwith(
   659                    desc=None, untrusted=False):
   697         self, convert, section, name, default=_unset, desc=None, untrusted=False
       
   698     ):
   660         """parse a configuration element with a conversion function
   699         """parse a configuration element with a conversion function
   661 
   700 
   662         >>> u = ui(); s = b'foo'
   701         >>> u = ui(); s = b'foo'
   663         >>> u.setconfig(s, b'float1', b'42')
   702         >>> u.setconfig(s, b'float1', b'42')
   664         >>> u.configwith(float, s, b'float1')
   703         >>> u.configwith(float, s, b'float1')
   679         ConfigError: foo.invalid is not a valid womble ('somevalue')
   718         ConfigError: foo.invalid is not a valid womble ('somevalue')
   680         """
   719         """
   681 
   720 
   682         v = self.config(section, name, default, untrusted)
   721         v = self.config(section, name, default, untrusted)
   683         if v is None:
   722         if v is None:
   684             return v # do not attempt to convert None
   723             return v  # do not attempt to convert None
   685         try:
   724         try:
   686             return convert(v)
   725             return convert(v)
   687         except (ValueError, error.ParseError):
   726         except (ValueError, error.ParseError):
   688             if desc is None:
   727             if desc is None:
   689                 desc = pycompat.sysbytes(convert.__name__)
   728                 desc = pycompat.sysbytes(convert.__name__)
   690             raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
   729             raise error.ConfigError(
   691                                     % (section, name, desc, v))
   730                 _("%s.%s is not a valid %s ('%s')") % (section, name, desc, v)
       
   731             )
   692 
   732 
   693     def configint(self, section, name, default=_unset, untrusted=False):
   733     def configint(self, section, name, default=_unset, untrusted=False):
   694         """parse a configuration element as an integer
   734         """parse a configuration element as an integer
   695 
   735 
   696         >>> u = ui(); s = b'foo'
   736         >>> u = ui(); s = b'foo'
   707         Traceback (most recent call last):
   747         Traceback (most recent call last):
   708             ...
   748             ...
   709         ConfigError: foo.invalid is not a valid integer ('somevalue')
   749         ConfigError: foo.invalid is not a valid integer ('somevalue')
   710         """
   750         """
   711 
   751 
   712         return self.configwith(int, section, name, default, 'integer',
   752         return self.configwith(
   713                                untrusted)
   753             int, section, name, default, 'integer', untrusted
       
   754         )
   714 
   755 
   715     def configbytes(self, section, name, default=_unset, untrusted=False):
   756     def configbytes(self, section, name, default=_unset, untrusted=False):
   716         """parse a configuration element as a quantity in bytes
   757         """parse a configuration element as a quantity in bytes
   717 
   758 
   718         Units can be specified as b (bytes), k or kb (kilobytes), m or
   759         Units can be specified as b (bytes), k or kb (kilobytes), m or
   742         if not isinstance(value, bytes):
   783         if not isinstance(value, bytes):
   743             return value
   784             return value
   744         try:
   785         try:
   745             return util.sizetoint(value)
   786             return util.sizetoint(value)
   746         except error.ParseError:
   787         except error.ParseError:
   747             raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
   788             raise error.ConfigError(
   748                                     % (section, name, value))
   789                 _("%s.%s is not a byte quantity ('%s')")
       
   790                 % (section, name, value)
       
   791             )
   749 
   792 
   750     def configlist(self, section, name, default=_unset, untrusted=False):
   793     def configlist(self, section, name, default=_unset, untrusted=False):
   751         """parse a configuration element as a list of comma/space separated
   794         """parse a configuration element as a list of comma/space separated
   752         strings
   795         strings
   753 
   796 
   758         >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
   801         >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
   759         >>> u.configlist(s, b'list2')
   802         >>> u.configlist(s, b'list2')
   760         ['this', 'is', 'a small', 'test']
   803         ['this', 'is', 'a small', 'test']
   761         """
   804         """
   762         # default is not always a list
   805         # default is not always a list
   763         v = self.configwith(config.parselist, section, name, default,
   806         v = self.configwith(
   764                                'list', untrusted)
   807             config.parselist, section, name, default, 'list', untrusted
       
   808         )
   765         if isinstance(v, bytes):
   809         if isinstance(v, bytes):
   766             return config.parselist(v)
   810             return config.parselist(v)
   767         elif v is None:
   811         elif v is None:
   768             return []
   812             return []
   769         return v
   813         return v
   775         >>> u.setconfig(s, b'date', b'0 0')
   819         >>> u.setconfig(s, b'date', b'0 0')
   776         >>> u.configdate(s, b'date')
   820         >>> u.configdate(s, b'date')
   777         (0, 0)
   821         (0, 0)
   778         """
   822         """
   779         if self.config(section, name, default, untrusted):
   823         if self.config(section, name, default, untrusted):
   780             return self.configwith(dateutil.parsedate, section, name, default,
   824             return self.configwith(
   781                                    'date', untrusted)
   825                 dateutil.parsedate, section, name, default, 'date', untrusted
       
   826             )
   782         if default is _unset:
   827         if default is _unset:
   783             return None
   828             return None
   784         return default
   829         return default
   785 
   830 
   786     def configdefault(self, section, name):
   831     def configdefault(self, section, name):
   806         if ignoresub:
   851         if ignoresub:
   807             items = [i for i in items if ':' not in i[0]]
   852             items = [i for i in items if ':' not in i[0]]
   808         if self.debugflag and not untrusted and self._reportuntrusted:
   853         if self.debugflag and not untrusted and self._reportuntrusted:
   809             for k, v in self._ucfg.items(section):
   854             for k, v in self._ucfg.items(section):
   810                 if self._tcfg.get(section, k) != v:
   855                 if self._tcfg.get(section, k) != v:
   811                     self.debug("ignoring untrusted configuration option "
   856                     self.debug(
   812                                "%s.%s = %s\n" % (section, k, v))
   857                         "ignoring untrusted configuration option "
       
   858                         "%s.%s = %s\n" % (section, k, v)
       
   859                     )
   813         return items
   860         return items
   814 
   861 
   815     def walkconfig(self, untrusted=False):
   862     def walkconfig(self, untrusted=False):
   816         cfg = self._data(untrusted)
   863         cfg = self._data(untrusted)
   817         for section in cfg.sections():
   864         for section in cfg.sections():
   832         The return value can either be
   879         The return value can either be
   833         - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
   880         - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
   834         - False if feature is disabled by default and not included in HGPLAIN
   881         - False if feature is disabled by default and not included in HGPLAIN
   835         - True otherwise
   882         - True otherwise
   836         '''
   883         '''
   837         if ('HGPLAIN' not in encoding.environ and
   884         if (
   838                 'HGPLAINEXCEPT' not in encoding.environ):
   885             'HGPLAIN' not in encoding.environ
       
   886             and 'HGPLAINEXCEPT' not in encoding.environ
       
   887         ):
   839             return False
   888             return False
   840         exceptions = encoding.environ.get('HGPLAINEXCEPT',
   889         exceptions = (
   841                 '').strip().split(',')
   890             encoding.environ.get('HGPLAINEXCEPT', '').strip().split(',')
       
   891         )
   842         # TODO: add support for HGPLAIN=+feature,-feature syntax
   892         # TODO: add support for HGPLAIN=+feature,-feature syntax
   843         if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
   893         if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
   844             exceptions.append('strictflags')
   894             exceptions.append('strictflags')
   845         if feature and exceptions:
   895         if feature and exceptions:
   846             return feature not in exceptions
   896             return feature not in exceptions
   867             return user
   917             return user
   868         if user is None and self.configbool("ui", "askusername"):
   918         if user is None and self.configbool("ui", "askusername"):
   869             user = self.prompt(_("enter a commit username:"), default=None)
   919             user = self.prompt(_("enter a commit username:"), default=None)
   870         if user is None and not self.interactive():
   920         if user is None and not self.interactive():
   871             try:
   921             try:
   872                 user = '%s@%s' % (procutil.getuser(),
   922                 user = '%s@%s' % (
   873                                   encoding.strtolocal(socket.getfqdn()))
   923                     procutil.getuser(),
       
   924                     encoding.strtolocal(socket.getfqdn()),
       
   925                 )
   874                 self.warn(_("no username found, using '%s' instead\n") % user)
   926                 self.warn(_("no username found, using '%s' instead\n") % user)
   875             except KeyError:
   927             except KeyError:
   876                 pass
   928                 pass
   877         if not user:
   929         if not user:
   878             raise error.Abort(_('no username supplied'),
   930             raise error.Abort(
   879                              hint=_("use 'hg config --edit' "
   931                 _('no username supplied'),
   880                                     'to set your username'))
   932                 hint=_("use 'hg config --edit' " 'to set your username'),
       
   933             )
   881         if "\n" in user:
   934         if "\n" in user:
   882             raise error.Abort(_("username %r contains a newline\n")
   935             raise error.Abort(
   883                               % pycompat.bytestr(user))
   936                 _("username %r contains a newline\n") % pycompat.bytestr(user)
       
   937             )
   884         return user
   938         return user
   885 
   939 
   886     def shortuser(self, user):
   940     def shortuser(self, user):
   887         """Return a short representation of a user name or email address."""
   941         """Return a short representation of a user name or email address."""
   888         if not self.verbose:
   942         if not self.verbose:
  1041                 dest.write(msg)
  1095                 dest.write(msg)
  1042         except IOError as err:
  1096         except IOError as err:
  1043             raise error.StdioError(err)
  1097             raise error.StdioError(err)
  1044         finally:
  1098         finally:
  1045             self._blockedtimes['stdio_blocked'] += (
  1099             self._blockedtimes['stdio_blocked'] += (
  1046                 (util.timer() - starttime) * 1000)
  1100                 util.timer() - starttime
       
  1101             ) * 1000
  1047 
  1102 
  1048     def write_err(self, *args, **opts):
  1103     def write_err(self, *args, **opts):
  1049         self._write(self._ferr, *args, **opts)
  1104         self._write(self._ferr, *args, **opts)
  1050 
  1105 
  1051     def _write(self, dest, *args, **opts):
  1106     def _write(self, dest, *args, **opts):
  1085             # stderr may be buffered under win32 when redirected to files,
  1140             # stderr may be buffered under win32 when redirected to files,
  1086             # including stdout.
  1141             # including stdout.
  1087             if dest is self._ferr and not getattr(self._ferr, 'closed', False):
  1142             if dest is self._ferr and not getattr(self._ferr, 'closed', False):
  1088                 dest.flush()
  1143                 dest.flush()
  1089         except IOError as err:
  1144         except IOError as err:
  1090             if (dest is self._ferr
  1145             if dest is self._ferr and err.errno in (
  1091                 and err.errno in (errno.EPIPE, errno.EIO, errno.EBADF)):
  1146                 errno.EPIPE,
       
  1147                 errno.EIO,
       
  1148                 errno.EBADF,
       
  1149             ):
  1092                 # no way to report the error, so ignore it
  1150                 # no way to report the error, so ignore it
  1093                 return
  1151                 return
  1094             raise error.StdioError(err)
  1152             raise error.StdioError(err)
  1095         finally:
  1153         finally:
  1096             self._blockedtimes['stdio_blocked'] += (
  1154             self._blockedtimes['stdio_blocked'] += (
  1097                 (util.timer() - starttime) * 1000)
  1155                 util.timer() - starttime
       
  1156             ) * 1000
  1098 
  1157 
  1099     def _writemsg(self, dest, *args, **opts):
  1158     def _writemsg(self, dest, *args, **opts):
  1100         _writemsgwith(self._write, dest, *args, **opts)
  1159         _writemsgwith(self._write, dest, *args, **opts)
  1101 
  1160 
  1102     def _writemsgnobuf(self, dest, *args, **opts):
  1161     def _writemsgnobuf(self, dest, *args, **opts):
  1117                 except IOError as err:
  1176                 except IOError as err:
  1118                     if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
  1177                     if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
  1119                         raise error.StdioError(err)
  1178                         raise error.StdioError(err)
  1120         finally:
  1179         finally:
  1121             self._blockedtimes['stdio_blocked'] += (
  1180             self._blockedtimes['stdio_blocked'] += (
  1122                 (util.timer() - starttime) * 1000)
  1181                 util.timer() - starttime
       
  1182             ) * 1000
  1123 
  1183 
  1124     def _isatty(self, fh):
  1184     def _isatty(self, fh):
  1125         if self.configbool('ui', 'nontty'):
  1185         if self.configbool('ui', 'nontty'):
  1126             return False
  1186             return False
  1127         return procutil.isatty(fh)
  1187         return procutil.isatty(fh)
  1173 
  1233 
  1174         Args:
  1234         Args:
  1175           command: The full, non-aliased name of the command. That is, "log"
  1235           command: The full, non-aliased name of the command. That is, "log"
  1176                    not "history, "summary" not "summ", etc.
  1236                    not "history, "summary" not "summ", etc.
  1177         """
  1237         """
  1178         if (self._disablepager
  1238         if self._disablepager or self.pageractive:
  1179             or self.pageractive):
       
  1180             # how pager should do is already determined
  1239             # how pager should do is already determined
  1181             return
  1240             return
  1182 
  1241 
  1183         if not command.startswith('internal-always-') and (
  1242         if not command.startswith('internal-always-') and (
  1184             # explicit --pager=on (= 'internal-always-' prefix) should
  1243             # explicit --pager=on (= 'internal-always-' prefix) should
  1191             # formatted() will need some adjustment.
  1250             # formatted() will need some adjustment.
  1192             or not self.formatted()
  1251             or not self.formatted()
  1193             or self.plain()
  1252             or self.plain()
  1194             or self._buffers
  1253             or self._buffers
  1195             # TODO: expose debugger-enabled on the UI object
  1254             # TODO: expose debugger-enabled on the UI object
  1196             or '--debugger' in pycompat.sysargv):
  1255             or '--debugger' in pycompat.sysargv
       
  1256         ):
  1197             # We only want to paginate if the ui appears to be
  1257             # We only want to paginate if the ui appears to be
  1198             # interactive, the user didn't say HGPLAIN or
  1258             # interactive, the user didn't say HGPLAIN or
  1199             # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
  1259             # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
  1200             return
  1260             return
  1201 
  1261 
  1206         pagerenv = {}
  1266         pagerenv = {}
  1207         for name, value in rcutil.defaultpagerenv().items():
  1267         for name, value in rcutil.defaultpagerenv().items():
  1208             if name not in encoding.environ:
  1268             if name not in encoding.environ:
  1209                 pagerenv[name] = value
  1269                 pagerenv[name] = value
  1210 
  1270 
  1211         self.debug('starting pager for command %s\n' %
  1271         self.debug(
  1212                    stringutil.pprint(command))
  1272             'starting pager for command %s\n' % stringutil.pprint(command)
       
  1273         )
  1213         self.flush()
  1274         self.flush()
  1214 
  1275 
  1215         wasformatted = self.formatted()
  1276         wasformatted = self.formatted()
  1216         if util.safehasattr(signal, "SIGPIPE"):
  1277         if util.safehasattr(signal, "SIGPIPE"):
  1217             signal.signal(signal.SIGPIPE, _catchterm)
  1278             signal.signal(signal.SIGPIPE, _catchterm)
  1255             # user so we can also get sane bad PAGER behavior.  MSYS has
  1316             # user so we can also get sane bad PAGER behavior.  MSYS has
  1256             # `more.exe`, so do a cmd.exe style resolution of the executable to
  1317             # `more.exe`, so do a cmd.exe style resolution of the executable to
  1257             # determine which one to use.
  1318             # determine which one to use.
  1258             fullcmd = procutil.findexe(command)
  1319             fullcmd = procutil.findexe(command)
  1259             if not fullcmd:
  1320             if not fullcmd:
  1260                 self.warn(_("missing pager command '%s', skipping pager\n")
  1321                 self.warn(
  1261                           % command)
  1322                     _("missing pager command '%s', skipping pager\n") % command
       
  1323                 )
  1262                 return False
  1324                 return False
  1263 
  1325 
  1264             command = fullcmd
  1326             command = fullcmd
  1265 
  1327 
  1266         try:
  1328         try:
  1267             pager = subprocess.Popen(
  1329             pager = subprocess.Popen(
  1268                 procutil.tonativestr(command), shell=shell, bufsize=-1,
  1330                 procutil.tonativestr(command),
  1269                 close_fds=procutil.closefds, stdin=subprocess.PIPE,
  1331                 shell=shell,
  1270                 stdout=procutil.stdout, stderr=procutil.stderr,
  1332                 bufsize=-1,
  1271                 env=procutil.tonativeenv(procutil.shellenviron(env)))
  1333                 close_fds=procutil.closefds,
       
  1334                 stdin=subprocess.PIPE,
       
  1335                 stdout=procutil.stdout,
       
  1336                 stderr=procutil.stderr,
       
  1337                 env=procutil.tonativeenv(procutil.shellenviron(env)),
       
  1338             )
  1272         except OSError as e:
  1339         except OSError as e:
  1273             if e.errno == errno.ENOENT and not shell:
  1340             if e.errno == errno.ENOENT and not shell:
  1274                 self.warn(_("missing pager command '%s', skipping pager\n")
  1341                 self.warn(
  1275                           % command)
  1342                     _("missing pager command '%s', skipping pager\n") % command
       
  1343                 )
  1276                 return False
  1344                 return False
  1277             raise
  1345             raise
  1278 
  1346 
  1279         # back up original file descriptors
  1347         # back up original file descriptors
  1280         stdoutfd = os.dup(procutil.stdout.fileno())
  1348         stdoutfd = os.dup(procutil.stdout.fileno())
  1330         the default curses interface (crecord at the moment).
  1398         the default curses interface (crecord at the moment).
  1331         """
  1399         """
  1332         alldefaults = frozenset(["text", "curses"])
  1400         alldefaults = frozenset(["text", "curses"])
  1333 
  1401 
  1334         featureinterfaces = {
  1402         featureinterfaces = {
  1335             "chunkselector": [
  1403             "chunkselector": ["text", "curses",],
  1336                 "text",
  1404             "histedit": ["text", "curses",],
  1337                 "curses",
       
  1338             ],
       
  1339             "histedit": [
       
  1340                 "text",
       
  1341                 "curses",
       
  1342             ],
       
  1343         }
  1405         }
  1344 
  1406 
  1345         # Feature-specific interface
  1407         # Feature-specific interface
  1346         if feature not in featureinterfaces.keys():
  1408         if feature not in featureinterfaces.keys():
  1347             # Programming error, not user error
  1409             # Programming error, not user error
  1350         availableinterfaces = frozenset(featureinterfaces[feature])
  1412         availableinterfaces = frozenset(featureinterfaces[feature])
  1351         if alldefaults > availableinterfaces:
  1413         if alldefaults > availableinterfaces:
  1352             # Programming error, not user error. We need a use case to
  1414             # Programming error, not user error. We need a use case to
  1353             # define the right thing to do here.
  1415             # define the right thing to do here.
  1354             raise ValueError(
  1416             raise ValueError(
  1355                 "Feature %s does not handle all default interfaces" %
  1417                 "Feature %s does not handle all default interfaces" % feature
  1356                 feature)
  1418             )
  1357 
  1419 
  1358         if self.plain() or encoding.environ.get('TERM') == 'dumb':
  1420         if self.plain() or encoding.environ.get('TERM') == 'dumb':
  1359             return "text"
  1421             return "text"
  1360 
  1422 
  1361         # Default interface for all the features
  1423         # Default interface for all the features
  1369         if f in availableinterfaces:
  1431         if f in availableinterfaces:
  1370             choseninterface = f
  1432             choseninterface = f
  1371 
  1433 
  1372         if i is not None and defaultinterface != i:
  1434         if i is not None and defaultinterface != i:
  1373             if f is not None:
  1435             if f is not None:
  1374                 self.warn(_("invalid value for ui.interface: %s\n") %
  1436                 self.warn(_("invalid value for ui.interface: %s\n") % (i,))
  1375                           (i,))
       
  1376             else:
  1437             else:
  1377                 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
  1438                 self.warn(
  1378                          (i, choseninterface))
  1439                     _("invalid value for ui.interface: %s (using %s)\n")
       
  1440                     % (i, choseninterface)
       
  1441                 )
  1379         if f is not None and choseninterface != f:
  1442         if f is not None and choseninterface != f:
  1380             self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
  1443             self.warn(
  1381                       (feature, f, choseninterface))
  1444                 _("invalid value for ui.interface.%s: %s (using %s)\n")
       
  1445                 % (feature, f, choseninterface)
       
  1446             )
  1382 
  1447 
  1383         return choseninterface
  1448         return choseninterface
  1384 
  1449 
  1385     def interactive(self):
  1450     def interactive(self):
  1386         '''is interactive input allowed?
  1451         '''is interactive input allowed?
  1445     def _readline(self, prompt=' ', promptopts=None):
  1510     def _readline(self, prompt=' ', promptopts=None):
  1446         # Replacing stdin/stdout temporarily is a hard problem on Python 3
  1511         # Replacing stdin/stdout temporarily is a hard problem on Python 3
  1447         # because they have to be text streams with *no buffering*. Instead,
  1512         # because they have to be text streams with *no buffering*. Instead,
  1448         # we use rawinput() only if call_readline() will be invoked by
  1513         # we use rawinput() only if call_readline() will be invoked by
  1449         # PyOS_Readline(), so no I/O will be made at Python layer.
  1514         # PyOS_Readline(), so no I/O will be made at Python layer.
  1450         usereadline = (self._isatty(self._fin) and self._isatty(self._fout)
  1515         usereadline = (
  1451                        and procutil.isstdin(self._fin)
  1516             self._isatty(self._fin)
  1452                        and procutil.isstdout(self._fout))
  1517             and self._isatty(self._fout)
       
  1518             and procutil.isstdin(self._fin)
       
  1519             and procutil.isstdout(self._fout)
       
  1520         )
  1453         if usereadline:
  1521         if usereadline:
  1454             try:
  1522             try:
  1455                 # magically add command line editing support, where
  1523                 # magically add command line editing support, where
  1456                 # available
  1524                 # available
  1457                 import readline
  1525                 import readline
       
  1526 
  1458                 # force demandimport to really load the module
  1527                 # force demandimport to really load the module
  1459                 readline.read_history_file
  1528                 readline.read_history_file
  1460                 # windows sometimes raises something other than ImportError
  1529                 # windows sometimes raises something other than ImportError
  1461             except Exception:
  1530             except Exception:
  1462                 usereadline = False
  1531                 usereadline = False
  1463 
  1532 
  1464         if self._colormode == 'win32' or not usereadline:
  1533         if self._colormode == 'win32' or not usereadline:
  1465             if not promptopts:
  1534             if not promptopts:
  1466                 promptopts = {}
  1535                 promptopts = {}
  1467             self._writemsgnobuf(self._fmsgout, prompt, type='prompt',
  1536             self._writemsgnobuf(
  1468                                 **promptopts)
  1537                 self._fmsgout, prompt, type='prompt', **promptopts
       
  1538             )
  1469             self.flush()
  1539             self.flush()
  1470             prompt = ' '
  1540             prompt = ' '
  1471         else:
  1541         else:
  1472             prompt = self.label(prompt, 'ui.prompt') + ' '
  1542             prompt = self.label(prompt, 'ui.prompt') + ' '
  1473 
  1543 
  1498 
  1568 
  1499     def _prompt(self, msg, **opts):
  1569     def _prompt(self, msg, **opts):
  1500         default = opts[r'default']
  1570         default = opts[r'default']
  1501         if not self.interactive():
  1571         if not self.interactive():
  1502             self._writemsg(self._fmsgout, msg, ' ', type='prompt', **opts)
  1572             self._writemsg(self._fmsgout, msg, ' ', type='prompt', **opts)
  1503             self._writemsg(self._fmsgout, default or '', "\n",
  1573             self._writemsg(
  1504                            type='promptecho')
  1574                 self._fmsgout, default or '', "\n", type='promptecho'
       
  1575             )
  1505             return default
  1576             return default
  1506         try:
  1577         try:
  1507             r = self._readline(prompt=msg, promptopts=opts)
  1578             r = self._readline(prompt=msg, promptopts=opts)
  1508             if not r:
  1579             if not r:
  1509                 r = default
  1580                 r = default
  1534         # choices containing spaces, ASCII, or basically anything
  1605         # choices containing spaces, ASCII, or basically anything
  1535         # except an ampersand followed by a character.
  1606         # except an ampersand followed by a character.
  1536         m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
  1607         m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
  1537         msg = m.group(1)
  1608         msg = m.group(1)
  1538         choices = [p.strip(' ') for p in m.group(2).split('$$')]
  1609         choices = [p.strip(' ') for p in m.group(2).split('$$')]
       
  1610 
  1539         def choicetuple(s):
  1611         def choicetuple(s):
  1540             ampidx = s.index('&')
  1612             ampidx = s.index('&')
  1541             return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
  1613             return s[ampidx + 1 : ampidx + 2].lower(), s.replace('&', '', 1)
       
  1614 
  1542         return (msg, [choicetuple(s) for s in choices])
  1615         return (msg, [choicetuple(s) for s in choices])
  1543 
  1616 
  1544     def promptchoice(self, prompt, default=0):
  1617     def promptchoice(self, prompt, default=0):
  1545         """Prompt user with a message, read response, and ensure it matches
  1618         """Prompt user with a message, read response, and ensure it matches
  1546         one of the provided choices. The prompt is formatted as follows:
  1619         one of the provided choices. The prompt is formatted as follows:
  1563 
  1636 
  1564     def getpass(self, prompt=None, default=None):
  1637     def getpass(self, prompt=None, default=None):
  1565         if not self.interactive():
  1638         if not self.interactive():
  1566             return default
  1639             return default
  1567         try:
  1640         try:
  1568             self._writemsg(self._fmsgerr, prompt or _('password: '),
  1641             self._writemsg(
  1569                            type='prompt', password=True)
  1642                 self._fmsgerr,
       
  1643                 prompt or _('password: '),
       
  1644                 type='prompt',
       
  1645                 password=True,
       
  1646             )
  1570             # disable getpass() only if explicitly specified. it's still valid
  1647             # disable getpass() only if explicitly specified. it's still valid
  1571             # to interact with tty even if fin is not a tty.
  1648             # to interact with tty even if fin is not a tty.
  1572             with self.timeblockedsection('stdio'):
  1649             with self.timeblockedsection('stdio'):
  1573                 if self.configbool('ui', 'nontty'):
  1650                 if self.configbool('ui', 'nontty'):
  1574                     l = self._fin.readline()
  1651                     l = self._fin.readline()
  1617         '''
  1694         '''
  1618         if self.debugflag:
  1695         if self.debugflag:
  1619             self._writemsg(self._fmsgout, type='debug', *msg, **opts)
  1696             self._writemsg(self._fmsgout, type='debug', *msg, **opts)
  1620             self.log(b'debug', b'%s', b''.join(msg))
  1697             self.log(b'debug', b'%s', b''.join(msg))
  1621 
  1698 
  1622     def edit(self, text, user, extra=None, editform=None, pending=None,
  1699     def edit(
  1623              repopath=None, action=None):
  1700         self,
       
  1701         text,
       
  1702         user,
       
  1703         extra=None,
       
  1704         editform=None,
       
  1705         pending=None,
       
  1706         repopath=None,
       
  1707         action=None,
       
  1708     ):
  1624         if action is None:
  1709         if action is None:
  1625             self.develwarn('action is None but will soon be a required '
  1710             self.develwarn(
  1626                            'parameter to ui.edit()')
  1711                 'action is None but will soon be a required '
       
  1712                 'parameter to ui.edit()'
       
  1713             )
  1627         extra_defaults = {
  1714         extra_defaults = {
  1628             'prefix': 'editor',
  1715             'prefix': 'editor',
  1629             'suffix': '.txt',
  1716             'suffix': '.txt',
  1630         }
  1717         }
  1631         if extra is not None:
  1718         if extra is not None:
  1632             if extra.get('suffix') is not None:
  1719             if extra.get('suffix') is not None:
  1633                 self.develwarn('extra.suffix is not None but will soon be '
  1720                 self.develwarn(
  1634                                'ignored by ui.edit()')
  1721                     'extra.suffix is not None but will soon be '
       
  1722                     'ignored by ui.edit()'
       
  1723                 )
  1635             extra_defaults.update(extra)
  1724             extra_defaults.update(extra)
  1636         extra = extra_defaults
  1725         extra = extra_defaults
  1637 
  1726 
  1638         if action == 'diff':
  1727         if action == 'diff':
  1639             suffix = '.diff'
  1728             suffix = '.diff'
  1643             suffix = extra['suffix']
  1732             suffix = extra['suffix']
  1644 
  1733 
  1645         rdir = None
  1734         rdir = None
  1646         if self.configbool('experimental', 'editortmpinhg'):
  1735         if self.configbool('experimental', 'editortmpinhg'):
  1647             rdir = repopath
  1736             rdir = repopath
  1648         (fd, name) = pycompat.mkstemp(prefix='hg-' + extra['prefix'] + '-',
  1737         (fd, name) = pycompat.mkstemp(
  1649                                       suffix=suffix,
  1738             prefix='hg-' + extra['prefix'] + '-', suffix=suffix, dir=rdir
  1650                                       dir=rdir)
  1739         )
  1651         try:
  1740         try:
  1652             f = os.fdopen(fd, r'wb')
  1741             f = os.fdopen(fd, r'wb')
  1653             f.write(util.tonativeeol(text))
  1742             f.write(util.tonativeeol(text))
  1654             f.close()
  1743             f.close()
  1655 
  1744 
  1665             if pending:
  1754             if pending:
  1666                 environ.update({'HG_PENDING': pending})
  1755                 environ.update({'HG_PENDING': pending})
  1667 
  1756 
  1668             editor = self.geteditor()
  1757             editor = self.geteditor()
  1669 
  1758 
  1670             self.system("%s \"%s\"" % (editor, name),
  1759             self.system(
  1671                         environ=environ,
  1760                 "%s \"%s\"" % (editor, name),
  1672                         onerr=error.Abort, errprefix=_("edit failed"),
  1761                 environ=environ,
  1673                         blockedtag='editor')
  1762                 onerr=error.Abort,
       
  1763                 errprefix=_("edit failed"),
       
  1764                 blockedtag='editor',
       
  1765             )
  1674 
  1766 
  1675             f = open(name, r'rb')
  1767             f = open(name, r'rb')
  1676             t = util.fromnativeeol(f.read())
  1768             t = util.fromnativeeol(f.read())
  1677             f.close()
  1769             f.close()
  1678         finally:
  1770         finally:
  1679             os.unlink(name)
  1771             os.unlink(name)
  1680 
  1772 
  1681         return t
  1773         return t
  1682 
  1774 
  1683     def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
  1775     def system(
  1684                blockedtag=None):
  1776         self,
       
  1777         cmd,
       
  1778         environ=None,
       
  1779         cwd=None,
       
  1780         onerr=None,
       
  1781         errprefix=None,
       
  1782         blockedtag=None,
       
  1783     ):
  1685         '''execute shell command with appropriate output stream. command
  1784         '''execute shell command with appropriate output stream. command
  1686         output will be redirected if fout is not stdout.
  1785         output will be redirected if fout is not stdout.
  1687 
  1786 
  1688         if command fails and onerr is None, return status, else raise onerr
  1787         if command fails and onerr is None, return status, else raise onerr
  1689         object as exception.
  1788         object as exception.
  1697         if any(s[1] for s in self._bufferstates):
  1796         if any(s[1] for s in self._bufferstates):
  1698             out = self
  1797             out = self
  1699         with self.timeblockedsection(blockedtag):
  1798         with self.timeblockedsection(blockedtag):
  1700             rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
  1799             rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
  1701         if rc and onerr:
  1800         if rc and onerr:
  1702             errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
  1801             errmsg = '%s %s' % (
  1703                                 procutil.explainexit(rc))
  1802                 os.path.basename(cmd.split(None, 1)[0]),
       
  1803                 procutil.explainexit(rc),
       
  1804             )
  1704             if errprefix:
  1805             if errprefix:
  1705                 errmsg = '%s: %s' % (errprefix, errmsg)
  1806                 errmsg = '%s: %s' % (errprefix, errmsg)
  1706             raise onerr(errmsg)
  1807             raise onerr(errmsg)
  1707         return rc
  1808         return rc
  1708 
  1809 
  1724                 causetb = traceback.format_tb(cause[2])
  1825                 causetb = traceback.format_tb(cause[2])
  1725                 exctb = traceback.format_tb(exc[2])
  1826                 exctb = traceback.format_tb(exc[2])
  1726                 exconly = traceback.format_exception_only(cause[0], cause[1])
  1827                 exconly = traceback.format_exception_only(cause[0], cause[1])
  1727 
  1828 
  1728                 # exclude frame where 'exc' was chained and rethrown from exctb
  1829                 # exclude frame where 'exc' was chained and rethrown from exctb
  1729                 self.write_err('Traceback (most recent call last):\n',
  1830                 self.write_err(
  1730                                ''.join(exctb[:-1]),
  1831                     'Traceback (most recent call last):\n',
  1731                                ''.join(causetb),
  1832                     ''.join(exctb[:-1]),
  1732                                ''.join(exconly))
  1833                     ''.join(causetb),
       
  1834                     ''.join(exconly),
       
  1835                 )
  1733             else:
  1836             else:
  1734                 output = traceback.format_exception(exc[0], exc[1], exc[2])
  1837                 output = traceback.format_exception(exc[0], exc[1], exc[2])
  1735                 self.write_err(encoding.strtolocal(r''.join(output)))
  1838                 self.write_err(encoding.strtolocal(r''.join(output)))
  1736         return self.tracebackflag or force
  1839         return self.tracebackflag or force
  1737 
  1840 
  1742             # instead default to E to plumb commit messages to
  1845             # instead default to E to plumb commit messages to
  1743             # avoid confusion.
  1846             # avoid confusion.
  1744             editor = 'E'
  1847             editor = 'E'
  1745         else:
  1848         else:
  1746             editor = 'vi'
  1849             editor = 'vi'
  1747         return (encoding.environ.get("HGEDITOR") or
  1850         return encoding.environ.get("HGEDITOR") or self.config(
  1748                 self.config("ui", "editor", editor))
  1851             "ui", "editor", editor
       
  1852         )
  1749 
  1853 
  1750     @util.propertycache
  1854     @util.propertycache
  1751     def _progbar(self):
  1855     def _progbar(self):
  1752         """setup the progbar singleton to the ui object"""
  1856         """setup the progbar singleton to the ui object"""
  1753         if (self.quiet or self.debugflag
  1857         if (
  1754                 or self.configbool('progress', 'disable')
  1858             self.quiet
  1755                 or not progress.shouldprint(self)):
  1859             or self.debugflag
       
  1860             or self.configbool('progress', 'disable')
       
  1861             or not progress.shouldprint(self)
       
  1862         ):
  1756             return None
  1863             return None
  1757         return getprogbar(self)
  1864         return getprogbar(self)
  1758 
  1865 
  1759     def _progclear(self):
  1866     def _progclear(self):
  1760         """clear progress bar output if any. use it before any output"""
  1867         """clear progress bar output if any. use it before any output"""
  1761         if not haveprogbar(): # nothing loaded yet
  1868         if not haveprogbar():  # nothing loaded yet
  1762             return
  1869             return
  1763         if self._progbar is not None and self._progbar.printed:
  1870         if self._progbar is not None and self._progbar.printed:
  1764             self._progbar.clear()
  1871             self._progbar.clear()
  1765 
  1872 
  1766     def progress(self, topic, pos, item="", unit="", total=None):
  1873     def progress(self, topic, pos, item="", unit="", total=None):
  1776         Multiple nested topics may be active at a time.
  1883         Multiple nested topics may be active at a time.
  1777 
  1884 
  1778         All topics should be marked closed by setting pos to None at
  1885         All topics should be marked closed by setting pos to None at
  1779         termination.
  1886         termination.
  1780         '''
  1887         '''
  1781         self.deprecwarn("use ui.makeprogress() instead of ui.progress()",
  1888         self.deprecwarn("use ui.makeprogress() instead of ui.progress()", "5.1")
  1782                         "5.1")
       
  1783         progress = self.makeprogress(topic, unit, total)
  1889         progress = self.makeprogress(topic, unit, total)
  1784         if pos is not None:
  1890         if pos is not None:
  1785             progress.update(pos, item=item)
  1891             progress.update(pos, item=item)
  1786         else:
  1892         else:
  1787             progress.complete()
  1893             progress.complete()
  1793             # raw information
  1899             # raw information
  1794             # TODO: consider porting some useful information (e.g. estimated
  1900             # TODO: consider porting some useful information (e.g. estimated
  1795             # time) from progbar. we might want to support update delay to
  1901             # time) from progbar. we might want to support update delay to
  1796             # reduce the cost of transferring progress messages.
  1902             # reduce the cost of transferring progress messages.
  1797             def updatebar(topic, pos, item, unit, total):
  1903             def updatebar(topic, pos, item, unit, total):
  1798                 self._fmsgerr.write(None, type=b'progress', topic=topic,
  1904                 self._fmsgerr.write(
  1799                                     pos=pos, item=item, unit=unit, total=total)
  1905                     None,
       
  1906                     type=b'progress',
       
  1907                     topic=topic,
       
  1908                     pos=pos,
       
  1909                     item=item,
       
  1910                     unit=unit,
       
  1911                     total=total,
       
  1912                 )
       
  1913 
  1800         elif self._progbar is not None:
  1914         elif self._progbar is not None:
  1801             updatebar = self._progbar.progress
  1915             updatebar = self._progbar.progress
  1802         else:
  1916         else:
       
  1917 
  1803             def updatebar(topic, pos, item, unit, total):
  1918             def updatebar(topic, pos, item, unit, total):
  1804                 pass
  1919                 pass
       
  1920 
  1805         return scmutil.progress(self, updatebar, topic, unit, total)
  1921         return scmutil.progress(self, updatebar, topic, unit, total)
  1806 
  1922 
  1807     def getlogger(self, name):
  1923     def getlogger(self, name):
  1808         """Returns a logger of the given name; or None if not registered"""
  1924         """Returns a logger of the given name; or None if not registered"""
  1809         return self._loggers.get(name)
  1925         return self._loggers.get(name)
  1827 
  1943 
  1828         **opts currently has no defined meanings.
  1944         **opts currently has no defined meanings.
  1829         '''
  1945         '''
  1830         if not self._loggers:
  1946         if not self._loggers:
  1831             return
  1947             return
  1832         activeloggers = [l for l in self._loggers.itervalues()
  1948         activeloggers = [
  1833                          if l.tracked(event)]
  1949             l for l in self._loggers.itervalues() if l.tracked(event)
       
  1950         ]
  1834         if not activeloggers:
  1951         if not activeloggers:
  1835             return
  1952             return
  1836         msg = msgfmt % msgargs
  1953         msg = msgfmt % msgargs
  1837         opts = pycompat.byteskwargs(opts)
  1954         opts = pycompat.byteskwargs(opts)
  1838         # guard against recursion from e.g. ui.debug()
  1955         # guard against recursion from e.g. ui.debug()
  1866         """
  1983         """
  1867         if not self.configbool('devel', 'all-warnings'):
  1984         if not self.configbool('devel', 'all-warnings'):
  1868             if config is None or not self.configbool('devel', config):
  1985             if config is None or not self.configbool('devel', config):
  1869                 return
  1986                 return
  1870         msg = 'devel-warn: ' + msg
  1987         msg = 'devel-warn: ' + msg
  1871         stacklevel += 1 # get in develwarn
  1988         stacklevel += 1  # get in develwarn
  1872         if self.tracebackflag:
  1989         if self.tracebackflag:
  1873             util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
  1990             util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
  1874             self.log('develwarn', '%s at:\n%s' %
  1991             self.log(
  1875                      (msg, ''.join(util.getstackframes(stacklevel))))
  1992                 'develwarn',
       
  1993                 '%s at:\n%s' % (msg, ''.join(util.getstackframes(stacklevel))),
       
  1994             )
  1876         else:
  1995         else:
  1877             curframe = inspect.currentframe()
  1996             curframe = inspect.currentframe()
  1878             calframe = inspect.getouterframes(curframe, 2)
  1997             calframe = inspect.getouterframes(curframe, 2)
  1879             fname, lineno, fmsg = calframe[stacklevel][1:4]
  1998             fname, lineno, fmsg = calframe[stacklevel][1:4]
  1880             fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
  1999             fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
  1881             self.write_err('%s at: %s:%d (%s)\n'
  2000             self.write_err('%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
  1882                            % (msg, fname, lineno, fmsg))
  2001             self.log(
  1883             self.log('develwarn', '%s at: %s:%d (%s)\n',
  2002                 'develwarn', '%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
  1884                      msg, fname, lineno, fmsg)
  2003             )
  1885             curframe = calframe = None  # avoid cycles
  2004             curframe = calframe = None  # avoid cycles
  1886 
  2005 
  1887     def deprecwarn(self, msg, version, stacklevel=2):
  2006     def deprecwarn(self, msg, version, stacklevel=2):
  1888         """issue a deprecation warning
  2007         """issue a deprecation warning
  1889 
  2008 
  1890         - msg: message explaining what is deprecated and how to upgrade,
  2009         - msg: message explaining what is deprecated and how to upgrade,
  1891         - version: last version where the API will be supported,
  2010         - version: last version where the API will be supported,
  1892         """
  2011         """
  1893         if not (self.configbool('devel', 'all-warnings')
  2012         if not (
  1894                 or self.configbool('devel', 'deprec-warn')):
  2013             self.configbool('devel', 'all-warnings')
       
  2014             or self.configbool('devel', 'deprec-warn')
       
  2015         ):
  1895             return
  2016             return
  1896         msg += ("\n(compatibility will be dropped after Mercurial-%s,"
  2017         msg += (
  1897                 " update your code.)") % version
  2018             "\n(compatibility will be dropped after Mercurial-%s,"
       
  2019             " update your code.)"
       
  2020         ) % version
  1898         self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
  2021         self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
  1899 
  2022 
  1900     def exportableenviron(self):
  2023     def exportableenviron(self):
  1901         """The environment variables that are safe to export, e.g. through
  2024         """The environment variables that are safe to export, e.g. through
  1902         hgweb.
  2025         hgweb.
  1920             # just restoring ui.quiet config to the previous value is not enough
  2043             # just restoring ui.quiet config to the previous value is not enough
  1921             # as it does not update ui.quiet class member
  2044             # as it does not update ui.quiet class member
  1922             if ('ui', 'quiet') in overrides:
  2045             if ('ui', 'quiet') in overrides:
  1923                 self.fixconfig(section='ui')
  2046                 self.fixconfig(section='ui')
  1924 
  2047 
       
  2048 
  1925 class paths(dict):
  2049 class paths(dict):
  1926     """Represents a collection of paths and their configs.
  2050     """Represents a collection of paths and their configs.
  1927 
  2051 
  1928     Data is initially derived from ui instances and the config files they have
  2052     Data is initially derived from ui instances and the config files they have
  1929     loaded.
  2053     loaded.
  1930     """
  2054     """
       
  2055 
  1931     def __init__(self, ui):
  2056     def __init__(self, ui):
  1932         dict.__init__(self)
  2057         dict.__init__(self)
  1933 
  2058 
  1934         for name, loc in ui.configitems('paths', ignoresub=True):
  2059         for name, loc in ui.configitems('paths', ignoresub=True):
  1935             # No location is the same as not existing.
  2060             # No location is the same as not existing.
  1971             # Try to resolve as a local path or URI.
  2096             # Try to resolve as a local path or URI.
  1972             try:
  2097             try:
  1973                 # We don't pass sub-options in, so no need to pass ui instance.
  2098                 # We don't pass sub-options in, so no need to pass ui instance.
  1974                 return path(None, None, rawloc=name)
  2099                 return path(None, None, rawloc=name)
  1975             except ValueError:
  2100             except ValueError:
  1976                 raise error.RepoError(_('repository %s does not exist') %
  2101                 raise error.RepoError(_('repository %s does not exist') % name)
  1977                                         name)
  2102 
  1978 
  2103 
  1979 _pathsuboptions = {}
  2104 _pathsuboptions = {}
       
  2105 
  1980 
  2106 
  1981 def pathsuboption(option, attr):
  2107 def pathsuboption(option, attr):
  1982     """Decorator used to declare a path sub-option.
  2108     """Decorator used to declare a path sub-option.
  1983 
  2109 
  1984     Arguments are the sub-option name and the attribute it should set on
  2110     Arguments are the sub-option name and the attribute it should set on
  1990     instance.
  2116     instance.
  1991 
  2117 
  1992     This decorator can be used to perform additional verification of
  2118     This decorator can be used to perform additional verification of
  1993     sub-options and to change the type of sub-options.
  2119     sub-options and to change the type of sub-options.
  1994     """
  2120     """
       
  2121 
  1995     def register(func):
  2122     def register(func):
  1996         _pathsuboptions[option] = (attr, func)
  2123         _pathsuboptions[option] = (attr, func)
  1997         return func
  2124         return func
       
  2125 
  1998     return register
  2126     return register
       
  2127 
  1999 
  2128 
  2000 @pathsuboption('pushurl', 'pushloc')
  2129 @pathsuboption('pushurl', 'pushloc')
  2001 def pushurlpathoption(ui, path, value):
  2130 def pushurlpathoption(ui, path, value):
  2002     u = util.url(value)
  2131     u = util.url(value)
  2003     # Actually require a URL.
  2132     # Actually require a URL.
  2006         return None
  2135         return None
  2007 
  2136 
  2008     # Don't support the #foo syntax in the push URL to declare branch to
  2137     # Don't support the #foo syntax in the push URL to declare branch to
  2009     # push.
  2138     # push.
  2010     if u.fragment:
  2139     if u.fragment:
  2011         ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
  2140         ui.warn(
  2012                   'ignoring)\n') % path.name)
  2141             _('("#fragment" in paths.%s:pushurl not supported; ' 'ignoring)\n')
       
  2142             % path.name
       
  2143         )
  2013         u.fragment = None
  2144         u.fragment = None
  2014 
  2145 
  2015     return bytes(u)
  2146     return bytes(u)
       
  2147 
  2016 
  2148 
  2017 @pathsuboption('pushrev', 'pushrev')
  2149 @pathsuboption('pushrev', 'pushrev')
  2018 def pushrevpathoption(ui, path, value):
  2150 def pushrevpathoption(ui, path, value):
  2019     return value
  2151     return value
       
  2152 
  2020 
  2153 
  2021 class path(object):
  2154 class path(object):
  2022     """Represents an individual path and its configuration."""
  2155     """Represents an individual path and its configuration."""
  2023 
  2156 
  2024     def __init__(self, ui, name, rawloc=None, suboptions=None):
  2157     def __init__(self, ui, name, rawloc=None, suboptions=None):
  2051         self.loc = '%s' % u
  2184         self.loc = '%s' % u
  2052 
  2185 
  2053         # When given a raw location but not a symbolic name, validate the
  2186         # When given a raw location but not a symbolic name, validate the
  2054         # location is valid.
  2187         # location is valid.
  2055         if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
  2188         if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
  2056             raise ValueError('location is not a URL or path to a local '
  2189             raise ValueError(
  2057                              'repo: %s' % rawloc)
  2190                 'location is not a URL or path to a local ' 'repo: %s' % rawloc
       
  2191             )
  2058 
  2192 
  2059         suboptions = suboptions or {}
  2193         suboptions = suboptions or {}
  2060 
  2194 
  2061         # Now process the sub-options. If a sub-option is registered, its
  2195         # Now process the sub-options. If a sub-option is registered, its
  2062         # attribute will always be present. The value will be None if there
  2196         # attribute will always be present. The value will be None if there
  2091             value = getattr(self, attr)
  2225             value = getattr(self, attr)
  2092             if value is not None:
  2226             if value is not None:
  2093                 d[subopt] = value
  2227                 d[subopt] = value
  2094         return d
  2228         return d
  2095 
  2229 
       
  2230 
  2096 # we instantiate one globally shared progress bar to avoid
  2231 # we instantiate one globally shared progress bar to avoid
  2097 # competing progress bars when multiple UI objects get created
  2232 # competing progress bars when multiple UI objects get created
  2098 _progresssingleton = None
  2233 _progresssingleton = None
       
  2234 
  2099 
  2235 
  2100 def getprogbar(ui):
  2236 def getprogbar(ui):
  2101     global _progresssingleton
  2237     global _progresssingleton
  2102     if _progresssingleton is None:
  2238     if _progresssingleton is None:
  2103         # passing 'ui' object to the singleton is fishy,
  2239         # passing 'ui' object to the singleton is fishy,
  2104         # this is how the extension used to work but feel free to rework it.
  2240         # this is how the extension used to work but feel free to rework it.
  2105         _progresssingleton = progress.progbar(ui)
  2241         _progresssingleton = progress.progbar(ui)
  2106     return _progresssingleton
  2242     return _progresssingleton
  2107 
  2243 
       
  2244 
  2108 def haveprogbar():
  2245 def haveprogbar():
  2109     return _progresssingleton is not None
  2246     return _progresssingleton is not None
       
  2247 
  2110 
  2248 
  2111 def _selectmsgdests(ui):
  2249 def _selectmsgdests(ui):
  2112     name = ui.config(b'ui', b'message-output')
  2250     name = ui.config(b'ui', b'message-output')
  2113     if name == b'channel':
  2251     if name == b'channel':
  2114         if ui.fmsg:
  2252         if ui.fmsg:
  2121         return ui.fout, ui.ferr
  2259         return ui.fout, ui.ferr
  2122     if name == b'stderr':
  2260     if name == b'stderr':
  2123         return ui.ferr, ui.ferr
  2261         return ui.ferr, ui.ferr
  2124     raise error.Abort(b'invalid ui.message-output destination: %s' % name)
  2262     raise error.Abort(b'invalid ui.message-output destination: %s' % name)
  2125 
  2263 
       
  2264 
  2126 def _writemsgwith(write, dest, *args, **opts):
  2265 def _writemsgwith(write, dest, *args, **opts):
  2127     """Write ui message with the given ui._write*() function
  2266     """Write ui message with the given ui._write*() function
  2128 
  2267 
  2129     The specified message type is translated to 'ui.<type>' label if the dest
  2268     The specified message type is translated to 'ui.<type>' label if the dest
  2130     isn't a structured channel, so that the message will be colorized.
  2269     isn't a structured channel, so that the message will be colorized.