Patchwork [8,of,8,v2] registrar: add funcregistrar class to register function for specific purpose

login
register
mail settings
Submitter Eric Sumner
Date Jan. 1, 2016, 6:36 p.m.
Message ID <37d50250b696c6d1f275.1451673408@waste.org>
Download mbox | patch
Permalink /patch/12463/
State Accepted
Headers show

Comments

Eric Sumner - Jan. 1, 2016, 6:36 p.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1451401110 -32400
#      Tue Dec 29 23:58:30 2015 +0900
# Node ID 37d50250b696c6d1f2751f1cb6aae61715ff39ff
# Parent  8f8f3b13252d20143643f6b95a3c8cbf7b4f819a
registrar: add funcregistrar class to register function for specific purpose

This class centralizes the common logic to register function for
specific purpose like below:

    - template keyword, filter and function
    - revset predicate
    - fileset predicate
    - webcommand

'funcregistrar' also formats help document of the function with the
'decl'(aration) specified at the construction.

This can avoid (1) redundancy between 'decl' and help document, and
(2) accidental typo of help document. For example, 'foo' should appear
twice like below, if without such formatting:

    @keyword('foo')
    def foo(....):
        """:foo: Explanation of keyword foo ..."""

Almost all cases needs very simple document formatting like below:

    - "``DECL``\n    EXPLANATION"
    - ":DECL: EXPLANATION"

But webcommand needs a little complicated formatting like:

    /PATH/SPEC
    ----------

    EXPLANATION ....

To make minirst recognize the section header, hyphen line should be as
long as "/PATH/SPEC". It should be arranged by program.

Implementing 'formatdoc()' in derived class can support complicated
formatting in the latter case. But it seems redundant for simple one
in the former case.

Therefore, 'funcregistrar' does:

    - invoke 'self.formatdoc', if it is callable (for the latter case)
    - use it as the format string, otherwise (for the former case)

Patch

diff --git a/mercurial/registrar.py b/mercurial/registrar.py
new file mode 100644
--- /dev/null
+++ b/mercurial/registrar.py
@@ -0,0 +1,87 @@ 
+# registrar.py - utilities to register function for specific purpose
+#
+#  Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+from . import (
+    util,
+)
+
+class funcregistrar(object):
+    """Base of decorator to register a fuction for specific purpose
+
+    The least derived class can be defined by overriding 'table' and
+    'formatdoc', for example::
+
+        symbols = {}
+        class keyword(funcregistrar):
+            table = symbols
+            formatdoc = ":%s: %s"
+
+        @keyword('bar')
+        def barfunc(*args, **kwargs):
+            '''Explanation of bar keyword ....
+            '''
+            pass
+
+    In this case:
+
+    - 'barfunc' is registered as 'bar' in 'symbols'
+    - online help uses ":bar: Explanation of bar keyword"
+    """
+
+    def __init__(self, decl):
+        """'decl' is a name or more descriptive string of a function
+
+        Specification of 'decl' depends on registration purpose.
+        """
+        self.decl = decl
+
+    table = None
+
+    def __call__(self, func):
+        """Execute actual registration for specified function
+        """
+        name = self.getname()
+
+        if func.__doc__ and not util.safehasattr(func, '_origdoc'):
+            doc = func.__doc__.strip()
+            func._origdoc = doc
+            if callable(self.formatdoc):
+                func.__doc__ = self.formatdoc(doc)
+            else:
+                # convenient shortcut for simple format
+                func.__doc__ = self.formatdoc % (self.decl, doc)
+
+        self.table[name] = func
+        self.extraaction(name, func)
+
+        return func
+
+    def getname(self):
+        """Return the name of the registered function from self.decl
+
+        Derived class should override this, if it allows more
+        descriptive 'decl' string than just a name.
+        """
+        return self.decl
+
+    def formatdoc(self, doc):
+        """Return formatted document of the registered function for help
+
+        'doc' is '__doc__.strip()' of the registered function.
+
+        If this is overridden by non-callable object in derived class,
+        such value is treated as "format string" and used to format
+        document by 'self.formatdoc % (self.decl, doc)' for convenience.
+        """
+        raise NotImplementedError()
+
+    def extraaction(self, name, func):
+        """Execute exra action for registered function, if needed
+        """
+        pass