##// END OF EJS Templates
hook: be prepared for __stdout/err__ not having fileno()...
Idan Kamara -
r14993:e5b2ee51 stable
parent child Browse files
Show More
@@ -1,168 +1,172 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 68 try:
69 69 # redirect IO descriptors the the ui descriptors so hooks
70 70 # that write directly to these don't mess up the command
71 71 # protocol when running through the command server
72 72 old = sys.stdout, sys.stderr, sys.stdin
73 73 sys.stdout, sys.stderr, sys.stdin = ui.fout, ui.ferr, ui.fin
74 74
75 75 r = obj(ui=ui, repo=repo, hooktype=name, **args)
76 76 except KeyboardInterrupt:
77 77 raise
78 78 except Exception, exc:
79 79 if isinstance(exc, util.Abort):
80 80 ui.warn(_('error: %s hook failed: %s\n') %
81 81 (hname, exc.args[0]))
82 82 else:
83 83 ui.warn(_('error: %s hook raised an exception: '
84 84 '%s\n') % (hname, exc))
85 85 if throw:
86 86 raise
87 87 ui.traceback()
88 88 return True
89 89 finally:
90 90 sys.stdout, sys.stderr, sys.stdin = old
91 91 if r:
92 92 if throw:
93 93 raise util.Abort(_('%s hook failed') % hname)
94 94 ui.warn(_('warning: %s hook failed\n') % hname)
95 95 return r
96 96
97 97 def _exthook(ui, repo, name, cmd, args, throw):
98 98 ui.note(_("running hook %s: %s\n") % (name, cmd))
99 99
100 100 env = {}
101 101 for k, v in args.iteritems():
102 102 if hasattr(v, '__call__'):
103 103 v = v()
104 104 if isinstance(v, dict):
105 105 # make the dictionary element order stable across Python
106 106 # implementations
107 107 v = ('{' +
108 108 ', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
109 109 '}')
110 110 env['HG_' + k.upper()] = v
111 111
112 112 if repo:
113 113 cwd = repo.root
114 114 else:
115 115 cwd = os.getcwd()
116 116 if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
117 117 r = util.system(cmd, environ=env, cwd=cwd, out=ui)
118 118 else:
119 119 r = util.system(cmd, environ=env, cwd=cwd, out=ui.fout)
120 120 if r:
121 121 desc, r = util.explainexit(r)
122 122 if throw:
123 123 raise util.Abort(_('%s hook %s') % (name, desc))
124 124 ui.warn(_('warning: %s hook %s\n') % (name, desc))
125 125 return r
126 126
127 127 _redirect = False
128 128 def redirect(state):
129 129 global _redirect
130 130 _redirect = state
131 131
132 132 def hook(ui, repo, name, throw=False, **args):
133 133 r = False
134 134
135 135 oldstdout = -1
136 136 if _redirect:
137 stdoutno = sys.__stdout__.fileno()
138 stderrno = sys.__stderr__.fileno()
139 # temporarily redirect stdout to stderr, if possible
140 if stdoutno >= 0 and stderrno >= 0:
141 oldstdout = os.dup(stdoutno)
142 os.dup2(stderrno, stdoutno)
137 try:
138 stdoutno = sys.__stdout__.fileno()
139 stderrno = sys.__stderr__.fileno()
140 # temporarily redirect stdout to stderr, if possible
141 if stdoutno >= 0 and stderrno >= 0:
142 oldstdout = os.dup(stdoutno)
143 os.dup2(stderrno, stdoutno)
144 except AttributeError:
145 # __stdout/err__ doesn't have fileno(), it's not a real file
146 pass
143 147
144 148 try:
145 149 for hname, cmd in ui.configitems('hooks'):
146 150 if hname.split('.')[0] != name or not cmd:
147 151 continue
148 152 if hasattr(cmd, '__call__'):
149 153 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
150 154 elif cmd.startswith('python:'):
151 155 if cmd.count(':') >= 2:
152 156 path, cmd = cmd[7:].rsplit(':', 1)
153 157 path = util.expandpath(path)
154 158 if repo:
155 159 path = os.path.join(repo.root, path)
156 160 mod = extensions.loadpath(path, 'hghook.%s' % hname)
157 161 hookfn = getattr(mod, cmd)
158 162 else:
159 163 hookfn = cmd[7:].strip()
160 164 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
161 165 else:
162 166 r = _exthook(ui, repo, hname, cmd, args, throw) or r
163 167 finally:
164 168 if _redirect and oldstdout >= 0:
165 169 os.dup2(oldstdout, stdoutno)
166 170 os.close(oldstdout)
167 171
168 172 return r
General Comments 0
You need to be logged in to leave comments. Login now