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