comparison mercurial/localrepo.py @ 4623:fff50306e6dd

hooks: separate hook code into a separate module
author Matt Mackall <mpm@selenic.com>
date Mon, 18 Jun 2007 13:24:34 -0500
parents 5fd7cc897542
children ff7253a0d1da
comparison
equal deleted inserted replaced
4622:6fc26982f203 4623:fff50306e6dd
8 from node import * 8 from node import *
9 from i18n import _ 9 from i18n import _
10 import repo, changegroup 10 import repo, changegroup
11 import changelog, dirstate, filelog, manifest, context 11 import changelog, dirstate, filelog, manifest, context
12 import re, lock, transaction, tempfile, stat, mdiff, errno, ui 12 import re, lock, transaction, tempfile, stat, mdiff, errno, ui
13 import os, revlog, time, util, extensions 13 import os, revlog, time, util, extensions, hook
14 14
15 class localrepository(repo.repository): 15 class localrepository(repo.repository):
16 capabilities = ('lookup', 'changegroupsubset') 16 capabilities = ('lookup', 'changegroupsubset')
17 supported = ('revlogv1', 'store') 17 supported = ('revlogv1', 'store')
18 18
103 103
104 def url(self): 104 def url(self):
105 return 'file:' + self.root 105 return 'file:' + self.root
106 106
107 def hook(self, name, throw=False, **args): 107 def hook(self, name, throw=False, **args):
108 def callhook(hname, funcname): 108 return hook.hook(self.ui, self, name, throw, **args)
109 '''call python hook. hook is callable object, looked up as
110 name in python module. if callable returns "true", hook
111 fails, else passes. if hook raises exception, treated as
112 hook failure. exception propagates if throw is "true".
113
114 reason for "true" meaning "hook failed" is so that
115 unmodified commands (e.g. mercurial.commands.update) can
116 be run as hooks without wrappers to convert return values.'''
117
118 self.ui.note(_("calling hook %s: %s\n") % (hname, funcname))
119 obj = funcname
120 if not callable(obj):
121 d = funcname.rfind('.')
122 if d == -1:
123 raise util.Abort(_('%s hook is invalid ("%s" not in '
124 'a module)') % (hname, funcname))
125 modname = funcname[:d]
126 try:
127 obj = __import__(modname)
128 except ImportError:
129 try:
130 # extensions are loaded with hgext_ prefix
131 obj = __import__("hgext_%s" % modname)
132 except ImportError:
133 raise util.Abort(_('%s hook is invalid '
134 '(import of "%s" failed)') %
135 (hname, modname))
136 try:
137 for p in funcname.split('.')[1:]:
138 obj = getattr(obj, p)
139 except AttributeError, err:
140 raise util.Abort(_('%s hook is invalid '
141 '("%s" is not defined)') %
142 (hname, funcname))
143 if not callable(obj):
144 raise util.Abort(_('%s hook is invalid '
145 '("%s" is not callable)') %
146 (hname, funcname))
147 try:
148 r = obj(ui=self.ui, repo=self, hooktype=name, **args)
149 except (KeyboardInterrupt, util.SignalInterrupt):
150 raise
151 except Exception, exc:
152 if isinstance(exc, util.Abort):
153 self.ui.warn(_('error: %s hook failed: %s\n') %
154 (hname, exc.args[0]))
155 else:
156 self.ui.warn(_('error: %s hook raised an exception: '
157 '%s\n') % (hname, exc))
158 if throw:
159 raise
160 self.ui.print_exc()
161 return True
162 if r:
163 if throw:
164 raise util.Abort(_('%s hook failed') % hname)
165 self.ui.warn(_('warning: %s hook failed\n') % hname)
166 return r
167
168 def runhook(name, cmd):
169 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
170 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
171 r = util.system(cmd, environ=env, cwd=self.root)
172 if r:
173 desc, r = util.explain_exit(r)
174 if throw:
175 raise util.Abort(_('%s hook %s') % (name, desc))
176 self.ui.warn(_('warning: %s hook %s\n') % (name, desc))
177 return r
178
179 r = False
180 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
181 if hname.split(".", 1)[0] == name and cmd]
182 hooks.sort()
183 for hname, cmd in hooks:
184 if callable(cmd):
185 r = callhook(hname, cmd) or r
186 elif cmd.startswith('python:'):
187 r = callhook(hname, cmd[7:].strip()) or r
188 else:
189 r = runhook(hname, cmd) or r
190 return r
191 109
192 tag_disallowed = ':\r\n' 110 tag_disallowed = ':\r\n'
193 111
194 def _tag(self, name, node, message, local, user, date, parent=None): 112 def _tag(self, name, node, message, local, user, date, parent=None):
195 use_dirstate = parent is None 113 use_dirstate = parent is None