Repository: equinor/webviz-config

Issue:
Generated documentation does not support indented docstrings
If the docstring is indented, like
```python
class Markdown(WebvizPluginABC):
    """Renders and includes the content from a Markdown file.

    ---

    * **`markdown_file`:** Path to the markdown file to render and include. \
                       Either absolute path or relative to the configuration file.

    ---

    Images are supported, and should in the markdown file be given as either
    relative paths to the markdown file itself, or as absolute paths.

    > The markdown syntax for images has been extended to support \
      providing width and/or height for individual images (optional). \
      To specify the dimensions write e.g.
    > ```markdown
    > ![width=40%,height=300px](./example_banner.png "Some caption")
    > ```

    """
```
the "special section" with `>` is not rendered correctly.

Some plugins might want to consistently indent docstring lines. Remove all "consistent" indentation in docstring lines before giving it further to the documentation machinery?

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a50fc8c..46eed1e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ## [Unreleased] - YYYY-MM-DD
 ### Added
 - [#269](https://github.com/equinor/webviz-config/pull/269) - Added an optional argument `screenshot_filename` to `WebvizPluginABC`. Can be used to let plugin authors specify filename used when screenshots of the plugin are saved.
+- [#275](https://github.com/equinor/webviz-config/pull/275) - Indented docstrings are now supported by `webviz docs`.
 
 ## [0.1.0] - 2020-08-24
 ### Added
diff --git a/webviz_config/_docs/_build_docs.py b/webviz_config/_docs/_build_docs.py
index 93e3050..541d5ea 100644
--- a/webviz_config/_docs/_build_docs.py
+++ b/webviz_config/_docs/_build_docs.py
@@ -16,7 +16,7 @@ import inspect
 import pathlib
 from importlib import import_module
 from collections import defaultdict
-from typing import Any, Dict, Optional, Tuple
+from typing import Any, Dict, Optional, Tuple, List
 
 import pkg_resources
 import jinja2
@@ -52,7 +52,7 @@ def _document_plugin(plugin: Tuple[str, Any]) -> PluginInfo:
 
     name, reference = plugin
     docstring = reference.__doc__ if reference.__doc__ is not None else ""
-    docstring_parts = docstring.strip().split("\n---\n")
+    docstring_parts = _split_docstring(docstring)
     argspec = inspect.getfullargspec(reference.__init__)
     module = inspect.getmodule(reference)
     subpackage = inspect.getmodule(module).__package__  # type: ignore
@@ -162,3 +162,20 @@ def build_docs(build_directory: pathlib.Path) -> None:
     (build_directory / "sidebar.md").write_text(
         template.render({"packages": plugin_documentation.keys()})
     )
+
+
+def _split_docstring(docstring: str) -> List[str]:
+    """Divides docstring by splitting on ---, also unindents
+    first in case of indented docstrings (similar to this one)
+    """
+    lines = docstring.strip().split("\n")
+
+    try:
+        indent_spaces = min(
+            [len(line) - len(line.lstrip()) for line in lines[1:] if line.strip() != ""]
+        )
+    except ValueError:  # In the case of no original newline (\n)
+        indent_spaces = 0
+
+    unindented_lines = [lines[0]] + [line[indent_spaces:] for line in lines[1:]]
+    return "\n".join(unindented_lines).split("\n---\n")
diff --git a/webviz_config/plugins/_markdown.py b/webviz_config/plugins/_markdown.py
index 7fb0998..efcee81 100644
--- a/webviz_config/plugins/_markdown.py
+++ b/webviz_config/plugins/_markdown.py
@@ -93,12 +93,12 @@ class Markdown(WebvizPluginABC):
     Images are supported, and should in the markdown file be given as either
     relative paths to the markdown file itself, or as absolute paths.
 
-> The markdown syntax for images has been extended to support \
-providing width and/or height for individual images (optional). \
-To specify the dimensions write e.g.
-> ```markdown
-> ![width=40%,height=300px](./example_banner.png "Some caption")
-> ```
+    > The markdown syntax for images has been extended to support \
+    providing width and/or height for individual images (optional). \
+    To specify the dimensions write e.g.
+    > ```markdown
+    > ![width=40%,height=300px](./example_banner.png "Some caption")
+    > ```
 
     """
 


Test patch:
diff --git a/tests/test_docstring.py b/tests/test_docstring.py
new file mode 100644
index 0000000..399bccc
--- /dev/null
+++ b/tests/test_docstring.py
@@ -0,0 +1,76 @@
+from webviz_config._docs._build_docs import _split_docstring
+
+
+def test_split_docstring():
+    tests = [
+        (
+            "This is a test with only description.",
+            ["This is a test with only description."],
+        ),
+        (
+            (
+                " This is a test with only description, "
+                "but which has leading and trailing spaces.  "
+            ),
+            [
+                (
+                    "This is a test with only description, "
+                    "but which has leading and trailing spaces."
+                )
+            ],
+        ),
+        (
+            "Test with some newlines\n\n  \n    and a 4 space indent",
+            ["Test with some newlines\n\n\nand a 4 space indent"],
+        ),
+        (
+            "Test with a \n    4 space and a \n  2 space indent\n",
+            ["Test with a \n  4 space and a \n2 space indent"],
+        ),
+        (
+            "Test with a \n    4 space and a \n  2 space indent\n",
+            ["Test with a \n  4 space and a \n2 space indent"],
+        ),
+        (
+            (
+                " This is a test with description, arguments and data input."
+                "\n\n    ---\n\n    The docstring is indented by 4 spaces:\n    "
+                "* The argument list has a sub list indented by 8 spaces\n        like this."
+                "\n    ---\n    The data input section is not very interesting."
+            ),
+            [
+                "This is a test with description, arguments and data input.\n",
+                (
+                    "\nThe docstring is indented by 4 spaces:\n"
+                    "* The argument list has a sub list indented by 8 spaces\n    like this."
+                ),
+                "The data input section is not very interesting.",
+            ],
+        ),
+        (
+            (
+                "\tThis is a test with description, arguments and data input,"
+                " where indents are made with tabs and not spaces-"
+                "\n\n\t---\n\n\tThe docstring is indented by 1 tab:\n"
+                "\t* The argument list has a sub list indented by 2 tabs\n\t\tlike this."
+                "\n\t---\n\tThe data input section is not very interesting."
+            ),
+            [
+                (
+                    "This is a test with description, arguments and data input,"
+                    " where indents are made with tabs and not spaces-\n"
+                ),
+                (
+                    "\nThe docstring is indented by 1 tab:\n"
+                    "* The argument list has a sub list indented by 2 tabs\n\tlike this."
+                ),
+                "The data input section is not very interesting.",
+            ],
+        ),
+        (
+            "This is a test with only description, which includes a linebreak.\n",
+            ["This is a test with only description, which includes a linebreak."],
+        ),
+    ]
+    for test in tests:
+        assert _split_docstring(test[0]) == test[1]