handsdown icon indicating copy to clipboard operation
handsdown copied to clipboard

Doctest blocks are not closed

Open djanderson opened this issue 2 years ago • 5 comments

I have a module that works on multiline string input, like as read from a file.

In the Example section of my docstring, I show a contrived "multiline" string using the \n character, e.g. "a\nb".

Handsdown does not escape the \n character and it further causes it not to terminate the code block, which cascades formatting issues down the page.

To Reproduce

  1. Create a file containing
def test(s: str):
    """A pointless function that replaces \n with | in strings.

    Example:
        >>> s = 'this\nthat\nother'
        >>> test(s)
        this|that|other

    """
    pass

  1. Run handsdown on it
  2. Get the following markdown generated:
    ```python
    def test(s: str):
    ```
    
    A pointless function that replaces
    with | in strings.
    
    Example:
    
    ```python
    >>> s = 'this
    ```
    
    that
    other'
    
    ```python
    >>> test(s)
    this|that|other

Notice the final code block is also not closed.

Expected behavior

The \n character in the docstring should be escaped and displayed verbatim in the markdown.

I did notice that if I manually escape the character, i.e., use s = 'this\\nthat\\nother' it does show \n in the markdown, but this is not an acceptable workaround, because this leaves the actual docstring example incorrect. And, it still does appear to cause some formatting issues either way.

Desktop (please complete the following information): MacOS

$ handsdown --version
1.1.0

djanderson avatar Jun 24 '22 05:06 djanderson

This is not a bug in handsdown. The reason you get this output is that Python docstrings are just regular strings. So, in order to get a valid output, you should either:

  • escape \n as \\n. this is probably not what you want
  • use r string
def test(s: str):
    r"""A pointless function that replaces \n with | in strings.

    Example:
        >>> s = 'this\nthat\nother'
        >>> test(s)
        this|that|other

    """
    pass

Please let me know if this solution works for you.

vemel avatar Jun 28 '22 01:06 vemel

Good point, yes using a raw string as the comment would work for me!

However, I am still able to reproduce the parsing issue with the raw string. From my original comment, regarding manually escaping the newlines:

And, it still does appear to cause some formatting issues either way.

I'll try and provide you an example I can share even using raw strings.

djanderson avatar Jun 29 '22 03:06 djanderson

Given the following test setup:

$ tree helper/
helper/
├── __init__.py
└── helper.py

0 directories, 2 files
$ cat helper/__init__.py 
from . import helper
# helper.py

def helper1(s: str):
    r"""A pointless function that replaces \n with | in strings.

    Example:
        >>> s = 'this\nthat\nother'
        >>> helper1(s)
        this|that|other

    """
    return s.replace("\n", "|")


def helper2(s: str):
    r"""Another pointless function that replaces \n with - in strings.

    Example:
        >>> s = 'this\nthat\nother'
        >>> helper2(s)
        this-that-other

    """
    return s.replace("\n", "-")

I run

$ handsdown helper
INFO     Creating folder /private/tmp/docs
$ tree docs
docs
├── MODULES.md
├── README.md
└── helper
    ├── helper.md
    └── index.md

1 directory, 4 files

helper.md contains the following:


    # Helper
    
    > Auto-generated documentation for [helper.helper](../../helper/helper.py) module.
    
    - [Tmp](../README.md#tmp-index) / [Modules](../MODULES.md#tmp-modules) / [Helper](index.md#helper) / Helper
        - [helper1](#helper1)
        - [helper2](#helper2)
    
    ## helper1
    
    [[find in source code]](../../helper/helper.py#L3)
    
    ```python
    def helper1(s: str):
    ```
    
    A pointless function that replaces \n with | in strings.
    
    #### Examples
    
    ```python
    >>> s = 'this\nthat\nother'
    >>> helper1(s)
    this|that|other
    
    ## helper2
    
    [[find in source code]](../../helper/helper.py#L15)
    
    ```python
    def helper2(s: str):
    ```
    
    Another pointless function that replaces \n with - in strings.
    
    #### Examples
    
    ```python
    >>> s = 'this\nthat\nother'
    >>> helper2(s)
    this-that-other

This may be completely unrelated to \n, it may be just that handsdown does not close the ``` of the Example code block if it's the last section of the docstring?

djanderson avatar Jun 29 '22 04:06 djanderson

I see. Doctest blocks are not closed correctly. Thank you for the report!

The fix will be included in the upcoming 2.0.0 release.

vemel avatar Jun 29 '22 04:06 vemel

Finally! It took one month, but Handsdown 2.0 is ready to be used. Tons of new features and Material Design included!

vemel avatar Jul 21 '22 00:07 vemel

Looks great, thanks!

djanderson avatar Aug 27 '22 18:08 djanderson