##// END OF EJS Templates
hooks: restore io correctly on exception
Jesse Long -
r7416:196b05a5 default
parent child Browse files
Show More
@@ -1,114 +1,115
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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import util, os, sys
9 import util, os, sys
10
10
11 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
11 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
12 '''call python hook. hook is callable object, looked up as
12 '''call python hook. hook is callable object, looked up as
13 name in python module. if callable returns "true", hook
13 name in python module. if callable returns "true", hook
14 fails, else passes. if hook raises exception, treated as
14 fails, else passes. if hook raises exception, treated as
15 hook failure. exception propagates if throw is "true".
15 hook failure. exception propagates if throw is "true".
16
16
17 reason for "true" meaning "hook failed" is so that
17 reason for "true" meaning "hook failed" is so that
18 unmodified commands (e.g. mercurial.commands.update) can
18 unmodified commands (e.g. mercurial.commands.update) can
19 be run as hooks without wrappers to convert return values.'''
19 be run as hooks without wrappers to convert return values.'''
20
20
21 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
21 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
22 obj = funcname
22 obj = funcname
23 if not callable(obj):
23 if not callable(obj):
24 d = funcname.rfind('.')
24 d = funcname.rfind('.')
25 if d == -1:
25 if d == -1:
26 raise util.Abort(_('%s hook is invalid ("%s" not in '
26 raise util.Abort(_('%s hook is invalid ("%s" not in '
27 'a module)') % (hname, funcname))
27 'a module)') % (hname, funcname))
28 modname = funcname[:d]
28 modname = funcname[:d]
29 try:
29 try:
30 obj = __import__(modname)
30 obj = __import__(modname)
31 except ImportError:
31 except ImportError:
32 try:
32 try:
33 # extensions are loaded with hgext_ prefix
33 # extensions are loaded with hgext_ prefix
34 obj = __import__("hgext_%s" % modname)
34 obj = __import__("hgext_%s" % modname)
35 except ImportError:
35 except ImportError:
36 raise util.Abort(_('%s hook is invalid '
36 raise util.Abort(_('%s hook is invalid '
37 '(import of "%s" failed)') %
37 '(import of "%s" failed)') %
38 (hname, modname))
38 (hname, modname))
39 try:
39 try:
40 for p in funcname.split('.')[1:]:
40 for p in funcname.split('.')[1:]:
41 obj = getattr(obj, p)
41 obj = getattr(obj, p)
42 except AttributeError:
42 except AttributeError:
43 raise util.Abort(_('%s hook is invalid '
43 raise util.Abort(_('%s hook is invalid '
44 '("%s" is not defined)') %
44 '("%s" is not defined)') %
45 (hname, funcname))
45 (hname, funcname))
46 if not callable(obj):
46 if not callable(obj):
47 raise util.Abort(_('%s hook is invalid '
47 raise util.Abort(_('%s hook is invalid '
48 '("%s" is not callable)') %
48 '("%s" is not callable)') %
49 (hname, funcname))
49 (hname, funcname))
50 try:
50 try:
51 r = obj(ui=ui, repo=repo, hooktype=name, **args)
51 r = obj(ui=ui, repo=repo, hooktype=name, **args)
52 except (KeyboardInterrupt, util.SignalInterrupt):
52 except (KeyboardInterrupt, util.SignalInterrupt):
53 raise
53 raise
54 except Exception, exc:
54 except Exception, exc:
55 if isinstance(exc, util.Abort):
55 if isinstance(exc, util.Abort):
56 ui.warn(_('error: %s hook failed: %s\n') %
56 ui.warn(_('error: %s hook failed: %s\n') %
57 (hname, exc.args[0]))
57 (hname, exc.args[0]))
58 else:
58 else:
59 ui.warn(_('error: %s hook raised an exception: '
59 ui.warn(_('error: %s hook raised an exception: '
60 '%s\n') % (hname, exc))
60 '%s\n') % (hname, exc))
61 if throw:
61 if throw:
62 raise
62 raise
63 ui.print_exc()
63 ui.print_exc()
64 return True
64 return True
65 if r:
65 if r:
66 if throw:
66 if throw:
67 raise util.Abort(_('%s hook failed') % hname)
67 raise util.Abort(_('%s hook failed') % hname)
68 ui.warn(_('warning: %s hook failed\n') % hname)
68 ui.warn(_('warning: %s hook failed\n') % hname)
69 return r
69 return r
70
70
71 def _exthook(ui, repo, name, cmd, args, throw):
71 def _exthook(ui, repo, name, cmd, args, throw):
72 ui.note(_("running hook %s: %s\n") % (name, cmd))
72 ui.note(_("running hook %s: %s\n") % (name, cmd))
73 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
73 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
74 if repo:
74 if repo:
75 cwd = repo.root
75 cwd = repo.root
76 else:
76 else:
77 cwd = os.getcwd()
77 cwd = os.getcwd()
78 r = util.system(cmd, environ=env, cwd=cwd)
78 r = util.system(cmd, environ=env, cwd=cwd)
79 if r:
79 if r:
80 desc, r = util.explain_exit(r)
80 desc, r = util.explain_exit(r)
81 if throw:
81 if throw:
82 raise util.Abort(_('%s hook %s') % (name, desc))
82 raise util.Abort(_('%s hook %s') % (name, desc))
83 ui.warn(_('warning: %s hook %s\n') % (name, desc))
83 ui.warn(_('warning: %s hook %s\n') % (name, desc))
84 return r
84 return r
85
85
86 _redirect = False
86 _redirect = False
87 def redirect(state):
87 def redirect(state):
88 global _redirect
88 global _redirect
89 _redirect = state
89 _redirect = state
90
90
91 def hook(ui, repo, name, throw=False, **args):
91 def hook(ui, repo, name, throw=False, **args):
92 r = False
92 r = False
93
93
94 if _redirect:
94 if _redirect:
95 # temporarily redirect stdout to stderr
95 # temporarily redirect stdout to stderr
96 oldstdout = os.dup(sys.__stdout__.fileno())
96 oldstdout = os.dup(sys.__stdout__.fileno())
97 os.dup2(sys.__stderr__.fileno(), sys.__stdout__.fileno())
97 os.dup2(sys.__stderr__.fileno(), sys.__stdout__.fileno())
98
98
99 for hname, cmd in util.sort(ui.configitems('hooks')):
99 try:
100 if hname.split('.')[0] != name or not cmd:
100 for hname, cmd in util.sort(ui.configitems('hooks')):
101 continue
101 if hname.split('.')[0] != name or not cmd:
102 if callable(cmd):
102 continue
103 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
103 if callable(cmd):
104 elif cmd.startswith('python:'):
104 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
105 r = _pythonhook(ui, repo, name, hname, cmd[7:].strip(),
105 elif cmd.startswith('python:'):
106 args, throw) or r
106 r = _pythonhook(ui, repo, name, hname, cmd[7:].strip(),
107 else:
107 args, throw) or r
108 r = _exthook(ui, repo, hname, cmd, args, throw) or r
108 else:
109
109 r = _exthook(ui, repo, hname, cmd, args, throw) or r
110 if _redirect:
110 finally:
111 os.dup2(oldstdout, sys.__stdout__.fileno())
111 if _redirect:
112 os.close(oldstdout)
112 os.dup2(oldstdout, sys.__stdout__.fileno())
113 os.close(oldstdout)
113
114
114 return r
115 return r
General Comments 0
You need to be logged in to leave comments. Login now