chiark / gitweb /
documentation/python, m.sphinx: support for external enum value docs.
authorVladimír Vondruš <mosra@centrum.cz>
Fri, 30 Aug 2019 16:26:18 +0000 (18:26 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Fri, 30 Aug 2019 18:37:46 +0000 (20:37 +0200)
17 files changed:
doc/documentation/python.rst
doc/plugins/sphinx.rst
documentation/python.py
documentation/templates/python/details-enum.html
documentation/test_python/content/content.html
documentation/test_python/content/content/__init__.py
documentation/test_python/content/docs.rst
documentation/test_python/content_parse_docstrings/content_parse_docstrings.html
documentation/test_python/content_parse_docstrings/content_parse_docstrings.py
documentation/test_python/inspect_all_property/inspect_all_property.html
documentation/test_python/inspect_string/inspect_string.Foo.html
documentation/test_python/inspect_string/inspect_string.html
documentation/test_python/pybind_enums/docs.rst [new file with mode: 0644]
documentation/test_python/pybind_enums/pybind_enums.cpp
documentation/test_python/pybind_enums/pybind_enums.html
documentation/test_python/test_pybind.py
plugins/m/sphinx.py

index 3b54d6c0e156e4f26ca3fb10391431dd8434fb21..cfba339bd662dead56ee33245792f62c65a715ce 100644 (file)
@@ -780,36 +780,38 @@ plugin might or might not use them.
 
 .. class:: m-table
 
-=========================== ===================================================
-Keyword argument            Content
-=========================== ===================================================
-:py:`mcss_settings`         Dict containing all m.css settings
-:py:`jinja_environment`     Jinja2 environment. Useful for adding new filters
-                            etc.
-:py:`module_doc_contents`   Module documentation contents
-:py:`class_doc_contents`    Class documentation contents
-:py:`enum_doc_contents`     Enum documentation contents
-:py:`function_doc_contents` Function documentation contents
-:py:`property_doc_contents` Property documentation contents
-:py:`data_doc_contents`     Data documentation contents
-:py:`hooks_post_crawl`      Hooks to call after the initial name crawl
-:py:`hooks_scope_enter`     Hooks to call on scope enter
-:py:`hooks_scope_exit`      Hooks to call on scope exit
-:py:`hooks_docstring`       Hooks to call when parsing a docstring
-:py:`hooks_pre_page`        Hooks to call before each page gets rendered
-:py:`hooks_post_run`        Hooks to call at the very end of the script run
-=========================== ===================================================
+=============================== ===============================================
+Keyword argument                Content
+=============================== ===============================================
+:py:`mcss_settings`             Dict containing all m.css settings
+:py:`jinja_environment`         Jinja2 environment. Useful for adding new
+                                filters etc.
+:py:`module_doc_contents`       Module documentation contents
+:py:`class_doc_contents`        Class documentation contents
+:py:`enum_doc_contents`         Enum documentation contents
+:py:`enum_value_doc_contents`   Enum documentation contents
+:py:`function_doc_contents`     Function documentation contents
+:py:`property_doc_contents`     Property documentation contents
+:py:`data_doc_contents`         Data documentation contents
+:py:`hooks_post_crawl`          Hooks to call after the initial name crawl
+:py:`hooks_scope_enter`         Hooks to call on scope enter
+:py:`hooks_scope_exit`          Hooks to call on scope exit
+:py:`hooks_docstring`           Hooks to call when parsing a docstring
+:py:`hooks_pre_page`            Hooks to call before each page gets rendered
+:py:`hooks_post_run`            Hooks to call at the very end of the script run
+=============================== ===============================================
 
-The :py:`module_doc_contents`, :py:`class_doc_contents`,
-:py:`function_doc_contents`, :py:`property_doc_contents` and
-:py:`data_doc_contents` variables are :py:`Dict[str, Dict[str, str]]`, where
-the first level is a name and second level are key/value pairs of the actual
-HTML documentation content. Plugins that parse extra documentation inputs (such
-as `m.sphinx`_) are supposed to add to the dict, which is then used to fill the
-actual documentation contents. The following corresponds to the documentation
-source shown in the `External documentation content`_ section below. Note that
-the dict can already have existing entries added from elsewhere, so it's
-important to avoid fully overwriting it:
+The :py:`module_doc_contents`, :py:`class_doc_contents`, :py:`enum_doc_contents`,
+:py:`enum_value_doc_contents`, :py:`function_doc_contents`,
+:py:`property_doc_contents` and :py:`data_doc_contents` variables are
+:py:`Dict[str, Dict[str, str]]`, where the first level is a name and second
+level are key/value pairs of the actual HTML documentation content. Plugins
+that parse extra documentation inputs (such as `m.sphinx`_) are supposed to add
+to the dict, which is then used to fill the actual documentation contents. The
+following corresponds to the documentation source shown in the
+`External documentation content`_ section below. Note that the dict can already
+have existing entries added from elsewhere, so it's important to avoid fully
+overwriting it:
 
 .. code:: py
 
@@ -1160,7 +1162,7 @@ Property                    Description
 :py:`value.id`              Value ID [5]_
 :py:`value.value`           Value value. Set to :py:`None` if no value is
                             available.
-:py:`value.summary`         Value doc summary
+:py:`value.content`         Value documentation, if any
 =========================== ===================================================
 
 `Function properties`_
index c0cf160601380002b5e0763c736f9126fe3da63f..e5b839db4840865c2133c8dcf7dea7ec2da2e7e1 100644 (file)
@@ -253,15 +253,16 @@ doing the following will ensure it can be easily used:
 ========================================================
 
 In the Python doc theme, the :rst:`.. py:module::`, :rst:`.. py:class::`,
-:rst:`.. py:enum::`, :rst:`.. py:function::`, :rst:`.. py:property::` and
-:rst:`.. py:data::` directives provide a way to supply module, class, enum,
-function / method, property and data documentation content.
+:rst:`.. py:enum::`, :rst:`.. py:enumvalue::`, :rst:`.. py:function::`,
+:rst:`.. py:property::` and :rst:`.. py:data::` directives provide a way to
+supply module, class, enum, function / method, property and data documentation
+content.
 
 Directive option is the name to document, directive contents are the actual
-contents; in addition all the directives have the :py:`:summary:` option that
-can override the docstring extracted using inspection. No restrictions are made
-on the contents, it's also possible to make use of any additional plugins in
-the markup. Example:
+contents; in addition all directives except :rst:`.. py:enumvalue::` have an
+:py:`:summary:` option that can override the docstring extracted using
+inspection. No restrictions are made on the contents, it's also possible to
+make use of any additional plugins in the markup. Example:
 
 .. code:: rst
 
@@ -354,11 +355,30 @@ the :rst:`.. py:data::` / :rst:`.. py:property::` directives `described <#data>`
         Provides a key/value storage with :math:`\mathcal{O}(\log{}n)`-complexity
         access.
 
-`Enums`_
---------
+`Enums and enum values`_
+------------------------
 
-Use :rst:`.. py:enum::` for documenting enums. This directive doesn't support
-any additional options besides :rst:`:summary:`.
+Use :rst:`.. py:enum::` for documenting enums. Values can be documented either
+using the :rst:`.. py:enumvalue::` directive, or in case of short descriptions,
+conveniently directly in the :rst:`.. py:enum::` directive via
+:py:`:value <name>:` options. Example:
+
+.. code:: rst
+
+    .. py:enum:: mymodule.MemoryUsage
+        :summary: Specifies memory usage configuration
+        :value LOW: Optimized for low-memory big-storage devices, such as
+            refrigerators.
+        :value HIGH: The memory usage will make you angry.
+
+    .. py:enumvalue:: mymodule.MemoryUsage.DEFAULT
+
+        Default memory usage. Behavior depends on platform:
+
+        -   On low-memory devices such as refrigerators equivalent to :ref:`LOW`.
+        -   On high-end desktop PCs, this is equivalent to :ref:`HIGH`.
+        -   On laptops, this randomly chooses between the two values based
+            Murphy's law. Enjoy the battery life when you need it the least.
 
 `Functions`_
 ------------
index cd757a067b0c915293c6ff062a08aa90a4378f3e..09de823458c130ceb9e9ab7ff6cde08f7a1a4ba2 100755 (executable)
@@ -179,6 +179,7 @@ class State:
         self.module_docs: Dict[str, Dict[str, str]] = {}
         self.class_docs: Dict[str, Dict[str, str]] = {}
         self.enum_docs: Dict[str, Dict[str, str]] = {}
+        self.enum_value_docs: Dict[str, Dict[str, str]] = {}
         self.function_docs: Dict[str, Dict[str, str]] = {}
         self.property_docs: Dict[str, Dict[str, str]] = {}
         self.data_docs: Dict[str, Dict[str, str]] = {}
@@ -1127,15 +1128,12 @@ def extract_class_doc(state: State, entry: Empty):
     return out
 
 def extract_enum_doc(state: State, entry: Empty):
-    # Call all scope enter hooks first
-    for hook in state.hooks_pre_scope:
-        hook(type=entry.type, path=entry.path)
-
     out = Empty()
     out.name = entry.path[-1]
     out.id = state.config['ID_FORMATTER'](EntryType.ENUM, entry.path[-1:])
     out.values = []
     out.has_value_details = False
+    out.has_details = False
 
     # The happy case
     if issubclass(entry.object, enum.Enum):
@@ -1144,8 +1142,6 @@ def extract_enum_doc(state: State, entry: Empty):
             docstring = ''
         else:
             docstring = entry.object.__doc__
-        out.summary, out.content = extract_docs(state, state.enum_docs, entry.type, entry.path, docstring)
-        out.has_details = bool(out.content)
 
         out.base = extract_type(entry.object.__base__)
         if out.base: out.base_link = make_name_link(state, entry.path, out.base)
@@ -1158,17 +1154,12 @@ def extract_enum_doc(state: State, entry: Empty):
             value.value = html.escape(repr(i.value))
 
             # Value doc gets by default inherited from the enum, that's useless
+            # This gets further processed below.
             if i.__doc__ == entry.object.__doc__:
-                docstring = ''
+                value.content = ''
             else:
-                docstring = i.__doc__
+                value.content = i.__doc__
 
-            # TODO: external summary for enum values
-            value.summary = extract_docs(state, {}, EntryType.ENUM_VALUE, [], docstring, summary_only=True)
-
-            if value.summary:
-                out.has_details = True
-                out.has_value_details = True
             out.values += [value]
 
     # Pybind11 enums are ... different
@@ -1178,9 +1169,9 @@ def extract_enum_doc(state: State, entry: Empty):
         # Pybind 2.4 puts enum value docs inside the docstring. We don't parse
         # that yet and it adds clutter to the output (especially if the values
         # aren't documented), so cut that away
-        # TODO: implement this
-        out.summary, out.content = extract_docs(state, state.enum_docs, entry.type, entry.path, entry.object.__doc__.partition('\n\n')[0])
-        out.has_details = bool(out.content)
+        # TODO: implement this and populate each value.content
+        docstring = entry.object.__doc__.partition('\n\n')[0]
+
         out.base = None
 
         for name, v in entry.object.__members__.items():
@@ -1188,10 +1179,33 @@ def extract_enum_doc(state: State, entry: Empty):
             value. name = name
             value.id = state.config['ID_FORMATTER'](EntryType.ENUM_VALUE, entry.path[-1:] + [name])
             value.value = int(v)
-            # TODO: external summary for enum values
-            value.summary = ''
+            value.content = ''
             out.values += [value]
 
+    # Call all scope enter before rendering the docs
+    for hook in state.hooks_pre_scope:
+        hook(type=entry.type, path=entry.path)
+
+    out.summary, out.content = extract_docs(state, state.enum_docs, entry.type, entry.path, docstring)
+    if out.content: out.has_details = True
+
+    for value in out.values:
+        # Keeping the same scope for the value docs as for the outer scope.
+        # There's no distinction between summary and content for enum
+        # values so put that together in one. The summary is only produced by
+        # the raw docstring parser, the m.sphinx directives always produce only
+        # the content.
+        summary, value.content = extract_docs(state, state.enum_value_docs, EntryType.ENUM_VALUE, entry.path + [value.name], value.content)
+        if summary:
+            value.content = '<p>{}</p>\n{}'.format(summary, value.content).rstrip()
+        if value.content:
+            out.has_details = True
+            out.has_value_details = True
+
+    # Call all scope exit hooks after
+    for hook in state.hooks_post_scope:
+        hook(type=entry.type, path=entry.path)
+
     if not state.config['SEARCH_DISABLED']:
         page_url = state.name_map['.'.join(entry.path[:-1])].url
 
@@ -1210,10 +1224,6 @@ def extract_enum_doc(state: State, entry: Empty):
             result.name = value.name
             state.search += [result]
 
-    # Call all scope exit hooks last
-    for hook in state.hooks_post_scope:
-        hook(type=entry.type, path=entry.path)
-
     return out
 
 def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]:
@@ -2242,6 +2252,7 @@ def run(basedir, config, *, templates=default_templates, search_add_lookahead_ba
             module_doc_contents=state.module_docs,
             class_doc_contents=state.class_docs,
             enum_doc_contents=state.enum_docs,
+            enum_value_doc_contents=state.enum_value_docs,
             function_doc_contents=state.function_docs,
             property_doc_contents=state.property_docs,
             data_doc_contents=state.data_docs,
index 79f21cd41410d7bd8c32d8c7841d36b5fdd7c627..c884ebaf8e9bc0f996e04e69457e0df8d47df50b 100644 (file)
@@ -13,8 +13,8 @@
                 <tr>
                   <td><a href="#{{ value.id }}" id="{{ value.id }}" class="m-doc-self">{{ value.name }}</a></td>
                   <td>
-                  {% if value.summary %}
-                  <p>{{ value.summary }}</p>
+                  {% if value.content %}
+{{ value.content }}
                   {% endif %}
                   </td>
                 </tr>
index 1f98a3e94fcd78db90663d4727c05ca3b6f48a47..dbbf096a52b729747354a1991c692cebb7693506 100644 (file)
@@ -72,7 +72,8 @@ tho.</p>
 add any detailed block.</dd>
             <dt>
               <span class="m-doc-wrap-bumper">class <a href="#EnumWithSummary" class="m-doc">EnumWithSummary</a>(enum.Enum): </span><span class="m-doc-wrap"><a href="#EnumWithSummary-VALUE" class="m-doc">VALUE</a> = 0
-              <a href="#EnumWithSummary-ANOTHER" class="m-doc">ANOTHER</a> = 1</span>
+              <a href="#EnumWithSummary-ANOTHER" class="m-doc">ANOTHER</a> = 1
+              <a href="#EnumWithSummary-THIRD" class="m-doc">THIRD</a> = 3</span>
             </dt>
             <dd>This summary is preserved</dd>
           </dl>
@@ -151,12 +152,22 @@ add any detailed block.</dd>
                 <tr>
                   <td><a href="#EnumWithSummary-VALUE" id="EnumWithSummary-VALUE" class="m-doc-self">VALUE</a></td>
                   <td>
-                  <p>A value</p>
+<p>Value docs where this is treated as summary.</p>
+<p>And this as detailed docs by the raw docstring parser, but the theme doesn&#x27;t
+distinguish between them so they get merged together.</p>
                   </td>
                 </tr>
                 <tr>
                   <td><a href="#EnumWithSummary-ANOTHER" id="EnumWithSummary-ANOTHER" class="m-doc-self">ANOTHER</a></td>
                   <td>
+<p>This value is documented from within the <code>enum</code>
+directive...</p>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="#EnumWithSummary-THIRD" id="EnumWithSummary-THIRD" class="m-doc-self">THIRD</a></td>
+                  <td>
+<p>... while this comes from the <code>enumvalue</code> directive.</p>
                   </td>
                 </tr>
               </tbody>
index b95306e238b141b60f46a8823fd229bce2e44ba4..ace7afb68fd582fea524f838b2297d5ccb682321 100644 (file)
@@ -68,8 +68,13 @@ class EnumWithSummary(enum.Enum):
 
     VALUE = 0
     ANOTHER = 1
+    THIRD = 3
 
-EnumWithSummary.VALUE.__doc__ = "A value"
+EnumWithSummary.VALUE.__doc__ = """Value docs where this is treated as summary.
+
+And this as detailed docs by the raw docstring parser, but the theme doesn't
+distinguish between them so they get merged together.
+"""
 
 def foo(a, b):
     """This summary is not shown either"""
index 23696220782e5180dcbc8084ed2f15724241e20e..7ab0eed2f86837196f24772e5f814c27b9c3c71a 100644 (file)
         add any detailed block.
 
 .. py:enum:: content.EnumWithSummary
+    :value ANOTHER: This value is documented from within the ``enum``
+        directive...
 
     And this is detailed docs added to the docstring summary. :ref:`VALUE`!!
 
+.. py:enumvalue:: content.EnumWithSummary.THIRD
+
+    ... while this comes from the ``enumvalue`` directive.
+
 .. py:function:: content.foo
     :summary: This overwrites the docstring for :ref:`foo()`, but doesn't
         add any detailed block.
index f37b9eaa5f2b3e2ba1ef8a0054b1e067637de42b..a6e895bf260b6d6edb2587a8d7c158740f8669df 100644 (file)
@@ -50,7 +50,8 @@ even from a summary.</dd>
           <h2><a href="#enums">Enums</a></h2>
           <dl class="m-doc">
             <dt>
-              <span class="m-doc-wrap-bumper">class <a href="#Enum" class="m-doc">Enum</a>(enum.Enum): </span><span class="m-doc-wrap"><a href="#Enum-VALUE" class="m-doc">VALUE</a> = 3</span>
+              <span class="m-doc-wrap-bumper">class <a href="#Enum" class="m-doc">Enum</a>(enum.Enum): </span><span class="m-doc-wrap"><a href="#Enum-VALUE" class="m-doc">VALUE</a> = 3
+              <a href="#Enum-ANOTHER" class="m-doc">ANOTHER</a> = 4</span>
             </dt>
             <dd>This enum has a <em>serious</em> docstring. <a class="m-doc" href="content_parse_docstrings.html#Enum-VALUE">VALUE</a> works from a summary.</dd>
           </dl>
@@ -90,12 +91,19 @@ even from a summary.</dd>
                 <tr>
                   <td><a href="#Enum-VALUE" id="Enum-VALUE" class="m-doc-self">VALUE</a></td>
                   <td>
-                  <p>Tho enum value docs are unfortunately *not* processed.</p>
+<p>Enum value docstrings are <em>processed</em> as well.</p>
+<p>The <a class="m-doc" href="content_parse_docstrings.html#Enum-ANOTHER">ANOTHER</a> value is documented from within the <a class="m-doc" href="content_parse_docstrings.html#Enum">Enum</a> itself.</p>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="#Enum-ANOTHER" id="Enum-ANOTHER" class="m-doc-self">ANOTHER</a></td>
+                  <td>
+<p>Values can be documented from a docstring, too.</p>
                   </td>
                 </tr>
               </tbody>
             </table>
-<p>And property <strong>details</strong> as well.</p>
+<p>And enum <strong>details</strong> as well.</p>
           </div></section>
         </section>
         <section>
index 1250a76c3c67b6b75e168f8e91ed983a5ce250d8..c248a4c269094611b971e600e1f7721a09c28670 100644 (file)
@@ -20,11 +20,17 @@ class Class:
 class Enum(enum.Enum):
     """This enum has a *serious* docstring. :ref:`VALUE` works from a summary.
 
-    And property **details** as well."""
+    :value ANOTHER: Values can be documented from a docstring, too.
+
+    And enum **details** as well."""
 
     VALUE = 3
+    ANOTHER = 4
+
+Enum.VALUE.__doc__ = """Enum value docstrings are *processed* as well.
 
-Enum.VALUE.__doc__ = "Tho enum value docs are unfortunately *not* processed."
+The :ref:`ANOTHER` value is documented from within the :ref:`Enum` itself.
+"""
 
 def function(a: str, b: int) -> float:
     """This :ref:`function()` has a *serious* docstring.
index ae97cb2d6f47ebeeb3908fa6797bd4d6c3ef5b88..0692c9030cf8f8b924cde04e5f564e41121c4db8 100644 (file)
                 <tr>
                   <td><a href="#_MyPrivateEnum-VALUE" id="_MyPrivateEnum-VALUE" class="m-doc-self">VALUE</a></td>
                   <td>
-                  <p>A value</p>
+<p>A value</p>
                   </td>
                 </tr>
                 <tr>
                   <td><a href="#_MyPrivateEnum-ANOTHER" id="_MyPrivateEnum-ANOTHER" class="m-doc-self">ANOTHER</a></td>
                   <td>
-                  <p>Another value</p>
+<p>Another value</p>
                   </td>
                 </tr>
                 <tr>
index 0569310b04d4aa2c32b7a2a95da958e27ea3a8be..d3abbd90ab53d6e4d8816feab8059da8418e6dc4 100644 (file)
                 <tr>
                   <td><a href="#InnerEnum-VALUE" id="InnerEnum-VALUE" class="m-doc-self">VALUE</a></td>
                   <td>
-                  <p>A value</p>
+<p>A value</p>
                   </td>
                 </tr>
                 <tr>
                   <td><a href="#InnerEnum-ANOTHER" id="InnerEnum-ANOTHER" class="m-doc-self">ANOTHER</a></td>
                   <td>
-                  <p>Another value</p>
+<p>Another value</p>
                   </td>
                 </tr>
                 <tr>
index 3bd5a79f9f66fdcf764f16c3dff6bdf5e56e64b8..6eca098a98264b60fc04d7416956cfb4c4a772d2 100644 (file)
                 <tr>
                   <td><a href="#MyEnum-VALUE" id="MyEnum-VALUE" class="m-doc-self">VALUE</a></td>
                   <td>
-                  <p>A value</p>
+<p>A value</p>
                   </td>
                 </tr>
                 <tr>
                   <td><a href="#MyEnum-ANOTHER" id="MyEnum-ANOTHER" class="m-doc-self">ANOTHER</a></td>
                   <td>
-                  <p>Another value</p>
+<p>Another value</p>
                   </td>
                 </tr>
                 <tr>
diff --git a/documentation/test_python/pybind_enums/docs.rst b/documentation/test_python/pybind_enums/docs.rst
new file mode 100644 (file)
index 0000000..13724de
--- /dev/null
@@ -0,0 +1,5 @@
+.. py:enum:: pybind_enums.MyEnum
+    :value First: First value external documentation
+    :value Second: Second value external documentation
+
+    External details.
index 1befd5cd35548148cd9c8cd9c0e78b14c8686603..58532001a599a9cf225c60d9ff4cd0371519bab2 100644 (file)
@@ -17,7 +17,7 @@ enum SixtyfourBitFlag: std::uint64_t {
 PYBIND11_MODULE(pybind_enums, m) {
     m.doc() = "pybind11 enum parsing";
 
-    py::enum_<MyEnum>(m, "MyEnum", "An enum without value docs :(")
+    py::enum_<MyEnum>(m, "MyEnum", "An enum with external value docs, at least")
         .value("First", MyEnum::First)
         .value("Second", MyEnum::Second)
         .value("Third", MyEnum::Third)
index e776b896d4840a4de122b234f561ecb8196e908b..a9e25d9854885fecc1332f74d6ff388bb0e3742f 100644 (file)
         <section id="enums">
           <h2><a href="#enums">Enums</a></h2>
           <dl class="m-doc">
-            <dt id="MyEnum">
-              <span class="m-doc-wrap-bumper">class <a href="#MyEnum" class="m-doc-self">MyEnum</a>: </span><span class="m-doc-wrap"><a href="#MyEnum-First" class="m-doc-self" id="MyEnum-First">First</a> = 0
-              <a href="#MyEnum-Second" class="m-doc-self" id="MyEnum-Second">Second</a> = 1
-              <a href="#MyEnum-Third" class="m-doc-self" id="MyEnum-Third">Third</a> = 74
-              <a href="#MyEnum-CONSISTANTE" class="m-doc-self" id="MyEnum-CONSISTANTE">CONSISTANTE</a> = -5</span>
+            <dt>
+              <span class="m-doc-wrap-bumper">class <a href="#MyEnum" class="m-doc">MyEnum</a>: </span><span class="m-doc-wrap"><a href="#MyEnum-First" class="m-doc">First</a> = 0
+              <a href="#MyEnum-Second" class="m-doc">Second</a> = 1
+              <a href="#MyEnum-Third" class="m-doc">Third</a> = 74
+              <a href="#MyEnum-CONSISTANTE" class="m-doc">CONSISTANTE</a> = -5</span>
             </dt>
-            <dd>An enum without value docs :(</dd>
+            <dd>An enum with external value docs, at least</dd>
             <dt id="SixtyfourBitFlag">
               <span class="m-doc-wrap-bumper">class <a href="#SixtyfourBitFlag" class="m-doc-self">SixtyfourBitFlag</a>: </span><span class="m-doc-wrap"><a href="#SixtyfourBitFlag-Yes" class="m-doc-self" id="SixtyfourBitFlag-Yes">Yes</a> = 1000000000000
               <a href="#SixtyfourBitFlag-No" class="m-doc-self" id="SixtyfourBitFlag-No">No</a> = 18446744073709551615</span>
             <dd>64-bit flags</dd>
           </dl>
         </section>
+        <section>
+          <h2>Enum documentation</h2>
+          <section class="m-doc-details" id="MyEnum"><div>
+            <h3>
+              class pybind_enums.<wbr /><a href="#MyEnum" class="m-doc-self">MyEnum</a>()
+            </h3>
+            <p>An enum with external value docs, at least</p>
+            <table class="m-table m-fullwidth m-flat m-doc">
+              <thead><tr><th style="width: 1%">Enumerators</th><th></th></tr></thead>
+              <tbody>
+                <tr>
+                  <td><a href="#MyEnum-First" id="MyEnum-First" class="m-doc-self">First</a></td>
+                  <td>
+<p>First value external documentation</p>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="#MyEnum-Second" id="MyEnum-Second" class="m-doc-self">Second</a></td>
+                  <td>
+<p>Second value external documentation</p>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="#MyEnum-Third" id="MyEnum-Third" class="m-doc-self">Third</a></td>
+                  <td>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="#MyEnum-CONSISTANTE" id="MyEnum-CONSISTANTE" class="m-doc-self">CONSISTANTE</a></td>
+                  <td>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+<p>External details.</p>
+          </div></section>
+        </section>
       </div>
     </div>
   </div>
index b0aa1110ee88c215604a3dee3d213080ccf959d1..1bb622e3e21931f4ccf111d77b61dd15e2a00629 100644 (file)
@@ -231,7 +231,9 @@ class Signatures(BaseInspectTestCase):
 class Enums(BaseInspectTestCase):
     def test(self):
         self.run_python({
-            'PYBIND11_COMPATIBILITY': True
+            'PLUGINS': ['m.sphinx'],
+            'INPUT_DOCS': ['docs.rst'],
+            'PYBIND11_COMPATIBILITY': True,
         })
         self.assertEqual(*self.actual_expected_contents('pybind_enums.html'))
 
index 20418bac379d34f639a41f53e9c555e4bf7e4c70..cc10d7211cf7b98c79dc6effb7f82d5cb136b07d 100755 (executable)
@@ -49,6 +49,7 @@ current_referer_path = None
 current_param_names = None
 module_doc_output = None
 class_doc_output = None
+enum_value_doc_output = None
 enum_doc_output = None
 function_doc_output = None
 property_doc_output = None
@@ -124,14 +125,37 @@ class PyEnum(rst.Directive):
     final_argument_whitespace = True
     has_content = True
     required_arguments = 1
-    option_spec = {'summary': directives.unchanged}
+    option_spec = {'summary': directives.unchanged,
+                   'value': directives_unchanged_list}
 
     def run(self):
+        # Check that values are parsed properly, turn them into a dict. This
+        # will blow up if the param name is not specified.
+        values = {}
+        for name, summary in self.options.get('value', []):
+            if name in values: raise KeyError("duplicate param {}".format(name))
+            values[name] = summary
+
         output = enum_doc_output.setdefault(self.arguments[0], {})
         if self.options.get('summary'):
             output['summary'] = self.options['summary']
         if self.content:
             output['content'] = '\n'.join(self.content)
+
+        for name, content in values.items():
+            enum_value_doc_output.setdefault('{}.{}'.format(self.arguments[0], name), {})['content'] = content
+
+        return []
+
+class PyEnumValue(rst.Directive):
+    final_argument_whitespace = True
+    has_content = True
+    required_arguments = 1
+
+    def run(self):
+        output = enum_value_doc_output.setdefault(self.arguments[0], {})
+        if self.content:
+            output['content'] = '\n'.join(self.content)
         return []
 
 class PyFunction(rst.Directive):
@@ -372,9 +396,12 @@ def consume_docstring(type, path: List[str], signature: Optional[str], doc: str)
     elif type.name == 'CLASS':
         source = '.. py:class:: '
         doc_output = class_doc_output
-    elif type.name == 'ENUM': # TODO: enum values?
+    elif type.name == 'ENUM':
         source = '.. py:enum:: '
         doc_output = enum_doc_output
+    elif type.name == 'ENUM_VALUE':
+        source = '.. py:enumvalue:: '
+        doc_output = enum_value_doc_output
     elif type.name in ['FUNCTION', 'OVERLOADED_FUNCTION']:
         source = '.. py:function:: '
         doc_output = function_doc_output
@@ -391,10 +418,12 @@ def consume_docstring(type, path: List[str], signature: Optional[str], doc: str)
     path_signature_str = '.'.join(path) + (signature if signature else '')
     source += path_signature_str + '\n'
 
-    # Assuming first paragraph is summary, turn it into a :summary: directive
-    # option with successive lines indented
-    summary, _, doc = doc.partition('\n\n')
-    source += '    :summary: {}\n'.format(summary.replace('\n', '\n        '))
+    # Everything except enum values has a separate summary. Assuming first
+    # paragraph is a summary, turn it into a :summary: directive option with
+    # successive lines indented.
+    if type.name != 'ENUM_VALUE':
+        summary, _, doc = doc.partition('\n\n')
+        source += '    :summary: {}\n'.format(summary.replace('\n', '\n        '))
 
     # The next paragraph could be option list. If that's so, indent those as
     # well, append
@@ -513,12 +542,13 @@ def merge_inventories(name_map, **kwargs):
                     f.write(compressor.compress('{} {} 2 {} {}\n'.format(path, type_, url, title).encode('utf-8')))
             f.write(compressor.flush())
 
-def register_mcss(mcss_settings, module_doc_contents, class_doc_contents, enum_doc_contents, function_doc_contents, property_doc_contents, data_doc_contents, hooks_post_crawl, hooks_pre_scope, hooks_post_scope, hooks_docstring, hooks_post_run, **kwargs):
-    global current_referer_path, module_doc_output, class_doc_output, enum_doc_output, function_doc_output, property_doc_output, data_doc_output, inventory_filename
+def register_mcss(mcss_settings, module_doc_contents, class_doc_contents, enum_doc_contents, enum_value_doc_contents, function_doc_contents, property_doc_contents, data_doc_contents, hooks_post_crawl, hooks_pre_scope, hooks_post_scope, hooks_docstring, hooks_post_run, **kwargs):
+    global current_referer_path, module_doc_output, class_doc_output, enum_doc_output, enum_value_doc_output, function_doc_output, property_doc_output, data_doc_output, inventory_filename
     current_referer_path = []
     module_doc_output = module_doc_contents
     class_doc_output = class_doc_contents
     enum_doc_output = enum_doc_contents
+    enum_value_doc_output = enum_value_doc_contents
     function_doc_output = function_doc_contents
     property_doc_output = property_doc_contents
     data_doc_output = data_doc_contents
@@ -530,6 +560,7 @@ def register_mcss(mcss_settings, module_doc_contents, class_doc_contents, enum_d
     rst.directives.register_directive('py:module', PyModule)
     rst.directives.register_directive('py:class', PyClass)
     rst.directives.register_directive('py:enum', PyEnum)
+    rst.directives.register_directive('py:enumvalue', PyEnumValue)
     rst.directives.register_directive('py:function', PyFunction)
     rst.directives.register_directive('py:property', PyProperty)
     rst.directives.register_directive('py:data', PyData)