##// END OF EJS Templates
ui: add config knob to redirect status messages to stderr (API)...
Yuya Nishihara -
r40580:840cd57c default
parent child Browse files
Show More
@@ -1181,6 +1181,9 b" coreconfigitem('ui', 'mergemarkertemplat"
1181 '{ifeq(branch, "default", "", "{branch} ")}'
1181 '{ifeq(branch, "default", "", "{branch} ")}'
1182 '- {author|user}: {desc|firstline}')
1182 '- {author|user}: {desc|firstline}')
1183 )
1183 )
1184 coreconfigitem('ui', 'message-output',
1185 default='stdio',
1186 )
1184 coreconfigitem('ui', 'nontty',
1187 coreconfigitem('ui', 'nontty',
1185 default=False,
1188 default=False,
1186 )
1189 )
@@ -2246,6 +2246,14 b' User interface controls.'
2246
2246
2247 Can be overridden per-merge-tool, see the ``[merge-tools]`` section.
2247 Can be overridden per-merge-tool, see the ``[merge-tools]`` section.
2248
2248
2249 ``message-output``
2250 Where to write status and error messages. (default: ``stdio``)
2251
2252 ``stderr``
2253 Everything to stderr.
2254 ``stdio``
2255 Status to stdout, and error to stderr.
2256
2249 ``origbackuppath``
2257 ``origbackuppath``
2250 The path to a directory used to store generated .orig files. If the path is
2258 The path to a directory used to store generated .orig files. If the path is
2251 not a directory, one will be created. If set, files stored in this
2259 not a directory, one will be created. If set, files stored in this
@@ -234,6 +234,8 b' class ui(object):'
234 self._fout = src._fout
234 self._fout = src._fout
235 self._ferr = src._ferr
235 self._ferr = src._ferr
236 self._fin = src._fin
236 self._fin = src._fin
237 self._fmsgout = src._fmsgout
238 self._fmsgerr = src._fmsgerr
237 self._finoutredirected = src._finoutredirected
239 self._finoutredirected = src._finoutredirected
238 self.pageractive = src.pageractive
240 self.pageractive = src.pageractive
239 self._disablepager = src._disablepager
241 self._disablepager = src._disablepager
@@ -259,6 +261,8 b' class ui(object):'
259 self._fout = procutil.stdout
261 self._fout = procutil.stdout
260 self._ferr = procutil.stderr
262 self._ferr = procutil.stderr
261 self._fin = procutil.stdin
263 self._fin = procutil.stdin
264 self._fmsgout = self.fout # configurable
265 self._fmsgerr = self.ferr # configurable
262 self._finoutredirected = False
266 self._finoutredirected = False
263 self.pageractive = False
267 self.pageractive = False
264 self._disablepager = False
268 self._disablepager = False
@@ -416,7 +420,7 b' class ui(object):'
416
420
417 if self.plain():
421 if self.plain():
418 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
422 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
419 'logtemplate', 'statuscopies', 'style',
423 'logtemplate', 'message-output', 'statuscopies', 'style',
420 'traceback', 'verbose'):
424 'traceback', 'verbose'):
421 if k in cfg['ui']:
425 if k in cfg['ui']:
422 del cfg['ui'][k]
426 del cfg['ui'][k]
@@ -469,6 +473,7 b' class ui(object):'
469
473
470 if section in (None, 'ui'):
474 if section in (None, 'ui'):
471 # update ui options
475 # update ui options
476 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
472 self.debugflag = self.configbool('ui', 'debug')
477 self.debugflag = self.configbool('ui', 'debug')
473 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
478 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
474 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
479 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
@@ -891,6 +896,7 b' class ui(object):'
891 @fout.setter
896 @fout.setter
892 def fout(self, f):
897 def fout(self, f):
893 self._fout = f
898 self._fout = f
899 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
894
900
895 @property
901 @property
896 def ferr(self):
902 def ferr(self):
@@ -899,6 +905,7 b' class ui(object):'
899 @ferr.setter
905 @ferr.setter
900 def ferr(self, f):
906 def ferr(self, f):
901 self._ferr = f
907 self._ferr = f
908 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
902
909
903 @property
910 @property
904 def fin(self):
911 def fin(self):
@@ -1364,17 +1371,18 b' class ui(object):'
1364 If ui is not interactive, the default is returned.
1371 If ui is not interactive, the default is returned.
1365 """
1372 """
1366 if not self.interactive():
1373 if not self.interactive():
1367 self.write(msg, ' ', label='ui.prompt')
1374 self._write(self._fmsgout, msg, ' ', label='ui.prompt')
1368 self.write(default or '', "\n", label='ui.promptecho')
1375 self._write(self._fmsgout, default or '', "\n",
1376 label='ui.promptecho')
1369 return default
1377 return default
1370 self._writenobuf(self._fout, msg, label='ui.prompt')
1378 self._writenobuf(self._fmsgout, msg, label='ui.prompt')
1371 self.flush()
1379 self.flush()
1372 try:
1380 try:
1373 r = self._readline()
1381 r = self._readline()
1374 if not r:
1382 if not r:
1375 r = default
1383 r = default
1376 if self.configbool('ui', 'promptecho'):
1384 if self.configbool('ui', 'promptecho'):
1377 self.write(r, "\n", label='ui.promptecho')
1385 self._write(self._fmsgout, r, "\n", label='ui.promptecho')
1378 return r
1386 return r
1379 except EOFError:
1387 except EOFError:
1380 raise error.ResponseExpected()
1388 raise error.ResponseExpected()
@@ -1424,13 +1432,15 b' class ui(object):'
1424 r = self.prompt(msg, resps[default])
1432 r = self.prompt(msg, resps[default])
1425 if r.lower() in resps:
1433 if r.lower() in resps:
1426 return resps.index(r.lower())
1434 return resps.index(r.lower())
1427 self.write(_("unrecognized response\n"))
1435 # TODO: shouldn't it be a warning?
1436 self._write(self._fmsgout, _("unrecognized response\n"))
1428
1437
1429 def getpass(self, prompt=None, default=None):
1438 def getpass(self, prompt=None, default=None):
1430 if not self.interactive():
1439 if not self.interactive():
1431 return default
1440 return default
1432 try:
1441 try:
1433 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1442 self._write(self._fmsgerr, prompt or _('password: '),
1443 label='ui.prompt')
1434 # disable getpass() only if explicitly specified. it's still valid
1444 # disable getpass() only if explicitly specified. it's still valid
1435 # to interact with tty even if fin is not a tty.
1445 # to interact with tty even if fin is not a tty.
1436 with self.timeblockedsection('stdio'):
1446 with self.timeblockedsection('stdio'):
@@ -1451,7 +1461,7 b' class ui(object):'
1451 '''
1461 '''
1452 if not self.quiet:
1462 if not self.quiet:
1453 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1463 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1454 self.write(*msg, **opts)
1464 self._write(self._fmsgout, *msg, **opts)
1455
1465
1456 def warn(self, *msg, **opts):
1466 def warn(self, *msg, **opts):
1457 '''write warning message to output (stderr)
1467 '''write warning message to output (stderr)
@@ -1459,7 +1469,7 b' class ui(object):'
1459 This adds an output label of "ui.warning".
1469 This adds an output label of "ui.warning".
1460 '''
1470 '''
1461 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1471 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1462 self.write_err(*msg, **opts)
1472 self._write(self._fmsgerr, *msg, **opts)
1463
1473
1464 def error(self, *msg, **opts):
1474 def error(self, *msg, **opts):
1465 '''write error message to output (stderr)
1475 '''write error message to output (stderr)
@@ -1467,7 +1477,7 b' class ui(object):'
1467 This adds an output label of "ui.error".
1477 This adds an output label of "ui.error".
1468 '''
1478 '''
1469 opts[r'label'] = opts.get(r'label', '') + ' ui.error'
1479 opts[r'label'] = opts.get(r'label', '') + ' ui.error'
1470 self.write_err(*msg, **opts)
1480 self._write(self._fmsgerr, *msg, **opts)
1471
1481
1472 def note(self, *msg, **opts):
1482 def note(self, *msg, **opts):
1473 '''write note to output (if ui.verbose is True)
1483 '''write note to output (if ui.verbose is True)
@@ -1476,7 +1486,7 b' class ui(object):'
1476 '''
1486 '''
1477 if self.verbose:
1487 if self.verbose:
1478 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1488 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1479 self.write(*msg, **opts)
1489 self._write(self._fmsgout, *msg, **opts)
1480
1490
1481 def debug(self, *msg, **opts):
1491 def debug(self, *msg, **opts):
1482 '''write debug message to output (if ui.debugflag is True)
1492 '''write debug message to output (if ui.debugflag is True)
@@ -1485,7 +1495,7 b' class ui(object):'
1485 '''
1495 '''
1486 if self.debugflag:
1496 if self.debugflag:
1487 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1497 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1488 self.write(*msg, **opts)
1498 self._write(self._fmsgout, *msg, **opts)
1489
1499
1490 def edit(self, text, user, extra=None, editform=None, pending=None,
1500 def edit(self, text, user, extra=None, editform=None, pending=None,
1491 repopath=None, action=None):
1501 repopath=None, action=None):
@@ -1939,3 +1949,11 b' def getprogbar(ui):'
1939
1949
1940 def haveprogbar():
1950 def haveprogbar():
1941 return _progresssingleton is not None
1951 return _progresssingleton is not None
1952
1953 def _selectmsgdests(ui):
1954 name = ui.config(b'ui', b'message-output')
1955 if name == b'stdio':
1956 return ui.fout, ui.ferr
1957 if name == b'stderr':
1958 return ui.ferr, ui.ferr
1959 raise error.Abort(b'invalid ui.message-output destination: %s' % name)
@@ -102,3 +102,118 b' Repository root:'
102 At the end...
102 At the end...
103
103
104 $ cd ..
104 $ cd ..
105
106 Status message redirection:
107
108 $ hg init empty
109
110 status messages are sent to stdout by default:
111
112 $ hg outgoing -R t empty -Tjson 2>/dev/null
113 comparing with empty
114 searching for changes
115 [
116 {
117 "bookmarks": [],
118 "branch": "default",
119 "date": [0, 0],
120 "desc": "test",
121 "node": "acb14030fe0a21b60322c440ad2d20cf7685a376",
122 "parents": ["0000000000000000000000000000000000000000"],
123 "phase": "draft",
124 "rev": 0,
125 "tags": ["tip"],
126 "user": "test"
127 }
128 ]
129
130 which can be configured to send to stderr, so the output wouldn't be
131 interleaved:
132
133 $ cat <<'EOF' >> "$HGRCPATH"
134 > [ui]
135 > message-output = stderr
136 > EOF
137 $ hg outgoing -R t empty -Tjson 2>/dev/null
138 [
139 {
140 "bookmarks": [],
141 "branch": "default",
142 "date": [0, 0],
143 "desc": "test",
144 "node": "acb14030fe0a21b60322c440ad2d20cf7685a376",
145 "parents": ["0000000000000000000000000000000000000000"],
146 "phase": "draft",
147 "rev": 0,
148 "tags": ["tip"],
149 "user": "test"
150 }
151 ]
152 $ hg outgoing -R t empty -Tjson >/dev/null
153 comparing with empty
154 searching for changes
155
156 this option should be turned off by HGPLAIN= since it may break scripting use:
157
158 $ HGPLAIN= hg outgoing -R t empty -Tjson 2>/dev/null
159 comparing with empty
160 searching for changes
161 [
162 {
163 "bookmarks": [],
164 "branch": "default",
165 "date": [0, 0],
166 "desc": "test",
167 "node": "acb14030fe0a21b60322c440ad2d20cf7685a376",
168 "parents": ["0000000000000000000000000000000000000000"],
169 "phase": "draft",
170 "rev": 0,
171 "tags": ["tip"],
172 "user": "test"
173 }
174 ]
175
176 but still overridden by --config:
177
178 $ HGPLAIN= hg outgoing -R t empty -Tjson --config ui.message-output=stderr \
179 > 2>/dev/null
180 [
181 {
182 "bookmarks": [],
183 "branch": "default",
184 "date": [0, 0],
185 "desc": "test",
186 "node": "acb14030fe0a21b60322c440ad2d20cf7685a376",
187 "parents": ["0000000000000000000000000000000000000000"],
188 "phase": "draft",
189 "rev": 0,
190 "tags": ["tip"],
191 "user": "test"
192 }
193 ]
194
195 Invalid ui.message-output option:
196
197 $ hg log -R t --config ui.message-output=bad
198 abort: invalid ui.message-output destination: bad
199 [255]
200
201 Underlying message streams should be updated when ui.fout/ferr are set:
202
203 $ cat <<'EOF' > capui.py
204 > from mercurial import pycompat, registrar
205 > cmdtable = {}
206 > command = registrar.command(cmdtable)
207 > @command(b'capui', norepo=True)
208 > def capui(ui):
209 > out = ui.fout
210 > ui.fout = pycompat.bytesio()
211 > ui.status(b'status\n')
212 > ui.ferr = pycompat.bytesio()
213 > ui.warn(b'warn\n')
214 > out.write(b'stdout: %s' % ui.fout.getvalue())
215 > out.write(b'stderr: %s' % ui.ferr.getvalue())
216 > EOF
217 $ hg --config extensions.capui=capui.py --config ui.message-output=stdio capui
218 stdout: status
219 stderr: warn
General Comments 0
You need to be logged in to leave comments. Login now