martinvonz created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Several extensions exist that temporarily want to wrap a function (at
  least narrowhg, any many of the extensions in hg-experimental). That's
  why we have the unwrapfunction() that was introduced in 
https://phab.mercurial-scm.org/rHG19578bb847310e32eae3d44466b51a698ced5b95
  (extensions: add unwrapfunction to undo wrapfunction, 2016-08-10).
  
  This patch makes wrapfunction() return a context manager. Since
  wrapfunction() returned the original function before this patch, we
  will be changing the return type. In order to limit breakages for
  callers that kept the reference to the unbound method around and then
  set it back on the container after it was done, this patch also
  implements a __get__ method that binds the original method to the
  container instance.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D472

AFFECTED FILES
  mercurial/extensions.py
  tests/test-extensions-wrapfunction.py
  tests/test-extensions-wrapfunction.py.out
  tests/test-filecache.py

CHANGE DETAILS

diff --git a/tests/test-filecache.py b/tests/test-filecache.py
--- a/tests/test-filecache.py
+++ b/tests/test-filecache.py
@@ -129,20 +129,16 @@
     def wrapinit(orig, *args, **kwargs):
         pass
 
-    originit = extensions.wrapfunction(util.cachestat, '__init__', wrapinit)
-    origcacheable = extensions.wrapfunction(util.cachestat, 'cacheable',
-                                            wrapcacheable)
+    with extensions.wrapfunction(util.cachestat, '__init__', wrapinit),\
+         extensions.wrapfunction(util.cachestat, 'cacheable', wrapcacheable):
 
-    for fn in ['x', 'y']:
-        try:
-            os.remove(fn)
-        except OSError:
-            pass
+        for fn in ['x', 'y']:
+            try:
+                os.remove(fn)
+            except OSError:
+                pass
 
-    basic(fakerepo())
-
-    util.cachestat.cacheable = origcacheable
-    util.cachestat.__init__ = originit
+        basic(fakerepo())
 
 def test_filecache_synced():
     # test old behavior that caused filecached properties to go out of sync
diff --git a/tests/test-extensions-wrapfunction.py.out 
b/tests/test-extensions-wrapfunction.py.out
--- a/tests/test-extensions-wrapfunction.py.out
+++ b/tests/test-extensions-wrapfunction.py.out
@@ -12,3 +12,8 @@
 unwrap 2: 2: [1, 'orig']
 unwrap 1: 1: ['orig']
 unwrap -: -: IndexError
+context manager ['orig']
+context manager [0, 'orig']
+context manager [1, 0, 'orig']
+context manager [0, 'orig']
+context manager ['orig']
diff --git a/tests/test-extensions-wrapfunction.py 
b/tests/test-extensions-wrapfunction.py
--- a/tests/test-extensions-wrapfunction.py
+++ b/tests/test-extensions-wrapfunction.py
@@ -37,3 +37,11 @@
 batchwrap(wrappers + [wrappers[0]])
 batchunwrap([(wrappers[i] if i >= 0 else None)
              for i in [3, None, 0, 4, 0, 2, 1, None]])
+
+print('context manager', dummy.getstack())
+with extensions.wrapfunction(dummy, 'getstack', wrappers[0]):
+    print('context manager', dummy.getstack())
+    with extensions.wrapfunction(dummy, 'getstack', wrappers[1]):
+        print('context manager', dummy.getstack())
+    print('context manager', dummy.getstack())
+print('context manager', dummy.getstack())
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -399,6 +399,34 @@
         raise AttributeError(r"type '%s' has no property '%s'" % (
             cls, propname))
 
+class _wrappedfunction(object):
+    '''context manager for temporarily wrapping a function'''
+
+    def __init__(self, container, funcname, wrapper):
+        assert callable(wrapper)
+
+        origfn = getattr(container, funcname)
+        assert callable(origfn)
+        wrap = bind(wrapper, origfn)
+        _updatewrapper(wrap, origfn, wrapper)
+        setattr(container, funcname, wrap)
+
+        self._container = container
+        self._funcname = funcname
+        self._origfn = origfn
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exctype, excvalue, traceback):
+        unwrapfunction(self._container, self._funcname)
+
+    def __get__(self, instance, owner):
+        util.nouideprecwarn("Using the return value of wrapfunction() as a "
+                            "function is deprecated. Use it as context manager 
"
+                            "instead","4.4", stacklevel=2)
+        return bind(self._origfn, instance)
+
 def wrapfunction(container, funcname, wrapper):
     '''Wrap the function named funcname in container
 
@@ -432,14 +460,7 @@
     your end users, you should play nicely with others by using the
     subclass trick.
     '''
-    assert callable(wrapper)
-
-    origfn = getattr(container, funcname)
-    assert callable(origfn)
-    wrap = bind(wrapper, origfn)
-    _updatewrapper(wrap, origfn, wrapper)
-    setattr(container, funcname, wrap)
-    return origfn
+    return _wrappedfunction(container, funcname, wrapper)
 
 def unwrapfunction(container, funcname, wrapper=None):
     '''undo wrapfunction



To: martinvonz, #hg-reviewers
Cc: mercurial-devel
_______________________________________________
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

Reply via email to