Patchwork [4,of,5] registrar: add classes to wrap existing webcommand

login
register
mail settings
Submitter Katsunori FUJIWARA
Date Aug. 22, 2018, 2:21 a.m.
Message ID <afe84a051a391724a7d1.1534904515@blacknile>
Download mbox | patch
Permalink /patch/33953/
State New
Headers show

Comments

Katsunori FUJIWARA - Aug. 22, 2018, 2:21 a.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1534896968 -32400
#      Wed Aug 22 09:16:08 2018 +0900
# Node ID afe84a051a391724a7d11f07513cf69f2e46b563
# Parent  15909a34b98c7d07d7c7fe1eb2fd4a466a7f41e8
# Available At https://bitbucket.org/foozy/mercurial-wip
#              hg pull https://bitbucket.org/foozy/mercurial-wip -r afe84a051a39
# EXP-Topic use-decorator-for-webcommand
registrar: add classes to wrap existing webcommand

This allows extensions to wrap existing webcommand functions at
loading automatically by annotation.

Wrapping itself is tested in subsequent patch by testing actual
wrapping.

Patch

diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -254,6 +254,12 @@  def loadwrapping(wrapperlist):
 
         if wrapentry.target == registrar._wrapentrybase.TARGET_FUNCTION:
             wrapfunction(container, name, wrapper)
+        elif wrapentry.target == registrar._wrapentrybase.TARGET_DISPATCH:
+            origfn = container[name]
+            assert callable(origfn)
+            wrap = bind(wrapper, origfn)
+            _updatewrapper(wrap, origfn, wrapper)
+            container[name] = wrapentry.extrasetup(origfn, wrap)
         else:
             raise NotImplemented(wrapentry.target)
 
diff --git a/mercurial/registrar.py b/mercurial/registrar.py
--- a/mercurial/registrar.py
+++ b/mercurial/registrar.py
@@ -120,6 +120,7 @@  class _wrapentrybase(object):
     """
     target = None
 
+    TARGET_DISPATCH = 'dispatch'
     TARGET_FUNCTION = 'function'
 
     def __init__(self, name, extname):
@@ -665,3 +666,52 @@  class webcommand(_funcregistrarbase):
     %s""" % (decl, '-' * len(decl), doc)
 
         return doc
+
+class _wrapwebcommandentry(_wrapentrybase):
+    target = _wrapentrybase.TARGET_DISPATCH
+
+    def __init__(self, name, extname=None):
+        super(_wrapwebcommandentry, self).__init__(name, extname)
+
+    def getcontainer(self, extmod):
+        if extmod:
+            registrarobj = getattr(extmod, 'webcommand')
+            return registrarobj._table
+        else:
+            from .hgweb import webcommands
+            return getattr(webcommands, 'commands')
+
+class wrapwebcommand(_wrapdecoratorbase):
+    """Decorator to register webcommand wrapping
+
+    Usage::
+
+        hgwrapperlist = []
+        wrapwebcommand = registrar.wrapwebcommand(hgwrapperlist)
+
+        # wrapping the webcommand 'foo' of Mercurial core
+        @wrapwebcommand('foo')
+        def foowrapper(origfn, *args, **kwargs):
+            pass
+
+        # wrapping the webcommand 'bar' of another extension 'barbar'
+        @wrapwebcommand('bar', 'barbar')
+        def barwrapper(origfn, *args, **kwargs):
+            pass
+
+    Decorated functions are processed to wrap a corresponded webcommand
+    automatically at loading extension, if a list named as
+    'hgwrapperlist' is used to create 'wrapwebcommand' instance.
+
+    Otherwise, explicit 'extensions.loadwrapping()' is needed.
+
+    'hgwrapperlist' can be shared with other decorators for wrapping.
+
+    This decorator only causes wrapping entries in the dispatch table
+    of web commands. If an original web command function is invoked
+    directly, wrapping is ineffective.
+
+    Therefore, simultaneous wrapping original web command function
+    with 'registrar.wrapfunc' decorator might be recommended.
+    """
+    _entrycls = _wrapwebcommandentry