Mercurial > public > mercurial-scm > hg-stable
diff mercurial/ui.py @ 40594:840cd57cde32
ui: add config knob to redirect status messages to stderr (API)
This option can be used to isolate structured output from status messages.
For now, "stdio" (stdout/err pair) and "stderr" are supported. In future
patches, I'll add the "channel" option which will send status messages to
a separate command-server channel with some metadata attached, maybe in
CBOR encoding.
This is a part of the generic templating plan:
https://www.mercurial-scm.org/wiki/GenericTemplatingPlan#Sanity_check_output
.. api::
Status messages may be sent to a dedicated stream depending on
configuration. Don't use ``ui.status()``, etc. as a shorthand for
conditional writes. Use ``ui.write()`` for data output.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sat, 03 Nov 2018 19:42:50 +0900 |
parents | 7bffbbe03e90 |
children | db61a18148a4 |
line wrap: on
line diff
--- a/mercurial/ui.py Sat Nov 03 20:53:31 2018 +0900 +++ b/mercurial/ui.py Sat Nov 03 19:42:50 2018 +0900 @@ -234,6 +234,8 @@ self._fout = src._fout self._ferr = src._ferr self._fin = src._fin + self._fmsgout = src._fmsgout + self._fmsgerr = src._fmsgerr self._finoutredirected = src._finoutredirected self.pageractive = src.pageractive self._disablepager = src._disablepager @@ -259,6 +261,8 @@ self._fout = procutil.stdout self._ferr = procutil.stderr self._fin = procutil.stdin + self._fmsgout = self.fout # configurable + self._fmsgerr = self.ferr # configurable self._finoutredirected = False self.pageractive = False self._disablepager = False @@ -416,7 +420,7 @@ if self.plain(): for k in ('debug', 'fallbackencoding', 'quiet', 'slash', - 'logtemplate', 'statuscopies', 'style', + 'logtemplate', 'message-output', 'statuscopies', 'style', 'traceback', 'verbose'): if k in cfg['ui']: del cfg['ui'][k] @@ -469,6 +473,7 @@ if section in (None, 'ui'): # update ui options + self._fmsgout, self._fmsgerr = _selectmsgdests(self) self.debugflag = self.configbool('ui', 'debug') self.verbose = self.debugflag or self.configbool('ui', 'verbose') self.quiet = not self.debugflag and self.configbool('ui', 'quiet') @@ -891,6 +896,7 @@ @fout.setter def fout(self, f): self._fout = f + self._fmsgout, self._fmsgerr = _selectmsgdests(self) @property def ferr(self): @@ -899,6 +905,7 @@ @ferr.setter def ferr(self, f): self._ferr = f + self._fmsgout, self._fmsgerr = _selectmsgdests(self) @property def fin(self): @@ -1364,17 +1371,18 @@ If ui is not interactive, the default is returned. """ if not self.interactive(): - self.write(msg, ' ', label='ui.prompt') - self.write(default or '', "\n", label='ui.promptecho') + self._write(self._fmsgout, msg, ' ', label='ui.prompt') + self._write(self._fmsgout, default or '', "\n", + label='ui.promptecho') return default - self._writenobuf(self._fout, msg, label='ui.prompt') + self._writenobuf(self._fmsgout, msg, label='ui.prompt') self.flush() try: r = self._readline() if not r: r = default if self.configbool('ui', 'promptecho'): - self.write(r, "\n", label='ui.promptecho') + self._write(self._fmsgout, r, "\n", label='ui.promptecho') return r except EOFError: raise error.ResponseExpected() @@ -1424,13 +1432,15 @@ r = self.prompt(msg, resps[default]) if r.lower() in resps: return resps.index(r.lower()) - self.write(_("unrecognized response\n")) + # TODO: shouldn't it be a warning? + self._write(self._fmsgout, _("unrecognized response\n")) def getpass(self, prompt=None, default=None): if not self.interactive(): return default try: - self.write_err(self.label(prompt or _('password: '), 'ui.prompt')) + self._write(self._fmsgerr, prompt or _('password: '), + label='ui.prompt') # disable getpass() only if explicitly specified. it's still valid # to interact with tty even if fin is not a tty. with self.timeblockedsection('stdio'): @@ -1451,7 +1461,7 @@ ''' if not self.quiet: opts[r'label'] = opts.get(r'label', '') + ' ui.status' - self.write(*msg, **opts) + self._write(self._fmsgout, *msg, **opts) def warn(self, *msg, **opts): '''write warning message to output (stderr) @@ -1459,7 +1469,7 @@ This adds an output label of "ui.warning". ''' opts[r'label'] = opts.get(r'label', '') + ' ui.warning' - self.write_err(*msg, **opts) + self._write(self._fmsgerr, *msg, **opts) def error(self, *msg, **opts): '''write error message to output (stderr) @@ -1467,7 +1477,7 @@ This adds an output label of "ui.error". ''' opts[r'label'] = opts.get(r'label', '') + ' ui.error' - self.write_err(*msg, **opts) + self._write(self._fmsgerr, *msg, **opts) def note(self, *msg, **opts): '''write note to output (if ui.verbose is True) @@ -1476,7 +1486,7 @@ ''' if self.verbose: opts[r'label'] = opts.get(r'label', '') + ' ui.note' - self.write(*msg, **opts) + self._write(self._fmsgout, *msg, **opts) def debug(self, *msg, **opts): '''write debug message to output (if ui.debugflag is True) @@ -1485,7 +1495,7 @@ ''' if self.debugflag: opts[r'label'] = opts.get(r'label', '') + ' ui.debug' - self.write(*msg, **opts) + self._write(self._fmsgout, *msg, **opts) def edit(self, text, user, extra=None, editform=None, pending=None, repopath=None, action=None): @@ -1939,3 +1949,11 @@ def haveprogbar(): return _progresssingleton is not None + +def _selectmsgdests(ui): + name = ui.config(b'ui', b'message-output') + if name == b'stdio': + return ui.fout, ui.ferr + if name == b'stderr': + return ui.ferr, ui.ferr + raise error.Abort(b'invalid ui.message-output destination: %s' % name)