D472: extensions: make wrapfunction() return a context manager

2017-08-29 Thread quark (Jun Wu)
quark added a comment.


  In https://phab.mercurial-scm.org/D472#9064, @martinvonz wrote:
  
  > I failed with the update of this patch, so see 
https://phab.mercurial-scm.org/D563 instead
  
  
  I guess it's because of the metaedit crash. This can be updated if you have 
`Differential Revision: https://phab.mercurial-scm.org/D472` at the end of 
commit message.

REPOSITORY
  rHG Mercurial

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

To: martinvonz, #hg-reviewers, quark, mjpieters
Cc: yuja, lothiraldan, quark, mjpieters, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D472: extensions: make wrapfunction() return a context manager

2017-08-29 Thread martinvonz (Martin von Zweigbergk)
martinvonz added a comment.


  I failed with the update of this patch, so see 
https://phab.mercurial-scm.org/D563 instead

REPOSITORY
  rHG Mercurial

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

To: martinvonz, #hg-reviewers, quark, mjpieters
Cc: yuja, lothiraldan, quark, mjpieters, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D472: extensions: make wrapfunction() return a context manager

2017-08-29 Thread yuja (Yuya Nishihara)
yuja added a comment.


  This is off-topic, but if `wrapfunction()` is a context manager, I think
  `unwrapfunction()` could also be a context manager.

INLINE COMMENTS

> quark wrote in extensions.py:424
> I think using the return value of wrapfunction could lead to hidden bugs:
> 
>   oldfunc = wrapfunction(x, 'y', newfunc)
>   ...
>   # what if somebody else wraps x.y here?
>   ...
>   x.y = oldfunc
> 
> So it seems reasonable to me to deprecate that and migrate users to 
> `unwrapfunction`.

My point is why do we care only for `classobj.method = oldfunc`, which seems 
less likely than `oldfunc()`.

REPOSITORY
  rHG Mercurial

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

To: martinvonz, #hg-reviewers, quark, mjpieters
Cc: yuja, lothiraldan, quark, mjpieters, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D472: extensions: make wrapfunction() return a context manager

2017-08-28 Thread quark (Jun Wu)
quark added inline comments.

INLINE COMMENTS

> yuja wrote in extensions.py:424
> I see, but I don't think it's more common than wrapping a plain
> function.
> 
> If the compatibility matters, we probably shouldn't replace
> `wrapfunction` with a context manager.

I think using the return value of wrapfunction could lead to hidden bugs:

  oldfunc = wrapfunction(x, 'y', newfunc)
  ...
  # what if somebody else wraps x.y here?
  ...
  x.y = oldfunc

So it seems reasonable to me to deprecate that and migrate users to 
`unwrapfunction`.

REPOSITORY
  rHG Mercurial

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

To: martinvonz, #hg-reviewers, quark, mjpieters
Cc: yuja, lothiraldan, quark, mjpieters, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D472: extensions: make wrapfunction() return a context manager

2017-08-28 Thread yuja (Yuya Nishihara)
yuja added inline comments.

INLINE COMMENTS

> martinvonz wrote in extensions.py:424
> See commit message. It's for compatibility with existing callers that bind 
> the returned value.

I see, but I don't think it's more common than wrapping a plain
function.

If the compatibility matters, we probably shouldn't replace
`wrapfunction` with a context manager.

REPOSITORY
  rHG Mercurial

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

To: martinvonz, #hg-reviewers, quark, mjpieters
Cc: yuja, lothiraldan, quark, mjpieters, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D472: extensions: make wrapfunction() return a context manager

2017-08-27 Thread yuja (Yuya Nishihara)
yuja added inline comments.

INLINE COMMENTS

> extensions.py:424
> +
> +def __get__(self, instance, owner):
> +util.nouideprecwarn("Using the return value of wrapfunction() as a "

When will this `__get__()` be called?

`wrapfunction()` is generally used for wrapping a free function,
not an unbound method.

REPOSITORY
  rHG Mercurial

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

To: martinvonz, #hg-reviewers, quark, mjpieters
Cc: yuja, lothiraldan, quark, mjpieters, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D472: extensions: make wrapfunction() return a context manager

2017-08-24 Thread mjpieters (Martijn Pieters)
mjpieters accepted this revision.
mjpieters added a comment.


  I was being too strict; like `open()` it's sometimes fine to apply the action 
in the constructor.

REPOSITORY
  rHG Mercurial

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

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


D472: extensions: make wrapfunction() return a context manager

2017-08-23 Thread martinvonz (Martin von Zweigbergk)
martinvonz added a comment.


  In https://phab.mercurial-scm.org/D472#7758, @quark wrote:
  
  > In https://phab.mercurial-scm.org/D472#7756, @lothiraldan wrote:
  >
  > > All users of wrapfunction will need to be updated anyway to use the 
context manager style
  >
  >
  > I think only those who use the return value of wrapfunction will need 
change (actually, new code is more correct regarding on nested wrapfunction 
handling) and expect that to be not too many. If wrapfunction is used to 
replace a function permanently, it does not need change.
  >
  > Reusing name is more consistent with `repo.transaction`, `repo.lock` and 
`open`.
  
  
  I personally don't care much which way we go here. I originally did make it a 
separate function (wrappedfunction()) and did the wrapping in __enter__, but 
I'm fine either way.

REPOSITORY
  rHG Mercurial

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

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


D472: extensions: make wrapfunction() return a context manager

2017-08-23 Thread quark (Jun Wu)
quark added a comment.


  In https://phab.mercurial-scm.org/D472#7756, @lothiraldan wrote:
  
  > All users of wrapfunction will need to be updated anyway to use the context 
manager style
  
  
  I think only those who use the return value of wrapfunction will need change 
(actually, new code is more correct regarding on nested wrapfunction handling) 
and expect that to be not too many. If wrapfunction is used to replace a 
function permanently, it does not need change.

REPOSITORY
  rHG Mercurial

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

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


D472: extensions: make wrapfunction() return a context manager

2017-08-23 Thread lothiraldan (Boris Feld)
lothiraldan added a comment.


  Maybe instead of replacing wrapfunction with a context manager, what about 
introducing it with a separate name? All users of wrapfunction will need to be 
updated anyway to use the context manager style

REPOSITORY
  rHG Mercurial

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

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


D472: extensions: make wrapfunction() return a context manager

2017-08-23 Thread quark (Jun Wu)
quark added inline comments.

INLINE COMMENTS

> mjpieters wrote in extensions.py:408-412
> Move this to `__enter__`. You want to be able to create the context manager 
> separately from actually entering the context:
> 
>   cmwrapper = wrapfunction(cont, 'name', wrapper)
>   
>   # do other things, `cont.name` is *not* wrapped
>   
>   with cmwrapper:
>   # `cont.name` is wrapped

The point is `wrapfunction` is also intended to work with non-context case:

  wrapfunction(x, y, z) # works and should have immediate side effect

REPOSITORY
  rHG Mercurial

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

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


D472: extensions: make wrapfunction() return a context manager

2017-08-23 Thread mjpieters (Martijn Pieters)
mjpieters added inline comments.

INLINE COMMENTS

> extensions.py:408-412
> +origfn = getattr(container, funcname)
> +assert callable(origfn)
> +wrap = bind(wrapper, origfn)
> +_updatewrapper(wrap, origfn, wrapper)
> +setattr(container, funcname, wrap)

Move this to `__enter__`. You want to be able to create the context manager 
separately from actually entering the context:

  cmwrapper = wrapfunction(cont, 'name', wrapper)
  
  # do other things, `cont.name` is *not* wrapped
  
  with cmwrapper:
  # `cont.name` is wrapped

REPOSITORY
  rHG Mercurial

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

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


D472: extensions: make wrapfunction() return a context manager

2017-08-22 Thread quark (Jun Wu)
quark added subscribers: mjpieters, quark.
quark accepted this revision.
quark added a comment.


  cc python expert @mjpieters to see if he has additional comments. But I think 
this is good enough.

REPOSITORY
  rHG Mercurial

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

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


D472: extensions: make wrapfunction() return a context manager

2017-08-22 Thread martinvonz (Martin von Zweigbergk)
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)
-