##// END OF EJS Templates
hook: disable demandimport before importing hooks...
Dirkjan Ochtman -
r18111:d7c28954 default
parent child Browse files
Show More
@@ -1,189 +1,193
1 # hook.py - hook support for mercurial
1 # hook.py - hook support for mercurial
2 #
2 #
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys
9 import os, sys
10 import extensions, util
10 import extensions, util, demandimport
11
11
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
13 '''call python hook. hook is callable object, looked up as
13 '''call python hook. hook is callable object, looked up as
14 name in python module. if callable returns "true", hook
14 name in python module. if callable returns "true", hook
15 fails, else passes. if hook raises exception, treated as
15 fails, else passes. if hook raises exception, treated as
16 hook failure. exception propagates if throw is "true".
16 hook failure. exception propagates if throw is "true".
17
17
18 reason for "true" meaning "hook failed" is so that
18 reason for "true" meaning "hook failed" is so that
19 unmodified commands (e.g. mercurial.commands.update) can
19 unmodified commands (e.g. mercurial.commands.update) can
20 be run as hooks without wrappers to convert return values.'''
20 be run as hooks without wrappers to convert return values.'''
21
21
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
23 obj = funcname
23 obj = funcname
24 if not util.safehasattr(obj, '__call__'):
24 if not util.safehasattr(obj, '__call__'):
25 d = funcname.rfind('.')
25 d = funcname.rfind('.')
26 if d == -1:
26 if d == -1:
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
28 'a module)') % (hname, funcname))
28 'a module)') % (hname, funcname))
29 modname = funcname[:d]
29 modname = funcname[:d]
30 oldpaths = sys.path
30 oldpaths = sys.path
31 if util.mainfrozen():
31 if util.mainfrozen():
32 # binary installs require sys.path manipulation
32 # binary installs require sys.path manipulation
33 modpath, modfile = os.path.split(modname)
33 modpath, modfile = os.path.split(modname)
34 if modpath and modfile:
34 if modpath and modfile:
35 sys.path = sys.path[:] + [modpath]
35 sys.path = sys.path[:] + [modpath]
36 modname = modfile
36 modname = modfile
37 try:
37 try:
38 demandimport.disable()
38 obj = __import__(modname)
39 obj = __import__(modname)
40 demandimport.enable()
39 except ImportError:
41 except ImportError:
40 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
42 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
41 try:
43 try:
42 # extensions are loaded with hgext_ prefix
44 # extensions are loaded with hgext_ prefix
43 obj = __import__("hgext_%s" % modname)
45 obj = __import__("hgext_%s" % modname)
46 demandimport.enable()
44 except ImportError:
47 except ImportError:
48 demandimport.enable()
45 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
49 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
46 if ui.tracebackflag:
50 if ui.tracebackflag:
47 ui.warn(_('exception from first failed import attempt:\n'))
51 ui.warn(_('exception from first failed import attempt:\n'))
48 ui.traceback(e1)
52 ui.traceback(e1)
49 if ui.tracebackflag:
53 if ui.tracebackflag:
50 ui.warn(_('exception from second failed import attempt:\n'))
54 ui.warn(_('exception from second failed import attempt:\n'))
51 ui.traceback(e2)
55 ui.traceback(e2)
52 raise util.Abort(_('%s hook is invalid '
56 raise util.Abort(_('%s hook is invalid '
53 '(import of "%s" failed)') %
57 '(import of "%s" failed)') %
54 (hname, modname))
58 (hname, modname))
55 sys.path = oldpaths
59 sys.path = oldpaths
56 try:
60 try:
57 for p in funcname.split('.')[1:]:
61 for p in funcname.split('.')[1:]:
58 obj = getattr(obj, p)
62 obj = getattr(obj, p)
59 except AttributeError:
63 except AttributeError:
60 raise util.Abort(_('%s hook is invalid '
64 raise util.Abort(_('%s hook is invalid '
61 '("%s" is not defined)') %
65 '("%s" is not defined)') %
62 (hname, funcname))
66 (hname, funcname))
63 if not util.safehasattr(obj, '__call__'):
67 if not util.safehasattr(obj, '__call__'):
64 raise util.Abort(_('%s hook is invalid '
68 raise util.Abort(_('%s hook is invalid '
65 '("%s" is not callable)') %
69 '("%s" is not callable)') %
66 (hname, funcname))
70 (hname, funcname))
67 try:
71 try:
68 try:
72 try:
69 # redirect IO descriptors to the ui descriptors so hooks
73 # redirect IO descriptors to the ui descriptors so hooks
70 # that write directly to these don't mess up the command
74 # that write directly to these don't mess up the command
71 # protocol when running through the command server
75 # protocol when running through the command server
72 old = sys.stdout, sys.stderr, sys.stdin
76 old = sys.stdout, sys.stderr, sys.stdin
73 sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
77 sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
74
78
75 r = obj(ui=ui, repo=repo, hooktype=name, **args)
79 r = obj(ui=ui, repo=repo, hooktype=name, **args)
76 except KeyboardInterrupt:
80 except KeyboardInterrupt:
77 raise
81 raise
78 except Exception, exc:
82 except Exception, exc:
79 if isinstance(exc, util.Abort):
83 if isinstance(exc, util.Abort):
80 ui.warn(_('error: %s hook failed: %s\n') %
84 ui.warn(_('error: %s hook failed: %s\n') %
81 (hname, exc.args[0]))
85 (hname, exc.args[0]))
82 else:
86 else:
83 ui.warn(_('error: %s hook raised an exception: '
87 ui.warn(_('error: %s hook raised an exception: '
84 '%s\n') % (hname, exc))
88 '%s\n') % (hname, exc))
85 if throw:
89 if throw:
86 raise
90 raise
87 ui.traceback()
91 ui.traceback()
88 return True
92 return True
89 finally:
93 finally:
90 sys.stdout, sys.stderr, sys.stdin = old
94 sys.stdout, sys.stderr, sys.stdin = old
91 if r:
95 if r:
92 if throw:
96 if throw:
93 raise util.Abort(_('%s hook failed') % hname)
97 raise util.Abort(_('%s hook failed') % hname)
94 ui.warn(_('warning: %s hook failed\n') % hname)
98 ui.warn(_('warning: %s hook failed\n') % hname)
95 return r
99 return r
96
100
97 def _exthook(ui, repo, name, cmd, args, throw):
101 def _exthook(ui, repo, name, cmd, args, throw):
98 ui.note(_("running hook %s: %s\n") % (name, cmd))
102 ui.note(_("running hook %s: %s\n") % (name, cmd))
99
103
100 env = {}
104 env = {}
101 for k, v in args.iteritems():
105 for k, v in args.iteritems():
102 if util.safehasattr(v, '__call__'):
106 if util.safehasattr(v, '__call__'):
103 v = v()
107 v = v()
104 if isinstance(v, dict):
108 if isinstance(v, dict):
105 # make the dictionary element order stable across Python
109 # make the dictionary element order stable across Python
106 # implementations
110 # implementations
107 v = ('{' +
111 v = ('{' +
108 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
112 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
109 '}')
113 '}')
110 env['HG_' + k.upper()] = v
114 env['HG_' + k.upper()] = v
111
115
112 if repo:
116 if repo:
113 cwd = repo.root
117 cwd = repo.root
114 else:
118 else:
115 cwd = os.getcwd()
119 cwd = os.getcwd()
116 if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
120 if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
117 r = util.system(cmd, environ=env, cwd=cwd, out=ui)
121 r = util.system(cmd, environ=env, cwd=cwd, out=ui)
118 else:
122 else:
119 r = util.system(cmd, environ=env, cwd=cwd, out=ui.fout)
123 r = util.system(cmd, environ=env, cwd=cwd, out=ui.fout)
120 if r:
124 if r:
121 desc, r = util.explainexit(r)
125 desc, r = util.explainexit(r)
122 if throw:
126 if throw:
123 raise util.Abort(_('%s hook %s') % (name, desc))
127 raise util.Abort(_('%s hook %s') % (name, desc))
124 ui.warn(_('warning: %s hook %s\n') % (name, desc))
128 ui.warn(_('warning: %s hook %s\n') % (name, desc))
125 return r
129 return r
126
130
127 def _allhooks(ui):
131 def _allhooks(ui):
128 hooks = []
132 hooks = []
129 for name, cmd in ui.configitems('hooks'):
133 for name, cmd in ui.configitems('hooks'):
130 if not name.startswith('priority'):
134 if not name.startswith('priority'):
131 priority = ui.configint('hooks', 'priority.%s' % name, 0)
135 priority = ui.configint('hooks', 'priority.%s' % name, 0)
132 hooks.append((-priority, len(hooks), name, cmd))
136 hooks.append((-priority, len(hooks), name, cmd))
133 return [(k, v) for p, o, k, v in sorted(hooks)]
137 return [(k, v) for p, o, k, v in sorted(hooks)]
134
138
135 _redirect = False
139 _redirect = False
136 def redirect(state):
140 def redirect(state):
137 global _redirect
141 global _redirect
138 _redirect = state
142 _redirect = state
139
143
140 def hook(ui, repo, name, throw=False, **args):
144 def hook(ui, repo, name, throw=False, **args):
141 if not ui.callhooks:
145 if not ui.callhooks:
142 return False
146 return False
143
147
144 r = False
148 r = False
145 oldstdout = -1
149 oldstdout = -1
146
150
147 try:
151 try:
148 for hname, cmd in _allhooks(ui):
152 for hname, cmd in _allhooks(ui):
149 if hname.split('.')[0] != name or not cmd:
153 if hname.split('.')[0] != name or not cmd:
150 continue
154 continue
151
155
152 if oldstdout == -1 and _redirect:
156 if oldstdout == -1 and _redirect:
153 try:
157 try:
154 stdoutno = sys.__stdout__.fileno()
158 stdoutno = sys.__stdout__.fileno()
155 stderrno = sys.__stderr__.fileno()
159 stderrno = sys.__stderr__.fileno()
156 # temporarily redirect stdout to stderr, if possible
160 # temporarily redirect stdout to stderr, if possible
157 if stdoutno >= 0 and stderrno >= 0:
161 if stdoutno >= 0 and stderrno >= 0:
158 sys.__stdout__.flush()
162 sys.__stdout__.flush()
159 oldstdout = os.dup(stdoutno)
163 oldstdout = os.dup(stdoutno)
160 os.dup2(stderrno, stdoutno)
164 os.dup2(stderrno, stdoutno)
161 except (OSError, AttributeError):
165 except (OSError, AttributeError):
162 # files seem to be bogus, give up on redirecting (WSGI, etc)
166 # files seem to be bogus, give up on redirecting (WSGI, etc)
163 pass
167 pass
164
168
165 if util.safehasattr(cmd, '__call__'):
169 if util.safehasattr(cmd, '__call__'):
166 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
170 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
167 elif cmd.startswith('python:'):
171 elif cmd.startswith('python:'):
168 if cmd.count(':') >= 2:
172 if cmd.count(':') >= 2:
169 path, cmd = cmd[7:].rsplit(':', 1)
173 path, cmd = cmd[7:].rsplit(':', 1)
170 path = util.expandpath(path)
174 path = util.expandpath(path)
171 if repo:
175 if repo:
172 path = os.path.join(repo.root, path)
176 path = os.path.join(repo.root, path)
173 try:
177 try:
174 mod = extensions.loadpath(path, 'hghook.%s' % hname)
178 mod = extensions.loadpath(path, 'hghook.%s' % hname)
175 except Exception:
179 except Exception:
176 ui.write(_("loading %s hook failed:\n") % hname)
180 ui.write(_("loading %s hook failed:\n") % hname)
177 raise
181 raise
178 hookfn = getattr(mod, cmd)
182 hookfn = getattr(mod, cmd)
179 else:
183 else:
180 hookfn = cmd[7:].strip()
184 hookfn = cmd[7:].strip()
181 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
185 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
182 else:
186 else:
183 r = _exthook(ui, repo, hname, cmd, args, throw) or r
187 r = _exthook(ui, repo, hname, cmd, args, throw) or r
184 finally:
188 finally:
185 if _redirect and oldstdout >= 0:
189 if _redirect and oldstdout >= 0:
186 os.dup2(oldstdout, stdoutno)
190 os.dup2(oldstdout, stdoutno)
187 os.close(oldstdout)
191 os.close(oldstdout)
188
192
189 return r
193 return r
General Comments 0
You need to be logged in to leave comments. Login now