##// END OF EJS Templates
merge with stable
Dirkjan Ochtman -
r10244:c4c0502b merge default
parent child Browse files
Show More
@@ -1,141 +1,142
1 # demandimport.py - global demand-loading of modules for Mercurial
1 # demandimport.py - global demand-loading of modules for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 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 '''
8 '''
9 demandimport - automatic demandloading of modules
9 demandimport - automatic demandloading of modules
10
10
11 To enable this module, do:
11 To enable this module, do:
12
12
13 import demandimport; demandimport.enable()
13 import demandimport; demandimport.enable()
14
14
15 Imports of the following forms will be demand-loaded:
15 Imports of the following forms will be demand-loaded:
16
16
17 import a, b.c
17 import a, b.c
18 import a.b as c
18 import a.b as c
19 from a import b,c # a will be loaded immediately
19 from a import b,c # a will be loaded immediately
20
20
21 These imports will not be delayed:
21 These imports will not be delayed:
22
22
23 from a import *
23 from a import *
24 b = __import__(a)
24 b = __import__(a)
25 '''
25 '''
26
26
27 import __builtin__
27 import __builtin__
28 _origimport = __import__
28 _origimport = __import__
29
29
30 class _demandmod(object):
30 class _demandmod(object):
31 """module demand-loader and proxy"""
31 """module demand-loader and proxy"""
32 def __init__(self, name, globals, locals):
32 def __init__(self, name, globals, locals):
33 if '.' in name:
33 if '.' in name:
34 head, rest = name.split('.', 1)
34 head, rest = name.split('.', 1)
35 after = [rest]
35 after = [rest]
36 else:
36 else:
37 head = name
37 head = name
38 after = []
38 after = []
39 object.__setattr__(self, "_data", (head, globals, locals, after))
39 object.__setattr__(self, "_data", (head, globals, locals, after))
40 object.__setattr__(self, "_module", None)
40 object.__setattr__(self, "_module", None)
41 def _extend(self, name):
41 def _extend(self, name):
42 """add to the list of submodules to load"""
42 """add to the list of submodules to load"""
43 self._data[3].append(name)
43 self._data[3].append(name)
44 def _load(self):
44 def _load(self):
45 if not self._module:
45 if not self._module:
46 head, globals, locals, after = self._data
46 head, globals, locals, after = self._data
47 mod = _origimport(head, globals, locals)
47 mod = _origimport(head, globals, locals)
48 # load submodules
48 # load submodules
49 def subload(mod, p):
49 def subload(mod, p):
50 h, t = p, None
50 h, t = p, None
51 if '.' in p:
51 if '.' in p:
52 h, t = p.split('.', 1)
52 h, t = p.split('.', 1)
53 if not hasattr(mod, h):
53 if not hasattr(mod, h):
54 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
54 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
55 elif t:
55 elif t:
56 subload(getattr(mod, h), t)
56 subload(getattr(mod, h), t)
57
57
58 for x in after:
58 for x in after:
59 subload(mod, x)
59 subload(mod, x)
60
60
61 # are we in the locals dictionary still?
61 # are we in the locals dictionary still?
62 if locals and locals.get(head) == self:
62 if locals and locals.get(head) == self:
63 locals[head] = mod
63 locals[head] = mod
64 object.__setattr__(self, "_module", mod)
64 object.__setattr__(self, "_module", mod)
65
65
66 def __repr__(self):
66 def __repr__(self):
67 if self._module:
67 if self._module:
68 return "<proxied module '%s'>" % self._data[0]
68 return "<proxied module '%s'>" % self._data[0]
69 return "<unloaded module '%s'>" % self._data[0]
69 return "<unloaded module '%s'>" % self._data[0]
70 def __call__(self, *args, **kwargs):
70 def __call__(self, *args, **kwargs):
71 raise TypeError("%s object is not callable" % repr(self))
71 raise TypeError("%s object is not callable" % repr(self))
72 def __getattribute__(self, attr):
72 def __getattribute__(self, attr):
73 if attr in ('_data', '_extend', '_load', '_module'):
73 if attr in ('_data', '_extend', '_load', '_module'):
74 return object.__getattribute__(self, attr)
74 return object.__getattribute__(self, attr)
75 self._load()
75 self._load()
76 return getattr(self._module, attr)
76 return getattr(self._module, attr)
77 def __setattr__(self, attr, val):
77 def __setattr__(self, attr, val):
78 self._load()
78 self._load()
79 setattr(self._module, attr, val)
79 setattr(self._module, attr, val)
80
80
81 def _demandimport(name, globals=None, locals=None, fromlist=None, level=None):
81 def _demandimport(name, globals=None, locals=None, fromlist=None, level=None):
82 if not locals or name in ignore or fromlist == ('*',):
82 if not locals or name in ignore or fromlist == ('*',):
83 # these cases we can't really delay
83 # these cases we can't really delay
84 if level is None:
84 if level is None:
85 return _origimport(name, globals, locals, fromlist)
85 return _origimport(name, globals, locals, fromlist)
86 else:
86 else:
87 return _origimport(name, globals, locals, fromlist, level)
87 return _origimport(name, globals, locals, fromlist, level)
88 elif not fromlist:
88 elif not fromlist:
89 # import a [as b]
89 # import a [as b]
90 if '.' in name: # a.b
90 if '.' in name: # a.b
91 base, rest = name.split('.', 1)
91 base, rest = name.split('.', 1)
92 # email.__init__ loading email.mime
92 # email.__init__ loading email.mime
93 if globals and globals.get('__name__', None) == base:
93 if globals and globals.get('__name__', None) == base:
94 return _origimport(name, globals, locals, fromlist)
94 return _origimport(name, globals, locals, fromlist)
95 # if a is already demand-loaded, add b to its submodule list
95 # if a is already demand-loaded, add b to its submodule list
96 if base in locals:
96 if base in locals:
97 if isinstance(locals[base], _demandmod):
97 if isinstance(locals[base], _demandmod):
98 locals[base]._extend(rest)
98 locals[base]._extend(rest)
99 return locals[base]
99 return locals[base]
100 return _demandmod(name, globals, locals)
100 return _demandmod(name, globals, locals)
101 else:
101 else:
102 if level is not None:
102 if level is not None:
103 # from . import b,c,d or from .a import b,c,d
103 # from . import b,c,d or from .a import b,c,d
104 return _origimport(name, globals, locals, fromlist, level)
104 return _origimport(name, globals, locals, fromlist, level)
105 # from a import b,c,d
105 # from a import b,c,d
106 mod = _origimport(name, globals, locals)
106 mod = _origimport(name, globals, locals)
107 # recurse down the module chain
107 # recurse down the module chain
108 for comp in name.split('.')[1:]:
108 for comp in name.split('.')[1:]:
109 if not hasattr(mod, comp):
109 if not hasattr(mod, comp):
110 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
110 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
111 mod = getattr(mod, comp)
111 mod = getattr(mod, comp)
112 for x in fromlist:
112 for x in fromlist:
113 # set requested submodules for demand load
113 # set requested submodules for demand load
114 if not(hasattr(mod, x)):
114 if not(hasattr(mod, x)):
115 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
115 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
116 return mod
116 return mod
117
117
118 ignore = [
118 ignore = [
119 '_hashlib',
119 '_hashlib',
120 '_xmlplus',
120 '_xmlplus',
121 'fcntl',
121 'fcntl',
122 'win32com.gen_py',
122 'win32com.gen_py',
123 '_winreg', # 2.7 mimetypes needs immediate ImportError
123 'pythoncom',
124 'pythoncom',
124 # imported by tarfile, not available under Windows
125 # imported by tarfile, not available under Windows
125 'pwd',
126 'pwd',
126 'grp',
127 'grp',
127 # imported by profile, itself imported by hotshot.stats,
128 # imported by profile, itself imported by hotshot.stats,
128 # not available under Windows
129 # not available under Windows
129 'resource',
130 'resource',
130 # this trips up many extension authors
131 # this trips up many extension authors
131 'gtk',
132 'gtk',
132 ]
133 ]
133
134
134 def enable():
135 def enable():
135 "enable global demand-loading of modules"
136 "enable global demand-loading of modules"
136 __builtin__.__import__ = _demandimport
137 __builtin__.__import__ = _demandimport
137
138
138 def disable():
139 def disable():
139 "disable global demand-loading of modules"
140 "disable global demand-loading of modules"
140 __builtin__.__import__ = _origimport
141 __builtin__.__import__ = _origimport
141
142
@@ -1,395 +1,397
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 isinstance(v, bool):
138 return v
137 if v.lower() not in _booleans:
139 if v.lower() not in _booleans:
138 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
140 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
139 % (section, name, v))
141 % (section, name, v))
140 return _booleans[v.lower()]
142 return _booleans[v.lower()]
141
143
142 def configlist(self, section, name, default=None, untrusted=False):
144 def configlist(self, section, name, default=None, untrusted=False):
143 """Return a list of comma/space separated strings"""
145 """Return a list of comma/space separated strings"""
144 result = self.config(section, name, untrusted=untrusted)
146 result = self.config(section, name, untrusted=untrusted)
145 if result is None:
147 if result is None:
146 result = default or []
148 result = default or []
147 if isinstance(result, basestring):
149 if isinstance(result, basestring):
148 result = result.replace(",", " ").split()
150 result = result.replace(",", " ").split()
149 return result
151 return result
150
152
151 def has_section(self, section, untrusted=False):
153 def has_section(self, section, untrusted=False):
152 '''tell whether section exists in config.'''
154 '''tell whether section exists in config.'''
153 return section in self._data(untrusted)
155 return section in self._data(untrusted)
154
156
155 def configitems(self, section, untrusted=False):
157 def configitems(self, section, untrusted=False):
156 items = self._data(untrusted).items(section)
158 items = self._data(untrusted).items(section)
157 if self.debugflag and not untrusted and self._reportuntrusted:
159 if self.debugflag and not untrusted and self._reportuntrusted:
158 for k, v in self._ucfg.items(section):
160 for k, v in self._ucfg.items(section):
159 if self._tcfg.get(section, k) != v:
161 if self._tcfg.get(section, k) != v:
160 self.debug(_("ignoring untrusted configuration option "
162 self.debug(_("ignoring untrusted configuration option "
161 "%s.%s = %s\n") % (section, k, v))
163 "%s.%s = %s\n") % (section, k, v))
162 return items
164 return items
163
165
164 def walkconfig(self, untrusted=False):
166 def walkconfig(self, untrusted=False):
165 cfg = self._data(untrusted)
167 cfg = self._data(untrusted)
166 for section in cfg.sections():
168 for section in cfg.sections():
167 for name, value in self.configitems(section, untrusted):
169 for name, value in self.configitems(section, untrusted):
168 yield section, name, str(value).replace('\n', '\\n')
170 yield section, name, str(value).replace('\n', '\\n')
169
171
170 def username(self):
172 def username(self):
171 """Return default username to be used in commits.
173 """Return default username to be used in commits.
172
174
173 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
175 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
174 and stop searching if one of these is set.
176 and stop searching if one of these is set.
175 If not found and ui.askusername is True, ask the user, else use
177 If not found and ui.askusername is True, ask the user, else use
176 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
178 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
177 """
179 """
178 user = os.environ.get("HGUSER")
180 user = os.environ.get("HGUSER")
179 if user is None:
181 if user is None:
180 user = self.config("ui", "username")
182 user = self.config("ui", "username")
181 if user is None:
183 if user is None:
182 user = os.environ.get("EMAIL")
184 user = os.environ.get("EMAIL")
183 if user is None and self.configbool("ui", "askusername"):
185 if user is None and self.configbool("ui", "askusername"):
184 user = self.prompt(_("enter a commit username:"), default=None)
186 user = self.prompt(_("enter a commit username:"), default=None)
185 if user is None and not self.interactive():
187 if user is None and not self.interactive():
186 try:
188 try:
187 user = '%s@%s' % (util.getuser(), socket.getfqdn())
189 user = '%s@%s' % (util.getuser(), socket.getfqdn())
188 self.warn(_("No username found, using '%s' instead\n") % user)
190 self.warn(_("No username found, using '%s' instead\n") % user)
189 except KeyError:
191 except KeyError:
190 pass
192 pass
191 if not user:
193 if not user:
192 raise util.Abort(_('no username supplied (see "hg help config")'))
194 raise util.Abort(_('no username supplied (see "hg help config")'))
193 if "\n" in user:
195 if "\n" in user:
194 raise util.Abort(_("username %s contains a newline\n") % repr(user))
196 raise util.Abort(_("username %s contains a newline\n") % repr(user))
195 return user
197 return user
196
198
197 def shortuser(self, user):
199 def shortuser(self, user):
198 """Return a short representation of a user name or email address."""
200 """Return a short representation of a user name or email address."""
199 if not self.verbose: user = util.shortuser(user)
201 if not self.verbose: user = util.shortuser(user)
200 return user
202 return user
201
203
202 def _path(self, loc):
204 def _path(self, loc):
203 p = self.config('paths', loc)
205 p = self.config('paths', loc)
204 if p:
206 if p:
205 if '%%' in p:
207 if '%%' in p:
206 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
208 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
207 (loc, p, self.configsource('paths', loc)))
209 (loc, p, self.configsource('paths', loc)))
208 p = p.replace('%%', '%')
210 p = p.replace('%%', '%')
209 p = util.expandpath(p)
211 p = util.expandpath(p)
210 return p
212 return p
211
213
212 def expandpath(self, loc, default=None):
214 def expandpath(self, loc, default=None):
213 """Return repository location relative to cwd or from [paths]"""
215 """Return repository location relative to cwd or from [paths]"""
214 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
216 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
215 return loc
217 return loc
216
218
217 path = self._path(loc)
219 path = self._path(loc)
218 if not path and default is not None:
220 if not path and default is not None:
219 path = self._path(default)
221 path = self._path(default)
220 return path or loc
222 return path or loc
221
223
222 def pushbuffer(self):
224 def pushbuffer(self):
223 self._buffers.append([])
225 self._buffers.append([])
224
226
225 def popbuffer(self):
227 def popbuffer(self):
226 return "".join(self._buffers.pop())
228 return "".join(self._buffers.pop())
227
229
228 def write(self, *args):
230 def write(self, *args):
229 if self._buffers:
231 if self._buffers:
230 self._buffers[-1].extend([str(a) for a in args])
232 self._buffers[-1].extend([str(a) for a in args])
231 else:
233 else:
232 for a in args:
234 for a in args:
233 sys.stdout.write(str(a))
235 sys.stdout.write(str(a))
234
236
235 def write_err(self, *args):
237 def write_err(self, *args):
236 try:
238 try:
237 if not sys.stdout.closed: sys.stdout.flush()
239 if not sys.stdout.closed: sys.stdout.flush()
238 for a in args:
240 for a in args:
239 sys.stderr.write(str(a))
241 sys.stderr.write(str(a))
240 # stderr may be buffered under win32 when redirected to files,
242 # stderr may be buffered under win32 when redirected to files,
241 # including stdout.
243 # including stdout.
242 if not sys.stderr.closed: sys.stderr.flush()
244 if not sys.stderr.closed: sys.stderr.flush()
243 except IOError, inst:
245 except IOError, inst:
244 if inst.errno != errno.EPIPE:
246 if inst.errno != errno.EPIPE:
245 raise
247 raise
246
248
247 def flush(self):
249 def flush(self):
248 try: sys.stdout.flush()
250 try: sys.stdout.flush()
249 except: pass
251 except: pass
250 try: sys.stderr.flush()
252 try: sys.stderr.flush()
251 except: pass
253 except: pass
252
254
253 def interactive(self):
255 def interactive(self):
254 i = self.configbool("ui", "interactive", None)
256 i = self.configbool("ui", "interactive", None)
255 if i is None:
257 if i is None:
256 try:
258 try:
257 return sys.stdin.isatty()
259 return sys.stdin.isatty()
258 except AttributeError:
260 except AttributeError:
259 # some environments replace stdin without implementing isatty
261 # some environments replace stdin without implementing isatty
260 # usually those are non-interactive
262 # usually those are non-interactive
261 return False
263 return False
262
264
263 return i
265 return i
264
266
265 def _readline(self, prompt=''):
267 def _readline(self, prompt=''):
266 if sys.stdin.isatty():
268 if sys.stdin.isatty():
267 try:
269 try:
268 # magically add command line editing support, where
270 # magically add command line editing support, where
269 # available
271 # available
270 import readline
272 import readline
271 # force demandimport to really load the module
273 # force demandimport to really load the module
272 readline.read_history_file
274 readline.read_history_file
273 # windows sometimes raises something other than ImportError
275 # windows sometimes raises something other than ImportError
274 except Exception:
276 except Exception:
275 pass
277 pass
276 line = raw_input(prompt)
278 line = raw_input(prompt)
277 # When stdin is in binary mode on Windows, it can cause
279 # When stdin is in binary mode on Windows, it can cause
278 # raw_input() to emit an extra trailing carriage return
280 # raw_input() to emit an extra trailing carriage return
279 if os.linesep == '\r\n' and line and line[-1] == '\r':
281 if os.linesep == '\r\n' and line and line[-1] == '\r':
280 line = line[:-1]
282 line = line[:-1]
281 return line
283 return line
282
284
283 def prompt(self, msg, default="y"):
285 def prompt(self, msg, default="y"):
284 """Prompt user with msg, read response.
286 """Prompt user with msg, read response.
285 If ui is not interactive, the default is returned.
287 If ui is not interactive, the default is returned.
286 """
288 """
287 if not self.interactive():
289 if not self.interactive():
288 self.write(msg, ' ', default, "\n")
290 self.write(msg, ' ', default, "\n")
289 return default
291 return default
290 try:
292 try:
291 r = self._readline(msg + ' ')
293 r = self._readline(msg + ' ')
292 if not r:
294 if not r:
293 return default
295 return default
294 return r
296 return r
295 except EOFError:
297 except EOFError:
296 raise util.Abort(_('response expected'))
298 raise util.Abort(_('response expected'))
297
299
298 def promptchoice(self, msg, choices, default=0):
300 def promptchoice(self, msg, choices, default=0):
299 """Prompt user with msg, read response, and ensure it matches
301 """Prompt user with msg, read response, and ensure it matches
300 one of the provided choices. The index of the choice is returned.
302 one of the provided choices. The index of the choice is returned.
301 choices is a sequence of acceptable responses with the format:
303 choices is a sequence of acceptable responses with the format:
302 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
304 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
303 If ui is not interactive, the default is returned.
305 If ui is not interactive, the default is returned.
304 """
306 """
305 resps = [s[s.index('&')+1].lower() for s in choices]
307 resps = [s[s.index('&')+1].lower() for s in choices]
306 while True:
308 while True:
307 r = self.prompt(msg, resps[default])
309 r = self.prompt(msg, resps[default])
308 if r.lower() in resps:
310 if r.lower() in resps:
309 return resps.index(r.lower())
311 return resps.index(r.lower())
310 self.write(_("unrecognized response\n"))
312 self.write(_("unrecognized response\n"))
311
313
312
314
313 def getpass(self, prompt=None, default=None):
315 def getpass(self, prompt=None, default=None):
314 if not self.interactive(): return default
316 if not self.interactive(): return default
315 try:
317 try:
316 return getpass.getpass(prompt or _('password: '))
318 return getpass.getpass(prompt or _('password: '))
317 except EOFError:
319 except EOFError:
318 raise util.Abort(_('response expected'))
320 raise util.Abort(_('response expected'))
319 def status(self, *msg):
321 def status(self, *msg):
320 if not self.quiet: self.write(*msg)
322 if not self.quiet: self.write(*msg)
321 def warn(self, *msg):
323 def warn(self, *msg):
322 self.write_err(*msg)
324 self.write_err(*msg)
323 def note(self, *msg):
325 def note(self, *msg):
324 if self.verbose: self.write(*msg)
326 if self.verbose: self.write(*msg)
325 def debug(self, *msg):
327 def debug(self, *msg):
326 if self.debugflag: self.write(*msg)
328 if self.debugflag: self.write(*msg)
327 def edit(self, text, user):
329 def edit(self, text, user):
328 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
330 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
329 text=True)
331 text=True)
330 try:
332 try:
331 f = os.fdopen(fd, "w")
333 f = os.fdopen(fd, "w")
332 f.write(text)
334 f.write(text)
333 f.close()
335 f.close()
334
336
335 editor = self.geteditor()
337 editor = self.geteditor()
336
338
337 util.system("%s \"%s\"" % (editor, name),
339 util.system("%s \"%s\"" % (editor, name),
338 environ={'HGUSER': user},
340 environ={'HGUSER': user},
339 onerr=util.Abort, errprefix=_("edit failed"))
341 onerr=util.Abort, errprefix=_("edit failed"))
340
342
341 f = open(name)
343 f = open(name)
342 t = f.read()
344 t = f.read()
343 f.close()
345 f.close()
344 finally:
346 finally:
345 os.unlink(name)
347 os.unlink(name)
346
348
347 return t
349 return t
348
350
349 def traceback(self, exc=None):
351 def traceback(self, exc=None):
350 '''print exception traceback if traceback printing enabled.
352 '''print exception traceback if traceback printing enabled.
351 only to call in exception handler. returns true if traceback
353 only to call in exception handler. returns true if traceback
352 printed.'''
354 printed.'''
353 if self.tracebackflag:
355 if self.tracebackflag:
354 if exc:
356 if exc:
355 traceback.print_exception(exc[0], exc[1], exc[2])
357 traceback.print_exception(exc[0], exc[1], exc[2])
356 else:
358 else:
357 traceback.print_exc()
359 traceback.print_exc()
358 return self.tracebackflag
360 return self.tracebackflag
359
361
360 def geteditor(self):
362 def geteditor(self):
361 '''return editor to use'''
363 '''return editor to use'''
362 return (os.environ.get("HGEDITOR") or
364 return (os.environ.get("HGEDITOR") or
363 self.config("ui", "editor") or
365 self.config("ui", "editor") or
364 os.environ.get("VISUAL") or
366 os.environ.get("VISUAL") or
365 os.environ.get("EDITOR", "vi"))
367 os.environ.get("EDITOR", "vi"))
366
368
367 def progress(self, topic, pos, item="", unit="", total=None):
369 def progress(self, topic, pos, item="", unit="", total=None):
368 '''show a progress message
370 '''show a progress message
369
371
370 With stock hg, this is simply a debug message that is hidden
372 With stock hg, this is simply a debug message that is hidden
371 by default, but with extensions or GUI tools it may be
373 by default, but with extensions or GUI tools it may be
372 visible. 'topic' is the current operation, 'item' is a
374 visible. 'topic' is the current operation, 'item' is a
373 non-numeric marker of the current position (ie the currently
375 non-numeric marker of the current position (ie the currently
374 in-process file), 'pos' is the current numeric position (ie
376 in-process file), 'pos' is the current numeric position (ie
375 revision, bytes, etc.), unit is a corresponding unit label,
377 revision, bytes, etc.), unit is a corresponding unit label,
376 and total is the highest expected pos.
378 and total is the highest expected pos.
377
379
378 Multiple nested topics may be active at a time. All topics
380 Multiple nested topics may be active at a time. All topics
379 should be marked closed by setting pos to None at termination.
381 should be marked closed by setting pos to None at termination.
380 '''
382 '''
381
383
382 if pos == None or not self.debugflag:
384 if pos == None or not self.debugflag:
383 return
385 return
384
386
385 if unit:
387 if unit:
386 unit = ' ' + unit
388 unit = ' ' + unit
387 if item:
389 if item:
388 item = ' ' + item
390 item = ' ' + item
389
391
390 if total:
392 if total:
391 pct = 100.0 * pos / total
393 pct = 100.0 * pos / total
392 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
394 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
393 % (topic, item, pos, total, unit, pct))
395 % (topic, item, pos, total, unit, pct))
394 else:
396 else:
395 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
397 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