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