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