chiark / gitweb /
m.sphinx: added a :p: role for parameter referencing.
authorVladimír Vondruš <mosra@centrum.cz>
Wed, 28 Aug 2019 20:31:29 +0000 (22:31 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Fri, 30 Aug 2019 14:47:58 +0000 (16:47 +0200)
So tiny yet so useful. Doxygen, take a note.

doc/plugins/sphinx.rst
documentation/test_python/content/content.html
documentation/test_python/content/docs.rst
plugins/m/sphinx.py

index 5a81819e40dac43449071b87f19f7c0995025668..fbade22f7c6b868236e73a1734c0ed0bc0c3ce2d 100644 (file)
@@ -313,10 +313,17 @@ function signature will cause a warning. Example:
         :param overwrite_existing:  Overwrite existing value if already present
             in the container
         :return:                    The inserted tuple or the existing
-            key/value pair in case ``overwrite_existing`` is not set
+            key/value pair in case :p:`overwrite_existing` is not set
 
         The operation has a :math:`\mathcal{O}(\log{}n)` complexity.
 
+.. block-success::  Referencing function parameters
+
+    What's also shown in the above snippet is the :rst:`:p:` directive. It
+    looks the same as if you would write just :rst:```overwrite_existing```,
+    but in addition it checks the parameter name against current function signature, emitting a warning in case of a mismatch. This is useful to
+    ensure the documentation doesn't get out of sync with the actual signature.
+
 For overloaded functions (such as those coming from pybind11), it's possible to
 specify the full signature to distinguish between particular overloads.
 Directives with the full signature have a priority, if no signature matches
index 47506485776850bed993b0b24a60926532e24fc7..1d8573f81ac11981f422c515c053b3991c16b469 100644 (file)
@@ -213,7 +213,7 @@ or parsed in any way.</p>
                 </tr>
                 <tr>
                   <td>b</td>
-                  <td>The second one</td>
+                  <td>The second one is different from <code>a</code></td>
                 </tr>
                 <tr>
                   <td>c</td>
index 3c10487be34255daf68b78509f3d994c2e56fae8..e9303f126d72bae685436b6d4b33fbf867d88d2d 100644 (file)
 
 .. py:function:: content.param_docs
     :param a: First parameter
-    :param b: The second one
+    :param b: The second one is different from :p:`a`
     :param c: And a ``float``
     :return: String, of course, it's all *stringly* typed
 
     :param a: First
     :param c: Third
 
-    The ``b`` is not documented, while ``c`` isn't in the signature.
+    The :p:`b` is not documented, while :p:`c` isn't in the signature.
 
 .. py:function:: content.full_docstring
     :param a: First parameter
index 49b706e0dc3f9909e60050d709af8edd58359c4b..9d37e215df503b02a840f935050bdadf19c34b43 100755 (executable)
@@ -46,6 +46,7 @@ import m.htmlsanity
 
 # All those initialized in register() or register_mcss()
 current_referer_path = None
+current_param_names = None
 module_doc_output = None
 class_doc_output = None
 enum_doc_output = None
@@ -306,19 +307,31 @@ def ref(name, rawtext, text, lineno, inliner: Inliner, options={}, content=[]):
         node = nodes.literal(rawtext, target, **_options)
     return [node], []
 
-def scope_enter(path, **kwargs):
-    global current_referer_path
+def scope_enter(path, param_names=None, **kwargs):
+    global current_referer_path, current_param_names
     current_referer_path += [path]
+    current_param_names = param_names
 
 def scope_exit(path, **kwargs):
-    global current_referer_path
+    global current_referer_path, current_param_names
     assert current_referer_path[-1] == path, "%s %s" % (current_referer_path, path)
     current_referer_path = current_referer_path[:-1]
+    current_param_names = None
 
 def check_scope_stack_empty(**kwargs):
     global current_referer_path
     assert not current_referer_path
 
+def p(name, rawtext, text, lineno, inliner: Inliner, options={}, content=[]):
+    global current_referer_path, current_param_names
+    if not current_param_names:
+        logging.warning("can't reference parameter %s outside of a function scope", text)
+    elif text not in current_param_names:
+        logging.warning("parameter %s not found in %s(%s) function signature", text, '.'.join(current_referer_path[-1]), ', '.join(current_param_names))
+
+    node = nodes.literal(rawtext, text, **options)
+    return [node], []
+
 def consume_docstring(type, path: List[str], signature: Optional[str], doc: str) -> str:
     # Create the directive header based on type
     if type.name == 'MODULE':
@@ -495,6 +508,7 @@ def register_mcss(mcss_settings, module_doc_contents, class_doc_contents, enum_d
     rst.directives.register_directive('py:data', PyData)
 
     rst.roles.register_local_role('ref', ref)
+    rst.roles.register_local_role('p', p)
 
     hooks_pre_scope += [scope_enter]
     hooks_post_scope += [scope_exit]