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