changeset 28238:37d50250b696

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)
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Tue, 29 Dec 2015 23:58:30 +0900
parents 8f8f3b13252d
children fc7c8cac6a4b
files mercurial/registrar.py
diffstat 1 files changed, 87 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
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