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