comparison mercurial/extensions.py @ 34087:5361771f9714

wrapfunction: use functools.partial if possible Every `extensions.bind` call inserts a frame in traceback: ... in closure return func(*(args + a), **kw) which makes traceback noisy. The Python stdlib has a `functools.partial` which is backed by C code and does not pollute traceback. However it does not support instancemethod and sets `args` attribute which could be problematic for alias handling. This patch makes `wrapfunction` use `functools.partial` if we are wrapping a function directly exported by a module (so it's impossible to be a class or instance method), and special handles `wrapfunction` results so alias handling code could handle `args` just fine. As an example, `hg rebase -s . -d . --traceback` got 6 lines removed in my setup: File "hg/mercurial/dispatch.py", line 898, in _dispatch cmdpats, cmdoptions) -File "hg/mercurial/extensions.py", line 333, in closure - return func(*(args + a), **kw) File "hg/hgext/journal.py", line 84, in runcommand return orig(lui, repo, cmd, fullargs, *args) -File "hg/mercurial/extensions.py", line 333, in closure - return func(*(args + a), **kw) File "fb-hgext/hgext3rd/fbamend/hiddenoverride.py", line 119, in runcommand result = orig(lui, repo, cmd, fullargs, *args) File "hg/mercurial/dispatch.py", line 660, in runcommand ret = _runcommand(ui, options, cmd, d) -File "hg/mercurial/extensions.py", line 333, in closure - return func(*(args + a), **kw) File "hg/hgext/pager.py", line 69, in pagecmd return orig(ui, options, cmd, cmdfunc) .... Differential Revision: https://phab.mercurial-scm.org/D632
author Jun Wu <quark@fb.com>
date Tue, 05 Sep 2017 13:37:36 -0700
parents 0e0ac8f09048
children a763c891f36e
comparison
equal deleted inserted replaced
34086:a39dce4a76b8 34087:5361771f9714
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 __future__ import absolute_import 8 from __future__ import absolute_import
9 9
10 import functools
10 import imp 11 import imp
11 import inspect 12 import inspect
12 import os 13 import os
13 14
14 from .i18n import ( 15 from .i18n import (
330 return func(*(args + a), **kw) 331 return func(*(args + a), **kw)
331 return closure 332 return closure
332 333
333 def _updatewrapper(wrap, origfn, unboundwrapper): 334 def _updatewrapper(wrap, origfn, unboundwrapper):
334 '''Copy and add some useful attributes to wrapper''' 335 '''Copy and add some useful attributes to wrapper'''
336 wrap.__name__ = origfn.__name__
335 wrap.__module__ = getattr(origfn, '__module__') 337 wrap.__module__ = getattr(origfn, '__module__')
336 wrap.__doc__ = getattr(origfn, '__doc__') 338 wrap.__doc__ = getattr(origfn, '__doc__')
337 wrap.__dict__.update(getattr(origfn, '__dict__', {})) 339 wrap.__dict__.update(getattr(origfn, '__dict__', {}))
338 wrap._origfunc = origfn 340 wrap._origfunc = origfn
339 wrap._unboundwrapper = unboundwrapper 341 wrap._unboundwrapper = unboundwrapper
457 ''' 459 '''
458 assert callable(wrapper) 460 assert callable(wrapper)
459 461
460 origfn = getattr(container, funcname) 462 origfn = getattr(container, funcname)
461 assert callable(origfn) 463 assert callable(origfn)
462 wrap = bind(wrapper, origfn) 464 if inspect.ismodule(container):
465 # origfn is not an instance or class method. "partial" can be used.
466 # "partial" won't insert a frame in traceback.
467 wrap = functools.partial(wrapper, origfn)
468 else:
469 # "partial" cannot be safely used. Emulate its effect by using "bind".
470 # The downside is one more frame in traceback.
471 wrap = bind(wrapper, origfn)
463 _updatewrapper(wrap, origfn, wrapper) 472 _updatewrapper(wrap, origfn, wrapper)
464 setattr(container, funcname, wrap) 473 setattr(container, funcname, wrap)
465 return origfn 474 return origfn
466 475
467 def unwrapfunction(container, funcname, wrapper=None): 476 def unwrapfunction(container, funcname, wrapper=None):