##// END OF EJS Templates
ui: add ui.write() output labeling API...
Brodie Rao -
r10815:32b213b9 default
parent child Browse files
Show More
@@ -1,192 +1,192
1 # progress.py show progress bars for some actions
1 # progress.py show progress bars for some actions
2 #
2 #
3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
4 #
4 #
5 # This program is free software; you can redistribute it and/or modify it
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
8 # option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful, but
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
13 # Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License along
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
18
19 """show progress bars for some actions
19 """show progress bars for some actions
20
20
21 This extension uses the progress information logged by hg commands
21 This extension uses the progress information logged by hg commands
22 to draw progress bars that are as informative as possible. Some progress
22 to draw progress bars that are as informative as possible. Some progress
23 bars only offer indeterminate information, while others have a definite
23 bars only offer indeterminate information, while others have a definite
24 end point.
24 end point.
25
25
26 The following settings are available::
26 The following settings are available::
27
27
28 [progress]
28 [progress]
29 delay = 3 # number of seconds (float) before showing the progress bar
29 delay = 3 # number of seconds (float) before showing the progress bar
30 refresh = 0.1 # time in seconds between refreshes of the progress bar
30 refresh = 0.1 # time in seconds between refreshes of the progress bar
31 format = topic bar number # format of the progress bar
31 format = topic bar number # format of the progress bar
32 width = <none> # if set, the maximum width of the progress information
32 width = <none> # if set, the maximum width of the progress information
33 # (that is, min(width, term width) will be used)
33 # (that is, min(width, term width) will be used)
34 clear-complete = True # clear the progress bar after it's done
34 clear-complete = True # clear the progress bar after it's done
35 disable = False # if true, don't show a progress bar
35 disable = False # if true, don't show a progress bar
36 assume-tty = False # if true, ALWAYS show a progress bar, unless
36 assume-tty = False # if true, ALWAYS show a progress bar, unless
37 # disable is given
37 # disable is given
38
38
39 Valid entries for the format field are topic, bar, number, unit, and
39 Valid entries for the format field are topic, bar, number, unit, and
40 item. item defaults to the last 20 characters of the item, but this
40 item. item defaults to the last 20 characters of the item, but this
41 can be changed by adding either ``-<num>`` which would take the last
41 can be changed by adding either ``-<num>`` which would take the last
42 num characters, or ``+<num>`` for the first num characters.
42 num characters, or ``+<num>`` for the first num characters.
43 """
43 """
44
44
45 import sys
45 import sys
46 import time
46 import time
47
47
48 from mercurial import extensions
48 from mercurial import extensions
49 from mercurial import util
49 from mercurial import util
50
50
51 def spacejoin(*args):
51 def spacejoin(*args):
52 return ' '.join(s for s in args if s)
52 return ' '.join(s for s in args if s)
53
53
54 class progbar(object):
54 class progbar(object):
55 def __init__(self, ui):
55 def __init__(self, ui):
56 self.ui = ui
56 self.ui = ui
57 self.resetstate()
57 self.resetstate()
58
58
59 def resetstate(self):
59 def resetstate(self):
60 self.topics = []
60 self.topics = []
61 self.printed = False
61 self.printed = False
62 self.lastprint = time.time() + float(self.ui.config(
62 self.lastprint = time.time() + float(self.ui.config(
63 'progress', 'delay', default=3))
63 'progress', 'delay', default=3))
64 self.indetcount = 0
64 self.indetcount = 0
65 self.refresh = float(self.ui.config(
65 self.refresh = float(self.ui.config(
66 'progress', 'refresh', default=0.1))
66 'progress', 'refresh', default=0.1))
67 self.order = self.ui.configlist(
67 self.order = self.ui.configlist(
68 'progress', 'format',
68 'progress', 'format',
69 default=['topic', 'bar', 'number'])
69 default=['topic', 'bar', 'number'])
70
70
71 def show(self, topic, pos, item, unit, total):
71 def show(self, topic, pos, item, unit, total):
72 termwidth = self.width()
72 termwidth = self.width()
73 self.printed = True
73 self.printed = True
74 head = ''
74 head = ''
75 needprogress = False
75 needprogress = False
76 tail = ''
76 tail = ''
77 for indicator in self.order:
77 for indicator in self.order:
78 add = ''
78 add = ''
79 if indicator == 'topic':
79 if indicator == 'topic':
80 add = topic
80 add = topic
81 elif indicator == 'number':
81 elif indicator == 'number':
82 if total:
82 if total:
83 add = ('% ' + str(len(str(total))) +
83 add = ('% ' + str(len(str(total))) +
84 's/%s') % (pos, total)
84 's/%s') % (pos, total)
85 else:
85 else:
86 add = str(pos)
86 add = str(pos)
87 elif indicator.startswith('item') and item:
87 elif indicator.startswith('item') and item:
88 slice = 'end'
88 slice = 'end'
89 if '-' in indicator:
89 if '-' in indicator:
90 wid = int(indicator.split('-')[1])
90 wid = int(indicator.split('-')[1])
91 elif '+' in indicator:
91 elif '+' in indicator:
92 slice = 'beginning'
92 slice = 'beginning'
93 wid = int(indicator.split('+')[1])
93 wid = int(indicator.split('+')[1])
94 else:
94 else:
95 wid = 20
95 wid = 20
96 if slice == 'end':
96 if slice == 'end':
97 add = item[-wid:]
97 add = item[-wid:]
98 else:
98 else:
99 add = item[:wid]
99 add = item[:wid]
100 add += (wid - len(add)) * ' '
100 add += (wid - len(add)) * ' '
101 elif indicator == 'bar':
101 elif indicator == 'bar':
102 add = ''
102 add = ''
103 needprogress = True
103 needprogress = True
104 elif indicator == 'unit' and unit:
104 elif indicator == 'unit' and unit:
105 add = unit
105 add = unit
106 if not needprogress:
106 if not needprogress:
107 head = spacejoin(head, add)
107 head = spacejoin(head, add)
108 else:
108 else:
109 tail = spacejoin(add, tail)
109 tail = spacejoin(add, tail)
110 if needprogress:
110 if needprogress:
111 used = 0
111 used = 0
112 if head:
112 if head:
113 used += len(head) + 1
113 used += len(head) + 1
114 if tail:
114 if tail:
115 used += len(tail) + 1
115 used += len(tail) + 1
116 progwidth = termwidth - used - 3
116 progwidth = termwidth - used - 3
117 if total:
117 if total:
118 amt = pos * progwidth // total
118 amt = pos * progwidth // total
119 bar = '=' * (amt - 1)
119 bar = '=' * (amt - 1)
120 if amt > 0:
120 if amt > 0:
121 bar += '>'
121 bar += '>'
122 bar += ' ' * (progwidth - amt)
122 bar += ' ' * (progwidth - amt)
123 else:
123 else:
124 progwidth -= 3
124 progwidth -= 3
125 self.indetcount += 1
125 self.indetcount += 1
126 # mod the count by twice the width so we can make the
126 # mod the count by twice the width so we can make the
127 # cursor bounce between the right and left sides
127 # cursor bounce between the right and left sides
128 amt = self.indetcount % (2 * progwidth)
128 amt = self.indetcount % (2 * progwidth)
129 amt -= progwidth
129 amt -= progwidth
130 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
130 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
131 ' ' * int(abs(amt)))
131 ' ' * int(abs(amt)))
132 prog = ''.join(('[', bar , ']'))
132 prog = ''.join(('[', bar , ']'))
133 out = spacejoin(head, prog, tail)
133 out = spacejoin(head, prog, tail)
134 else:
134 else:
135 out = spacejoin(head, tail)
135 out = spacejoin(head, tail)
136 sys.stderr.write('\r' + out[:termwidth])
136 sys.stderr.write('\r' + out[:termwidth])
137 sys.stderr.flush()
137 sys.stderr.flush()
138
138
139 def clear(self):
139 def clear(self):
140 sys.stderr.write('\r%s\r' % (' ' * self.width()))
140 sys.stderr.write('\r%s\r' % (' ' * self.width()))
141
141
142 def complete(self):
142 def complete(self):
143 if self.ui.configbool('progress', 'clear-complete', default=True):
143 if self.ui.configbool('progress', 'clear-complete', default=True):
144 self.clear()
144 self.clear()
145 else:
145 else:
146 sys.stderr.write('\n')
146 sys.stderr.write('\n')
147 sys.stderr.flush()
147 sys.stderr.flush()
148
148
149 def width(self):
149 def width(self):
150 tw = util.termwidth()
150 tw = util.termwidth()
151 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
151 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
152
152
153 def progress(self, orig, topic, pos, item='', unit='', total=None):
153 def progress(self, orig, topic, pos, item='', unit='', total=None):
154 if pos is None:
154 if pos is None:
155 if self.topics and self.topics[-1] == topic and self.printed:
155 if self.topics and self.topics[-1] == topic and self.printed:
156 self.complete()
156 self.complete()
157 self.resetstate()
157 self.resetstate()
158 else:
158 else:
159 if topic not in self.topics:
159 if topic not in self.topics:
160 self.topics.append(topic)
160 self.topics.append(topic)
161 now = time.time()
161 now = time.time()
162 if (now - self.lastprint >= self.refresh
162 if (now - self.lastprint >= self.refresh
163 and topic == self.topics[-1]):
163 and topic == self.topics[-1]):
164 self.lastprint = now
164 self.lastprint = now
165 self.show(topic, pos, item, unit, total)
165 self.show(topic, pos, item, unit, total)
166 return orig(topic, pos, item=item, unit=unit, total=total)
166 return orig(topic, pos, item=item, unit=unit, total=total)
167
167
168 def write(self, orig, *args):
168 def write(self, orig, *args, **opts):
169 if self.printed:
169 if self.printed:
170 self.clear()
170 self.clear()
171 return orig(*args)
171 return orig(*args, **opts)
172
172
173 sharedprog = None
173 sharedprog = None
174
174
175 def uisetup(ui):
175 def uisetup(ui):
176 # Apps that derive a class from ui.ui() can use
176 # Apps that derive a class from ui.ui() can use
177 # setconfig('progress', 'disable', 'True') to disable this extension
177 # setconfig('progress', 'disable', 'True') to disable this extension
178 if ui.configbool('progress', 'disable'):
178 if ui.configbool('progress', 'disable'):
179 return
179 return
180 if ((sys.stderr.isatty() or ui.configbool('progress', 'assume-tty'))
180 if ((sys.stderr.isatty() or ui.configbool('progress', 'assume-tty'))
181 and not ui.debugflag and not ui.quiet):
181 and not ui.debugflag and not ui.quiet):
182 # we instantiate one globally shared progress bar to avoid
182 # we instantiate one globally shared progress bar to avoid
183 # competing progress bars when multiple UI objects get created
183 # competing progress bars when multiple UI objects get created
184 global sharedprog
184 global sharedprog
185 if not sharedprog:
185 if not sharedprog:
186 sharedprog = progbar(ui)
186 sharedprog = progbar(ui)
187 extensions.wrapfunction(ui, 'progress', sharedprog.progress)
187 extensions.wrapfunction(ui, 'progress', sharedprog.progress)
188 extensions.wrapfunction(ui, 'write', sharedprog.write)
188 extensions.wrapfunction(ui, 'write', sharedprog.write)
189 extensions.wrapfunction(ui, 'write_err', sharedprog.write)
189 extensions.wrapfunction(ui, 'write_err', sharedprog.write)
190
190
191 def reposetup(ui, repo):
191 def reposetup(ui, repo):
192 uisetup(repo.ui)
192 uisetup(repo.ui)
@@ -1,419 +1,476
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, util, error
10 import config, util, error
11
11
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
13 '0': False, 'no': False, 'false': False, 'off': False}
13 '0': False, 'no': False, 'false': False, 'off': False}
14
14
15 class ui(object):
15 class ui(object):
16 def __init__(self, src=None):
16 def __init__(self, src=None):
17 self._buffers = []
17 self._buffers = []
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
19 self._reportuntrusted = True
19 self._reportuntrusted = True
20 self._ocfg = config.config() # overlay
20 self._ocfg = config.config() # overlay
21 self._tcfg = config.config() # trusted
21 self._tcfg = config.config() # trusted
22 self._ucfg = config.config() # untrusted
22 self._ucfg = config.config() # untrusted
23 self._trustusers = set()
23 self._trustusers = set()
24 self._trustgroups = set()
24 self._trustgroups = set()
25
25
26 if src:
26 if src:
27 self._tcfg = src._tcfg.copy()
27 self._tcfg = src._tcfg.copy()
28 self._ucfg = src._ucfg.copy()
28 self._ucfg = src._ucfg.copy()
29 self._ocfg = src._ocfg.copy()
29 self._ocfg = src._ocfg.copy()
30 self._trustusers = src._trustusers.copy()
30 self._trustusers = src._trustusers.copy()
31 self._trustgroups = src._trustgroups.copy()
31 self._trustgroups = src._trustgroups.copy()
32 self.environ = src.environ
32 self.environ = src.environ
33 self.fixconfig()
33 self.fixconfig()
34 else:
34 else:
35 # shared read-only environment
35 # shared read-only environment
36 self.environ = os.environ
36 self.environ = os.environ
37 # we always trust global config files
37 # we always trust global config files
38 for f in util.rcpath():
38 for f in util.rcpath():
39 self.readconfig(f, trust=True)
39 self.readconfig(f, trust=True)
40
40
41 def copy(self):
41 def copy(self):
42 return self.__class__(self)
42 return self.__class__(self)
43
43
44 def _is_trusted(self, fp, f):
44 def _is_trusted(self, fp, f):
45 st = util.fstat(fp)
45 st = util.fstat(fp)
46 if util.isowner(st):
46 if util.isowner(st):
47 return True
47 return True
48
48
49 tusers, tgroups = self._trustusers, self._trustgroups
49 tusers, tgroups = self._trustusers, self._trustgroups
50 if '*' in tusers or '*' in tgroups:
50 if '*' in tusers or '*' in tgroups:
51 return True
51 return True
52
52
53 user = util.username(st.st_uid)
53 user = util.username(st.st_uid)
54 group = util.groupname(st.st_gid)
54 group = util.groupname(st.st_gid)
55 if user in tusers or group in tgroups or user == util.username():
55 if user in tusers or group in tgroups or user == util.username():
56 return True
56 return True
57
57
58 if self._reportuntrusted:
58 if self._reportuntrusted:
59 self.warn(_('Not trusting file %s from untrusted '
59 self.warn(_('Not trusting file %s from untrusted '
60 'user %s, group %s\n') % (f, user, group))
60 'user %s, group %s\n') % (f, user, group))
61 return False
61 return False
62
62
63 def readconfig(self, filename, root=None, trust=False,
63 def readconfig(self, filename, root=None, trust=False,
64 sections=None, remap=None):
64 sections=None, remap=None):
65 try:
65 try:
66 fp = open(filename)
66 fp = open(filename)
67 except IOError:
67 except IOError:
68 if not sections: # ignore unless we were looking for something
68 if not sections: # ignore unless we were looking for something
69 return
69 return
70 raise
70 raise
71
71
72 cfg = config.config()
72 cfg = config.config()
73 trusted = sections or trust or self._is_trusted(fp, filename)
73 trusted = sections or trust or self._is_trusted(fp, filename)
74
74
75 try:
75 try:
76 cfg.read(filename, fp, sections=sections, remap=remap)
76 cfg.read(filename, fp, sections=sections, remap=remap)
77 except error.ConfigError, inst:
77 except error.ConfigError, inst:
78 if trusted:
78 if trusted:
79 raise
79 raise
80 self.warn(_("Ignored: %s\n") % str(inst))
80 self.warn(_("Ignored: %s\n") % str(inst))
81
81
82 if self.plain():
82 if self.plain():
83 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
83 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
84 'logtemplate', 'style',
84 'logtemplate', 'style',
85 'traceback', 'verbose'):
85 'traceback', 'verbose'):
86 if k in cfg['ui']:
86 if k in cfg['ui']:
87 del cfg['ui'][k]
87 del cfg['ui'][k]
88 for k, v in cfg.items('alias'):
88 for k, v in cfg.items('alias'):
89 del cfg['alias'][k]
89 del cfg['alias'][k]
90 for k, v in cfg.items('defaults'):
90 for k, v in cfg.items('defaults'):
91 del cfg['defaults'][k]
91 del cfg['defaults'][k]
92
92
93 if trusted:
93 if trusted:
94 self._tcfg.update(cfg)
94 self._tcfg.update(cfg)
95 self._tcfg.update(self._ocfg)
95 self._tcfg.update(self._ocfg)
96 self._ucfg.update(cfg)
96 self._ucfg.update(cfg)
97 self._ucfg.update(self._ocfg)
97 self._ucfg.update(self._ocfg)
98
98
99 if root is None:
99 if root is None:
100 root = os.path.expanduser('~')
100 root = os.path.expanduser('~')
101 self.fixconfig(root=root)
101 self.fixconfig(root=root)
102
102
103 def fixconfig(self, root=None):
103 def fixconfig(self, root=None):
104 # translate paths relative to root (or home) into absolute paths
104 # translate paths relative to root (or home) into absolute paths
105 root = root or os.getcwd()
105 root = root or os.getcwd()
106 for c in self._tcfg, self._ucfg, self._ocfg:
106 for c in self._tcfg, self._ucfg, self._ocfg:
107 for n, p in c.items('paths'):
107 for n, p in c.items('paths'):
108 if p and "://" not in p and not os.path.isabs(p):
108 if p and "://" not in p and not os.path.isabs(p):
109 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
109 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
110
110
111 # update ui options
111 # update ui options
112 self.debugflag = self.configbool('ui', 'debug')
112 self.debugflag = self.configbool('ui', 'debug')
113 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
113 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
114 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
114 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
115 if self.verbose and self.quiet:
115 if self.verbose and self.quiet:
116 self.quiet = self.verbose = False
116 self.quiet = self.verbose = False
117 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
117 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
118 self.tracebackflag = self.configbool('ui', 'traceback', False)
118 self.tracebackflag = self.configbool('ui', 'traceback', False)
119
119
120 # update trust information
120 # update trust information
121 self._trustusers.update(self.configlist('trusted', 'users'))
121 self._trustusers.update(self.configlist('trusted', 'users'))
122 self._trustgroups.update(self.configlist('trusted', 'groups'))
122 self._trustgroups.update(self.configlist('trusted', 'groups'))
123
123
124 def setconfig(self, section, name, value):
124 def setconfig(self, section, name, value):
125 for cfg in (self._ocfg, self._tcfg, self._ucfg):
125 for cfg in (self._ocfg, self._tcfg, self._ucfg):
126 cfg.set(section, name, value)
126 cfg.set(section, name, value)
127 self.fixconfig()
127 self.fixconfig()
128
128
129 def _data(self, untrusted):
129 def _data(self, untrusted):
130 return untrusted and self._ucfg or self._tcfg
130 return untrusted and self._ucfg or self._tcfg
131
131
132 def configsource(self, section, name, untrusted=False):
132 def configsource(self, section, name, untrusted=False):
133 return self._data(untrusted).source(section, name) or 'none'
133 return self._data(untrusted).source(section, name) or 'none'
134
134
135 def config(self, section, name, default=None, untrusted=False):
135 def config(self, section, name, default=None, untrusted=False):
136 value = self._data(untrusted).get(section, name, default)
136 value = self._data(untrusted).get(section, name, default)
137 if self.debugflag and not untrusted and self._reportuntrusted:
137 if self.debugflag and not untrusted and self._reportuntrusted:
138 uvalue = self._ucfg.get(section, name)
138 uvalue = self._ucfg.get(section, name)
139 if uvalue is not None and uvalue != value:
139 if uvalue is not None and uvalue != value:
140 self.debug(_("ignoring untrusted configuration option "
140 self.debug(_("ignoring untrusted configuration option "
141 "%s.%s = %s\n") % (section, name, uvalue))
141 "%s.%s = %s\n") % (section, name, uvalue))
142 return value
142 return value
143
143
144 def configbool(self, section, name, default=False, untrusted=False):
144 def configbool(self, section, name, default=False, untrusted=False):
145 v = self.config(section, name, None, untrusted)
145 v = self.config(section, name, None, untrusted)
146 if v is None:
146 if v is None:
147 return default
147 return default
148 if isinstance(v, bool):
148 if isinstance(v, bool):
149 return v
149 return v
150 if v.lower() not in _booleans:
150 if v.lower() not in _booleans:
151 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
151 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
152 % (section, name, v))
152 % (section, name, v))
153 return _booleans[v.lower()]
153 return _booleans[v.lower()]
154
154
155 def configlist(self, section, name, default=None, untrusted=False):
155 def configlist(self, section, name, default=None, untrusted=False):
156 """Return a list of comma/space separated strings"""
156 """Return a list of comma/space separated strings"""
157 result = self.config(section, name, untrusted=untrusted)
157 result = self.config(section, name, untrusted=untrusted)
158 if result is None:
158 if result is None:
159 result = default or []
159 result = default or []
160 if isinstance(result, basestring):
160 if isinstance(result, basestring):
161 result = result.replace(",", " ").split()
161 result = result.replace(",", " ").split()
162 return result
162 return result
163
163
164 def has_section(self, section, untrusted=False):
164 def has_section(self, section, untrusted=False):
165 '''tell whether section exists in config.'''
165 '''tell whether section exists in config.'''
166 return section in self._data(untrusted)
166 return section in self._data(untrusted)
167
167
168 def configitems(self, section, untrusted=False):
168 def configitems(self, section, untrusted=False):
169 items = self._data(untrusted).items(section)
169 items = self._data(untrusted).items(section)
170 if self.debugflag and not untrusted and self._reportuntrusted:
170 if self.debugflag and not untrusted and self._reportuntrusted:
171 for k, v in self._ucfg.items(section):
171 for k, v in self._ucfg.items(section):
172 if self._tcfg.get(section, k) != v:
172 if self._tcfg.get(section, k) != v:
173 self.debug(_("ignoring untrusted configuration option "
173 self.debug(_("ignoring untrusted configuration option "
174 "%s.%s = %s\n") % (section, k, v))
174 "%s.%s = %s\n") % (section, k, v))
175 return items
175 return items
176
176
177 def walkconfig(self, untrusted=False):
177 def walkconfig(self, untrusted=False):
178 cfg = self._data(untrusted)
178 cfg = self._data(untrusted)
179 for section in cfg.sections():
179 for section in cfg.sections():
180 for name, value in self.configitems(section, untrusted):
180 for name, value in self.configitems(section, untrusted):
181 yield section, name, str(value).replace('\n', '\\n')
181 yield section, name, str(value).replace('\n', '\\n')
182
182
183 def plain(self):
183 def plain(self):
184 return 'HGPLAIN' in os.environ
184 return 'HGPLAIN' in os.environ
185
185
186 def username(self):
186 def username(self):
187 """Return default username to be used in commits.
187 """Return default username to be used in commits.
188
188
189 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
189 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
190 and stop searching if one of these is set.
190 and stop searching if one of these is set.
191 If not found and ui.askusername is True, ask the user, else use
191 If not found and ui.askusername is True, ask the user, else use
192 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
192 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
193 """
193 """
194 user = os.environ.get("HGUSER")
194 user = os.environ.get("HGUSER")
195 if user is None:
195 if user is None:
196 user = self.config("ui", "username")
196 user = self.config("ui", "username")
197 if user is None:
197 if user is None:
198 user = os.environ.get("EMAIL")
198 user = os.environ.get("EMAIL")
199 if user is None and self.configbool("ui", "askusername"):
199 if user is None and self.configbool("ui", "askusername"):
200 user = self.prompt(_("enter a commit username:"), default=None)
200 user = self.prompt(_("enter a commit username:"), default=None)
201 if user is None and not self.interactive():
201 if user is None and not self.interactive():
202 try:
202 try:
203 user = '%s@%s' % (util.getuser(), socket.getfqdn())
203 user = '%s@%s' % (util.getuser(), socket.getfqdn())
204 self.warn(_("No username found, using '%s' instead\n") % user)
204 self.warn(_("No username found, using '%s' instead\n") % user)
205 except KeyError:
205 except KeyError:
206 pass
206 pass
207 if not user:
207 if not user:
208 raise util.Abort(_('no username supplied (see "hg help config")'))
208 raise util.Abort(_('no username supplied (see "hg help config")'))
209 if "\n" in user:
209 if "\n" in user:
210 raise util.Abort(_("username %s contains a newline\n") % repr(user))
210 raise util.Abort(_("username %s contains a newline\n") % repr(user))
211 return user
211 return user
212
212
213 def shortuser(self, user):
213 def shortuser(self, user):
214 """Return a short representation of a user name or email address."""
214 """Return a short representation of a user name or email address."""
215 if not self.verbose:
215 if not self.verbose:
216 user = util.shortuser(user)
216 user = util.shortuser(user)
217 return user
217 return user
218
218
219 def _path(self, loc):
219 def _path(self, loc):
220 p = self.config('paths', loc)
220 p = self.config('paths', loc)
221 if p:
221 if p:
222 if '%%' in p:
222 if '%%' in p:
223 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
223 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
224 (loc, p, self.configsource('paths', loc)))
224 (loc, p, self.configsource('paths', loc)))
225 p = p.replace('%%', '%')
225 p = p.replace('%%', '%')
226 p = util.expandpath(p)
226 p = util.expandpath(p)
227 return p
227 return p
228
228
229 def expandpath(self, loc, default=None):
229 def expandpath(self, loc, default=None):
230 """Return repository location relative to cwd or from [paths]"""
230 """Return repository location relative to cwd or from [paths]"""
231 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
231 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
232 return loc
232 return loc
233
233
234 path = self._path(loc)
234 path = self._path(loc)
235 if not path and default is not None:
235 if not path and default is not None:
236 path = self._path(default)
236 path = self._path(default)
237 return path or loc
237 return path or loc
238
238
239 def pushbuffer(self):
239 def pushbuffer(self):
240 self._buffers.append([])
240 self._buffers.append([])
241
241
242 def popbuffer(self):
242 def popbuffer(self, labeled=False):
243 '''pop the last buffer and return the buffered output
244
245 If labeled is True, any labels associated with buffered
246 output will be handled. By default, this has no effect
247 on the output returned, but extensions and GUI tools may
248 handle this argument and returned styled output. If output
249 is being buffered so it can be captured and parsed or
250 processed, labeled should not be set to True.
251 '''
243 return "".join(self._buffers.pop())
252 return "".join(self._buffers.pop())
244
253
245 def write(self, *args):
254 def write(self, *args, **opts):
255 '''write args to output
256
257 By default, this method simply writes to the buffer or stdout,
258 but extensions or GUI tools may override this method,
259 write_err(), popbuffer(), and label() to style output from
260 various parts of hg.
261
262 An optional keyword argument, "label", can be passed in.
263 This should be a string containing label names separated by
264 space. Label names take the form of "topic.type". For example,
265 ui.debug() issues a label of "ui.debug".
266
267 When labeling output for a specific command, a label of
268 "cmdname.type" is recommended. For example, status issues
269 a label of "status.modified" for modified files.
270 '''
246 if self._buffers:
271 if self._buffers:
247 self._buffers[-1].extend([str(a) for a in args])
272 self._buffers[-1].extend([str(a) for a in args])
248 else:
273 else:
249 for a in args:
274 for a in args:
250 sys.stdout.write(str(a))
275 sys.stdout.write(str(a))
251
276
252 def write_err(self, *args):
277 def write_err(self, *args, **opts):
253 try:
278 try:
254 if not getattr(sys.stdout, 'closed', False):
279 if not getattr(sys.stdout, 'closed', False):
255 sys.stdout.flush()
280 sys.stdout.flush()
256 for a in args:
281 for a in args:
257 sys.stderr.write(str(a))
282 sys.stderr.write(str(a))
258 # stderr may be buffered under win32 when redirected to files,
283 # stderr may be buffered under win32 when redirected to files,
259 # including stdout.
284 # including stdout.
260 if not getattr(sys.stderr, 'closed', False):
285 if not getattr(sys.stderr, 'closed', False):
261 sys.stderr.flush()
286 sys.stderr.flush()
262 except IOError, inst:
287 except IOError, inst:
263 if inst.errno != errno.EPIPE:
288 if inst.errno != errno.EPIPE:
264 raise
289 raise
265
290
266 def flush(self):
291 def flush(self):
267 try: sys.stdout.flush()
292 try: sys.stdout.flush()
268 except: pass
293 except: pass
269 try: sys.stderr.flush()
294 try: sys.stderr.flush()
270 except: pass
295 except: pass
271
296
272 def interactive(self):
297 def interactive(self):
273 i = self.configbool("ui", "interactive", None)
298 i = self.configbool("ui", "interactive", None)
274 if i is None:
299 if i is None:
275 try:
300 try:
276 return sys.stdin.isatty()
301 return sys.stdin.isatty()
277 except AttributeError:
302 except AttributeError:
278 # some environments replace stdin without implementing isatty
303 # some environments replace stdin without implementing isatty
279 # usually those are non-interactive
304 # usually those are non-interactive
280 return False
305 return False
281
306
282 return i
307 return i
283
308
284 def _readline(self, prompt=''):
309 def _readline(self, prompt=''):
285 if sys.stdin.isatty():
310 if sys.stdin.isatty():
286 try:
311 try:
287 # magically add command line editing support, where
312 # magically add command line editing support, where
288 # available
313 # available
289 import readline
314 import readline
290 # force demandimport to really load the module
315 # force demandimport to really load the module
291 readline.read_history_file
316 readline.read_history_file
292 # windows sometimes raises something other than ImportError
317 # windows sometimes raises something other than ImportError
293 except Exception:
318 except Exception:
294 pass
319 pass
295 line = raw_input(prompt)
320 line = raw_input(prompt)
296 # When stdin is in binary mode on Windows, it can cause
321 # When stdin is in binary mode on Windows, it can cause
297 # raw_input() to emit an extra trailing carriage return
322 # raw_input() to emit an extra trailing carriage return
298 if os.linesep == '\r\n' and line and line[-1] == '\r':
323 if os.linesep == '\r\n' and line and line[-1] == '\r':
299 line = line[:-1]
324 line = line[:-1]
300 return line
325 return line
301
326
302 def prompt(self, msg, default="y"):
327 def prompt(self, msg, default="y"):
303 """Prompt user with msg, read response.
328 """Prompt user with msg, read response.
304 If ui is not interactive, the default is returned.
329 If ui is not interactive, the default is returned.
305 """
330 """
306 if not self.interactive():
331 if not self.interactive():
307 self.write(msg, ' ', default, "\n")
332 self.write(msg, ' ', default, "\n")
308 return default
333 return default
309 try:
334 try:
310 r = self._readline(msg + ' ')
335 r = self._readline(msg + ' ')
311 if not r:
336 if not r:
312 return default
337 return default
313 return r
338 return r
314 except EOFError:
339 except EOFError:
315 raise util.Abort(_('response expected'))
340 raise util.Abort(_('response expected'))
316
341
317 def promptchoice(self, msg, choices, default=0):
342 def promptchoice(self, msg, choices, default=0):
318 """Prompt user with msg, read response, and ensure it matches
343 """Prompt user with msg, read response, and ensure it matches
319 one of the provided choices. The index of the choice is returned.
344 one of the provided choices. The index of the choice is returned.
320 choices is a sequence of acceptable responses with the format:
345 choices is a sequence of acceptable responses with the format:
321 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
346 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
322 If ui is not interactive, the default is returned.
347 If ui is not interactive, the default is returned.
323 """
348 """
324 resps = [s[s.index('&')+1].lower() for s in choices]
349 resps = [s[s.index('&')+1].lower() for s in choices]
325 while True:
350 while True:
326 r = self.prompt(msg, resps[default])
351 r = self.prompt(msg, resps[default])
327 if r.lower() in resps:
352 if r.lower() in resps:
328 return resps.index(r.lower())
353 return resps.index(r.lower())
329 self.write(_("unrecognized response\n"))
354 self.write(_("unrecognized response\n"))
330
355
331 def getpass(self, prompt=None, default=None):
356 def getpass(self, prompt=None, default=None):
332 if not self.interactive():
357 if not self.interactive():
333 return default
358 return default
334 try:
359 try:
335 return getpass.getpass(prompt or _('password: '))
360 return getpass.getpass(prompt or _('password: '))
336 except EOFError:
361 except EOFError:
337 raise util.Abort(_('response expected'))
362 raise util.Abort(_('response expected'))
338 def status(self, *msg):
363 def status(self, *msg, **opts):
364 '''write status message to output (if ui.quiet is False)
365
366 This adds an output label of "ui.status".
367 '''
339 if not self.quiet:
368 if not self.quiet:
340 self.write(*msg)
369 opts['label'] = opts.get('label', '') + ' ui.status'
341 def warn(self, *msg):
370 self.write(*msg, **opts)
342 self.write_err(*msg)
371 def warn(self, *msg, **opts):
343 def note(self, *msg):
372 '''write warning message to output (stderr)
373
374 This adds an output label of "ui.warning".
375 '''
376 opts['label'] = opts.get('label', '') + ' ui.warning'
377 self.write_err(*msg, **opts)
378 def note(self, *msg, **opts):
379 '''write note to output (if ui.verbose is True)
380
381 This adds an output label of "ui.note".
382 '''
344 if self.verbose:
383 if self.verbose:
345 self.write(*msg)
384 opts['label'] = opts.get('label', '') + ' ui.note'
346 def debug(self, *msg):
385 self.write(*msg, **opts)
386 def debug(self, *msg, **opts):
387 '''write debug message to output (if ui.debugflag is True)
388
389 This adds an output label of "ui.debug".
390 '''
347 if self.debugflag:
391 if self.debugflag:
348 self.write(*msg)
392 opts['label'] = opts.get('label', '') + ' ui.debug'
393 self.write(*msg, **opts)
349 def edit(self, text, user):
394 def edit(self, text, user):
350 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
395 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
351 text=True)
396 text=True)
352 try:
397 try:
353 f = os.fdopen(fd, "w")
398 f = os.fdopen(fd, "w")
354 f.write(text)
399 f.write(text)
355 f.close()
400 f.close()
356
401
357 editor = self.geteditor()
402 editor = self.geteditor()
358
403
359 util.system("%s \"%s\"" % (editor, name),
404 util.system("%s \"%s\"" % (editor, name),
360 environ={'HGUSER': user},
405 environ={'HGUSER': user},
361 onerr=util.Abort, errprefix=_("edit failed"))
406 onerr=util.Abort, errprefix=_("edit failed"))
362
407
363 f = open(name)
408 f = open(name)
364 t = f.read()
409 t = f.read()
365 f.close()
410 f.close()
366 finally:
411 finally:
367 os.unlink(name)
412 os.unlink(name)
368
413
369 return t
414 return t
370
415
371 def traceback(self, exc=None):
416 def traceback(self, exc=None):
372 '''print exception traceback if traceback printing enabled.
417 '''print exception traceback if traceback printing enabled.
373 only to call in exception handler. returns true if traceback
418 only to call in exception handler. returns true if traceback
374 printed.'''
419 printed.'''
375 if self.tracebackflag:
420 if self.tracebackflag:
376 if exc:
421 if exc:
377 traceback.print_exception(exc[0], exc[1], exc[2])
422 traceback.print_exception(exc[0], exc[1], exc[2])
378 else:
423 else:
379 traceback.print_exc()
424 traceback.print_exc()
380 return self.tracebackflag
425 return self.tracebackflag
381
426
382 def geteditor(self):
427 def geteditor(self):
383 '''return editor to use'''
428 '''return editor to use'''
384 return (os.environ.get("HGEDITOR") or
429 return (os.environ.get("HGEDITOR") or
385 self.config("ui", "editor") or
430 self.config("ui", "editor") or
386 os.environ.get("VISUAL") or
431 os.environ.get("VISUAL") or
387 os.environ.get("EDITOR", "vi"))
432 os.environ.get("EDITOR", "vi"))
388
433
389 def progress(self, topic, pos, item="", unit="", total=None):
434 def progress(self, topic, pos, item="", unit="", total=None):
390 '''show a progress message
435 '''show a progress message
391
436
392 With stock hg, this is simply a debug message that is hidden
437 With stock hg, this is simply a debug message that is hidden
393 by default, but with extensions or GUI tools it may be
438 by default, but with extensions or GUI tools it may be
394 visible. 'topic' is the current operation, 'item' is a
439 visible. 'topic' is the current operation, 'item' is a
395 non-numeric marker of the current position (ie the currently
440 non-numeric marker of the current position (ie the currently
396 in-process file), 'pos' is the current numeric position (ie
441 in-process file), 'pos' is the current numeric position (ie
397 revision, bytes, etc.), unit is a corresponding unit label,
442 revision, bytes, etc.), unit is a corresponding unit label,
398 and total is the highest expected pos.
443 and total is the highest expected pos.
399
444
400 Multiple nested topics may be active at a time.
445 Multiple nested topics may be active at a time.
401
446
402 All topics should be marked closed by setting pos to None at
447 All topics should be marked closed by setting pos to None at
403 termination.
448 termination.
404 '''
449 '''
405
450
406 if pos == None or not self.debugflag:
451 if pos == None or not self.debugflag:
407 return
452 return
408
453
409 if unit:
454 if unit:
410 unit = ' ' + unit
455 unit = ' ' + unit
411 if item:
456 if item:
412 item = ' ' + item
457 item = ' ' + item
413
458
414 if total:
459 if total:
415 pct = 100.0 * pos / total
460 pct = 100.0 * pos / total
416 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
461 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
417 % (topic, item, pos, total, unit, pct))
462 % (topic, item, pos, total, unit, pct))
418 else:
463 else:
419 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
464 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
465
466 def label(self, msg, label):
467 '''style msg based on supplied label
468
469 Like ui.write(), this just returns msg unchanged, but extensions
470 and GUI tools can override it to allow styling output without
471 writing it.
472
473 ui.write(s, 'label') is equivalent to
474 ui.write(ui.label(s, 'label')).
475 '''
476 return msg
General Comments 0
You need to be logged in to leave comments. Login now