comparison mercurial/extensions.py @ 24734:fb6cb1b82f4f

extensions: extract partial application into a bind() function We use partial function application for wrapping existing Mercurial functions, and it's implemented separately each time. This patch extracts the partial application into a new bind() function that can be used on its own by extensions when appropriate. In particular, the evolve extension needs to wrap functions in the various bundle2 processing dictionaries, which the pre-existing methods don't support.
author Eric Sumner <ericsumner@fb.com>
date Wed, 15 Apr 2015 12:18:05 -0400
parents 28af978c8f13
children e6e7d1cce04d
comparison
equal deleted inserted replaced
24733:c00e4338fa4b 24734:fb6cb1b82f4f
150 if extension in _extensions: 150 if extension in _extensions:
151 callback(loaded=True) 151 callback(loaded=True)
152 else: 152 else:
153 _aftercallbacks.setdefault(extension, []).append(callback) 153 _aftercallbacks.setdefault(extension, []).append(callback)
154 154
155 def bind(func, *args):
156 '''Partial function application
157
158 Returns a new function that is the partial application of args and kwargs
159 to func. For example,
160
161 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)'''
162 assert callable(func)
163 def closure(*a, **kw):
164 return func(*(args + a), **kw)
165 return closure
166
155 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None): 167 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
156 '''Wrap the command named `command' in table 168 '''Wrap the command named `command' in table
157 169
158 Replace command in the command table with wrapper. The wrapped command will 170 Replace command in the command table with wrapper. The wrapped command will
159 be inserted into the command table specified by the table argument. 171 be inserted into the command table specified by the table argument.
187 if e is entry: 199 if e is entry:
188 key = alias 200 key = alias
189 break 201 break
190 202
191 origfn = entry[0] 203 origfn = entry[0]
192 def wrap(*args, **kwargs): 204 wrap = bind(util.checksignature(wrapper), util.checksignature(origfn))
193 return util.checksignature(wrapper)(
194 util.checksignature(origfn), *args, **kwargs)
195 205
196 wrap.__module__ = getattr(origfn, '__module__') 206 wrap.__module__ = getattr(origfn, '__module__')
197 207
198 doc = getattr(origfn, '__doc__') 208 doc = getattr(origfn, '__doc__')
199 if docstring is not None: 209 if docstring is not None:
239 work. Since you cannot control what other extensions are loaded by 249 work. Since you cannot control what other extensions are loaded by
240 your end users, you should play nicely with others by using the 250 your end users, you should play nicely with others by using the
241 subclass trick. 251 subclass trick.
242 ''' 252 '''
243 assert callable(wrapper) 253 assert callable(wrapper)
244 def wrap(*args, **kwargs):
245 return wrapper(origfn, *args, **kwargs)
246 254
247 origfn = getattr(container, funcname) 255 origfn = getattr(container, funcname)
248 assert callable(origfn) 256 assert callable(origfn)
249 setattr(container, funcname, wrap) 257 setattr(container, funcname, bind(wrapper, origfn))
250 return origfn 258 return origfn
251 259
252 def _disabledpaths(strip_init=False): 260 def _disabledpaths(strip_init=False):
253 '''find paths of disabled extensions. returns a dict of {name: path} 261 '''find paths of disabled extensions. returns a dict of {name: path}
254 removes /__init__.py from packages if strip_init is True''' 262 removes /__init__.py from packages if strip_init is True'''