NiklasRosenstein / pydoc-markdown

Create Python API documentation in Markdown format.
http://niklasrosenstein.github.io/pydoc-markdown/
Other
460 stars 105 forks source link

Google style docstring code block is not rendered correctly #296

Open ShawnHymel opened 1 year ago

ShawnHymel commented 1 year ago

Environment

Describe the bug

Using a Google-style code block retains its indentation and does not render correctly in markdown (VS Code and GitHub rendering).

Simple Python example (in src/module-test.py):

from typing import Union, Optional, Any

def str_test(
    in_str: Any,
    num: int
) -> str:
    """
    Prints something to the screen and adds 'something' to the end.

    Args:
        in_str (Any): any string
        num: Number of times to add 'something' at the end in order to test
            how this darn thing works

    Returns:
        str: String with 'something' added

    Raises:

    Example:

        How to use:

        ```python
        # A comment
        msg = str_test("Hello", 2)
        print(msg)
"""

msg = str(in_str)
for i in range(num):
    msg = f"{msg} something"
return msg

if name == "main": text = str_test("hello", 2) print(text)


Produce markdown with:

```bash
pydoc-markdown -I src/ '{renderer: {type: markdown, escape_html_in_docstring: false}}' > module-test.md

module-test.md has the following:

<a id="module-test"></a>

# module-test

<a id="module-test.str_test"></a>

#### str\_test

```python
def str_test(in_str: Any, num: int) -> str

Prints something to the screen and adds 'something' to the end.

Arguments:

Returns:

Raises:

Example:

How to use:

```python
# A comment
msg = str_test("Hello", 2)
print(msg)
```


The extra indentation on the code block renders the backticks as follows:

![Screenshot 2023-07-07 at 11 46 29 AM](https://github.com/NiklasRosenstein/pydoc-markdown/assets/5232145/06694705-403f-4e82-b771-df668fb29d4e)

I've tried many different combinations of with/without backticks and indentations. It either renders as shown above or does not render the code block at all (treated as markdown text).

Any idea on how to format the Google-style docstring to make the code block render correctly?

**Expected behavior**

Google-style docstring with "Examples:" section should render correctly in markdown.
lraubuch commented 4 months ago

I had a quick solution, which I didn't put as a pull request as it seemed a bit hacky and I didn't check the tests, but for me it worked. I simply added 4 lines to the google processor Hope this helps in resolving the issue.

def _process(self, node: docspec.ApiObject):
        if not node.docstring:
            return

        lines = []
        current_lines: t.List[str] = []
        in_codeblock = False
        keyword = None

        def _commit():
            if keyword:
                generate_sections_markdown(lines, {keyword: current_lines})
            else:
                lines.extend(current_lines)
            current_lines.clear()

        for line in node.docstring.content.split("\n"):
            if line.lstrip().startswith("```"):
                in_codeblock = not in_codeblock
                current_lines.append(line)
                continue

            if in_codeblock:
                current_lines.append(line)
                continue

            line = line.strip()
            if line in self._keywords_map:
                _commit()
                keyword = self._keywords_map[line]
                if keyword == 'Examples' or keyword == 'Example':
                    current_lines.append('```python')
                continue

            if keyword is None:
                lines.append(line)
                continue

            for param_re in self._param_res:
                param_match = param_re.match(line)
                if param_match:
                    if "type" in param_match.groupdict():
                        current_lines.append("- `{param}` _{type}_ - {desc}".format(**param_match.groupdict()))
                    else:
                        current_lines.append("- `{param}` - {desc}".format(**param_match.groupdict()))
                    break

            if not param_match:
                current_lines.append("  {line}".format(line=line))
        if keyword == 'Examples' or keyword == 'Example':
            current_lines.append('```')
        _commit()
        node.docstring.content = "\n".join(lines)