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