##// END OF EJS Templates
ui: add a variable to control whether hooks should be called...
Idan Kamara -
r17048:15d4d475 stable
parent child Browse files
Show More
@@ -1,181 +1,184 b''
1 # hook.py - hook support for mercurial
1 # hook.py - hook support for mercurial
2 #
2 #
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys
9 import os, sys
10 import extensions, util
10 import extensions, util
11
11
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
13 '''call python hook. hook is callable object, looked up as
13 '''call python hook. hook is callable object, looked up as
14 name in python module. if callable returns "true", hook
14 name in python module. if callable returns "true", hook
15 fails, else passes. if hook raises exception, treated as
15 fails, else passes. if hook raises exception, treated as
16 hook failure. exception propagates if throw is "true".
16 hook failure. exception propagates if throw is "true".
17
17
18 reason for "true" meaning "hook failed" is so that
18 reason for "true" meaning "hook failed" is so that
19 unmodified commands (e.g. mercurial.commands.update) can
19 unmodified commands (e.g. mercurial.commands.update) can
20 be run as hooks without wrappers to convert return values.'''
20 be run as hooks without wrappers to convert return values.'''
21
21
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
23 obj = funcname
23 obj = funcname
24 if not util.safehasattr(obj, '__call__'):
24 if not util.safehasattr(obj, '__call__'):
25 d = funcname.rfind('.')
25 d = funcname.rfind('.')
26 if d == -1:
26 if d == -1:
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
28 'a module)') % (hname, funcname))
28 'a module)') % (hname, funcname))
29 modname = funcname[:d]
29 modname = funcname[:d]
30 oldpaths = sys.path
30 oldpaths = sys.path
31 if util.mainfrozen():
31 if util.mainfrozen():
32 # binary installs require sys.path manipulation
32 # binary installs require sys.path manipulation
33 modpath, modfile = os.path.split(modname)
33 modpath, modfile = os.path.split(modname)
34 if modpath and modfile:
34 if modpath and modfile:
35 sys.path = sys.path[:] + [modpath]
35 sys.path = sys.path[:] + [modpath]
36 modname = modfile
36 modname = modfile
37 try:
37 try:
38 obj = __import__(modname)
38 obj = __import__(modname)
39 except ImportError:
39 except ImportError:
40 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
40 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
41 try:
41 try:
42 # extensions are loaded with hgext_ prefix
42 # extensions are loaded with hgext_ prefix
43 obj = __import__("hgext_%s" % modname)
43 obj = __import__("hgext_%s" % modname)
44 except ImportError:
44 except ImportError:
45 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
45 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
46 if ui.tracebackflag:
46 if ui.tracebackflag:
47 ui.warn(_('exception from first failed import attempt:\n'))
47 ui.warn(_('exception from first failed import attempt:\n'))
48 ui.traceback(e1)
48 ui.traceback(e1)
49 if ui.tracebackflag:
49 if ui.tracebackflag:
50 ui.warn(_('exception from second failed import attempt:\n'))
50 ui.warn(_('exception from second failed import attempt:\n'))
51 ui.traceback(e2)
51 ui.traceback(e2)
52 raise util.Abort(_('%s hook is invalid '
52 raise util.Abort(_('%s hook is invalid '
53 '(import of "%s" failed)') %
53 '(import of "%s" failed)') %
54 (hname, modname))
54 (hname, modname))
55 sys.path = oldpaths
55 sys.path = oldpaths
56 try:
56 try:
57 for p in funcname.split('.')[1:]:
57 for p in funcname.split('.')[1:]:
58 obj = getattr(obj, p)
58 obj = getattr(obj, p)
59 except AttributeError:
59 except AttributeError:
60 raise util.Abort(_('%s hook is invalid '
60 raise util.Abort(_('%s hook is invalid '
61 '("%s" is not defined)') %
61 '("%s" is not defined)') %
62 (hname, funcname))
62 (hname, funcname))
63 if not util.safehasattr(obj, '__call__'):
63 if not util.safehasattr(obj, '__call__'):
64 raise util.Abort(_('%s hook is invalid '
64 raise util.Abort(_('%s hook is invalid '
65 '("%s" is not callable)') %
65 '("%s" is not callable)') %
66 (hname, funcname))
66 (hname, funcname))
67 try:
67 try:
68 try:
68 try:
69 # redirect IO descriptors the the ui descriptors so hooks
69 # redirect IO descriptors the the ui descriptors so hooks
70 # that write directly to these don't mess up the command
70 # that write directly to these don't mess up the command
71 # protocol when running through the command server
71 # protocol when running through the command server
72 old = sys.stdout, sys.stderr, sys.stdin
72 old = sys.stdout, sys.stderr, sys.stdin
73 sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
73 sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
74
74
75 r = obj(ui=ui, repo=repo, hooktype=name, **args)
75 r = obj(ui=ui, repo=repo, hooktype=name, **args)
76 except KeyboardInterrupt:
76 except KeyboardInterrupt:
77 raise
77 raise
78 except Exception, exc:
78 except Exception, exc:
79 if isinstance(exc, util.Abort):
79 if isinstance(exc, util.Abort):
80 ui.warn(_('error: %s hook failed: %s\n') %
80 ui.warn(_('error: %s hook failed: %s\n') %
81 (hname, exc.args[0]))
81 (hname, exc.args[0]))
82 else:
82 else:
83 ui.warn(_('error: %s hook raised an exception: '
83 ui.warn(_('error: %s hook raised an exception: '
84 '%s\n') % (hname, exc))
84 '%s\n') % (hname, exc))
85 if throw:
85 if throw:
86 raise
86 raise
87 ui.traceback()
87 ui.traceback()
88 return True
88 return True
89 finally:
89 finally:
90 sys.stdout, sys.stderr, sys.stdin = old
90 sys.stdout, sys.stderr, sys.stdin = old
91 if r:
91 if r:
92 if throw:
92 if throw:
93 raise util.Abort(_('%s hook failed') % hname)
93 raise util.Abort(_('%s hook failed') % hname)
94 ui.warn(_('warning: %s hook failed\n') % hname)
94 ui.warn(_('warning: %s hook failed\n') % hname)
95 return r
95 return r
96
96
97 def _exthook(ui, repo, name, cmd, args, throw):
97 def _exthook(ui, repo, name, cmd, args, throw):
98 ui.note(_("running hook %s: %s\n") % (name, cmd))
98 ui.note(_("running hook %s: %s\n") % (name, cmd))
99
99
100 env = {}
100 env = {}
101 for k, v in args.iteritems():
101 for k, v in args.iteritems():
102 if util.safehasattr(v, '__call__'):
102 if util.safehasattr(v, '__call__'):
103 v = v()
103 v = v()
104 if isinstance(v, dict):
104 if isinstance(v, dict):
105 # make the dictionary element order stable across Python
105 # make the dictionary element order stable across Python
106 # implementations
106 # implementations
107 v = ('{' +
107 v = ('{' +
108 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
108 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
109 '}')
109 '}')
110 env['HG_' + k.upper()] = v
110 env['HG_' + k.upper()] = v
111
111
112 if repo:
112 if repo:
113 cwd = repo.root
113 cwd = repo.root
114 else:
114 else:
115 cwd = os.getcwd()
115 cwd = os.getcwd()
116 if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
116 if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
117 r = util.system(cmd, environ=env, cwd=cwd, out=ui)
117 r = util.system(cmd, environ=env, cwd=cwd, out=ui)
118 else:
118 else:
119 r = util.system(cmd, environ=env, cwd=cwd, out=ui.fout)
119 r = util.system(cmd, environ=env, cwd=cwd, out=ui.fout)
120 if r:
120 if r:
121 desc, r = util.explainexit(r)
121 desc, r = util.explainexit(r)
122 if throw:
122 if throw:
123 raise util.Abort(_('%s hook %s') % (name, desc))
123 raise util.Abort(_('%s hook %s') % (name, desc))
124 ui.warn(_('warning: %s hook %s\n') % (name, desc))
124 ui.warn(_('warning: %s hook %s\n') % (name, desc))
125 return r
125 return r
126
126
127 def _allhooks(ui):
127 def _allhooks(ui):
128 hooks = []
128 hooks = []
129 for name, cmd in ui.configitems('hooks'):
129 for name, cmd in ui.configitems('hooks'):
130 if not name.startswith('priority'):
130 if not name.startswith('priority'):
131 priority = ui.configint('hooks', 'priority.%s' % name, 0)
131 priority = ui.configint('hooks', 'priority.%s' % name, 0)
132 hooks.append((-priority, len(hooks), name, cmd))
132 hooks.append((-priority, len(hooks), name, cmd))
133 return [(k, v) for p, o, k, v in sorted(hooks)]
133 return [(k, v) for p, o, k, v in sorted(hooks)]
134
134
135 _redirect = False
135 _redirect = False
136 def redirect(state):
136 def redirect(state):
137 global _redirect
137 global _redirect
138 _redirect = state
138 _redirect = state
139
139
140 def hook(ui, repo, name, throw=False, **args):
140 def hook(ui, repo, name, throw=False, **args):
141 if not ui.callhooks:
142 return False
143
141 r = False
144 r = False
142
145
143 oldstdout = -1
146 oldstdout = -1
144 if _redirect:
147 if _redirect:
145 try:
148 try:
146 stdoutno = sys.__stdout__.fileno()
149 stdoutno = sys.__stdout__.fileno()
147 stderrno = sys.__stderr__.fileno()
150 stderrno = sys.__stderr__.fileno()
148 # temporarily redirect stdout to stderr, if possible
151 # temporarily redirect stdout to stderr, if possible
149 if stdoutno >= 0 and stderrno >= 0:
152 if stdoutno >= 0 and stderrno >= 0:
150 sys.__stdout__.flush()
153 sys.__stdout__.flush()
151 oldstdout = os.dup(stdoutno)
154 oldstdout = os.dup(stdoutno)
152 os.dup2(stderrno, stdoutno)
155 os.dup2(stderrno, stdoutno)
153 except AttributeError:
156 except AttributeError:
154 # __stdout/err__ doesn't have fileno(), it's not a real file
157 # __stdout/err__ doesn't have fileno(), it's not a real file
155 pass
158 pass
156
159
157 try:
160 try:
158 for hname, cmd in _allhooks(ui):
161 for hname, cmd in _allhooks(ui):
159 if hname.split('.')[0] != name or not cmd:
162 if hname.split('.')[0] != name or not cmd:
160 continue
163 continue
161 if util.safehasattr(cmd, '__call__'):
164 if util.safehasattr(cmd, '__call__'):
162 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
165 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
163 elif cmd.startswith('python:'):
166 elif cmd.startswith('python:'):
164 if cmd.count(':') >= 2:
167 if cmd.count(':') >= 2:
165 path, cmd = cmd[7:].rsplit(':', 1)
168 path, cmd = cmd[7:].rsplit(':', 1)
166 path = util.expandpath(path)
169 path = util.expandpath(path)
167 if repo:
170 if repo:
168 path = os.path.join(repo.root, path)
171 path = os.path.join(repo.root, path)
169 mod = extensions.loadpath(path, 'hghook.%s' % hname)
172 mod = extensions.loadpath(path, 'hghook.%s' % hname)
170 hookfn = getattr(mod, cmd)
173 hookfn = getattr(mod, cmd)
171 else:
174 else:
172 hookfn = cmd[7:].strip()
175 hookfn = cmd[7:].strip()
173 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
176 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
174 else:
177 else:
175 r = _exthook(ui, repo, hname, cmd, args, throw) or r
178 r = _exthook(ui, repo, hname, cmd, args, throw) or r
176 finally:
179 finally:
177 if _redirect and oldstdout >= 0:
180 if _redirect and oldstdout >= 0:
178 os.dup2(oldstdout, stdoutno)
181 os.dup2(oldstdout, stdoutno)
179 os.close(oldstdout)
182 os.close(oldstdout)
180
183
181 return r
184 return r
@@ -1,753 +1,755 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, scmutil, util, error, formatter
10 import config, scmutil, util, error, formatter
11
11
12 class ui(object):
12 class ui(object):
13 def __init__(self, src=None):
13 def __init__(self, src=None):
14 self._buffers = []
14 self._buffers = []
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 self._reportuntrusted = True
16 self._reportuntrusted = True
17 self._ocfg = config.config() # overlay
17 self._ocfg = config.config() # overlay
18 self._tcfg = config.config() # trusted
18 self._tcfg = config.config() # trusted
19 self._ucfg = config.config() # untrusted
19 self._ucfg = config.config() # untrusted
20 self._trustusers = set()
20 self._trustusers = set()
21 self._trustgroups = set()
21 self._trustgroups = set()
22 self.callhooks = True
22
23
23 if src:
24 if src:
24 self.fout = src.fout
25 self.fout = src.fout
25 self.ferr = src.ferr
26 self.ferr = src.ferr
26 self.fin = src.fin
27 self.fin = src.fin
27
28
28 self._tcfg = src._tcfg.copy()
29 self._tcfg = src._tcfg.copy()
29 self._ucfg = src._ucfg.copy()
30 self._ucfg = src._ucfg.copy()
30 self._ocfg = src._ocfg.copy()
31 self._ocfg = src._ocfg.copy()
31 self._trustusers = src._trustusers.copy()
32 self._trustusers = src._trustusers.copy()
32 self._trustgroups = src._trustgroups.copy()
33 self._trustgroups = src._trustgroups.copy()
33 self.environ = src.environ
34 self.environ = src.environ
35 self.callhooks = src.callhooks
34 self.fixconfig()
36 self.fixconfig()
35 else:
37 else:
36 self.fout = sys.stdout
38 self.fout = sys.stdout
37 self.ferr = sys.stderr
39 self.ferr = sys.stderr
38 self.fin = sys.stdin
40 self.fin = sys.stdin
39
41
40 # shared read-only environment
42 # shared read-only environment
41 self.environ = os.environ
43 self.environ = os.environ
42 # we always trust global config files
44 # we always trust global config files
43 for f in scmutil.rcpath():
45 for f in scmutil.rcpath():
44 self.readconfig(f, trust=True)
46 self.readconfig(f, trust=True)
45
47
46 def copy(self):
48 def copy(self):
47 return self.__class__(self)
49 return self.__class__(self)
48
50
49 def formatter(self, topic, opts):
51 def formatter(self, topic, opts):
50 return formatter.formatter(self, topic, opts)
52 return formatter.formatter(self, topic, opts)
51
53
52 def _trusted(self, fp, f):
54 def _trusted(self, fp, f):
53 st = util.fstat(fp)
55 st = util.fstat(fp)
54 if util.isowner(st):
56 if util.isowner(st):
55 return True
57 return True
56
58
57 tusers, tgroups = self._trustusers, self._trustgroups
59 tusers, tgroups = self._trustusers, self._trustgroups
58 if '*' in tusers or '*' in tgroups:
60 if '*' in tusers or '*' in tgroups:
59 return True
61 return True
60
62
61 user = util.username(st.st_uid)
63 user = util.username(st.st_uid)
62 group = util.groupname(st.st_gid)
64 group = util.groupname(st.st_gid)
63 if user in tusers or group in tgroups or user == util.username():
65 if user in tusers or group in tgroups or user == util.username():
64 return True
66 return True
65
67
66 if self._reportuntrusted:
68 if self._reportuntrusted:
67 self.warn(_('Not trusting file %s from untrusted '
69 self.warn(_('Not trusting file %s from untrusted '
68 'user %s, group %s\n') % (f, user, group))
70 'user %s, group %s\n') % (f, user, group))
69 return False
71 return False
70
72
71 def readconfig(self, filename, root=None, trust=False,
73 def readconfig(self, filename, root=None, trust=False,
72 sections=None, remap=None):
74 sections=None, remap=None):
73 try:
75 try:
74 fp = open(filename)
76 fp = open(filename)
75 except IOError:
77 except IOError:
76 if not sections: # ignore unless we were looking for something
78 if not sections: # ignore unless we were looking for something
77 return
79 return
78 raise
80 raise
79
81
80 cfg = config.config()
82 cfg = config.config()
81 trusted = sections or trust or self._trusted(fp, filename)
83 trusted = sections or trust or self._trusted(fp, filename)
82
84
83 try:
85 try:
84 cfg.read(filename, fp, sections=sections, remap=remap)
86 cfg.read(filename, fp, sections=sections, remap=remap)
85 fp.close()
87 fp.close()
86 except error.ConfigError, inst:
88 except error.ConfigError, inst:
87 if trusted:
89 if trusted:
88 raise
90 raise
89 self.warn(_("Ignored: %s\n") % str(inst))
91 self.warn(_("Ignored: %s\n") % str(inst))
90
92
91 if self.plain():
93 if self.plain():
92 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
94 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
93 'logtemplate', 'style',
95 'logtemplate', 'style',
94 'traceback', 'verbose'):
96 'traceback', 'verbose'):
95 if k in cfg['ui']:
97 if k in cfg['ui']:
96 del cfg['ui'][k]
98 del cfg['ui'][k]
97 for k, v in cfg.items('defaults'):
99 for k, v in cfg.items('defaults'):
98 del cfg['defaults'][k]
100 del cfg['defaults'][k]
99 # Don't remove aliases from the configuration if in the exceptionlist
101 # Don't remove aliases from the configuration if in the exceptionlist
100 if self.plain('alias'):
102 if self.plain('alias'):
101 for k, v in cfg.items('alias'):
103 for k, v in cfg.items('alias'):
102 del cfg['alias'][k]
104 del cfg['alias'][k]
103
105
104 if trusted:
106 if trusted:
105 self._tcfg.update(cfg)
107 self._tcfg.update(cfg)
106 self._tcfg.update(self._ocfg)
108 self._tcfg.update(self._ocfg)
107 self._ucfg.update(cfg)
109 self._ucfg.update(cfg)
108 self._ucfg.update(self._ocfg)
110 self._ucfg.update(self._ocfg)
109
111
110 if root is None:
112 if root is None:
111 root = os.path.expanduser('~')
113 root = os.path.expanduser('~')
112 self.fixconfig(root=root)
114 self.fixconfig(root=root)
113
115
114 def fixconfig(self, root=None, section=None):
116 def fixconfig(self, root=None, section=None):
115 if section in (None, 'paths'):
117 if section in (None, 'paths'):
116 # expand vars and ~
118 # expand vars and ~
117 # translate paths relative to root (or home) into absolute paths
119 # translate paths relative to root (or home) into absolute paths
118 root = root or os.getcwd()
120 root = root or os.getcwd()
119 for c in self._tcfg, self._ucfg, self._ocfg:
121 for c in self._tcfg, self._ucfg, self._ocfg:
120 for n, p in c.items('paths'):
122 for n, p in c.items('paths'):
121 if not p:
123 if not p:
122 continue
124 continue
123 if '%%' in p:
125 if '%%' in p:
124 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
126 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
125 % (n, p, self.configsource('paths', n)))
127 % (n, p, self.configsource('paths', n)))
126 p = p.replace('%%', '%')
128 p = p.replace('%%', '%')
127 p = util.expandpath(p)
129 p = util.expandpath(p)
128 if not util.hasscheme(p) and not os.path.isabs(p):
130 if not util.hasscheme(p) and not os.path.isabs(p):
129 p = os.path.normpath(os.path.join(root, p))
131 p = os.path.normpath(os.path.join(root, p))
130 c.set("paths", n, p)
132 c.set("paths", n, p)
131
133
132 if section in (None, 'ui'):
134 if section in (None, 'ui'):
133 # update ui options
135 # update ui options
134 self.debugflag = self.configbool('ui', 'debug')
136 self.debugflag = self.configbool('ui', 'debug')
135 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
137 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
136 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
138 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
137 if self.verbose and self.quiet:
139 if self.verbose and self.quiet:
138 self.quiet = self.verbose = False
140 self.quiet = self.verbose = False
139 self._reportuntrusted = self.debugflag or self.configbool("ui",
141 self._reportuntrusted = self.debugflag or self.configbool("ui",
140 "report_untrusted", True)
142 "report_untrusted", True)
141 self.tracebackflag = self.configbool('ui', 'traceback', False)
143 self.tracebackflag = self.configbool('ui', 'traceback', False)
142
144
143 if section in (None, 'trusted'):
145 if section in (None, 'trusted'):
144 # update trust information
146 # update trust information
145 self._trustusers.update(self.configlist('trusted', 'users'))
147 self._trustusers.update(self.configlist('trusted', 'users'))
146 self._trustgroups.update(self.configlist('trusted', 'groups'))
148 self._trustgroups.update(self.configlist('trusted', 'groups'))
147
149
148 def backupconfig(self, section, item):
150 def backupconfig(self, section, item):
149 return (self._ocfg.backup(section, item),
151 return (self._ocfg.backup(section, item),
150 self._tcfg.backup(section, item),
152 self._tcfg.backup(section, item),
151 self._ucfg.backup(section, item),)
153 self._ucfg.backup(section, item),)
152 def restoreconfig(self, data):
154 def restoreconfig(self, data):
153 self._ocfg.restore(data[0])
155 self._ocfg.restore(data[0])
154 self._tcfg.restore(data[1])
156 self._tcfg.restore(data[1])
155 self._ucfg.restore(data[2])
157 self._ucfg.restore(data[2])
156
158
157 def setconfig(self, section, name, value, overlay=True):
159 def setconfig(self, section, name, value, overlay=True):
158 if overlay:
160 if overlay:
159 self._ocfg.set(section, name, value)
161 self._ocfg.set(section, name, value)
160 self._tcfg.set(section, name, value)
162 self._tcfg.set(section, name, value)
161 self._ucfg.set(section, name, value)
163 self._ucfg.set(section, name, value)
162 self.fixconfig(section=section)
164 self.fixconfig(section=section)
163
165
164 def _data(self, untrusted):
166 def _data(self, untrusted):
165 return untrusted and self._ucfg or self._tcfg
167 return untrusted and self._ucfg or self._tcfg
166
168
167 def configsource(self, section, name, untrusted=False):
169 def configsource(self, section, name, untrusted=False):
168 return self._data(untrusted).source(section, name) or 'none'
170 return self._data(untrusted).source(section, name) or 'none'
169
171
170 def config(self, section, name, default=None, untrusted=False):
172 def config(self, section, name, default=None, untrusted=False):
171 if isinstance(name, list):
173 if isinstance(name, list):
172 alternates = name
174 alternates = name
173 else:
175 else:
174 alternates = [name]
176 alternates = [name]
175
177
176 for n in alternates:
178 for n in alternates:
177 value = self._data(untrusted).get(section, name, None)
179 value = self._data(untrusted).get(section, name, None)
178 if value is not None:
180 if value is not None:
179 name = n
181 name = n
180 break
182 break
181 else:
183 else:
182 value = default
184 value = default
183
185
184 if self.debugflag and not untrusted and self._reportuntrusted:
186 if self.debugflag and not untrusted and self._reportuntrusted:
185 uvalue = self._ucfg.get(section, name)
187 uvalue = self._ucfg.get(section, name)
186 if uvalue is not None and uvalue != value:
188 if uvalue is not None and uvalue != value:
187 self.debug("ignoring untrusted configuration option "
189 self.debug("ignoring untrusted configuration option "
188 "%s.%s = %s\n" % (section, name, uvalue))
190 "%s.%s = %s\n" % (section, name, uvalue))
189 return value
191 return value
190
192
191 def configpath(self, section, name, default=None, untrusted=False):
193 def configpath(self, section, name, default=None, untrusted=False):
192 'get a path config item, expanded relative to repo root or config file'
194 'get a path config item, expanded relative to repo root or config file'
193 v = self.config(section, name, default, untrusted)
195 v = self.config(section, name, default, untrusted)
194 if v is None:
196 if v is None:
195 return None
197 return None
196 if not os.path.isabs(v) or "://" not in v:
198 if not os.path.isabs(v) or "://" not in v:
197 src = self.configsource(section, name, untrusted)
199 src = self.configsource(section, name, untrusted)
198 if ':' in src:
200 if ':' in src:
199 base = os.path.dirname(src.rsplit(':')[0])
201 base = os.path.dirname(src.rsplit(':')[0])
200 v = os.path.join(base, os.path.expanduser(v))
202 v = os.path.join(base, os.path.expanduser(v))
201 return v
203 return v
202
204
203 def configbool(self, section, name, default=False, untrusted=False):
205 def configbool(self, section, name, default=False, untrusted=False):
204 """parse a configuration element as a boolean
206 """parse a configuration element as a boolean
205
207
206 >>> u = ui(); s = 'foo'
208 >>> u = ui(); s = 'foo'
207 >>> u.setconfig(s, 'true', 'yes')
209 >>> u.setconfig(s, 'true', 'yes')
208 >>> u.configbool(s, 'true')
210 >>> u.configbool(s, 'true')
209 True
211 True
210 >>> u.setconfig(s, 'false', 'no')
212 >>> u.setconfig(s, 'false', 'no')
211 >>> u.configbool(s, 'false')
213 >>> u.configbool(s, 'false')
212 False
214 False
213 >>> u.configbool(s, 'unknown')
215 >>> u.configbool(s, 'unknown')
214 False
216 False
215 >>> u.configbool(s, 'unknown', True)
217 >>> u.configbool(s, 'unknown', True)
216 True
218 True
217 >>> u.setconfig(s, 'invalid', 'somevalue')
219 >>> u.setconfig(s, 'invalid', 'somevalue')
218 >>> u.configbool(s, 'invalid')
220 >>> u.configbool(s, 'invalid')
219 Traceback (most recent call last):
221 Traceback (most recent call last):
220 ...
222 ...
221 ConfigError: foo.invalid is not a boolean ('somevalue')
223 ConfigError: foo.invalid is not a boolean ('somevalue')
222 """
224 """
223
225
224 v = self.config(section, name, None, untrusted)
226 v = self.config(section, name, None, untrusted)
225 if v is None:
227 if v is None:
226 return default
228 return default
227 if isinstance(v, bool):
229 if isinstance(v, bool):
228 return v
230 return v
229 b = util.parsebool(v)
231 b = util.parsebool(v)
230 if b is None:
232 if b is None:
231 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
232 % (section, name, v))
234 % (section, name, v))
233 return b
235 return b
234
236
235 def configint(self, section, name, default=None, untrusted=False):
237 def configint(self, section, name, default=None, untrusted=False):
236 """parse a configuration element as an integer
238 """parse a configuration element as an integer
237
239
238 >>> u = ui(); s = 'foo'
240 >>> u = ui(); s = 'foo'
239 >>> u.setconfig(s, 'int1', '42')
241 >>> u.setconfig(s, 'int1', '42')
240 >>> u.configint(s, 'int1')
242 >>> u.configint(s, 'int1')
241 42
243 42
242 >>> u.setconfig(s, 'int2', '-42')
244 >>> u.setconfig(s, 'int2', '-42')
243 >>> u.configint(s, 'int2')
245 >>> u.configint(s, 'int2')
244 -42
246 -42
245 >>> u.configint(s, 'unknown', 7)
247 >>> u.configint(s, 'unknown', 7)
246 7
248 7
247 >>> u.setconfig(s, 'invalid', 'somevalue')
249 >>> u.setconfig(s, 'invalid', 'somevalue')
248 >>> u.configint(s, 'invalid')
250 >>> u.configint(s, 'invalid')
249 Traceback (most recent call last):
251 Traceback (most recent call last):
250 ...
252 ...
251 ConfigError: foo.invalid is not an integer ('somevalue')
253 ConfigError: foo.invalid is not an integer ('somevalue')
252 """
254 """
253
255
254 v = self.config(section, name, None, untrusted)
256 v = self.config(section, name, None, untrusted)
255 if v is None:
257 if v is None:
256 return default
258 return default
257 try:
259 try:
258 return int(v)
260 return int(v)
259 except ValueError:
261 except ValueError:
260 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
261 % (section, name, v))
263 % (section, name, v))
262
264
263 def configlist(self, section, name, default=None, untrusted=False):
265 def configlist(self, section, name, default=None, untrusted=False):
264 """parse a configuration element as a list of comma/space separated
266 """parse a configuration element as a list of comma/space separated
265 strings
267 strings
266
268
267 >>> u = ui(); s = 'foo'
269 >>> u = ui(); s = 'foo'
268 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
270 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
269 >>> u.configlist(s, 'list1')
271 >>> u.configlist(s, 'list1')
270 ['this', 'is', 'a small', 'test']
272 ['this', 'is', 'a small', 'test']
271 """
273 """
272
274
273 def _parse_plain(parts, s, offset):
275 def _parse_plain(parts, s, offset):
274 whitespace = False
276 whitespace = False
275 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
277 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
276 whitespace = True
278 whitespace = True
277 offset += 1
279 offset += 1
278 if offset >= len(s):
280 if offset >= len(s):
279 return None, parts, offset
281 return None, parts, offset
280 if whitespace:
282 if whitespace:
281 parts.append('')
283 parts.append('')
282 if s[offset] == '"' and not parts[-1]:
284 if s[offset] == '"' and not parts[-1]:
283 return _parse_quote, parts, offset + 1
285 return _parse_quote, parts, offset + 1
284 elif s[offset] == '"' and parts[-1][-1] == '\\':
286 elif s[offset] == '"' and parts[-1][-1] == '\\':
285 parts[-1] = parts[-1][:-1] + s[offset]
287 parts[-1] = parts[-1][:-1] + s[offset]
286 return _parse_plain, parts, offset + 1
288 return _parse_plain, parts, offset + 1
287 parts[-1] += s[offset]
289 parts[-1] += s[offset]
288 return _parse_plain, parts, offset + 1
290 return _parse_plain, parts, offset + 1
289
291
290 def _parse_quote(parts, s, offset):
292 def _parse_quote(parts, s, offset):
291 if offset < len(s) and s[offset] == '"': # ""
293 if offset < len(s) and s[offset] == '"': # ""
292 parts.append('')
294 parts.append('')
293 offset += 1
295 offset += 1
294 while offset < len(s) and (s[offset].isspace() or
296 while offset < len(s) and (s[offset].isspace() or
295 s[offset] == ','):
297 s[offset] == ','):
296 offset += 1
298 offset += 1
297 return _parse_plain, parts, offset
299 return _parse_plain, parts, offset
298
300
299 while offset < len(s) and s[offset] != '"':
301 while offset < len(s) and s[offset] != '"':
300 if (s[offset] == '\\' and offset + 1 < len(s)
302 if (s[offset] == '\\' and offset + 1 < len(s)
301 and s[offset + 1] == '"'):
303 and s[offset + 1] == '"'):
302 offset += 1
304 offset += 1
303 parts[-1] += '"'
305 parts[-1] += '"'
304 else:
306 else:
305 parts[-1] += s[offset]
307 parts[-1] += s[offset]
306 offset += 1
308 offset += 1
307
309
308 if offset >= len(s):
310 if offset >= len(s):
309 real_parts = _configlist(parts[-1])
311 real_parts = _configlist(parts[-1])
310 if not real_parts:
312 if not real_parts:
311 parts[-1] = '"'
313 parts[-1] = '"'
312 else:
314 else:
313 real_parts[0] = '"' + real_parts[0]
315 real_parts[0] = '"' + real_parts[0]
314 parts = parts[:-1]
316 parts = parts[:-1]
315 parts.extend(real_parts)
317 parts.extend(real_parts)
316 return None, parts, offset
318 return None, parts, offset
317
319
318 offset += 1
320 offset += 1
319 while offset < len(s) and s[offset] in [' ', ',']:
321 while offset < len(s) and s[offset] in [' ', ',']:
320 offset += 1
322 offset += 1
321
323
322 if offset < len(s):
324 if offset < len(s):
323 if offset + 1 == len(s) and s[offset] == '"':
325 if offset + 1 == len(s) and s[offset] == '"':
324 parts[-1] += '"'
326 parts[-1] += '"'
325 offset += 1
327 offset += 1
326 else:
328 else:
327 parts.append('')
329 parts.append('')
328 else:
330 else:
329 return None, parts, offset
331 return None, parts, offset
330
332
331 return _parse_plain, parts, offset
333 return _parse_plain, parts, offset
332
334
333 def _configlist(s):
335 def _configlist(s):
334 s = s.rstrip(' ,')
336 s = s.rstrip(' ,')
335 if not s:
337 if not s:
336 return []
338 return []
337 parser, parts, offset = _parse_plain, [''], 0
339 parser, parts, offset = _parse_plain, [''], 0
338 while parser:
340 while parser:
339 parser, parts, offset = parser(parts, s, offset)
341 parser, parts, offset = parser(parts, s, offset)
340 return parts
342 return parts
341
343
342 result = self.config(section, name, untrusted=untrusted)
344 result = self.config(section, name, untrusted=untrusted)
343 if result is None:
345 if result is None:
344 result = default or []
346 result = default or []
345 if isinstance(result, basestring):
347 if isinstance(result, basestring):
346 result = _configlist(result.lstrip(' ,\n'))
348 result = _configlist(result.lstrip(' ,\n'))
347 if result is None:
349 if result is None:
348 result = default or []
350 result = default or []
349 return result
351 return result
350
352
351 def has_section(self, section, untrusted=False):
353 def has_section(self, section, untrusted=False):
352 '''tell whether section exists in config.'''
354 '''tell whether section exists in config.'''
353 return section in self._data(untrusted)
355 return section in self._data(untrusted)
354
356
355 def configitems(self, section, untrusted=False):
357 def configitems(self, section, untrusted=False):
356 items = self._data(untrusted).items(section)
358 items = self._data(untrusted).items(section)
357 if self.debugflag and not untrusted and self._reportuntrusted:
359 if self.debugflag and not untrusted and self._reportuntrusted:
358 for k, v in self._ucfg.items(section):
360 for k, v in self._ucfg.items(section):
359 if self._tcfg.get(section, k) != v:
361 if self._tcfg.get(section, k) != v:
360 self.debug("ignoring untrusted configuration option "
362 self.debug("ignoring untrusted configuration option "
361 "%s.%s = %s\n" % (section, k, v))
363 "%s.%s = %s\n" % (section, k, v))
362 return items
364 return items
363
365
364 def walkconfig(self, untrusted=False):
366 def walkconfig(self, untrusted=False):
365 cfg = self._data(untrusted)
367 cfg = self._data(untrusted)
366 for section in cfg.sections():
368 for section in cfg.sections():
367 for name, value in self.configitems(section, untrusted):
369 for name, value in self.configitems(section, untrusted):
368 yield section, name, value
370 yield section, name, value
369
371
370 def plain(self, feature=None):
372 def plain(self, feature=None):
371 '''is plain mode active?
373 '''is plain mode active?
372
374
373 Plain mode means that all configuration variables which affect
375 Plain mode means that all configuration variables which affect
374 the behavior and output of Mercurial should be
376 the behavior and output of Mercurial should be
375 ignored. Additionally, the output should be stable,
377 ignored. Additionally, the output should be stable,
376 reproducible and suitable for use in scripts or applications.
378 reproducible and suitable for use in scripts or applications.
377
379
378 The only way to trigger plain mode is by setting either the
380 The only way to trigger plain mode is by setting either the
379 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
381 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
380
382
381 The return value can either be
383 The return value can either be
382 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
384 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
383 - True otherwise
385 - True otherwise
384 '''
386 '''
385 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
387 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
386 return False
388 return False
387 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
389 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
388 if feature and exceptions:
390 if feature and exceptions:
389 return feature not in exceptions
391 return feature not in exceptions
390 return True
392 return True
391
393
392 def username(self):
394 def username(self):
393 """Return default username to be used in commits.
395 """Return default username to be used in commits.
394
396
395 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
397 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
396 and stop searching if one of these is set.
398 and stop searching if one of these is set.
397 If not found and ui.askusername is True, ask the user, else use
399 If not found and ui.askusername is True, ask the user, else use
398 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
400 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
399 """
401 """
400 user = os.environ.get("HGUSER")
402 user = os.environ.get("HGUSER")
401 if user is None:
403 if user is None:
402 user = self.config("ui", "username")
404 user = self.config("ui", "username")
403 if user is not None:
405 if user is not None:
404 user = os.path.expandvars(user)
406 user = os.path.expandvars(user)
405 if user is None:
407 if user is None:
406 user = os.environ.get("EMAIL")
408 user = os.environ.get("EMAIL")
407 if user is None and self.configbool("ui", "askusername"):
409 if user is None and self.configbool("ui", "askusername"):
408 user = self.prompt(_("enter a commit username:"), default=None)
410 user = self.prompt(_("enter a commit username:"), default=None)
409 if user is None and not self.interactive():
411 if user is None and not self.interactive():
410 try:
412 try:
411 user = '%s@%s' % (util.getuser(), socket.getfqdn())
413 user = '%s@%s' % (util.getuser(), socket.getfqdn())
412 self.warn(_("No username found, using '%s' instead\n") % user)
414 self.warn(_("No username found, using '%s' instead\n") % user)
413 except KeyError:
415 except KeyError:
414 pass
416 pass
415 if not user:
417 if not user:
416 raise util.Abort(_('no username supplied (see "hg help config")'))
418 raise util.Abort(_('no username supplied (see "hg help config")'))
417 if "\n" in user:
419 if "\n" in user:
418 raise util.Abort(_("username %s contains a newline\n") % repr(user))
420 raise util.Abort(_("username %s contains a newline\n") % repr(user))
419 return user
421 return user
420
422
421 def shortuser(self, user):
423 def shortuser(self, user):
422 """Return a short representation of a user name or email address."""
424 """Return a short representation of a user name or email address."""
423 if not self.verbose:
425 if not self.verbose:
424 user = util.shortuser(user)
426 user = util.shortuser(user)
425 return user
427 return user
426
428
427 def expandpath(self, loc, default=None):
429 def expandpath(self, loc, default=None):
428 """Return repository location relative to cwd or from [paths]"""
430 """Return repository location relative to cwd or from [paths]"""
429 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
431 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
430 return loc
432 return loc
431
433
432 path = self.config('paths', loc)
434 path = self.config('paths', loc)
433 if not path and default is not None:
435 if not path and default is not None:
434 path = self.config('paths', default)
436 path = self.config('paths', default)
435 return path or loc
437 return path or loc
436
438
437 def pushbuffer(self):
439 def pushbuffer(self):
438 self._buffers.append([])
440 self._buffers.append([])
439
441
440 def popbuffer(self, labeled=False):
442 def popbuffer(self, labeled=False):
441 '''pop the last buffer and return the buffered output
443 '''pop the last buffer and return the buffered output
442
444
443 If labeled is True, any labels associated with buffered
445 If labeled is True, any labels associated with buffered
444 output will be handled. By default, this has no effect
446 output will be handled. By default, this has no effect
445 on the output returned, but extensions and GUI tools may
447 on the output returned, but extensions and GUI tools may
446 handle this argument and returned styled output. If output
448 handle this argument and returned styled output. If output
447 is being buffered so it can be captured and parsed or
449 is being buffered so it can be captured and parsed or
448 processed, labeled should not be set to True.
450 processed, labeled should not be set to True.
449 '''
451 '''
450 return "".join(self._buffers.pop())
452 return "".join(self._buffers.pop())
451
453
452 def write(self, *args, **opts):
454 def write(self, *args, **opts):
453 '''write args to output
455 '''write args to output
454
456
455 By default, this method simply writes to the buffer or stdout,
457 By default, this method simply writes to the buffer or stdout,
456 but extensions or GUI tools may override this method,
458 but extensions or GUI tools may override this method,
457 write_err(), popbuffer(), and label() to style output from
459 write_err(), popbuffer(), and label() to style output from
458 various parts of hg.
460 various parts of hg.
459
461
460 An optional keyword argument, "label", can be passed in.
462 An optional keyword argument, "label", can be passed in.
461 This should be a string containing label names separated by
463 This should be a string containing label names separated by
462 space. Label names take the form of "topic.type". For example,
464 space. Label names take the form of "topic.type". For example,
463 ui.debug() issues a label of "ui.debug".
465 ui.debug() issues a label of "ui.debug".
464
466
465 When labeling output for a specific command, a label of
467 When labeling output for a specific command, a label of
466 "cmdname.type" is recommended. For example, status issues
468 "cmdname.type" is recommended. For example, status issues
467 a label of "status.modified" for modified files.
469 a label of "status.modified" for modified files.
468 '''
470 '''
469 if self._buffers:
471 if self._buffers:
470 self._buffers[-1].extend([str(a) for a in args])
472 self._buffers[-1].extend([str(a) for a in args])
471 else:
473 else:
472 for a in args:
474 for a in args:
473 self.fout.write(str(a))
475 self.fout.write(str(a))
474
476
475 def write_err(self, *args, **opts):
477 def write_err(self, *args, **opts):
476 try:
478 try:
477 if not getattr(self.fout, 'closed', False):
479 if not getattr(self.fout, 'closed', False):
478 self.fout.flush()
480 self.fout.flush()
479 for a in args:
481 for a in args:
480 self.ferr.write(str(a))
482 self.ferr.write(str(a))
481 # stderr may be buffered under win32 when redirected to files,
483 # stderr may be buffered under win32 when redirected to files,
482 # including stdout.
484 # including stdout.
483 if not getattr(self.ferr, 'closed', False):
485 if not getattr(self.ferr, 'closed', False):
484 self.ferr.flush()
486 self.ferr.flush()
485 except IOError, inst:
487 except IOError, inst:
486 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
488 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
487 raise
489 raise
488
490
489 def flush(self):
491 def flush(self):
490 try: self.fout.flush()
492 try: self.fout.flush()
491 except: pass
493 except: pass
492 try: self.ferr.flush()
494 try: self.ferr.flush()
493 except: pass
495 except: pass
494
496
495 def interactive(self):
497 def interactive(self):
496 '''is interactive input allowed?
498 '''is interactive input allowed?
497
499
498 An interactive session is a session where input can be reasonably read
500 An interactive session is a session where input can be reasonably read
499 from `sys.stdin'. If this function returns false, any attempt to read
501 from `sys.stdin'. If this function returns false, any attempt to read
500 from stdin should fail with an error, unless a sensible default has been
502 from stdin should fail with an error, unless a sensible default has been
501 specified.
503 specified.
502
504
503 Interactiveness is triggered by the value of the `ui.interactive'
505 Interactiveness is triggered by the value of the `ui.interactive'
504 configuration variable or - if it is unset - when `sys.stdin' points
506 configuration variable or - if it is unset - when `sys.stdin' points
505 to a terminal device.
507 to a terminal device.
506
508
507 This function refers to input only; for output, see `ui.formatted()'.
509 This function refers to input only; for output, see `ui.formatted()'.
508 '''
510 '''
509 i = self.configbool("ui", "interactive", None)
511 i = self.configbool("ui", "interactive", None)
510 if i is None:
512 if i is None:
511 # some environments replace stdin without implementing isatty
513 # some environments replace stdin without implementing isatty
512 # usually those are non-interactive
514 # usually those are non-interactive
513 return util.isatty(self.fin)
515 return util.isatty(self.fin)
514
516
515 return i
517 return i
516
518
517 def termwidth(self):
519 def termwidth(self):
518 '''how wide is the terminal in columns?
520 '''how wide is the terminal in columns?
519 '''
521 '''
520 if 'COLUMNS' in os.environ:
522 if 'COLUMNS' in os.environ:
521 try:
523 try:
522 return int(os.environ['COLUMNS'])
524 return int(os.environ['COLUMNS'])
523 except ValueError:
525 except ValueError:
524 pass
526 pass
525 return util.termwidth()
527 return util.termwidth()
526
528
527 def formatted(self):
529 def formatted(self):
528 '''should formatted output be used?
530 '''should formatted output be used?
529
531
530 It is often desirable to format the output to suite the output medium.
532 It is often desirable to format the output to suite the output medium.
531 Examples of this are truncating long lines or colorizing messages.
533 Examples of this are truncating long lines or colorizing messages.
532 However, this is not often not desirable when piping output into other
534 However, this is not often not desirable when piping output into other
533 utilities, e.g. `grep'.
535 utilities, e.g. `grep'.
534
536
535 Formatted output is triggered by the value of the `ui.formatted'
537 Formatted output is triggered by the value of the `ui.formatted'
536 configuration variable or - if it is unset - when `sys.stdout' points
538 configuration variable or - if it is unset - when `sys.stdout' points
537 to a terminal device. Please note that `ui.formatted' should be
539 to a terminal device. Please note that `ui.formatted' should be
538 considered an implementation detail; it is not intended for use outside
540 considered an implementation detail; it is not intended for use outside
539 Mercurial or its extensions.
541 Mercurial or its extensions.
540
542
541 This function refers to output only; for input, see `ui.interactive()'.
543 This function refers to output only; for input, see `ui.interactive()'.
542 This function always returns false when in plain mode, see `ui.plain()'.
544 This function always returns false when in plain mode, see `ui.plain()'.
543 '''
545 '''
544 if self.plain():
546 if self.plain():
545 return False
547 return False
546
548
547 i = self.configbool("ui", "formatted", None)
549 i = self.configbool("ui", "formatted", None)
548 if i is None:
550 if i is None:
549 # some environments replace stdout without implementing isatty
551 # some environments replace stdout without implementing isatty
550 # usually those are non-interactive
552 # usually those are non-interactive
551 return util.isatty(self.fout)
553 return util.isatty(self.fout)
552
554
553 return i
555 return i
554
556
555 def _readline(self, prompt=''):
557 def _readline(self, prompt=''):
556 if util.isatty(self.fin):
558 if util.isatty(self.fin):
557 try:
559 try:
558 # magically add command line editing support, where
560 # magically add command line editing support, where
559 # available
561 # available
560 import readline
562 import readline
561 # force demandimport to really load the module
563 # force demandimport to really load the module
562 readline.read_history_file
564 readline.read_history_file
563 # windows sometimes raises something other than ImportError
565 # windows sometimes raises something other than ImportError
564 except Exception:
566 except Exception:
565 pass
567 pass
566
568
567 # call write() so output goes through subclassed implementation
569 # call write() so output goes through subclassed implementation
568 # e.g. color extension on Windows
570 # e.g. color extension on Windows
569 self.write(prompt)
571 self.write(prompt)
570
572
571 # instead of trying to emulate raw_input, swap (self.fin,
573 # instead of trying to emulate raw_input, swap (self.fin,
572 # self.fout) with (sys.stdin, sys.stdout)
574 # self.fout) with (sys.stdin, sys.stdout)
573 oldin = sys.stdin
575 oldin = sys.stdin
574 oldout = sys.stdout
576 oldout = sys.stdout
575 sys.stdin = self.fin
577 sys.stdin = self.fin
576 sys.stdout = self.fout
578 sys.stdout = self.fout
577 line = raw_input(' ')
579 line = raw_input(' ')
578 sys.stdin = oldin
580 sys.stdin = oldin
579 sys.stdout = oldout
581 sys.stdout = oldout
580
582
581 # When stdin is in binary mode on Windows, it can cause
583 # When stdin is in binary mode on Windows, it can cause
582 # raw_input() to emit an extra trailing carriage return
584 # raw_input() to emit an extra trailing carriage return
583 if os.linesep == '\r\n' and line and line[-1] == '\r':
585 if os.linesep == '\r\n' and line and line[-1] == '\r':
584 line = line[:-1]
586 line = line[:-1]
585 return line
587 return line
586
588
587 def prompt(self, msg, default="y"):
589 def prompt(self, msg, default="y"):
588 """Prompt user with msg, read response.
590 """Prompt user with msg, read response.
589 If ui is not interactive, the default is returned.
591 If ui is not interactive, the default is returned.
590 """
592 """
591 if not self.interactive():
593 if not self.interactive():
592 self.write(msg, ' ', default, "\n")
594 self.write(msg, ' ', default, "\n")
593 return default
595 return default
594 try:
596 try:
595 r = self._readline(self.label(msg, 'ui.prompt'))
597 r = self._readline(self.label(msg, 'ui.prompt'))
596 if not r:
598 if not r:
597 return default
599 return default
598 return r
600 return r
599 except EOFError:
601 except EOFError:
600 raise util.Abort(_('response expected'))
602 raise util.Abort(_('response expected'))
601
603
602 def promptchoice(self, msg, choices, default=0):
604 def promptchoice(self, msg, choices, default=0):
603 """Prompt user with msg, read response, and ensure it matches
605 """Prompt user with msg, read response, and ensure it matches
604 one of the provided choices. The index of the choice is returned.
606 one of the provided choices. The index of the choice is returned.
605 choices is a sequence of acceptable responses with the format:
607 choices is a sequence of acceptable responses with the format:
606 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
608 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
607 If ui is not interactive, the default is returned.
609 If ui is not interactive, the default is returned.
608 """
610 """
609 resps = [s[s.index('&')+1].lower() for s in choices]
611 resps = [s[s.index('&')+1].lower() for s in choices]
610 while True:
612 while True:
611 r = self.prompt(msg, resps[default])
613 r = self.prompt(msg, resps[default])
612 if r.lower() in resps:
614 if r.lower() in resps:
613 return resps.index(r.lower())
615 return resps.index(r.lower())
614 self.write(_("unrecognized response\n"))
616 self.write(_("unrecognized response\n"))
615
617
616 def getpass(self, prompt=None, default=None):
618 def getpass(self, prompt=None, default=None):
617 if not self.interactive():
619 if not self.interactive():
618 return default
620 return default
619 try:
621 try:
620 return getpass.getpass(prompt or _('password: '))
622 return getpass.getpass(prompt or _('password: '))
621 except EOFError:
623 except EOFError:
622 raise util.Abort(_('response expected'))
624 raise util.Abort(_('response expected'))
623 def status(self, *msg, **opts):
625 def status(self, *msg, **opts):
624 '''write status message to output (if ui.quiet is False)
626 '''write status message to output (if ui.quiet is False)
625
627
626 This adds an output label of "ui.status".
628 This adds an output label of "ui.status".
627 '''
629 '''
628 if not self.quiet:
630 if not self.quiet:
629 opts['label'] = opts.get('label', '') + ' ui.status'
631 opts['label'] = opts.get('label', '') + ' ui.status'
630 self.write(*msg, **opts)
632 self.write(*msg, **opts)
631 def warn(self, *msg, **opts):
633 def warn(self, *msg, **opts):
632 '''write warning message to output (stderr)
634 '''write warning message to output (stderr)
633
635
634 This adds an output label of "ui.warning".
636 This adds an output label of "ui.warning".
635 '''
637 '''
636 opts['label'] = opts.get('label', '') + ' ui.warning'
638 opts['label'] = opts.get('label', '') + ' ui.warning'
637 self.write_err(*msg, **opts)
639 self.write_err(*msg, **opts)
638 def note(self, *msg, **opts):
640 def note(self, *msg, **opts):
639 '''write note to output (if ui.verbose is True)
641 '''write note to output (if ui.verbose is True)
640
642
641 This adds an output label of "ui.note".
643 This adds an output label of "ui.note".
642 '''
644 '''
643 if self.verbose:
645 if self.verbose:
644 opts['label'] = opts.get('label', '') + ' ui.note'
646 opts['label'] = opts.get('label', '') + ' ui.note'
645 self.write(*msg, **opts)
647 self.write(*msg, **opts)
646 def debug(self, *msg, **opts):
648 def debug(self, *msg, **opts):
647 '''write debug message to output (if ui.debugflag is True)
649 '''write debug message to output (if ui.debugflag is True)
648
650
649 This adds an output label of "ui.debug".
651 This adds an output label of "ui.debug".
650 '''
652 '''
651 if self.debugflag:
653 if self.debugflag:
652 opts['label'] = opts.get('label', '') + ' ui.debug'
654 opts['label'] = opts.get('label', '') + ' ui.debug'
653 self.write(*msg, **opts)
655 self.write(*msg, **opts)
654 def edit(self, text, user):
656 def edit(self, text, user):
655 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
657 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
656 text=True)
658 text=True)
657 try:
659 try:
658 f = os.fdopen(fd, "w")
660 f = os.fdopen(fd, "w")
659 f.write(text)
661 f.write(text)
660 f.close()
662 f.close()
661
663
662 editor = self.geteditor()
664 editor = self.geteditor()
663
665
664 util.system("%s \"%s\"" % (editor, name),
666 util.system("%s \"%s\"" % (editor, name),
665 environ={'HGUSER': user},
667 environ={'HGUSER': user},
666 onerr=util.Abort, errprefix=_("edit failed"),
668 onerr=util.Abort, errprefix=_("edit failed"),
667 out=self.fout)
669 out=self.fout)
668
670
669 f = open(name)
671 f = open(name)
670 t = f.read()
672 t = f.read()
671 f.close()
673 f.close()
672 finally:
674 finally:
673 os.unlink(name)
675 os.unlink(name)
674
676
675 return t
677 return t
676
678
677 def traceback(self, exc=None):
679 def traceback(self, exc=None):
678 '''print exception traceback if traceback printing enabled.
680 '''print exception traceback if traceback printing enabled.
679 only to call in exception handler. returns true if traceback
681 only to call in exception handler. returns true if traceback
680 printed.'''
682 printed.'''
681 if self.tracebackflag:
683 if self.tracebackflag:
682 if exc:
684 if exc:
683 traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
685 traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
684 else:
686 else:
685 traceback.print_exc(file=self.ferr)
687 traceback.print_exc(file=self.ferr)
686 return self.tracebackflag
688 return self.tracebackflag
687
689
688 def geteditor(self):
690 def geteditor(self):
689 '''return editor to use'''
691 '''return editor to use'''
690 if sys.platform == 'plan9':
692 if sys.platform == 'plan9':
691 # vi is the MIPS instruction simulator on Plan 9. We
693 # vi is the MIPS instruction simulator on Plan 9. We
692 # instead default to E to plumb commit messages to
694 # instead default to E to plumb commit messages to
693 # avoid confusion.
695 # avoid confusion.
694 editor = 'E'
696 editor = 'E'
695 else:
697 else:
696 editor = 'vi'
698 editor = 'vi'
697 return (os.environ.get("HGEDITOR") or
699 return (os.environ.get("HGEDITOR") or
698 self.config("ui", "editor") or
700 self.config("ui", "editor") or
699 os.environ.get("VISUAL") or
701 os.environ.get("VISUAL") or
700 os.environ.get("EDITOR", editor))
702 os.environ.get("EDITOR", editor))
701
703
702 def progress(self, topic, pos, item="", unit="", total=None):
704 def progress(self, topic, pos, item="", unit="", total=None):
703 '''show a progress message
705 '''show a progress message
704
706
705 With stock hg, this is simply a debug message that is hidden
707 With stock hg, this is simply a debug message that is hidden
706 by default, but with extensions or GUI tools it may be
708 by default, but with extensions or GUI tools it may be
707 visible. 'topic' is the current operation, 'item' is a
709 visible. 'topic' is the current operation, 'item' is a
708 non-numeric marker of the current position (ie the currently
710 non-numeric marker of the current position (ie the currently
709 in-process file), 'pos' is the current numeric position (ie
711 in-process file), 'pos' is the current numeric position (ie
710 revision, bytes, etc.), unit is a corresponding unit label,
712 revision, bytes, etc.), unit is a corresponding unit label,
711 and total is the highest expected pos.
713 and total is the highest expected pos.
712
714
713 Multiple nested topics may be active at a time.
715 Multiple nested topics may be active at a time.
714
716
715 All topics should be marked closed by setting pos to None at
717 All topics should be marked closed by setting pos to None at
716 termination.
718 termination.
717 '''
719 '''
718
720
719 if pos is None or not self.debugflag:
721 if pos is None or not self.debugflag:
720 return
722 return
721
723
722 if unit:
724 if unit:
723 unit = ' ' + unit
725 unit = ' ' + unit
724 if item:
726 if item:
725 item = ' ' + item
727 item = ' ' + item
726
728
727 if total:
729 if total:
728 pct = 100.0 * pos / total
730 pct = 100.0 * pos / total
729 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
731 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
730 % (topic, item, pos, total, unit, pct))
732 % (topic, item, pos, total, unit, pct))
731 else:
733 else:
732 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
734 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
733
735
734 def log(self, service, message):
736 def log(self, service, message):
735 '''hook for logging facility extensions
737 '''hook for logging facility extensions
736
738
737 service should be a readily-identifiable subsystem, which will
739 service should be a readily-identifiable subsystem, which will
738 allow filtering.
740 allow filtering.
739 message should be a newline-terminated string to log.
741 message should be a newline-terminated string to log.
740 '''
742 '''
741 pass
743 pass
742
744
743 def label(self, msg, label):
745 def label(self, msg, label):
744 '''style msg based on supplied label
746 '''style msg based on supplied label
745
747
746 Like ui.write(), this just returns msg unchanged, but extensions
748 Like ui.write(), this just returns msg unchanged, but extensions
747 and GUI tools can override it to allow styling output without
749 and GUI tools can override it to allow styling output without
748 writing it.
750 writing it.
749
751
750 ui.write(s, 'label') is equivalent to
752 ui.write(s, 'label') is equivalent to
751 ui.write(ui.label(s, 'label')).
753 ui.write(ui.label(s, 'label')).
752 '''
754 '''
753 return msg
755 return msg
General Comments 0
You need to be logged in to leave comments. Login now