Mercurial > hg
changeset 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 | a39dce4a76b8 |
children | a763c891f36e |
files | mercurial/dispatch.py mercurial/extensions.py |
diffstat | 2 files changed, 14 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/dispatch.py Fri Sep 01 12:34:36 2017 -0700 +++ b/mercurial/dispatch.py Tue Sep 05 13:37:36 2017 -0700 @@ -357,7 +357,10 @@ return -1 def aliasargs(fn, givenargs): - args = getattr(fn, 'args', []) + args = [] + # only care about alias 'args', ignore 'args' set by extensions.wrapfunction + if not util.safehasattr(fn, '_origfunc'): + args = getattr(fn, 'args', args) if args: cmd = ' '.join(map(util.shellquote, args))
--- a/mercurial/extensions.py Fri Sep 01 12:34:36 2017 -0700 +++ b/mercurial/extensions.py Tue Sep 05 13:37:36 2017 -0700 @@ -7,6 +7,7 @@ from __future__ import absolute_import +import functools import imp import inspect import os @@ -332,6 +333,7 @@ def _updatewrapper(wrap, origfn, unboundwrapper): '''Copy and add some useful attributes to wrapper''' + wrap.__name__ = origfn.__name__ wrap.__module__ = getattr(origfn, '__module__') wrap.__doc__ = getattr(origfn, '__doc__') wrap.__dict__.update(getattr(origfn, '__dict__', {})) @@ -459,7 +461,14 @@ origfn = getattr(container, funcname) assert callable(origfn) - wrap = bind(wrapper, origfn) + if inspect.ismodule(container): + # origfn is not an instance or class method. "partial" can be used. + # "partial" won't insert a frame in traceback. + wrap = functools.partial(wrapper, origfn) + else: + # "partial" cannot be safely used. Emulate its effect by using "bind". + # The downside is one more frame in traceback. + wrap = bind(wrapper, origfn) _updatewrapper(wrap, origfn, wrapper) setattr(container, funcname, wrap) return origfn