Patchwork [2,of,3] extensions: add getwrapperchain to get a list of wrappers

login
register
mail settings
Submitter Jun Wu
Date July 1, 2016, 12:09 p.m.
Message ID <de798903374922317eb2.1467374944@x1c>
Download mbox | patch
Permalink /patch/15701/
State Superseded
Delegated to: Yuya Nishihara
Headers show

Comments

Jun Wu - July 1, 2016, 12:09 p.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1467367028 -3600
#      Fri Jul 01 10:57:08 2016 +0100
# Node ID de798903374922317eb2cbca9733ba0cea415780
# Parent  21e49b9facede184e5d262211bd5cc34bddca974
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r de7989033749
extensions: add getwrapperchain to get a list of wrappers

The getwrapperchain returns a list of wrappers + the original function, making
it easier to understand what has been wrapped by whom. For example:

  In : mercurial.extensions.getwrapperchain(mercurial.dispatch, '_runcommand')
  Out:
  [<function hgext.pager.pagecmd>,
   <function hgext.color.colorcmd>,
   <function hgext.zeroconf.cleanupafterdispatch>,
   <function mercurial.dispatch._runcommand>]

It will also be useful to safely unwrap a function. See the next patch.
Yuya Nishihara - July 3, 2016, 7:49 a.m.
On Fri, 1 Jul 2016 13:09:04 +0100, Jun Wu wrote:
> # HG changeset patch
> # User Jun Wu <quark@fb.com>
> # Date 1467367028 -3600
> #      Fri Jul 01 10:57:08 2016 +0100
> # Node ID de798903374922317eb2cbca9733ba0cea415780
> # Parent  21e49b9facede184e5d262211bd5cc34bddca974
> # Available At https://bitbucket.org/quark-zju/hg-draft
> #              hg pull https://bitbucket.org/quark-zju/hg-draft -r de7989033749
> extensions: add getwrapperchain to get a list of wrappers
> 
> The getwrapperchain returns a list of wrappers + the original function, making
> it easier to understand what has been wrapped by whom. For example:
> 
>   In : mercurial.extensions.getwrapperchain(mercurial.dispatch, '_runcommand')
>   Out:
>   [<function hgext.pager.pagecmd>,
>    <function hgext.color.colorcmd>,
>    <function hgext.zeroconf.cleanupafterdispatch>,
>    <function mercurial.dispatch._runcommand>]
> 
> It will also be useful to safely unwrap a function. See the next patch.
> 
> diff --git a/mercurial/extensions.py b/mercurial/extensions.py
> --- a/mercurial/extensions.py
> +++ b/mercurial/extensions.py
> @@ -303,6 +303,25 @@ def wrapfunction(container, funcname, wr
>      setattr(container, funcname, wrap)
>      return origfn
>  
> +def getwrapperchain(container, funcname):
> +    '''get a chain of wrappers of a function
> +
> +    Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc]
> +
> +    The wrapper functions are the ones passed to wrapfunction, whose first
> +    argument is origfunc.
> +    '''
> +    result = []
> +    fn = getattr(container, funcname)
> +    while fn:
> +        assert callable(fn)
> +        if util.safehasattr(fn, '_unboundwrapper'):
> +            result.append(fn._unboundwrapper)
> +        else:
> +            result.append(fn)

Nit: it could be getattr(fn, '_unboundwrapper', fn) as you do at the next line.

> +        fn = getattr(fn, '_origfunc', None)
> +    return result

Patch

diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -303,6 +303,25 @@  def wrapfunction(container, funcname, wr
     setattr(container, funcname, wrap)
     return origfn
 
+def getwrapperchain(container, funcname):
+    '''get a chain of wrappers of a function
+
+    Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc]
+
+    The wrapper functions are the ones passed to wrapfunction, whose first
+    argument is origfunc.
+    '''
+    result = []
+    fn = getattr(container, funcname)
+    while fn:
+        assert callable(fn)
+        if util.safehasattr(fn, '_unboundwrapper'):
+            result.append(fn._unboundwrapper)
+        else:
+            result.append(fn)
+        fn = getattr(fn, '_origfunc', None)
+    return result
+
 def _disabledpaths(strip_init=False):
     '''find paths of disabled extensions. returns a dict of {name: path}
     removes /__init__.py from packages if strip_init is True'''