# HG changeset patch
# User FUJIWARA Katsunori <[email protected]>
# Date 1534511486 -32400
#      Fri Aug 17 22:11:26 2018 +0900
# Node ID 15909a34b98c7d07d7c7fe1eb2fd4a466a7f41e8
# Parent  eafbb78561cfd9d299f72fca659bf4747c4808c6
# Available At https://bitbucket.org/foozy/mercurial-wip
#              hg pull https://bitbucket.org/foozy/mercurial-wip -r 15909a34b98c
# EXP-Topic use-decorator-for-webcommand
registrar: add decorator class to register a function as web command (API)

Using decorator can localize changes for adding (or removing) a web
command function in source code.

This patch also makes extensions._loadextra() directly invoke
loadermod if name of loader function is not specified, in order to
delay importing webcommands, because web server feature is not used in
almost all usecases of 'hg' command.

This change requires that 'webcommand' attribute of (3rd party)
extension is registrar.webcommand or so. This is reason why this patch
is marked with "(API)".

  .. api::

     attribute name 'webcommand' of an extension is reserved to
     load webcommand automatically

diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -389,6 +389,13 @@ def loadall(ui, whitelist=None):
         templatekw,
     )
 
+    # delay importing webcommands, because it implies evaluation of
+    # hgweb/__init__.py, even though web server feature is not used in
+    # almost all client usecases
+    def loadwebcommand(ui, extname, registrarobj):
+        from .hgweb import webcommands
+        webcommands.loadcommand(ui, extname, registrarobj)
+
     # list of (objname, loadermod, loadername) tuple:
     # - objname is the name of an object in extension module,
     #   from which extra information is loaded
@@ -405,6 +412,7 @@ def loadall(ui, whitelist=None):
         ('templatefilter', templatefilters, 'loadfilter'),
         ('templatefunc', templatefuncs, 'loadfunction'),
         ('templatekeyword', templatekw, 'loadkeyword'),
+        ('webcommand', loadwebcommand, None),
     ]
     with util.timedcm() as stats:
         _loadextra(ui, newindex, extraloaders)
@@ -420,7 +428,11 @@ def _loadextra(ui, newindex, extraloader
         for objname, loadermod, loadername in extraloaders:
             extraobj = getattr(module, objname, None)
             if extraobj is not None:
-                getattr(loadermod, loadername)(ui, name, extraobj)
+                if loadername:
+                    loader = getattr(loadermod, loadername)
+                else:
+                    loader = loadermod
+                loader(ui, name, extraobj)
 
 def afterloaded(extension, callback):
     '''Run the specified function after a named extension is loaded.
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -1474,5 +1474,11 @@ def help(web):
         topic=topicname,
         doc=doc)
 
+def loadcommand(ui, extname, registrarobj):
+    """Load web command from specified registrarobj
+    """
+    for name, func in registrarobj._table.iteritems():
+        commands[name] = func
+
 # tell hggettext to extract docstrings from these functions:
 i18nfunctions = commands.values()
diff --git a/mercurial/registrar.py b/mercurial/registrar.py
--- a/mercurial/registrar.py
+++ b/mercurial/registrar.py
@@ -604,3 +604,64 @@ class internalmerge(_funcregistrarbase):
 
         # actual capabilities, which this internal merge tool has
         func.capabilities = {"binary": binarycap, "symlink": symlinkcap}
+
+class webcommand(_funcregistrarbase):
+    """Decorator to register web command
+
+    Usage::
+
+        webcommand = registrar.webcommand()
+
+        @webcommand('/cmd[/additional/path]')
+        def mycommand(web, req, tmpl):
+            '''Explanation of this web command ....
+            '''
+            pass
+
+    The first string argument is used also in online help, if it
+    matches against '/(\w+)' regexp pattern.
+
+    In this case, this string argument and subsequent section title
+    marker in RST syntax are placed at the beginning of doc
+    string. For example, sample code above causes doc string below::
+
+        /cmd[/additional/path]
+        ----------------------
+
+        Explanation of this web command ....
+
+    Specify just a name of your web command to this decorator, and
+    write full doc string of webcommand function manually, if you want
+    to register web command with a path not matching against the
+    regexp above (e.g. including '-' or other non alpha numeric
+    characters).
+
+    'webcommand' instance in example above can be used to
+    decorate multiple functions.
+
+    Decorated functions are registered automatically at loading
+    extension, if an instance named as 'webcommand' is used for
+    decorating in extension.
+
+    Otherwise, explicit 'webcommands.loadcommand()' is needed.
+    """
+    # this regexp is enough for Mercurial itself
+    _urlpat = util.re.compile(r'/(\w+)')
+
+    def _getname(self, decl):
+        matched = self._urlpat.match(decl)
+        if matched:
+            return matched.group(1)
+        else:
+            return decl
+
+    def _formatdoc(self, decl, doc):
+        matched = self._urlpat.match(decl)
+        if matched:
+            return """
+    %s
+    %s
+
+    %s""" % (decl, '-' * len(decl), doc)
+
+        return doc
_______________________________________________
Mercurial-devel mailing list
[email protected]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

Reply via email to