chiark / gitweb /
documentation/python: support external docs for all stuff.
authorVladimír Vondruš <mosra@centrum.cz>
Thu, 18 Jul 2019 18:42:44 +0000 (20:42 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Thu, 18 Jul 2019 18:57:37 +0000 (20:57 +0200)
And also detailed docs for enums, functions, properties and data.

19 files changed:
doc/documentation/python.rst
doc/plugins/sphinx.rst
documentation/python.py
documentation/templates/python/class.html
documentation/templates/python/details-data.html [new file with mode: 0644]
documentation/templates/python/details-function.html [new file with mode: 0644]
documentation/templates/python/details-property.html [new file with mode: 0644]
documentation/templates/python/module.html
documentation/test_python/content/classes.html
documentation/test_python/content/content.Class.html
documentation/test_python/content/content.ClassWithSummary.html [moved from documentation/test_python/content/content.AnotherClass.html with 88% similarity]
documentation/test_python/content/content.docstring_summary.html [new file with mode: 0644]
documentation/test_python/content/content.html
documentation/test_python/content/content.py [deleted file]
documentation/test_python/content/content/__init__.py [new file with mode: 0644]
documentation/test_python/content/content/docstring_summary.py [new file with mode: 0644]
documentation/test_python/content/docs.rst
documentation/test_python/test_content.py
plugins/m/sphinx.py

index 540a130321b1c4b98fe53948910ca2480b08a284..3a213c8a3a4f631b11560be0abf1a8f1d792c678 100644 (file)
@@ -663,12 +663,16 @@ Keyword argument            Content
                             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_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` and
+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
@@ -921,6 +925,11 @@ Property                                Description
                                         `Data properties`_ for details.
 :py:`page.has_enum_details`             If there is at least one enum with full
                                         description block [2]_
+:py:`page.has_function_details`         If there is at least one function (or
+                                        method, in case of classes) with full
+                                        description block [2]_
+:py:`page.has_data_details`             If there is at least one data with full
+                                        description block [2]_
 ======================================= =======================================
 
 Each class page, rendered with ``class.html``, has the following additional
@@ -944,6 +953,8 @@ Property                                Description
                                         `Function properties`_ for details.
 :py:`page.properties`                   List of properties. See
                                         `Property properties`_ for details.
+:py:`page.has_property_details`         If there is at least one property with
+                                        full description block [2]_
 ======================================= =======================================
 
 Explicit documentation pages rendered with ``class.html`` have additional
@@ -988,6 +999,7 @@ Property                                Description
 :py:`enum.name`                         Enum name
 :py:`enum.id`                           Enum ID [4]_
 :py:`enum.summary`                      Doc summary
+:py:`enum.content`                      Detailed documentation, if any
 :py:`enum.base`                         Base class from which the enum is
                                         derived. Set to :py:`None` if no base
                                         class information is available.
@@ -1023,6 +1035,7 @@ Property                            Description
 :py:`function.name`                 Function name
 :py:`function.id`                   Function ID [4]_
 :py:`function.summary`              Doc summary
+:py:`function.content`              Detailed documentation, if any
 :py:`function.type`                 Function return type annotation [1]_
 :py:`function.params`               List of function parameters. See below for
                                     details.
@@ -1034,8 +1047,7 @@ Property                            Description
                                     wrapping on multiple lines would only
                                     occupy too much vertical space.
 :py:`function.has_details`          If there is enough content for the full
-                                    description block. Currently always set to
-                                    :py:`False`. [2]_
+                                    description block [2]_
 :py:`function.is_classmethod`       Set to :py:`True` if the function is
                                     annotated with :py:`@classmethod`,
                                     :py:`False` otherwise.
@@ -1076,12 +1088,12 @@ Property                            Description
 :py:`property.id`                   Property ID [4]_
 :py:`property.type`                 Property getter return type annotation [1]_
 :py:`property.summary`              Doc summary
+:py:`property.content`              Detailed documentation, if any
 :py:`property.is_gettable`          If the property is gettable
 :py:`property.is_settable`          If the property is settable
 :py:`property.is_deletable`         If the property is deletable with :py:`del`
 :py:`property.has_details`          If there is enough content for the full
-                                    description block. Currently always set to
-                                    :py:`False`. [2]_
+                                    description block [2]_
 =================================== ===========================================
 
 `Data properties`_
@@ -1096,10 +1108,10 @@ Property                            Description
 :py:`data.id`                       Data ID [4]_
 :py:`data.type`                     Data type
 :py:`data.summary`                  Doc summary
+:py:`data.content`                  Detailed documentation, if any
 :py:`data.value`                    Data value representation
 :py:`data.has_details`              If there is enough content for the full
-                                    description block. Currently always set to
-                                    :py:`False`. [2]_
+                                    description block [2]_
 =================================== ===========================================
 
 `Index page templates`_
index 836ccecbf8a39f2e3b98af80c0c95873abd1f38e..f1f62d1ec1606bb67e3e89f04d4617e2868ad8c5 100644 (file)
@@ -57,15 +57,17 @@ List the plugin in your :py:`PLUGINS`.
 
     PLUGINS += ['m.sphinx']
 
-`Module, class and data docs`_
-==============================
-
-The :rst:`.. py:module::`, :rst:`.. py:class::` and :rst:`.. py:data::`
-directives provide a way to supply module, class and data documentation
-content. Directive option is the name to document, directive contents are
-the actual contents; in addition the :py:`:summary:` option can override the
-docstring extracted using inspection. No restrictions are made on the contents,
-it's possible to make use of any additional plugins in the markup. Example:
+`Module, class, enum, function, property and data docs`_
+========================================================
+
+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. Directive option is the name to
+document, directive contents are the actual contents; in addition the
+:py:`:summary:` option can override the docstring extracted using inspection.
+No restrictions are made on the contents, it's possible to make use of any
+additional plugins in the markup. Example:
 
 .. code:: rst
 
index 6d7987c19c596f8e86a0d48f6fc3016d3daf5a3e..3b8a202dc66c1f9feada3b59e56d06e3e08376f5 100755 (executable)
@@ -175,6 +175,9 @@ class State:
         self.module_mapping: Dict[str, str] = {}
         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.function_docs: Dict[str, Dict[str, str]] = {}
+        self.property_docs: Dict[str, Dict[str, str]] = {}
         self.data_docs: Dict[str, Dict[str, str]] = {}
         self.external_data: Set[str] = set()
 
@@ -706,13 +709,13 @@ def parse_pybind_signature(state: State, referrer_path: List[str], signature: st
         end = original_signature.find('\n')
         logging.warning("cannot parse pybind11 function signature %s", original_signature[:end if end != -1 else None])
         if end != -1 and len(original_signature) > end + 1 and original_signature[end + 1] == '\n':
-            summary = extract_summary(state, {}, [], original_signature[end + 1:])
+            summary = take_first_paragraph(inspect.cleandoc(original_signature[end + 1:]))
         else:
             summary = ''
         return (name, summary, [('…', None, None, None)], None)
 
     if len(signature) > 1 and signature[1] == '\n':
-        summary = extract_summary(state, {}, [], signature[2:])
+        summary = take_first_paragraph(inspect.cleandoc(signature[2:]))
     else:
         summary = ''
 
@@ -763,6 +766,10 @@ def format_value(state: State, referrer_path: List[str], value: str) -> Optional
     else:
         return None
 
+def take_first_paragraph(doc: str) -> str:
+    end = doc.find('\n\n')
+    return doc if end == -1 else doc [:end]
+
 def extract_summary(state: State, external_docs, path: List[str], doc: str) -> str:
     # Prefer external docs, if available
     path_str = '.'.join(path)
@@ -770,9 +777,35 @@ def extract_summary(state: State, external_docs, path: List[str], doc: str) -> s
         return render_inline_rst(state, external_docs[path_str]['summary'])
 
     if not doc: return '' # some modules (xml.etree) have that :(
-    doc = inspect.cleandoc(doc)
-    end = doc.find('\n\n')
-    return html.escape(doc if end == -1 else doc[:end])
+    # TODO: render as rst (config option for that)
+    return html.escape(take_first_paragraph(inspect.cleandoc(doc)))
+
+def extract_docs(state: State, external_docs, path: List[str], doc: str) -> Tuple[str, str]:
+    path_str = '.'.join(path)
+    if path_str in external_docs:
+        external_doc_entry = external_docs[path_str]
+    else:
+        external_doc_entry = None
+
+    # Summary. Prefer external docs, if available
+    if external_doc_entry and external_doc_entry['summary']:
+        summary = render_inline_rst(state, external_doc_entry['summary'])
+    else:
+        # some modules (xml.etree) have None as a docstring :(
+        # TODO: render as rst (config option for that)
+        summary = html.escape(take_first_paragraph(inspect.cleandoc(doc or '')))
+
+    # Content
+    if external_doc_entry and external_doc_entry['content']:
+        content = render_rst(state, external_doc_entry['content'])
+    else:
+        # TODO: extract more than just a summary from the docstring
+        content = None
+
+    # Mark the docs as used (so it can warn about unused docs at the end)
+    if external_doc_entry: external_doc_entry['used'] = True
+
+    return summary, content
 
 def extract_type(type) -> str:
     # For types we concatenate the type name with its module unless it's
@@ -893,17 +926,17 @@ def extract_enum_doc(state: State, entry: Empty):
     out.name = entry.path[-1]
     out.id = state.config['ID_FORMATTER'](EntryType.ENUM, entry.path[-1:])
     out.values = []
-    out.has_details = False
     out.has_value_details = False
 
     # The happy case
     if issubclass(entry.object, enum.Enum):
         # Enum doc is by default set to a generic value. That's useless as well.
         if entry.object.__doc__ == 'An enumeration.':
-            out.summary = ''
+            docstring = ''
         else:
-            # TODO: external summary for enums
-            out.summary = extract_summary(state, {}, [], entry.object.__doc__)
+            docstring = entry.object.__doc__
+        out.summary, out.content = extract_docs(state, state.enum_docs, entry.path, docstring)
+        out.has_details = bool(out.content)
 
         out.base = extract_type(entry.object.__base__)
         if out.base: out.base = make_name_link(state, entry.path, out.base)
@@ -930,8 +963,8 @@ def extract_enum_doc(state: State, entry: Empty):
     elif state.config['PYBIND11_COMPATIBILITY']:
         assert hasattr(entry.object, '__members__')
 
-        # TODO: external summary for enums
-        out.summary = extract_summary(state, {}, [], entry.object.__doc__)
+        out.summary, out.content = extract_docs(state, state.enum_docs, entry.path, entry.object.__doc__)
+        out.has_details = bool(out.content)
         out.base = None
 
         for name, v in entry.object.__members__.items():
@@ -988,9 +1021,8 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]:
             out.name = entry.path[-1]
             out.params = []
             out.has_complex_params = False
-            out.has_details = False
-            # TODO: external summary for functions
-            out.summary = summary
+            out.summary, out.content = extract_docs(state, state.function_docs, entry.path, summary)
+            out.has_details = bool(out.content)
 
             # Don't show None return type for functions w/o a return
             out.type = None if type == 'None' else type
@@ -1099,9 +1131,8 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]:
         out.id = state.config['ID_FORMATTER'](EntryType.FUNCTION, entry.path[-1:])
         out.params = []
         out.has_complex_params = False
-        out.has_details = False
-        # TODO: external summary for functions
-        out.summary = extract_summary(state, {}, [], entry.object.__doc__)
+        out.summary, out.content = extract_docs(state, state.function_docs, entry.path, entry.object.__doc__)
+        out.has_details = bool(out.content)
 
         # Decide if classmethod or staticmethod in case this is a method
         if inspect.isclass(parent):
@@ -1170,16 +1201,15 @@ def extract_property_doc(state: State, parent, entry: Empty):
     # gettable and settable (couldn't find any way to make them *inspectably*
     # readonly, all solutions involved throwing from __setattr__()) and
     # deletable as well (calling del on it seems to simply remove any
-    # previously set value). Unfortunately we can't get any docstring for these
-    # either.
+    # previously set value).
     # TODO: any better way to detect that those are slots?
     if entry.object.__class__.__name__ == 'member_descriptor' and entry.object.__class__.__module__ == 'builtins':
         out.is_gettable = True
         out.is_settable = True
         out.is_deletable = True
-        # TODO: external summary for properties
-        out.summary = ''
-        out.has_details = False
+        # Unfortunately we can't get any docstring for these
+        out.summary, out.content = extract_docs(state, state.property_docs, entry.path, '')
+        out.has_details = bool(out.content)
 
         # First try to get fully dereferenced type hints (with strings
         # converted to actual annotations). If that fails (e.g. because a type
@@ -1205,21 +1235,22 @@ def extract_property_doc(state: State, parent, entry: Empty):
         out.is_gettable = True
         out.is_settable = False
         out.is_deletable = False
-        out.summary = ''
-        out.has_details = False
+        # Unfortunately we can't get any docstring for these
+        out.summary, out.content = extract_docs(state, state.property_docs, entry.path, '')
+        out.has_details = bool(out.content)
         out.type = None
         return out
 
-    # TODO: external summary for properties
     out.is_gettable = entry.object.fget is not None
     if entry.object.fget or (entry.object.fset and entry.object.__doc__):
-        out.summary = extract_summary(state, {}, [], entry.object.__doc__)
+        docstring = entry.object.__doc__
     else:
         assert entry.object.fset
-        out.summary = extract_summary(state, {}, [], entry.object.fset.__doc__)
+        docstring = entry.object.fset.__doc__
+    out.summary, out.content = extract_docs(state, state.property_docs, entry.path, docstring)
     out.is_settable = entry.object.fset is not None
     out.is_deletable = entry.object.fdel is not None
-    out.has_details = False
+    out.has_details = bool(out.content)
 
     # For the type, if the property is gettable, get it from getters's return
     # type. For write-only properties get it from setter's second argument
@@ -1290,8 +1321,8 @@ def extract_data_doc(state: State, parent, entry: Empty):
     out.name = entry.path[-1]
     out.id = state.config['ID_FORMATTER'](EntryType.DATA, entry.path[-1:])
     # Welp. https://stackoverflow.com/questions/8820276/docstring-for-variable
-    out.summary = extract_summary(state, state.data_docs, entry.path, '')
-    out.has_details = False
+    out.summary, out.content = extract_docs(state, state.data_docs, entry.path, '')
+    out.has_details = bool(out.content)
 
     # First try to get fully dereferenced type hints (with strings converted to
     # actual annotations). If that fails (e.g. because a type doesn't exist),
@@ -1346,7 +1377,7 @@ def render_module(state: State, path, module, env):
     for hook in state.hooks_pre_page: hook()
 
     page = Empty()
-    page.summary = extract_summary(state, state.module_docs, path, module.__doc__)
+    page.summary, page.content = extract_docs(state, state.module_docs, path, module.__doc__)
     page.filename = filename
     page.url = url
     page.breadcrumb = breadcrumb
@@ -1357,15 +1388,11 @@ def render_module(state: State, path, module, env):
     page.functions = []
     page.data = []
     page.has_enum_details = False
-
-    # External page content, if provided
-    path_str = '.'.join(path)
-    if path_str in state.module_docs:
-        page.content = render_rst(state, state.module_docs[path_str]['content'])
-        state.module_docs[path_str]['used'] = True
+    page.has_function_details = False
+    page.has_data_details = False
 
     # Find itself in the global map, save the summary back there for index
-    module_entry = state.name_map[path_str]
+    module_entry = state.name_map['.'.join(path)]
     module_entry.summary = page.summary
 
     # Extract docs for all members
@@ -1386,9 +1413,14 @@ def render_module(state: State, path, module, env):
             page.enums += [enum_]
             if enum_.has_details: page.has_enum_details = True
         elif member_entry.type == EntryType.FUNCTION:
-            page.functions += extract_function_doc(state, module, member_entry)
+            functions = extract_function_doc(state, module, member_entry)
+            page.functions += functions
+            for function in functions:
+                if function.has_details: page.has_function_details = True
         elif member_entry.type == EntryType.DATA:
-            page.data += [extract_data_doc(state, module, member_entry)]
+            data = extract_data_doc(state, module, member_entry)
+            page.data += [data]
+            if data.has_details: page.has_data_details = True
         else: # pragma: no cover
             assert False
 
@@ -1420,7 +1452,7 @@ def render_class(state: State, path, class_, env):
     for hook in state.hooks_pre_page: hook()
 
     page = Empty()
-    page.summary = extract_summary(state, state.class_docs, path, class_.__doc__)
+    page.summary, page.content = extract_docs(state, state.class_docs, path, class_.__doc__)
     page.filename = filename
     page.url = url
     page.breadcrumb = breadcrumb
@@ -1434,15 +1466,12 @@ def render_class(state: State, path, class_, env):
     page.properties = []
     page.data = []
     page.has_enum_details = False
-
-    # External page content, if provided
-    path_str = '.'.join(path)
-    if path_str in state.class_docs:
-        page.content = render_rst(state, state.class_docs[path_str]['content'])
-        state.class_docs[path_str]['used'] = True
+    page.has_function_details = False
+    page.has_property_details = False
+    page.has_data_details = False
 
     # Find itself in the global map, save the summary back there for index
-    module_entry = state.name_map[path_str]
+    module_entry = state.name_map['.'.join(path)]
     module_entry.summary = page.summary
 
     # Extract docs for all members
@@ -1471,10 +1500,15 @@ def render_class(state: State, path, class_, env):
                     page.staticmethods += [function]
                 else:
                     page.methods += [function]
+                if function.has_details: page.has_function_details = True
         elif member_entry.type == EntryType.PROPERTY:
-            page.properties += [extract_property_doc(state, class_, member_entry)]
+            property = extract_property_doc(state, class_, member_entry)
+            page.properties += [property]
+            if property.has_details: page.has_property_details = True
         elif member_entry.type == EntryType.DATA:
-            page.data += [extract_data_doc(state, class_, member_entry)]
+            data = extract_data_doc(state, class_, member_entry)
+            page.data += [data]
+            if data.has_details: page.has_data_details = True
         else: # pragma: no cover
             assert False
 
@@ -1754,6 +1788,9 @@ def run(basedir, config, *, templates=default_templates, search_add_lookahead_ba
             jinja_environment=env,
             module_doc_contents=state.module_docs,
             class_doc_contents=state.class_docs,
+            enum_doc_contents=state.enum_docs,
+            function_doc_contents=state.function_docs,
+            property_doc_contents=state.property_docs,
             data_doc_contents=state.data_docs,
             hooks_pre_page=state.hooks_pre_page,
             hooks_post_run=state.hooks_post_run)
@@ -1821,15 +1858,10 @@ def run(basedir, config, *, templates=default_templates, search_add_lookahead_ba
             render_page(state, entry.path, entry.filename, env)
 
     # Warn if there are any unused contents left after processing everything
-    unused_module_docs = [key for key, value in state.module_docs.items() if not 'used' in value]
-    unused_class_docs = [key for key, value in state.class_docs.items() if not 'used' in value]
-    unused_data_docs = [key for key, value in state.data_docs.items() if not 'used' in value]
-    if unused_module_docs:
-        logging.warning("The following module doc contents were unused: %s", unused_module_docs)
-    if unused_class_docs:
-        logging.warning("The following class doc contents were unused: %s", unused_class_docs)
-    if unused_data_docs:
-        logging.warning("The following data doc contents were unused: %s", unused_data_docs)
+    for docs in ['module', 'class', 'enum', 'function', 'property', 'data']:
+        unused_docs = [key for key, value in getattr(state, f'{docs}_docs').items() if not 'used' in value]
+        if unused_docs:
+            logging.warning("The following %s doc contents were unused: %s", docs, unused_docs)
 
     # Create module and class index from the toplevel name list. Recursively go
     # from the top-level index list and gather all class/module children.
index b26078a3350f5c6390e009b8caf1e990b708b0dc..d60904511b8b27fc8b507a1877948fb4034e0207 100644 (file)
@@ -7,6 +7,9 @@
 {% macro entry_data(data) %}{% include 'entry-data.html' %}{% endmacro %}
 
 {% macro details_enum(enum, prefix) %}{% include 'details-enum.html' %}{% endmacro %}
+{% macro details_function(function, prefix) %}{% include 'details-function.html' %}{% endmacro %}
+{% macro details_property(property, prefix) %}{% include 'details-property.html' %}{% endmacro %}
+{% macro details_data(data, prefix) %}{% include 'details-data.html' %}{% endmacro %}
 
 {% block title %}{% set j = joiner('.') %}{% for name, _ in page.breadcrumb %}{{ j() }}{{ name }}{% endfor %} | {{ super() }}{% endblock %}
 
           {% endfor %}
         </section>
         {% endif %}
+        {% if page.has_function_details %}
+        <section>
+          <h2>Method documentation</h2>
+          {% for function in page.classmethods %}
+          {% if function.has_details %}
+{{ details_function(function, page.prefix_wbr) }}
+          {% endif %}
+          {% endfor %}
+          {% for function in page.staticmethods %}
+          {% if function.has_details %}
+{{ details_function(function, page.prefix_wbr) }}
+          {% endif %}
+          {% endfor %}
+          {% for function in page.methods %}
+          {% if function.has_details %}
+{{ details_function(function, page.prefix_wbr) }}
+          {% endif %}
+          {% endfor %}
+          {% for function in page.dunder_methods %}
+          {% if function.has_details %}
+{{ details_function(function, page.prefix_wbr) }}
+          {% endif %}
+          {% endfor %}
+        </section>
+        {% endif %}
+        {% if page.has_property_details %}
+        <section>
+          <h2>Property documentation</h2>
+          {% for property in page.properties %}
+          {% if property.has_details %}
+{{ details_property(property, page.prefix_wbr) }}
+          {% endif %}
+          {% endfor %}
+        </section>
+        {% endif %}
+        {% if page.has_data_details %}
+        <section>
+          <h2>Data documentation</h2>
+          {% for data in page.data %}
+          {% if data.has_details %}
+{{ details_data(data, page.prefix_wbr) }}
+          {% endif %}
+          {% endfor %}
+        </section>
+        {% endif %}
 {% endblock %}
diff --git a/documentation/templates/python/details-data.html b/documentation/templates/python/details-data.html
new file mode 100644 (file)
index 0000000..b386cda
--- /dev/null
@@ -0,0 +1,13 @@
+          <section class="m-doc-details" id="{{ data.id }}"><div>
+            <h3>
+              {{ prefix }}<a href="#{{ data.id }}" class="m-doc-self">{{ data.name }}</a>{% if data.type %}: {{ data.type }}{% endif %}
+              {# the empty line needs to be here to prevent the lines from merging #}
+
+            </h3>
+            {% if data.summary %}
+            <p>{{ data.summary }}</p>
+            {% endif %}
+            {% if data.content %}
+{{ data.content }}
+            {% endif %}
+          </div></section>
diff --git a/documentation/templates/python/details-function.html b/documentation/templates/python/details-function.html
new file mode 100644 (file)
index 0000000..b78ae71
--- /dev/null
@@ -0,0 +1,37 @@
+          <section class="m-doc-details" id="{{ function.id }}"><div>
+            <h3>
+              {% set j = joiner('\n              ' if function.has_complex_params else ' ') %}
+              <span class="m-doc-wrap-bumper">def {{ prefix }}</span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#{{ function.id }}" class="m-doc-self">{{ function.name }}</a>(</span><span class="m-doc-wrap">{% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %},<span class="m-text m-dim"> *,</span>{% else %},{% endif %}{% endif %}{{ j() }}{% if param.kind == 'VAR_POSITIONAL' %}*{% elif param.kind == 'VAR_KEYWORD' %}**{% endif %}{{ param.name }}{% if param.type %}: {{ param.type }}{% endif %}{% if param.default %} = {{ param.default }}{% endif %}{% if param.kind == 'POSITIONAL_ONLY' and (loop.last or function.params[loop.index0 + 1].kind != 'POSITIONAL_ONLY') %}<span class="m-text m-dim">, /</span>{% endif %}{% endfor %}){% if function.type %} -&gt; {{ function.type }}{% endif %}{% if function.is_classmethod %} <span class="m-label m-success">classmethod</span>{% elif function.is_staticmethod %} <span class="m-label m-info">staticmethod</span>{% endif %}</span></span>
+            </h3>
+            {% if function.summary %}
+            <p>{{ function.summary }}</p>
+            {% endif %}
+            {% if function.has_param_details or function.return_value %}
+            <table class="m-table m-fullwidth m-flat">
+              {% if function.has_param_details %}
+              <thead>
+                <tr><th colspan="2">Parameters</th></tr>
+              </thead>
+              <tbody>
+                {% for param in function.params %}
+                <tr>
+                  <td{% if loop.index == 1 %} style="width: 1%"{% endif %}>{{ param.name }}</td>
+                  <td>{{ param.description }}</td>
+                </tr>
+                {% endfor %}
+              </tbody>
+              {% endif %}
+              {% if function.return_value %}
+              <tfoot>
+                <tr>
+                  <th{% if not function.has_param_details %} style="width: 1%"{% endif %}>Returns</th>
+                  <td>{{ function.return_value }}</td>
+                </tr>
+              </tfoot>
+              {% endif %}
+            </table>
+            {% endif %}
+            {% if function.content %}
+{{ function.content }}
+            {% endif %}
+          </div></section>
diff --git a/documentation/templates/python/details-property.html b/documentation/templates/python/details-property.html
new file mode 100644 (file)
index 0000000..5f2159e
--- /dev/null
@@ -0,0 +1,11 @@
+          <section class="m-doc-details" id="{{ property.id }}"><div>
+            <h3>
+              {{ prefix }}<a href="#{{ property.id }}" class="m-doc-self">{{ property.name }}</a>{% if property.type %}: {{ property.type }}{% endif %} <span class="m-label m-flat {% if property.is_gettable and property.is_settable %}m-success{% elif property.is_gettable %}m-warning{% else %}m-danger{% endif %}">{% if property.is_gettable and property.is_settable %}get set{% elif property.is_gettable %}get{% else %}set{% endif %}{% if property.is_deletable %} del{% endif %}</span>
+            </h3>
+            {% if property.summary %}
+            <p>{{ property.summary }}</p>
+            {% endif %}
+            {% if property.content %}
+{{ property.content }}
+            {% endif %}
+          </div></section>
index 9cf77d456d7560efd802d1f349942300b57366cc..41acfd56adc147c8bb1059f171bad18927a543a6 100644 (file)
@@ -7,6 +7,8 @@
 {% macro entry_data(data) %}{% include 'entry-data.html' %}{% endmacro %}
 
 {% macro details_enum(enum, prefix) %}{% include 'details-enum.html' %}{% endmacro %}
+{% macro details_function(function, prefix) %}{% include 'details-function.html' %}{% endmacro %}
+{% macro details_data(data, prefix) %}{% include 'details-data.html' %}{% endmacro %}
 
 {% block title %}{% set j = joiner('.') %}{% for name, _ in page.breadcrumb %}{{ j() }}{{ name }}{% endfor %} | {{ super() }}{% endblock %}
 
           {% endfor %}
         </section>
         {% endif %}
+        {% if page.has_function_details %}
+        <section>
+          <h2>Function documentation</h2>
+          {% for function in page.functions %}
+          {% if function.has_details %}
+{{ details_function(function, page.prefix_wbr) }}
+          {% endif %}
+          {% endfor %}
+        </section>
+        {% endif %}
+        {% if page.has_data_details %}
+        <section>
+          <h2>Data documentation</h2>
+          {% for data in page.data %}
+          {% if data.has_details %}
+{{ details_data(data, page.prefix_wbr) }}
+          {% endif %}
+          {% endfor %}
+        </section>
+        {% endif %}
 {% endblock %}
index ff357b8eeadc01db8206585c3a4929e472045e79..293619c330c95efd392a068c3aed6a68177218a8 100644 (file)
@@ -24,8 +24,9 @@
           <li class="m-doc-collapsible">
             <a href="#" onclick="return toggle(this)">module</a> <a href="content.html" class="m-doc">content</a> <span class="m-doc">This overwrites the docstring for <code>content</code>.</span>
             <ul class="m-doc">
-              <li>class <a href="content.AnotherClass.html" class="m-doc">AnotherClass</a> <span class="m-doc">This class has summary from the docstring</span></li>
+              <li>module <a href="content.docstring_summary.html" class="m-doc">docstring_summary</a> <span class="m-doc">This module retains summary from the docstring</span></li>
               <li>class <a href="content.Class.html" class="m-doc">Class</a> <span class="m-doc">This overwrites the docstring for <code>content.Class</code>.</span></li>
+              <li>class <a href="content.ClassWithSummary.html" class="m-doc">ClassWithSummary</a> <span class="m-doc">This class has summary from the docstring</span></li>
             </ul>
           </li>
         </ul>
index 6643207a7490356c56bac9f369ca2efce0f43457..66eb582286f520eacd74e4a34272fef086f3fa5e 100644 (file)
           <span class="m-breadcrumb"><a href="content.html">content</a>.<wbr/></span>Class <span class="m-thin">class</span>
         </h1>
         <p>This overwrites the docstring for <code>content.Class</code>.</p>
+        <div class="m-block m-default">
+          <h3>Contents</h3>
+          <ul>
+            <li>
+              Reference
+              <ul>
+                <li><a href="#classmethods">Class methods</a></li>
+                <li><a href="#staticmethods">Static methods</a></li>
+                <li><a href="#methods">Methods</a></li>
+                <li><a href="#dunder-methods">Special methods</a></li>
+                <li><a href="#properties">Properties</a></li>
+                <li><a href="#data">Data</a></li>
+              </ul>
+            </li>
+          </ul>
+        </div>
 <p>This is detailed class docs. Here I <em>also</em> hate how it needs to be
 indented.</p>
+        <section id="classmethods">
+          <h2><a href="#classmethods">Class methods</a></h2>
+          <dl class="m-doc">
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#class_method" class="m-doc">class_method</a>(</span><span class="m-doc-wrap">b)</span>
+            </dt>
+            <dd>This function is a class method</dd>
+          </dl>
+        </section>
+        <section id="staticmethods">
+          <h2><a href="#staticmethods">Static methods</a></h2>
+          <dl class="m-doc">
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#static_method" class="m-doc">static_method</a>(</span><span class="m-doc-wrap">cls, a)</span>
+            </dt>
+            <dd>This function is a static method</dd>
+          </dl>
+        </section>
+        <section id="methods">
+          <h2><a href="#methods">Methods</a></h2>
+          <dl class="m-doc">
+            <dt id="method">
+              <span class="m-doc-wrap-bumper">def <a href="#method" class="m-doc-self">method</a>(</span><span class="m-doc-wrap">self)</span>
+            </dt>
+            <dd>This overwrites the docstring for <code>content.Class.method</code>, but
+doesn't add any detailed block.</dd>
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#method_with_details" class="m-doc">method_with_details</a>(</span><span class="m-doc-wrap">self)</span>
+            </dt>
+            <dd></dd>
+          </dl>
+        </section>
+        <section id="dunder-methods">
+          <h2><a href="#dunder-methods">Special methods</a></h2>
+          <dl class="m-doc">
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#__init__" class="m-doc">__init__</a>(</span><span class="m-doc-wrap">self)</span>
+            </dt>
+            <dd>A dunder method</dd>
+          </dl>
+        </section>
+        <section id="properties">
+          <h2><a href="#properties">Properties</a></h2>
+          <dl class="m-doc">
+            <dt id="a_property">
+              <a href="#a_property" class="m-doc-self">a_property</a> <span class="m-label m-flat m-warning">get</span>
+            </dt>
+            <dd>This overwrites the docstring for <code>content.Class.a_property</code>,
+but doesn't add any detailed block.</dd>
+            <dt>
+              <a href="#a_property_with_details" class="m-doc">a_property_with_details</a> <span class="m-label m-flat m-warning">get</span>
+            </dt>
+            <dd>This overwrites the docstring for <code>content.Class.a_property_with_details</code>.</dd>
+            <dt>
+              <a href="#annotated_property" class="m-doc">annotated_property</a>: float <span class="m-label m-flat m-warning">get</span>
+            </dt>
+            <dd>This is an annotated property</dd>
+          </dl>
+        </section>
+        <section id="data">
+          <h2><a href="#data">Data</a></h2>
+          <dl class="m-doc">
+            <dt>
+              <a href="#DATA_WITH_DETAILS" class="m-doc">DATA_WITH_DETAILS</a>: str = &#x27;this blows&#x27;
+            </dt>
+            <dd></dd>
+          </dl>
+        </section>
+        <section>
+          <h2>Method documentation</h2>
+          <section class="m-doc-details" id="class_method"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr />Class.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#class_method" class="m-doc-self">class_method</a>(</span><span class="m-doc-wrap">b) <span class="m-label m-success">classmethod</span></span></span>
+            </h3>
+            <p>This function is a class method</p>
+<p>The <span class="m-label m-success">classmethod</span> should be shown here.</p>
+          </div></section>
+          <section class="m-doc-details" id="static_method"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr />Class.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#static_method" class="m-doc-self">static_method</a>(</span><span class="m-doc-wrap">cls, a) <span class="m-label m-info">staticmethod</span></span></span>
+            </h3>
+            <p>This function is a static method</p>
+<p>The <span class="m-label m-info">staticmethod</span> should be shown here.</p>
+          </div></section>
+          <section class="m-doc-details" id="method_with_details"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr />Class.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#method_with_details" class="m-doc-self">method_with_details</a>(</span><span class="m-doc-wrap">self)</span></span>
+            </h3>
+<p>This one has a detailed block without any summary.</p>
+          </div></section>
+          <section class="m-doc-details" id="__init__"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr />Class.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#__init__" class="m-doc-self">__init__</a>(</span><span class="m-doc-wrap">self)</span></span>
+            </h3>
+            <p>A dunder method</p>
+<p>A dunder method shown in the detailed view.</p>
+          </div></section>
+        </section>
+        <section>
+          <h2>Property documentation</h2>
+          <section class="m-doc-details" id="a_property_with_details"><div>
+            <h3>
+              content.<wbr />Class.<wbr /><a href="#a_property_with_details" class="m-doc-self">a_property_with_details</a> <span class="m-label m-flat m-warning">get</span>
+            </h3>
+            <p>This overwrites the docstring for <code>content.Class.a_property_with_details</code>.</p>
+<p>Detailed property docs.</p>
+          </div></section>
+          <section class="m-doc-details" id="annotated_property"><div>
+            <h3>
+              content.<wbr />Class.<wbr /><a href="#annotated_property" class="m-doc-self">annotated_property</a>: float <span class="m-label m-flat m-warning">get</span>
+            </h3>
+            <p>This is an annotated property</p>
+<p>Annotated property, using summary from the docstring.</p>
+          </div></section>
+        </section>
+        <section>
+          <h2>Data documentation</h2>
+          <section class="m-doc-details" id="DATA_WITH_DETAILS"><div>
+            <h3>
+              content.<wbr />Class.<wbr /><a href="#DATA_WITH_DETAILS" class="m-doc-self">DATA_WITH_DETAILS</a>: str
+            </h3>
+<p>Detailed docs for <code>data</code> in a class to check rendering.</p>
+          </div></section>
+        </section>
       </div>
     </div>
   </div>
similarity index 88%
rename from documentation/test_python/content/content.AnotherClass.html
rename to documentation/test_python/content/content.ClassWithSummary.html
index 1075f475c5fa98771da77812780ffc88a171faba..23d5231aaefe3265bdcf4f69483cd531adad783a 100644 (file)
@@ -2,7 +2,7 @@
 <html lang="en">
 <head>
   <meta charset="UTF-8" />
-  <title>content.AnotherClass | My Python Project</title>
+  <title>content.ClassWithSummary | My Python Project</title>
   <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
   <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -20,7 +20,7 @@
     <div class="m-row">
       <div class="m-col-l-10 m-push-l-1">
         <h1>
-          <span class="m-breadcrumb"><a href="content.html">content</a>.<wbr/></span>AnotherClass <span class="m-thin">class</span>
+          <span class="m-breadcrumb"><a href="content.html">content</a>.<wbr/></span>ClassWithSummary <span class="m-thin">class</span>
         </h1>
         <p>This class has summary from the docstring</p>
 <p>This class has external details but summary from the docstring.</p>
diff --git a/documentation/test_python/content/content.docstring_summary.html b/documentation/test_python/content/content.docstring_summary.html
new file mode 100644 (file)
index 0000000..f9dc838
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>content.docstring_summary | My Python Project</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+  <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Python Project</a>
+    </div>
+  </div>
+</nav></header>
+<main><article>
+  <div class="m-container m-container-inflatable">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <h1>
+          <span class="m-breadcrumb"><a href="content.html">content</a>.<wbr/></span>docstring_summary <span class="m-thin">module</span>
+        </h1>
+        <p>This module retains summary from the docstring</p>
+<p>And adds detailed docs.</p>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
index 7797d68e651fcbfb78e9d5dbbbd4138205ce48e4..6fc4bcc87aa20605ab55401c48860e2035d9bcca 100644 (file)
             <li>
               Reference
               <ul>
+                <li><a href="#packages">Modules</a></li>
                 <li><a href="#classes">Classes</a></li>
+                <li><a href="#enums">Enums</a></li>
+                <li><a href="#functions">Functions</a></li>
                 <li><a href="#data">Data</a></li>
               </ul>
             </li>
         </div>
 <p>This is detailed module docs. I kinda <em>hate</em> how it needs to be indented,
 tho.</p>
+        <section id="namespaces">
+          <h2><a href="#namespaces">Modules</a></h2>
+          <dl class="m-doc">
+            <dt>module <a href="content.docstring_summary.html" class="m-doc">docstring_summary</a></dt>
+            <dd>This module retains summary from the docstring</dd>
+          </dl>
+        </section>
         <section id="classes">
           <h2><a href="#classes">Classes</a></h2>
           <dl class="m-doc">
-            <dt>class <a href="content.AnotherClass.html" class="m-doc">AnotherClass</a></dt>
-            <dd>This class has summary from the docstring</dd>
             <dt>class <a href="content.Class.html" class="m-doc">Class</a></dt>
             <dd>This overwrites the docstring for <code>content.Class</code>.</dd>
+            <dt>class <a href="content.ClassWithSummary.html" class="m-doc">ClassWithSummary</a></dt>
+            <dd>This class has summary from the docstring</dd>
+          </dl>
+        </section>
+        <section id="enums">
+          <h2><a href="#enums">Enums</a></h2>
+          <dl class="m-doc">
+            <dt id="Enum">
+              <span class="m-doc-wrap-bumper">class <a href="#Enum" class="m-doc-self">Enum</a>(enum.Enum): </span><span class="m-doc-wrap"></span>
+            </dt>
+            <dd>This overwrites the docstring for <code>content.Enum</code>, but
+doesn't 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>
+            </dt>
+            <dd>This summary is preserved</dd>
+          </dl>
+        </section>
+        <section id="functions">
+          <h2><a href="#functions">Functions</a></h2>
+          <dl class="m-doc">
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#annotations" class="m-doc">annotations</a>(</span><span class="m-doc-wrap">a: int,
+              b,
+              c: float) -&gt; str</span>
+            </dt>
+            <dd>No annotations shown for this</dd>
+            <dt id="foo">
+              <span class="m-doc-wrap-bumper">def <a href="#foo" class="m-doc-self">foo</a>(</span><span class="m-doc-wrap">a, b)</span>
+            </dt>
+            <dd>This overwrites the docstring for <code>content.foo</code>, but
+doesn't add any detailed block.</dd>
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#foo_with_details" class="m-doc">foo_with_details</a>(</span><span class="m-doc-wrap">a, b)</span>
+            </dt>
+            <dd>This overwrites the docstring for <code>content.foo_with_details()</code>.</dd>
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#function_with_summary" class="m-doc">function_with_summary</a>(</span><span class="m-doc-wrap">)</span>
+            </dt>
+            <dd>This function has summary from the docstring</dd>
           </dl>
         </section>
         <section id="data">
@@ -53,8 +102,84 @@ tho.</p>
               <a href="#CONSTANT" class="m-doc-self">CONSTANT</a>: float = 3.14
             </dt>
             <dd>This is finally a docstring for <code>content.CONSTANT</code></dd>
+            <dt>
+              <a href="#DATA_WITH_DETAILS" class="m-doc">DATA_WITH_DETAILS</a>: str = &#x27;heyoo&#x27;
+            </dt>
+            <dd>This is finally a docstring for <code>content.CONSTANT</code></dd>
+            <dt>
+              <a href="#DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE" class="m-doc">DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE</a> = None
+            </dt>
+            <dd></dd>
           </dl>
         </section>
+        <section>
+          <h2>Enum documentation</h2>
+          <section class="m-doc-details" id="EnumWithSummary"><div>
+            <h3>
+              class content.<wbr /><a href="#EnumWithSummary" class="m-doc-self">EnumWithSummary</a>(enum.Enum)
+            </h3>
+            <p>This summary is preserved</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="#EnumWithSummary-VALUE" id="EnumWithSummary-VALUE" class="m-doc-self">VALUE</a></td>
+                  <td>
+                  <p>A value</p>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="#EnumWithSummary-ANOTHER" id="EnumWithSummary-ANOTHER" class="m-doc-self">ANOTHER</a></td>
+                  <td>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </div></section>
+        </section>
+        <section>
+          <h2>Function documentation</h2>
+          <section class="m-doc-details" id="annotations"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#annotations" class="m-doc-self">annotations</a>(</span><span class="m-doc-wrap">a: int,
+              b,
+              c: float) -&gt; str</span></span>
+            </h3>
+            <p>No annotations shown for this</p>
+<p>Type annotations in detailed docs.</p>
+          </div></section>
+          <section class="m-doc-details" id="foo_with_details"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#foo_with_details" class="m-doc-self">foo_with_details</a>(</span><span class="m-doc-wrap">a, b)</span></span>
+            </h3>
+            <p>This overwrites the docstring for <code>content.foo_with_details()</code>.</p>
+<div class="m-note m-info">
+Detailed docs for this function</div>
+          </div></section>
+          <section class="m-doc-details" id="function_with_summary"><div>
+            <h3>
+              <span class="m-doc-wrap-bumper">def content.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#function_with_summary" class="m-doc-self">function_with_summary</a>(</span><span class="m-doc-wrap">)</span></span>
+            </h3>
+            <p>This function has summary from the docstring</p>
+<p>This function has external details but summary from the docstring.</p>
+          </div></section>
+        </section>
+        <section>
+          <h2>Data documentation</h2>
+          <section class="m-doc-details" id="DATA_WITH_DETAILS"><div>
+            <h3>
+              content.<wbr /><a href="#DATA_WITH_DETAILS" class="m-doc-self">DATA_WITH_DETAILS</a>: str
+            </h3>
+            <p>This is finally a docstring for <code>content.CONSTANT</code></p>
+<p>Detailed docs for the data. <strong>YAY.</strong></p>
+          </div></section>
+          <section class="m-doc-details" id="DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE"><div>
+            <h3>
+              content.<wbr /><a href="#DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE" class="m-doc-self">DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE</a>
+            </h3>
+<p>Why it has to be yelling?!</p>
+          </div></section>
+        </section>
       </div>
     </div>
   </div>
diff --git a/documentation/test_python/content/content.py b/documentation/test_python/content/content.py
deleted file mode 100644 (file)
index 185d93a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-"""Yes so this is module summary, not shown in the output"""
-
-class Class:
-    """And this class summary, not shown either"""
-
-class AnotherClass:
-    """This class has summary from the docstring"""
-
-CONSTANT: float = 3.14
diff --git a/documentation/test_python/content/content/__init__.py b/documentation/test_python/content/content/__init__.py
new file mode 100644 (file)
index 0000000..f48c850
--- /dev/null
@@ -0,0 +1,70 @@
+"""Yes so this is module summary, not shown in the output"""
+
+import enum
+from . import docstring_summary
+
+class Class:
+    """And this class summary, not shown either"""
+
+    @classmethod
+    def class_method(a, b):
+        """This function is a class method"""
+
+    @staticmethod
+    def static_method(cls, a):
+        """This function is a static method"""
+
+    def __init__(self):
+        """A dunder method"""
+
+    def method(self):
+        """This summary will get overriden from the docs"""
+
+    def method_with_details(self):
+        pass
+
+    @property
+    def a_property(self):
+        """This summary is not shown either"""
+
+    @property
+    def a_property_with_details(self):
+        """This summary is not shown either"""
+
+    @property
+    def annotated_property(self) -> float:
+        """This is an annotated property"""
+
+    DATA_WITH_DETAILS: str = 'this blows'
+
+class ClassWithSummary:
+    """This class has summary from the docstring"""
+
+class Enum(enum.Enum):
+    """This summary gets ignored"""
+
+class EnumWithSummary(enum.Enum):
+    """This summary is preserved"""
+
+    VALUE = 0
+    ANOTHER = 1
+
+EnumWithSummary.VALUE.__doc__ = "A value"
+
+def foo(a, b):
+    """This summary is not shown either"""
+
+def foo_with_details(a, b):
+    """This summary is not shown either"""
+
+def function_with_summary():
+    """This function has summary from the docstring"""
+
+def annotations(a: int, b, c: float) -> str:
+    """No annotations shown for this"""
+
+CONSTANT: float = 3.14
+
+DATA_WITH_DETAILS: str = 'heyoo'
+
+DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE = None
diff --git a/documentation/test_python/content/content/docstring_summary.py b/documentation/test_python/content/content/docstring_summary.py
new file mode 100644 (file)
index 0000000..98a7cd0
--- /dev/null
@@ -0,0 +1 @@
+"""This module retains summary from the docstring"""
index 930e64942781f29e2b08d284a249e9505d20986a..bef6a8e802da654d77790552581cacb9265ff7bc 100644 (file)
+.. role:: label-success
+    :class: m-label m-success
+.. role:: label-info
+    :class: m-label m-info
+
 .. py:module:: content
     :summary: This overwrites the docstring for ``content``.
 
     This is detailed module docs. I kinda *hate* how it needs to be indented,
     tho.
 
+.. py:module:: content.docstring_summary
+
+    And adds detailed docs.
+
 .. py:class:: content.Class
     :summary: This overwrites the docstring for ``content.Class``.
 
     This is detailed class docs. Here I *also* hate how it needs to be
     indented.
 
-.. py:class:: content.AnotherClass
+.. py:function:: content.Class.class_method
+
+    The :label-success:`classmethod` should be shown here.
+
+.. py:function:: content.Class.static_method
+
+    The :label-info:`staticmethod` should be shown here.
+
+.. py:function:: content.Class.__init__
+
+    A dunder method shown in the detailed view.
+
+.. py:function:: content.Class.method
+    :summary: This overwrites the docstring for ``content.Class.method``, but
+        doesn't add any detailed block.
+
+.. py:function:: content.Class.method_with_details
+
+    This one has a detailed block without any summary.
+
+.. py:property:: content.Class.a_property
+    :summary: This overwrites the docstring for ``content.Class.a_property``,
+        but doesn't add any detailed block.
+
+.. py:property:: content.Class.a_property_with_details
+    :summary: This overwrites the docstring for ``content.Class.a_property_with_details``.
+
+    Detailed property docs.
+
+.. py:property:: content.Class.annotated_property
+
+    Annotated property, using summary from the docstring.
+
+.. py:data:: content.Class.DATA_WITH_DETAILS
+
+    Detailed docs for ``data`` in a class to check rendering.
+
+.. py:class:: content.ClassWithSummary
 
     This class has external details but summary from the docstring.
 
+.. py:enum:: content.Enum
+    :summary: This overwrites the docstring for ``content.Enum``, but
+        doesn't add any detailed block.
+
+.. py:enum:: content.EnumWithSummary
+
+    And this is detailed docs added to the docstring summary.
+
+.. py:function:: content.foo
+    :summary: This overwrites the docstring for ``content.foo``, but
+        doesn't add any detailed block.
+
+.. py:function:: content.foo_with_details
+    :summary: This overwrites the docstring for ``content.foo_with_details()``.
+
+    .. container:: m-note m-info
+
+        Detailed docs for this function
+
+.. py:function:: content.function_with_summary
+
+    This function has external details but summary from the docstring.
+
+.. py:function:: content.annotations
+
+    Type annotations in detailed docs.
+
 .. py:data:: content.CONSTANT
     :summary: This is finally a docstring for ``content.CONSTANT``
 
+.. py:data:: content.DATA_WITH_DETAILS
+    :summary: This is finally a docstring for ``content.CONSTANT``
+
+    Detailed docs for the data. **YAY.**
+
+.. py:data:: content.DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE
+
+    Why it has to be yelling?!
+
+.. py:function: content.foo
+
+    Details for this function
+
 .. py:module:: thismoduledoesnotexist
     :summary: This docs get unused and produce a warning
 
 .. py:class:: content.ThisDoesNotExist
     :summary: This docs get unused and produce a warning
 
+.. py:enum:: content.ThisEnumDoesNotExist
+    :summary: This docs get unused and produce a warning
+
+.. py:function:: content.this_function_does_not_exist
+    :summary: This docs get unused and produce a warning
+
+.. py:property:: content.Class.this_property_does_not_exist
+    :summary: This docs get unused and produce a warning
+
 .. py:data:: content.THIS_DOES_NOT_EXIST
     :summary: This docs get unused and produce a warning
index 8cafecad00f0429828a87b809352a25744f99f63..5df26cf63177e2e018f7e41f34b7bd282f153d7b 100644 (file)
@@ -34,5 +34,6 @@ class Content(BaseInspectTestCase):
         })
         self.assertEqual(*self.actual_expected_contents('classes.html'))
         self.assertEqual(*self.actual_expected_contents('content.html'))
+        self.assertEqual(*self.actual_expected_contents('content.docstring_summary.html'))
         self.assertEqual(*self.actual_expected_contents('content.Class.html'))
-        self.assertEqual(*self.actual_expected_contents('content.AnotherClass.html'))
+        self.assertEqual(*self.actual_expected_contents('content.ClassWithSummary.html'))
index 99aece3de1b079060d5da4bf69a68566074169f1..cc11538ef454594d2d47c1938e97c1115eb14a10 100644 (file)
@@ -27,6 +27,9 @@ from docutils.parsers.rst import directives
 
 module_doc_output = None
 class_doc_output = None
+enum_doc_output = None
+function_doc_output = None
+property_doc_output = None
 data_doc_output = None
 
 class PyModule(rst.Directive):
@@ -55,6 +58,45 @@ class PyClass(rst.Directive):
         }
         return []
 
+class PyEnum(rst.Directive):
+    final_argument_whitespace = True
+    has_content = True
+    required_arguments = 1
+    option_spec = {'summary': directives.unchanged}
+
+    def run(self):
+        enum_doc_output[self.arguments[0]] = {
+            'summary': self.options.get('summary', ''),
+            'content': '\n'.join(self.content)
+        }
+        return []
+
+class PyFunction(rst.Directive):
+    final_argument_whitespace = True
+    has_content = True
+    required_arguments = 1
+    option_spec = {'summary': directives.unchanged}
+
+    def run(self):
+        function_doc_output[self.arguments[0]] = {
+            'summary': self.options.get('summary', ''),
+            'content': '\n'.join(self.content)
+        }
+        return []
+
+class PyProperty(rst.Directive):
+    final_argument_whitespace = True
+    has_content = True
+    required_arguments = 1
+    option_spec = {'summary': directives.unchanged}
+
+    def run(self):
+        property_doc_output[self.arguments[0]] = {
+            'summary': self.options.get('summary', ''),
+            'content': '\n'.join(self.content)
+        }
+        return []
+
 class PyData(rst.Directive):
     final_argument_whitespace = True
     has_content = True
@@ -68,14 +110,20 @@ class PyData(rst.Directive):
         }
         return []
 
-def register_mcss(module_doc_contents, class_doc_contents, data_doc_contents, **kwargs):
-    global module_doc_output, class_doc_output, data_doc_output
+def register_mcss(module_doc_contents, class_doc_contents, enum_doc_contents, function_doc_contents, property_doc_contents, data_doc_contents, **kwargs):
+    global module_doc_output, class_doc_output, enum_doc_output, function_doc_output, property_doc_output, data_doc_output
     module_doc_output = module_doc_contents
     class_doc_output = class_doc_contents
+    enum_doc_output = enum_doc_contents
+    function_doc_output = function_doc_contents
+    property_doc_output = property_doc_contents
     data_doc_output = data_doc_contents
 
     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:function', PyFunction)
+    rst.directives.register_directive('py:property', PyProperty)
     rst.directives.register_directive('py:data', PyData)
 
 def register(): # for Pelican