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