##// END OF EJS Templates
HGPLAIN: allow exceptions to plain mode, like i18n, via HGPLAINEXCEPT...
Brodie Rao -
r13849:9f97de15 default
parent child Browse files
Show More
@@ -1,93 +1,101 b''
1 1 HG
2 2 Path to the 'hg' executable, automatically passed when running
3 3 hooks, extensions or external tools. If unset or empty, this is
4 4 the hg executable's name if it's frozen, or an executable named
5 5 'hg' (with %PATHEXT% [defaulting to COM/EXE/BAT/CMD] extensions on
6 6 Windows) is searched.
7 7
8 8 HGEDITOR
9 9 This is the name of the editor to run when committing. See EDITOR.
10 10
11 11 (deprecated, use configuration file)
12 12
13 13 HGENCODING
14 14 This overrides the default locale setting detected by Mercurial.
15 15 This setting is used to convert data including usernames,
16 16 changeset descriptions, tag names, and branches. This setting can
17 17 be overridden with the --encoding command-line option.
18 18
19 19 HGENCODINGMODE
20 20 This sets Mercurial's behavior for handling unknown characters
21 21 while transcoding user input. The default is "strict", which
22 22 causes Mercurial to abort if it can't map a character. Other
23 23 settings include "replace", which replaces unknown characters, and
24 24 "ignore", which drops them. This setting can be overridden with
25 25 the --encodingmode command-line option.
26 26
27 27 HGENCODINGAMBIGUOUS
28 28 This sets Mercurial's behavior for handling characters with
29 29 "ambiguous" widths like accented Latin characters with East Asian
30 30 fonts. By default, Mercurial assumes ambiguous characters are
31 31 narrow, set this variable to "wide" if such characters cause
32 32 formatting problems.
33 33
34 34 HGMERGE
35 35 An executable to use for resolving merge conflicts. The program
36 36 will be executed with three arguments: local file, remote file,
37 37 ancestor file.
38 38
39 39 (deprecated, use configuration file)
40 40
41 41 HGRCPATH
42 42 A list of files or directories to search for configuration
43 43 files. Item separator is ":" on Unix, ";" on Windows. If HGRCPATH
44 44 is not set, platform default search path is used. If empty, only
45 45 the .hg/hgrc from the current repository is read.
46 46
47 47 For each element in HGRCPATH:
48 48
49 49 - if it's a directory, all files ending with .rc are added
50 50 - otherwise, the file itself will be added
51 51
52 52 HGPLAIN
53 53 When set, this disables any configuration settings that might
54 54 change Mercurial's default output. This includes encoding,
55 55 defaults, verbose mode, debug mode, quiet mode, tracebacks, and
56 56 localization. This can be useful when scripting against Mercurial
57 57 in the face of existing user configuration.
58 58
59 59 Equivalent options set via command line flags or environment
60 60 variables are not overridden.
61 61
62 HGPLAINEXCEPT
63 This is a comma-separated list of features to preserve when
64 HGPLAIN is enabled. Currently the only value supported is "i18n",
65 which preserves internationalization in plain mode.
66
67 Setting HGPLAINEXCEPT to anything (even an empty string) will
68 enable plain mode.
69
62 70 HGUSER
63 71 This is the string used as the author of a commit. If not set,
64 72 available values will be considered in this order:
65 73
66 74 - HGUSER (deprecated)
67 75 - configuration files from the HGRCPATH
68 76 - EMAIL
69 77 - interactive prompt
70 78 - LOGNAME (with ``@hostname`` appended)
71 79
72 80 (deprecated, use configuration file)
73 81
74 82 EMAIL
75 83 May be used as the author of a commit; see HGUSER.
76 84
77 85 LOGNAME
78 86 May be used as the author of a commit; see HGUSER.
79 87
80 88 VISUAL
81 89 This is the name of the editor to use when committing. See EDITOR.
82 90
83 91 EDITOR
84 92 Sometimes Mercurial needs to open a text file in an editor for a
85 93 user to modify, for example when writing commit messages. The
86 94 editor it uses is determined by looking at the environment
87 95 variables HGEDITOR, VISUAL and EDITOR, in that order. The first
88 96 non-empty one is chosen. If all of them are empty, the editor
89 97 defaults to 'vi'.
90 98
91 99 PYTHONPATH
92 100 This is used by Python to find imported modules and may need to be
93 101 set appropriately if this Mercurial is not installed system-wide.
@@ -1,58 +1,64 b''
1 1 # i18n.py - internationalization support for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import encoding
9 9 import gettext, sys, os
10 10
11 11 # modelled after templater.templatepath:
12 12 if hasattr(sys, 'frozen'):
13 13 module = sys.executable
14 14 else:
15 15 module = __file__
16 16
17 17 base = os.path.dirname(module)
18 18 for dir in ('.', '..'):
19 19 localedir = os.path.join(base, dir, 'locale')
20 20 if os.path.isdir(localedir):
21 21 break
22 22
23 23 t = gettext.translation('hg', localedir, fallback=True)
24 24
25 25 def gettext(message):
26 26 """Translate message.
27 27
28 28 The message is looked up in the catalog to get a Unicode string,
29 29 which is encoded in the local encoding before being returned.
30 30
31 31 Important: message is restricted to characters in the encoding
32 32 given by sys.getdefaultencoding() which is most likely 'ascii'.
33 33 """
34 34 # If message is None, t.ugettext will return u'None' as the
35 35 # translation whereas our callers expect us to return None.
36 36 if message is None:
37 37 return message
38 38
39 39 paragraphs = message.split('\n\n')
40 40 # Be careful not to translate the empty string -- it holds the
41 41 # meta data of the .po file.
42 42 u = u'\n\n'.join([p and t.ugettext(p) or '' for p in paragraphs])
43 43 try:
44 44 # encoding.tolocal cannot be used since it will first try to
45 45 # decode the Unicode string. Calling u.decode(enc) really
46 46 # means u.encode(sys.getdefaultencoding()).decode(enc). Since
47 47 # the Python encoding defaults to 'ascii', this fails if the
48 48 # translated string use non-ASCII characters.
49 49 return u.encode(encoding.encoding, "replace")
50 50 except LookupError:
51 51 # An unknown encoding results in a LookupError.
52 52 return message
53 53
54 if 'HGPLAIN' in os.environ:
54 def _plain():
55 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
56 return False
57 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
58 return 'i18n' not in exceptions
59
60 if _plain():
55 61 _ = lambda message: message
56 62 else:
57 63 _ = gettext
58 64
@@ -1,636 +1,643 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import errno, getpass, os, socket, sys, tempfile, traceback
10 10 import config, util, error, url
11 11
12 12 class ui(object):
13 13 def __init__(self, src=None):
14 14 self._buffers = []
15 15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 16 self._reportuntrusted = True
17 17 self._ocfg = config.config() # overlay
18 18 self._tcfg = config.config() # trusted
19 19 self._ucfg = config.config() # untrusted
20 20 self._trustusers = set()
21 21 self._trustgroups = set()
22 22
23 23 if src:
24 24 self._tcfg = src._tcfg.copy()
25 25 self._ucfg = src._ucfg.copy()
26 26 self._ocfg = src._ocfg.copy()
27 27 self._trustusers = src._trustusers.copy()
28 28 self._trustgroups = src._trustgroups.copy()
29 29 self.environ = src.environ
30 30 self.fixconfig()
31 31 else:
32 32 # shared read-only environment
33 33 self.environ = os.environ
34 34 # we always trust global config files
35 35 for f in util.rcpath():
36 36 self.readconfig(f, trust=True)
37 37
38 38 def copy(self):
39 39 return self.__class__(self)
40 40
41 41 def _is_trusted(self, fp, f):
42 42 st = util.fstat(fp)
43 43 if util.isowner(st):
44 44 return True
45 45
46 46 tusers, tgroups = self._trustusers, self._trustgroups
47 47 if '*' in tusers or '*' in tgroups:
48 48 return True
49 49
50 50 user = util.username(st.st_uid)
51 51 group = util.groupname(st.st_gid)
52 52 if user in tusers or group in tgroups or user == util.username():
53 53 return True
54 54
55 55 if self._reportuntrusted:
56 56 self.warn(_('Not trusting file %s from untrusted '
57 57 'user %s, group %s\n') % (f, user, group))
58 58 return False
59 59
60 60 def readconfig(self, filename, root=None, trust=False,
61 61 sections=None, remap=None):
62 62 try:
63 63 fp = open(filename)
64 64 except IOError:
65 65 if not sections: # ignore unless we were looking for something
66 66 return
67 67 raise
68 68
69 69 cfg = config.config()
70 70 trusted = sections or trust or self._is_trusted(fp, filename)
71 71
72 72 try:
73 73 cfg.read(filename, fp, sections=sections, remap=remap)
74 74 except error.ConfigError, inst:
75 75 if trusted:
76 76 raise
77 77 self.warn(_("Ignored: %s\n") % str(inst))
78 78
79 79 if self.plain():
80 80 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
81 81 'logtemplate', 'style',
82 82 'traceback', 'verbose'):
83 83 if k in cfg['ui']:
84 84 del cfg['ui'][k]
85 85 for k, v in cfg.items('alias'):
86 86 del cfg['alias'][k]
87 87 for k, v in cfg.items('defaults'):
88 88 del cfg['defaults'][k]
89 89
90 90 if trusted:
91 91 self._tcfg.update(cfg)
92 92 self._tcfg.update(self._ocfg)
93 93 self._ucfg.update(cfg)
94 94 self._ucfg.update(self._ocfg)
95 95
96 96 if root is None:
97 97 root = os.path.expanduser('~')
98 98 self.fixconfig(root=root)
99 99
100 100 def fixconfig(self, root=None, section=None):
101 101 if section in (None, 'paths'):
102 102 # expand vars and ~
103 103 # translate paths relative to root (or home) into absolute paths
104 104 root = root or os.getcwd()
105 105 for c in self._tcfg, self._ucfg, self._ocfg:
106 106 for n, p in c.items('paths'):
107 107 if not p:
108 108 continue
109 109 if '%%' in p:
110 110 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
111 111 % (n, p, self.configsource('paths', n)))
112 112 p = p.replace('%%', '%')
113 113 p = util.expandpath(p)
114 114 if not url.hasscheme(p) and not os.path.isabs(p):
115 115 p = os.path.normpath(os.path.join(root, p))
116 116 c.set("paths", n, p)
117 117
118 118 if section in (None, 'ui'):
119 119 # update ui options
120 120 self.debugflag = self.configbool('ui', 'debug')
121 121 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
122 122 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
123 123 if self.verbose and self.quiet:
124 124 self.quiet = self.verbose = False
125 125 self._reportuntrusted = self.debugflag or self.configbool("ui",
126 126 "report_untrusted", True)
127 127 self.tracebackflag = self.configbool('ui', 'traceback', False)
128 128
129 129 if section in (None, 'trusted'):
130 130 # update trust information
131 131 self._trustusers.update(self.configlist('trusted', 'users'))
132 132 self._trustgroups.update(self.configlist('trusted', 'groups'))
133 133
134 134 def setconfig(self, section, name, value, overlay=True):
135 135 if overlay:
136 136 self._ocfg.set(section, name, value)
137 137 self._tcfg.set(section, name, value)
138 138 self._ucfg.set(section, name, value)
139 139 self.fixconfig(section=section)
140 140
141 141 def _data(self, untrusted):
142 142 return untrusted and self._ucfg or self._tcfg
143 143
144 144 def configsource(self, section, name, untrusted=False):
145 145 return self._data(untrusted).source(section, name) or 'none'
146 146
147 147 def config(self, section, name, default=None, untrusted=False):
148 148 value = self._data(untrusted).get(section, name, default)
149 149 if self.debugflag and not untrusted and self._reportuntrusted:
150 150 uvalue = self._ucfg.get(section, name)
151 151 if uvalue is not None and uvalue != value:
152 152 self.debug(_("ignoring untrusted configuration option "
153 153 "%s.%s = %s\n") % (section, name, uvalue))
154 154 return value
155 155
156 156 def configpath(self, section, name, default=None, untrusted=False):
157 157 'get a path config item, expanded relative to config file'
158 158 v = self.config(section, name, default, untrusted)
159 159 if not os.path.isabs(v) or "://" not in v:
160 160 src = self.configsource(section, name, untrusted)
161 161 if ':' in src:
162 162 base = os.path.dirname(src.rsplit(':'))
163 163 v = os.path.join(base, os.path.expanduser(v))
164 164 return v
165 165
166 166 def configbool(self, section, name, default=False, untrusted=False):
167 167 v = self.config(section, name, None, untrusted)
168 168 if v is None:
169 169 return default
170 170 if isinstance(v, bool):
171 171 return v
172 172 b = util.parsebool(v)
173 173 if b is None:
174 174 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
175 175 % (section, name, v))
176 176 return b
177 177
178 178 def configlist(self, section, name, default=None, untrusted=False):
179 179 """Return a list of comma/space separated strings"""
180 180
181 181 def _parse_plain(parts, s, offset):
182 182 whitespace = False
183 183 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
184 184 whitespace = True
185 185 offset += 1
186 186 if offset >= len(s):
187 187 return None, parts, offset
188 188 if whitespace:
189 189 parts.append('')
190 190 if s[offset] == '"' and not parts[-1]:
191 191 return _parse_quote, parts, offset + 1
192 192 elif s[offset] == '"' and parts[-1][-1] == '\\':
193 193 parts[-1] = parts[-1][:-1] + s[offset]
194 194 return _parse_plain, parts, offset + 1
195 195 parts[-1] += s[offset]
196 196 return _parse_plain, parts, offset + 1
197 197
198 198 def _parse_quote(parts, s, offset):
199 199 if offset < len(s) and s[offset] == '"': # ""
200 200 parts.append('')
201 201 offset += 1
202 202 while offset < len(s) and (s[offset].isspace() or
203 203 s[offset] == ','):
204 204 offset += 1
205 205 return _parse_plain, parts, offset
206 206
207 207 while offset < len(s) and s[offset] != '"':
208 208 if (s[offset] == '\\' and offset + 1 < len(s)
209 209 and s[offset + 1] == '"'):
210 210 offset += 1
211 211 parts[-1] += '"'
212 212 else:
213 213 parts[-1] += s[offset]
214 214 offset += 1
215 215
216 216 if offset >= len(s):
217 217 real_parts = _configlist(parts[-1])
218 218 if not real_parts:
219 219 parts[-1] = '"'
220 220 else:
221 221 real_parts[0] = '"' + real_parts[0]
222 222 parts = parts[:-1]
223 223 parts.extend(real_parts)
224 224 return None, parts, offset
225 225
226 226 offset += 1
227 227 while offset < len(s) and s[offset] in [' ', ',']:
228 228 offset += 1
229 229
230 230 if offset < len(s):
231 231 if offset + 1 == len(s) and s[offset] == '"':
232 232 parts[-1] += '"'
233 233 offset += 1
234 234 else:
235 235 parts.append('')
236 236 else:
237 237 return None, parts, offset
238 238
239 239 return _parse_plain, parts, offset
240 240
241 241 def _configlist(s):
242 242 s = s.rstrip(' ,')
243 243 if not s:
244 244 return []
245 245 parser, parts, offset = _parse_plain, [''], 0
246 246 while parser:
247 247 parser, parts, offset = parser(parts, s, offset)
248 248 return parts
249 249
250 250 result = self.config(section, name, untrusted=untrusted)
251 251 if result is None:
252 252 result = default or []
253 253 if isinstance(result, basestring):
254 254 result = _configlist(result.lstrip(' ,\n'))
255 255 if result is None:
256 256 result = default or []
257 257 return result
258 258
259 259 def has_section(self, section, untrusted=False):
260 260 '''tell whether section exists in config.'''
261 261 return section in self._data(untrusted)
262 262
263 263 def configitems(self, section, untrusted=False):
264 264 items = self._data(untrusted).items(section)
265 265 if self.debugflag and not untrusted and self._reportuntrusted:
266 266 for k, v in self._ucfg.items(section):
267 267 if self._tcfg.get(section, k) != v:
268 268 self.debug(_("ignoring untrusted configuration option "
269 269 "%s.%s = %s\n") % (section, k, v))
270 270 return items
271 271
272 272 def walkconfig(self, untrusted=False):
273 273 cfg = self._data(untrusted)
274 274 for section in cfg.sections():
275 275 for name, value in self.configitems(section, untrusted):
276 276 yield section, name, value
277 277
278 278 def plain(self):
279 279 '''is plain mode active?
280 280
281 Plain mode means that all configuration variables which affect the
282 behavior and output of Mercurial should be ignored. Additionally, the
283 output should be stable, reproducible and suitable for use in scripts or
284 applications.
281 Plain mode means that all configuration variables which affect
282 the behavior and output of Mercurial should be
283 ignored. Additionally, the output should be stable,
284 reproducible and suitable for use in scripts or applications.
285
286 The only way to trigger plain mode is by setting either the
287 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
285 288
286 The only way to trigger plain mode is by setting the `HGPLAIN'
287 environment variable.
289 The return value can either be False, True, or a list of
290 features that plain mode should not apply to (e.g., i18n,
291 progress, etc).
288 292 '''
289 return 'HGPLAIN' in os.environ
293 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
294 return False
295 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
296 return exceptions or True
290 297
291 298 def username(self):
292 299 """Return default username to be used in commits.
293 300
294 301 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
295 302 and stop searching if one of these is set.
296 303 If not found and ui.askusername is True, ask the user, else use
297 304 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
298 305 """
299 306 user = os.environ.get("HGUSER")
300 307 if user is None:
301 308 user = self.config("ui", "username")
302 309 if user is not None:
303 310 user = os.path.expandvars(user)
304 311 if user is None:
305 312 user = os.environ.get("EMAIL")
306 313 if user is None and self.configbool("ui", "askusername"):
307 314 user = self.prompt(_("enter a commit username:"), default=None)
308 315 if user is None and not self.interactive():
309 316 try:
310 317 user = '%s@%s' % (util.getuser(), socket.getfqdn())
311 318 self.warn(_("No username found, using '%s' instead\n") % user)
312 319 except KeyError:
313 320 pass
314 321 if not user:
315 322 raise util.Abort(_('no username supplied (see "hg help config")'))
316 323 if "\n" in user:
317 324 raise util.Abort(_("username %s contains a newline\n") % repr(user))
318 325 return user
319 326
320 327 def shortuser(self, user):
321 328 """Return a short representation of a user name or email address."""
322 329 if not self.verbose:
323 330 user = util.shortuser(user)
324 331 return user
325 332
326 333 def expandpath(self, loc, default=None):
327 334 """Return repository location relative to cwd or from [paths]"""
328 335 if url.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
329 336 return loc
330 337
331 338 path = self.config('paths', loc)
332 339 if not path and default is not None:
333 340 path = self.config('paths', default)
334 341 return path or loc
335 342
336 343 def pushbuffer(self):
337 344 self._buffers.append([])
338 345
339 346 def popbuffer(self, labeled=False):
340 347 '''pop the last buffer and return the buffered output
341 348
342 349 If labeled is True, any labels associated with buffered
343 350 output will be handled. By default, this has no effect
344 351 on the output returned, but extensions and GUI tools may
345 352 handle this argument and returned styled output. If output
346 353 is being buffered so it can be captured and parsed or
347 354 processed, labeled should not be set to True.
348 355 '''
349 356 return "".join(self._buffers.pop())
350 357
351 358 def write(self, *args, **opts):
352 359 '''write args to output
353 360
354 361 By default, this method simply writes to the buffer or stdout,
355 362 but extensions or GUI tools may override this method,
356 363 write_err(), popbuffer(), and label() to style output from
357 364 various parts of hg.
358 365
359 366 An optional keyword argument, "label", can be passed in.
360 367 This should be a string containing label names separated by
361 368 space. Label names take the form of "topic.type". For example,
362 369 ui.debug() issues a label of "ui.debug".
363 370
364 371 When labeling output for a specific command, a label of
365 372 "cmdname.type" is recommended. For example, status issues
366 373 a label of "status.modified" for modified files.
367 374 '''
368 375 if self._buffers:
369 376 self._buffers[-1].extend([str(a) for a in args])
370 377 else:
371 378 for a in args:
372 379 sys.stdout.write(str(a))
373 380
374 381 def write_err(self, *args, **opts):
375 382 try:
376 383 if not getattr(sys.stdout, 'closed', False):
377 384 sys.stdout.flush()
378 385 for a in args:
379 386 sys.stderr.write(str(a))
380 387 # stderr may be buffered under win32 when redirected to files,
381 388 # including stdout.
382 389 if not getattr(sys.stderr, 'closed', False):
383 390 sys.stderr.flush()
384 391 except IOError, inst:
385 392 if inst.errno not in (errno.EPIPE, errno.EIO):
386 393 raise
387 394
388 395 def flush(self):
389 396 try: sys.stdout.flush()
390 397 except: pass
391 398 try: sys.stderr.flush()
392 399 except: pass
393 400
394 401 def interactive(self):
395 402 '''is interactive input allowed?
396 403
397 404 An interactive session is a session where input can be reasonably read
398 405 from `sys.stdin'. If this function returns false, any attempt to read
399 406 from stdin should fail with an error, unless a sensible default has been
400 407 specified.
401 408
402 409 Interactiveness is triggered by the value of the `ui.interactive'
403 410 configuration variable or - if it is unset - when `sys.stdin' points
404 411 to a terminal device.
405 412
406 413 This function refers to input only; for output, see `ui.formatted()'.
407 414 '''
408 415 i = self.configbool("ui", "interactive", None)
409 416 if i is None:
410 417 try:
411 418 return sys.stdin.isatty()
412 419 except AttributeError:
413 420 # some environments replace stdin without implementing isatty
414 421 # usually those are non-interactive
415 422 return False
416 423
417 424 return i
418 425
419 426 def termwidth(self):
420 427 '''how wide is the terminal in columns?
421 428 '''
422 429 if 'COLUMNS' in os.environ:
423 430 try:
424 431 return int(os.environ['COLUMNS'])
425 432 except ValueError:
426 433 pass
427 434 return util.termwidth()
428 435
429 436 def formatted(self):
430 437 '''should formatted output be used?
431 438
432 439 It is often desirable to format the output to suite the output medium.
433 440 Examples of this are truncating long lines or colorizing messages.
434 441 However, this is not often not desirable when piping output into other
435 442 utilities, e.g. `grep'.
436 443
437 444 Formatted output is triggered by the value of the `ui.formatted'
438 445 configuration variable or - if it is unset - when `sys.stdout' points
439 446 to a terminal device. Please note that `ui.formatted' should be
440 447 considered an implementation detail; it is not intended for use outside
441 448 Mercurial or its extensions.
442 449
443 450 This function refers to output only; for input, see `ui.interactive()'.
444 451 This function always returns false when in plain mode, see `ui.plain()'.
445 452 '''
446 453 if self.plain():
447 454 return False
448 455
449 456 i = self.configbool("ui", "formatted", None)
450 457 if i is None:
451 458 try:
452 459 return sys.stdout.isatty()
453 460 except AttributeError:
454 461 # some environments replace stdout without implementing isatty
455 462 # usually those are non-interactive
456 463 return False
457 464
458 465 return i
459 466
460 467 def _readline(self, prompt=''):
461 468 if sys.stdin.isatty():
462 469 try:
463 470 # magically add command line editing support, where
464 471 # available
465 472 import readline
466 473 # force demandimport to really load the module
467 474 readline.read_history_file
468 475 # windows sometimes raises something other than ImportError
469 476 except Exception:
470 477 pass
471 478 line = raw_input(prompt)
472 479 # When stdin is in binary mode on Windows, it can cause
473 480 # raw_input() to emit an extra trailing carriage return
474 481 if os.linesep == '\r\n' and line and line[-1] == '\r':
475 482 line = line[:-1]
476 483 return line
477 484
478 485 def prompt(self, msg, default="y"):
479 486 """Prompt user with msg, read response.
480 487 If ui is not interactive, the default is returned.
481 488 """
482 489 if not self.interactive():
483 490 self.write(msg, ' ', default, "\n")
484 491 return default
485 492 try:
486 493 r = self._readline(self.label(msg, 'ui.prompt') + ' ')
487 494 if not r:
488 495 return default
489 496 return r
490 497 except EOFError:
491 498 raise util.Abort(_('response expected'))
492 499
493 500 def promptchoice(self, msg, choices, default=0):
494 501 """Prompt user with msg, read response, and ensure it matches
495 502 one of the provided choices. The index of the choice is returned.
496 503 choices is a sequence of acceptable responses with the format:
497 504 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
498 505 If ui is not interactive, the default is returned.
499 506 """
500 507 resps = [s[s.index('&')+1].lower() for s in choices]
501 508 while True:
502 509 r = self.prompt(msg, resps[default])
503 510 if r.lower() in resps:
504 511 return resps.index(r.lower())
505 512 self.write(_("unrecognized response\n"))
506 513
507 514 def getpass(self, prompt=None, default=None):
508 515 if not self.interactive():
509 516 return default
510 517 try:
511 518 return getpass.getpass(prompt or _('password: '))
512 519 except EOFError:
513 520 raise util.Abort(_('response expected'))
514 521 def status(self, *msg, **opts):
515 522 '''write status message to output (if ui.quiet is False)
516 523
517 524 This adds an output label of "ui.status".
518 525 '''
519 526 if not self.quiet:
520 527 opts['label'] = opts.get('label', '') + ' ui.status'
521 528 self.write(*msg, **opts)
522 529 def warn(self, *msg, **opts):
523 530 '''write warning message to output (stderr)
524 531
525 532 This adds an output label of "ui.warning".
526 533 '''
527 534 opts['label'] = opts.get('label', '') + ' ui.warning'
528 535 self.write_err(*msg, **opts)
529 536 def note(self, *msg, **opts):
530 537 '''write note to output (if ui.verbose is True)
531 538
532 539 This adds an output label of "ui.note".
533 540 '''
534 541 if self.verbose:
535 542 opts['label'] = opts.get('label', '') + ' ui.note'
536 543 self.write(*msg, **opts)
537 544 def debug(self, *msg, **opts):
538 545 '''write debug message to output (if ui.debugflag is True)
539 546
540 547 This adds an output label of "ui.debug".
541 548 '''
542 549 if self.debugflag:
543 550 opts['label'] = opts.get('label', '') + ' ui.debug'
544 551 self.write(*msg, **opts)
545 552 def edit(self, text, user):
546 553 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
547 554 text=True)
548 555 try:
549 556 f = os.fdopen(fd, "w")
550 557 f.write(text)
551 558 f.close()
552 559
553 560 editor = self.geteditor()
554 561
555 562 util.system("%s \"%s\"" % (editor, name),
556 563 environ={'HGUSER': user},
557 564 onerr=util.Abort, errprefix=_("edit failed"))
558 565
559 566 f = open(name)
560 567 t = f.read()
561 568 f.close()
562 569 finally:
563 570 os.unlink(name)
564 571
565 572 return t
566 573
567 574 def traceback(self, exc=None):
568 575 '''print exception traceback if traceback printing enabled.
569 576 only to call in exception handler. returns true if traceback
570 577 printed.'''
571 578 if self.tracebackflag:
572 579 if exc:
573 580 traceback.print_exception(exc[0], exc[1], exc[2])
574 581 else:
575 582 traceback.print_exc()
576 583 return self.tracebackflag
577 584
578 585 def geteditor(self):
579 586 '''return editor to use'''
580 587 return (os.environ.get("HGEDITOR") or
581 588 self.config("ui", "editor") or
582 589 os.environ.get("VISUAL") or
583 590 os.environ.get("EDITOR", "vi"))
584 591
585 592 def progress(self, topic, pos, item="", unit="", total=None):
586 593 '''show a progress message
587 594
588 595 With stock hg, this is simply a debug message that is hidden
589 596 by default, but with extensions or GUI tools it may be
590 597 visible. 'topic' is the current operation, 'item' is a
591 598 non-numeric marker of the current position (ie the currently
592 599 in-process file), 'pos' is the current numeric position (ie
593 600 revision, bytes, etc.), unit is a corresponding unit label,
594 601 and total is the highest expected pos.
595 602
596 603 Multiple nested topics may be active at a time.
597 604
598 605 All topics should be marked closed by setting pos to None at
599 606 termination.
600 607 '''
601 608
602 609 if pos is None or not self.debugflag:
603 610 return
604 611
605 612 if unit:
606 613 unit = ' ' + unit
607 614 if item:
608 615 item = ' ' + item
609 616
610 617 if total:
611 618 pct = 100.0 * pos / total
612 619 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
613 620 % (topic, item, pos, total, unit, pct))
614 621 else:
615 622 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
616 623
617 624 def log(self, service, message):
618 625 '''hook for logging facility extensions
619 626
620 627 service should be a readily-identifiable subsystem, which will
621 628 allow filtering.
622 629 message should be a newline-terminated string to log.
623 630 '''
624 631 pass
625 632
626 633 def label(self, msg, label):
627 634 '''style msg based on supplied label
628 635
629 636 Like ui.write(), this just returns msg unchanged, but extensions
630 637 and GUI tools can override it to allow styling output without
631 638 writing it.
632 639
633 640 ui.write(s, 'label') is equivalent to
634 641 ui.write(ui.label(s, 'label')).
635 642 '''
636 643 return msg
@@ -1,135 +1,171 b''
1 1 Use hgrc within $TESTTMP
2 2
3 3 $ HGRCPATH=`pwd`/hgrc
4 4 $ export HGRCPATH
5 5
6 6 Basic syntax error
7 7
8 8 $ echo "invalid" > $HGRCPATH
9 9 $ hg version
10 10 hg: parse error at $TESTTMP/hgrc:1: invalid
11 11 [255]
12 12 $ echo "" > $HGRCPATH
13 13
14 14 Issue1199: Can't use '%' in hgrc (eg url encoded username)
15 15
16 16 $ hg init "foo%bar"
17 17 $ hg clone "foo%bar" foobar
18 18 updating to branch default
19 19 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 20 $ cd foobar
21 21 $ cat .hg/hgrc
22 22 [paths]
23 23 default = $TESTTMP/foo%bar
24 24 $ hg paths
25 25 default = $TESTTMP/foo%bar
26 26 $ hg showconfig
27 27 bundle.mainreporoot=$TESTTMP/foobar
28 28 paths.default=$TESTTMP/foo%bar
29 29 $ cd ..
30 30
31 31 issue1829: wrong indentation
32 32
33 33 $ echo '[foo]' > $HGRCPATH
34 34 $ echo ' x = y' >> $HGRCPATH
35 35 $ hg version
36 36 hg: parse error at $TESTTMP/hgrc:2: x = y
37 37 [255]
38 38
39 39 $ python -c "print '[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n'" \
40 40 > > $HGRCPATH
41 41 $ hg showconfig foo
42 42 foo.bar=a\nb\nc\nde\nfg
43 43 foo.baz=bif cb
44 44
45 45 $ FAKEPATH=/path/to/nowhere
46 46 $ export FAKEPATH
47 47 $ echo '%include $FAKEPATH/no-such-file' > $HGRCPATH
48 48 $ hg version
49 49 hg: parse error at $TESTTMP/hgrc:1: cannot include /path/to/nowhere/no-such-file (No such file or directory)
50 50 [255]
51 51 $ unset FAKEPATH
52 52
53 53 username expansion
54 54
55 55 $ olduser=$HGUSER
56 56 $ unset HGUSER
57 57
58 58 $ FAKEUSER='John Doe'
59 59 $ export FAKEUSER
60 60 $ echo '[ui]' > $HGRCPATH
61 61 $ echo 'username = $FAKEUSER' >> $HGRCPATH
62 62
63 63 $ hg init usertest
64 64 $ cd usertest
65 65 $ touch bar
66 66 $ hg commit --addremove --quiet -m "added bar"
67 67 $ hg log --template "{author}\n"
68 68 John Doe
69 69 $ cd ..
70 70
71 71 $ hg showconfig
72 72 ui.username=$FAKEUSER
73 73
74 74 $ unset FAKEUSER
75 75 $ HGUSER=$olduser
76 76 $ export HGUSER
77 77
78 78 showconfig with multiple arguments
79 79
80 80 $ echo "[alias]" > $HGRCPATH
81 81 $ echo "log = log -g" >> $HGRCPATH
82 82 $ echo "[defaults]" >> $HGRCPATH
83 83 $ echo "identify = -n" >> $HGRCPATH
84 84 $ hg showconfig alias defaults
85 85 alias.log=log -g
86 86 defaults.identify=-n
87 87 $ hg showconfig alias defaults.identify
88 88 abort: only one config item permitted
89 89 [255]
90 90 $ hg showconfig alias.log defaults.identify
91 91 abort: only one config item permitted
92 92 [255]
93 93
94 94 HGPLAIN
95 95
96 96 $ cd ..
97 97 $ p=`pwd`
98 98 $ echo "[ui]" > $HGRCPATH
99 99 $ echo "debug=true" >> $HGRCPATH
100 100 $ echo "fallbackencoding=ASCII" >> $HGRCPATH
101 101 $ echo "quiet=true" >> $HGRCPATH
102 102 $ echo "slash=true" >> $HGRCPATH
103 103 $ echo "traceback=true" >> $HGRCPATH
104 104 $ echo "verbose=true" >> $HGRCPATH
105 105 $ echo "style=~/.hgstyle" >> $HGRCPATH
106 106 $ echo "logtemplate={node}" >> $HGRCPATH
107 107 $ echo "[defaults]" >> $HGRCPATH
108 108 $ echo "identify=-n" >> $HGRCPATH
109 109 $ echo "[alias]" >> $HGRCPATH
110 110 $ echo "log=log -g" >> $HGRCPATH
111 111
112 112 customized hgrc
113 113
114 114 $ hg showconfig
115 115 read config from: $TESTTMP/hgrc
116 116 $TESTTMP/hgrc:13: alias.log=log -g
117 117 $TESTTMP/hgrc:11: defaults.identify=-n
118 118 $TESTTMP/hgrc:2: ui.debug=true
119 119 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
120 120 $TESTTMP/hgrc:4: ui.quiet=true
121 121 $TESTTMP/hgrc:5: ui.slash=true
122 122 $TESTTMP/hgrc:6: ui.traceback=true
123 123 $TESTTMP/hgrc:7: ui.verbose=true
124 124 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
125 125 $TESTTMP/hgrc:9: ui.logtemplate={node}
126 126
127 127 plain hgrc
128 128
129 129 $ HGPLAIN=; export HGPLAIN
130 130 $ hg showconfig --config ui.traceback=True --debug
131 131 read config from: $TESTTMP/hgrc
132 132 none: ui.traceback=True
133 133 none: ui.verbose=False
134 134 none: ui.debug=True
135 135 none: ui.quiet=False
136
137 plain mode with exceptions
138
139 $ cat > plain.py <<EOF
140 > def uisetup(ui):
141 > ui.write('plain: %r\n' % ui.plain())
142 > EOF
143 $ echo "[extensions]" >> $HGRCPATH
144 $ echo "plain=./plain.py" >> $HGRCPATH
145 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
146 $ hg showconfig --config ui.traceback=True --debug
147 plain: ['']
148 read config from: $TESTTMP/hgrc
149 $TESTTMP/hgrc:15: extensions.plain=./plain.py
150 none: ui.traceback=True
151 none: ui.verbose=False
152 none: ui.debug=True
153 none: ui.quiet=False
154 $ unset HGPLAIN
155 $ hg showconfig --config ui.traceback=True --debug
156 plain: ['']
157 read config from: $TESTTMP/hgrc
158 $TESTTMP/hgrc:15: extensions.plain=./plain.py
159 none: ui.traceback=True
160 none: ui.verbose=False
161 none: ui.debug=True
162 none: ui.quiet=False
163 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
164 $ hg showconfig --config ui.traceback=True --debug
165 plain: ['i18n']
166 read config from: $TESTTMP/hgrc
167 $TESTTMP/hgrc:15: extensions.plain=./plain.py
168 none: ui.traceback=True
169 none: ui.verbose=False
170 none: ui.debug=True
171 none: ui.quiet=False
General Comments 0
You need to be logged in to leave comments. Login now