##// END OF EJS Templates
make ui.interactive() return false in case stdin lacks isatty
Ronny Pfannschmidt -
r10077:89617aac default
parent child Browse files
Show More
@@ -1,389 +1,395 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
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 trusted:
82 if trusted:
83 self._tcfg.update(cfg)
83 self._tcfg.update(cfg)
84 self._tcfg.update(self._ocfg)
84 self._tcfg.update(self._ocfg)
85 self._ucfg.update(cfg)
85 self._ucfg.update(cfg)
86 self._ucfg.update(self._ocfg)
86 self._ucfg.update(self._ocfg)
87
87
88 if root is None:
88 if root is None:
89 root = os.path.expanduser('~')
89 root = os.path.expanduser('~')
90 self.fixconfig(root=root)
90 self.fixconfig(root=root)
91
91
92 def fixconfig(self, root=None):
92 def fixconfig(self, root=None):
93 # translate paths relative to root (or home) into absolute paths
93 # translate paths relative to root (or home) into absolute paths
94 root = root or os.getcwd()
94 root = root or os.getcwd()
95 for c in self._tcfg, self._ucfg, self._ocfg:
95 for c in self._tcfg, self._ucfg, self._ocfg:
96 for n, p in c.items('paths'):
96 for n, p in c.items('paths'):
97 if p and "://" not in p and not os.path.isabs(p):
97 if p and "://" not in p and not os.path.isabs(p):
98 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
98 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
99
99
100 # update ui options
100 # update ui options
101 self.debugflag = self.configbool('ui', 'debug')
101 self.debugflag = self.configbool('ui', 'debug')
102 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
102 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
103 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
103 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
104 if self.verbose and self.quiet:
104 if self.verbose and self.quiet:
105 self.quiet = self.verbose = False
105 self.quiet = self.verbose = False
106 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
106 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
107 self.tracebackflag = self.configbool('ui', 'traceback', False)
107 self.tracebackflag = self.configbool('ui', 'traceback', False)
108
108
109 # update trust information
109 # update trust information
110 self._trustusers.update(self.configlist('trusted', 'users'))
110 self._trustusers.update(self.configlist('trusted', 'users'))
111 self._trustgroups.update(self.configlist('trusted', 'groups'))
111 self._trustgroups.update(self.configlist('trusted', 'groups'))
112
112
113 def setconfig(self, section, name, value):
113 def setconfig(self, section, name, value):
114 for cfg in (self._ocfg, self._tcfg, self._ucfg):
114 for cfg in (self._ocfg, self._tcfg, self._ucfg):
115 cfg.set(section, name, value)
115 cfg.set(section, name, value)
116 self.fixconfig()
116 self.fixconfig()
117
117
118 def _data(self, untrusted):
118 def _data(self, untrusted):
119 return untrusted and self._ucfg or self._tcfg
119 return untrusted and self._ucfg or self._tcfg
120
120
121 def configsource(self, section, name, untrusted=False):
121 def configsource(self, section, name, untrusted=False):
122 return self._data(untrusted).source(section, name) or 'none'
122 return self._data(untrusted).source(section, name) or 'none'
123
123
124 def config(self, section, name, default=None, untrusted=False):
124 def config(self, section, name, default=None, untrusted=False):
125 value = self._data(untrusted).get(section, name, default)
125 value = self._data(untrusted).get(section, name, default)
126 if self.debugflag and not untrusted and self._reportuntrusted:
126 if self.debugflag and not untrusted and self._reportuntrusted:
127 uvalue = self._ucfg.get(section, name)
127 uvalue = self._ucfg.get(section, name)
128 if uvalue is not None and uvalue != value:
128 if uvalue is not None and uvalue != value:
129 self.debug(_("ignoring untrusted configuration option "
129 self.debug(_("ignoring untrusted configuration option "
130 "%s.%s = %s\n") % (section, name, uvalue))
130 "%s.%s = %s\n") % (section, name, uvalue))
131 return value
131 return value
132
132
133 def configbool(self, section, name, default=False, untrusted=False):
133 def configbool(self, section, name, default=False, untrusted=False):
134 v = self.config(section, name, None, untrusted)
134 v = self.config(section, name, None, untrusted)
135 if v is None:
135 if v is None:
136 return default
136 return default
137 if v.lower() not in _booleans:
137 if v.lower() not in _booleans:
138 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
138 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
139 % (section, name, v))
139 % (section, name, v))
140 return _booleans[v.lower()]
140 return _booleans[v.lower()]
141
141
142 def configlist(self, section, name, default=None, untrusted=False):
142 def configlist(self, section, name, default=None, untrusted=False):
143 """Return a list of comma/space separated strings"""
143 """Return a list of comma/space separated strings"""
144 result = self.config(section, name, untrusted=untrusted)
144 result = self.config(section, name, untrusted=untrusted)
145 if result is None:
145 if result is None:
146 result = default or []
146 result = default or []
147 if isinstance(result, basestring):
147 if isinstance(result, basestring):
148 result = result.replace(",", " ").split()
148 result = result.replace(",", " ").split()
149 return result
149 return result
150
150
151 def has_section(self, section, untrusted=False):
151 def has_section(self, section, untrusted=False):
152 '''tell whether section exists in config.'''
152 '''tell whether section exists in config.'''
153 return section in self._data(untrusted)
153 return section in self._data(untrusted)
154
154
155 def configitems(self, section, untrusted=False):
155 def configitems(self, section, untrusted=False):
156 items = self._data(untrusted).items(section)
156 items = self._data(untrusted).items(section)
157 if self.debugflag and not untrusted and self._reportuntrusted:
157 if self.debugflag and not untrusted and self._reportuntrusted:
158 for k, v in self._ucfg.items(section):
158 for k, v in self._ucfg.items(section):
159 if self._tcfg.get(section, k) != v:
159 if self._tcfg.get(section, k) != v:
160 self.debug(_("ignoring untrusted configuration option "
160 self.debug(_("ignoring untrusted configuration option "
161 "%s.%s = %s\n") % (section, k, v))
161 "%s.%s = %s\n") % (section, k, v))
162 return items
162 return items
163
163
164 def walkconfig(self, untrusted=False):
164 def walkconfig(self, untrusted=False):
165 cfg = self._data(untrusted)
165 cfg = self._data(untrusted)
166 for section in cfg.sections():
166 for section in cfg.sections():
167 for name, value in self.configitems(section, untrusted):
167 for name, value in self.configitems(section, untrusted):
168 yield section, name, str(value).replace('\n', '\\n')
168 yield section, name, str(value).replace('\n', '\\n')
169
169
170 def username(self):
170 def username(self):
171 """Return default username to be used in commits.
171 """Return default username to be used in commits.
172
172
173 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
173 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
174 and stop searching if one of these is set.
174 and stop searching if one of these is set.
175 If not found and ui.askusername is True, ask the user, else use
175 If not found and ui.askusername is True, ask the user, else use
176 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
176 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
177 """
177 """
178 user = os.environ.get("HGUSER")
178 user = os.environ.get("HGUSER")
179 if user is None:
179 if user is None:
180 user = self.config("ui", "username")
180 user = self.config("ui", "username")
181 if user is None:
181 if user is None:
182 user = os.environ.get("EMAIL")
182 user = os.environ.get("EMAIL")
183 if user is None and self.configbool("ui", "askusername"):
183 if user is None and self.configbool("ui", "askusername"):
184 user = self.prompt(_("enter a commit username:"), default=None)
184 user = self.prompt(_("enter a commit username:"), default=None)
185 if user is None and not self.interactive():
185 if user is None and not self.interactive():
186 try:
186 try:
187 user = '%s@%s' % (util.getuser(), socket.getfqdn())
187 user = '%s@%s' % (util.getuser(), socket.getfqdn())
188 self.warn(_("No username found, using '%s' instead\n") % user)
188 self.warn(_("No username found, using '%s' instead\n") % user)
189 except KeyError:
189 except KeyError:
190 pass
190 pass
191 if not user:
191 if not user:
192 raise util.Abort(_('no username supplied (see "hg help config")'))
192 raise util.Abort(_('no username supplied (see "hg help config")'))
193 if "\n" in user:
193 if "\n" in user:
194 raise util.Abort(_("username %s contains a newline\n") % repr(user))
194 raise util.Abort(_("username %s contains a newline\n") % repr(user))
195 return user
195 return user
196
196
197 def shortuser(self, user):
197 def shortuser(self, user):
198 """Return a short representation of a user name or email address."""
198 """Return a short representation of a user name or email address."""
199 if not self.verbose: user = util.shortuser(user)
199 if not self.verbose: user = util.shortuser(user)
200 return user
200 return user
201
201
202 def _path(self, loc):
202 def _path(self, loc):
203 p = self.config('paths', loc)
203 p = self.config('paths', loc)
204 if p:
204 if p:
205 if '%%' in p:
205 if '%%' in p:
206 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
206 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
207 (loc, p, self.configsource('paths', loc)))
207 (loc, p, self.configsource('paths', loc)))
208 p = p.replace('%%', '%')
208 p = p.replace('%%', '%')
209 p = util.expandpath(p)
209 p = util.expandpath(p)
210 return p
210 return p
211
211
212 def expandpath(self, loc, default=None):
212 def expandpath(self, loc, default=None):
213 """Return repository location relative to cwd or from [paths]"""
213 """Return repository location relative to cwd or from [paths]"""
214 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
214 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
215 return loc
215 return loc
216
216
217 path = self._path(loc)
217 path = self._path(loc)
218 if not path and default is not None:
218 if not path and default is not None:
219 path = self._path(default)
219 path = self._path(default)
220 return path or loc
220 return path or loc
221
221
222 def pushbuffer(self):
222 def pushbuffer(self):
223 self._buffers.append([])
223 self._buffers.append([])
224
224
225 def popbuffer(self):
225 def popbuffer(self):
226 return "".join(self._buffers.pop())
226 return "".join(self._buffers.pop())
227
227
228 def write(self, *args):
228 def write(self, *args):
229 if self._buffers:
229 if self._buffers:
230 self._buffers[-1].extend([str(a) for a in args])
230 self._buffers[-1].extend([str(a) for a in args])
231 else:
231 else:
232 for a in args:
232 for a in args:
233 sys.stdout.write(str(a))
233 sys.stdout.write(str(a))
234
234
235 def write_err(self, *args):
235 def write_err(self, *args):
236 try:
236 try:
237 if not sys.stdout.closed: sys.stdout.flush()
237 if not sys.stdout.closed: sys.stdout.flush()
238 for a in args:
238 for a in args:
239 sys.stderr.write(str(a))
239 sys.stderr.write(str(a))
240 # stderr may be buffered under win32 when redirected to files,
240 # stderr may be buffered under win32 when redirected to files,
241 # including stdout.
241 # including stdout.
242 if not sys.stderr.closed: sys.stderr.flush()
242 if not sys.stderr.closed: sys.stderr.flush()
243 except IOError, inst:
243 except IOError, inst:
244 if inst.errno != errno.EPIPE:
244 if inst.errno != errno.EPIPE:
245 raise
245 raise
246
246
247 def flush(self):
247 def flush(self):
248 try: sys.stdout.flush()
248 try: sys.stdout.flush()
249 except: pass
249 except: pass
250 try: sys.stderr.flush()
250 try: sys.stderr.flush()
251 except: pass
251 except: pass
252
252
253 def interactive(self):
253 def interactive(self):
254 i = self.configbool("ui", "interactive", None)
254 i = self.configbool("ui", "interactive", None)
255 if i is None:
255 if i is None:
256 try:
256 return sys.stdin.isatty()
257 return sys.stdin.isatty()
258 except AttributeError:
259 # some environments replace stdin without implementing isatty
260 # usually those are non-interactive
261 return False
262
257 return i
263 return i
258
264
259 def _readline(self, prompt=''):
265 def _readline(self, prompt=''):
260 if sys.stdin.isatty():
266 if sys.stdin.isatty():
261 try:
267 try:
262 # magically add command line editing support, where
268 # magically add command line editing support, where
263 # available
269 # available
264 import readline
270 import readline
265 # force demandimport to really load the module
271 # force demandimport to really load the module
266 readline.read_history_file
272 readline.read_history_file
267 # windows sometimes raises something other than ImportError
273 # windows sometimes raises something other than ImportError
268 except Exception:
274 except Exception:
269 pass
275 pass
270 line = raw_input(prompt)
276 line = raw_input(prompt)
271 # When stdin is in binary mode on Windows, it can cause
277 # When stdin is in binary mode on Windows, it can cause
272 # raw_input() to emit an extra trailing carriage return
278 # raw_input() to emit an extra trailing carriage return
273 if os.linesep == '\r\n' and line and line[-1] == '\r':
279 if os.linesep == '\r\n' and line and line[-1] == '\r':
274 line = line[:-1]
280 line = line[:-1]
275 return line
281 return line
276
282
277 def prompt(self, msg, default="y"):
283 def prompt(self, msg, default="y"):
278 """Prompt user with msg, read response.
284 """Prompt user with msg, read response.
279 If ui is not interactive, the default is returned.
285 If ui is not interactive, the default is returned.
280 """
286 """
281 if not self.interactive():
287 if not self.interactive():
282 self.write(msg, ' ', default, "\n")
288 self.write(msg, ' ', default, "\n")
283 return default
289 return default
284 try:
290 try:
285 r = self._readline(msg + ' ')
291 r = self._readline(msg + ' ')
286 if not r:
292 if not r:
287 return default
293 return default
288 return r
294 return r
289 except EOFError:
295 except EOFError:
290 raise util.Abort(_('response expected'))
296 raise util.Abort(_('response expected'))
291
297
292 def promptchoice(self, msg, choices, default=0):
298 def promptchoice(self, msg, choices, default=0):
293 """Prompt user with msg, read response, and ensure it matches
299 """Prompt user with msg, read response, and ensure it matches
294 one of the provided choices. The index of the choice is returned.
300 one of the provided choices. The index of the choice is returned.
295 choices is a sequence of acceptable responses with the format:
301 choices is a sequence of acceptable responses with the format:
296 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
302 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
297 If ui is not interactive, the default is returned.
303 If ui is not interactive, the default is returned.
298 """
304 """
299 resps = [s[s.index('&')+1].lower() for s in choices]
305 resps = [s[s.index('&')+1].lower() for s in choices]
300 while True:
306 while True:
301 r = self.prompt(msg, resps[default])
307 r = self.prompt(msg, resps[default])
302 if r.lower() in resps:
308 if r.lower() in resps:
303 return resps.index(r.lower())
309 return resps.index(r.lower())
304 self.write(_("unrecognized response\n"))
310 self.write(_("unrecognized response\n"))
305
311
306
312
307 def getpass(self, prompt=None, default=None):
313 def getpass(self, prompt=None, default=None):
308 if not self.interactive(): return default
314 if not self.interactive(): return default
309 try:
315 try:
310 return getpass.getpass(prompt or _('password: '))
316 return getpass.getpass(prompt or _('password: '))
311 except EOFError:
317 except EOFError:
312 raise util.Abort(_('response expected'))
318 raise util.Abort(_('response expected'))
313 def status(self, *msg):
319 def status(self, *msg):
314 if not self.quiet: self.write(*msg)
320 if not self.quiet: self.write(*msg)
315 def warn(self, *msg):
321 def warn(self, *msg):
316 self.write_err(*msg)
322 self.write_err(*msg)
317 def note(self, *msg):
323 def note(self, *msg):
318 if self.verbose: self.write(*msg)
324 if self.verbose: self.write(*msg)
319 def debug(self, *msg):
325 def debug(self, *msg):
320 if self.debugflag: self.write(*msg)
326 if self.debugflag: self.write(*msg)
321 def edit(self, text, user):
327 def edit(self, text, user):
322 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
328 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
323 text=True)
329 text=True)
324 try:
330 try:
325 f = os.fdopen(fd, "w")
331 f = os.fdopen(fd, "w")
326 f.write(text)
332 f.write(text)
327 f.close()
333 f.close()
328
334
329 editor = self.geteditor()
335 editor = self.geteditor()
330
336
331 util.system("%s \"%s\"" % (editor, name),
337 util.system("%s \"%s\"" % (editor, name),
332 environ={'HGUSER': user},
338 environ={'HGUSER': user},
333 onerr=util.Abort, errprefix=_("edit failed"))
339 onerr=util.Abort, errprefix=_("edit failed"))
334
340
335 f = open(name)
341 f = open(name)
336 t = f.read()
342 t = f.read()
337 f.close()
343 f.close()
338 finally:
344 finally:
339 os.unlink(name)
345 os.unlink(name)
340
346
341 return t
347 return t
342
348
343 def traceback(self, exc=None):
349 def traceback(self, exc=None):
344 '''print exception traceback if traceback printing enabled.
350 '''print exception traceback if traceback printing enabled.
345 only to call in exception handler. returns true if traceback
351 only to call in exception handler. returns true if traceback
346 printed.'''
352 printed.'''
347 if self.tracebackflag:
353 if self.tracebackflag:
348 if exc:
354 if exc:
349 traceback.print_exception(exc[0], exc[1], exc[2])
355 traceback.print_exception(exc[0], exc[1], exc[2])
350 else:
356 else:
351 traceback.print_exc()
357 traceback.print_exc()
352 return self.tracebackflag
358 return self.tracebackflag
353
359
354 def geteditor(self):
360 def geteditor(self):
355 '''return editor to use'''
361 '''return editor to use'''
356 return (os.environ.get("HGEDITOR") or
362 return (os.environ.get("HGEDITOR") or
357 self.config("ui", "editor") or
363 self.config("ui", "editor") or
358 os.environ.get("VISUAL") or
364 os.environ.get("VISUAL") or
359 os.environ.get("EDITOR", "vi"))
365 os.environ.get("EDITOR", "vi"))
360
366
361 def progress(self, topic, pos, item="", unit="", total=None):
367 def progress(self, topic, pos, item="", unit="", total=None):
362 '''show a progress message
368 '''show a progress message
363
369
364 With stock hg, this is simply a debug message that is hidden
370 With stock hg, this is simply a debug message that is hidden
365 by default, but with extensions or GUI tools it may be
371 by default, but with extensions or GUI tools it may be
366 visible. 'topic' is the current operation, 'item' is a
372 visible. 'topic' is the current operation, 'item' is a
367 non-numeric marker of the current position (ie the currently
373 non-numeric marker of the current position (ie the currently
368 in-process file), 'pos' is the current numeric position (ie
374 in-process file), 'pos' is the current numeric position (ie
369 revision, bytes, etc.), unit is a corresponding unit label,
375 revision, bytes, etc.), unit is a corresponding unit label,
370 and total is the highest expected pos.
376 and total is the highest expected pos.
371
377
372 Multiple nested topics may be active at a time. All topics
378 Multiple nested topics may be active at a time. All topics
373 should be marked closed by setting pos to None at termination.
379 should be marked closed by setting pos to None at termination.
374 '''
380 '''
375
381
376 if pos == None or not self.debugflag:
382 if pos == None or not self.debugflag:
377 return
383 return
378
384
379 if unit:
385 if unit:
380 unit = ' ' + unit
386 unit = ' ' + unit
381 if item:
387 if item:
382 item = ' ' + item
388 item = ' ' + item
383
389
384 if total:
390 if total:
385 pct = 100.0 * pos / total
391 pct = 100.0 * pos / total
386 self.debug('%s:%s %s/%s%s (%4.2g%%)\n'
392 self.debug('%s:%s %s/%s%s (%4.2g%%)\n'
387 % (topic, item, pos, total, unit, pct))
393 % (topic, item, pos, total, unit, pct))
388 else:
394 else:
389 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
395 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
General Comments 0
You need to be logged in to leave comments. Login now