blackdoc icon indicating copy to clipboard operation
blackdoc copied to clipboard

`doctest` command continuation and chained methods

Open Swandog opened this issue 1 year ago • 1 comments

I'm having a problem with a doctest in one of my repos. We have some doctests where methods are chained, and they are written across multiple lines using the "command continuation" format. But I seem to have trouble getting both doctest and blackdoc to work simultaneously.

This example is trivial, but I think shows the problem:

def do_things():
    """
    do things


    Examples
    --------
    do this

    >>> g=[8,2,3]
    >>> sorted(g) \
          .pop()
    8
    """
    pass


def do_things_differently():
    """
    do things


    Examples
    --------
    do this

    >>> g=[8,2,3]
    >>> sorted(g) \\
    ...   .pop()
    8
    """
    pass

def do_other_things():
    """
    do things


    Examples
    --------
    do this

    >>> g=[8,2,3]
    >>> sorted(g) \
    ...   .pop()
    8
    """
    pass

def do_that():
    """
    do things


    Examples
    --------
    do this

    >>> g = [8, 2, 3]
    >>> sorted(g)
          .pop()
    8
    """
    pass

if __name__ == "__main__":
    import doctest

    doctest.testmod()

do_things works as a doctest, but it fails to parse in blackdoc: error: cannot format /private/tmp/testing/test.py: Cannot parse: 12:0: EOF in multi-line statement do_things_differently also works as a doctest, but fails to parse in blackdoc: error: cannot format /private/tmp/testing/test.py: Cannot parse: 28:9: sorted(g) \\ do_other_things parses in blackdoc (and it suggests collapsing lines 44-45 into 1 line), but does not parse as a doctest:

**********************************************************************
File "/private/tmp/testing/test.py", line 44, in __main__.do_other_things
Failed example:
    sorted(g)     ...   .pop()
Exception raised:
    Traceback (most recent call last):
      File "/usr/local/Cellar/[email protected]/3.10.10_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/doctest.py", line 1350, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.do_other_things[1]>", line 1
        sorted(g)     ...   .pop()
                      ^^^
    SyntaxError: invalid syntax
**********************************************************************

do_that parses correctly in blackdoc, but gives the wrong answer in doctest:

**********************************************************************
File "/private/tmp/testing/test.py", line 60, in __main__.do_that
Failed example:
    sorted(g)
Expected:
          .pop()
    8
Got:
    [2, 3, 8]
**********************************************************************
1 items had failures:
   1 of   2 in __main__.do_that
***Test Failed*** 1 failures.

Obviously in this case we could just put everything on one line, but in general in our repo we'd to have it chained, on multiple lines. Is there a way to format this that I'm not seeing?

OS: MacOS 13.2.1 Python: 3.10.10 Blackdoc: 0.3.8

Thanks for your help.

Swandog avatar Mar 15 '23 22:03 Swandog

The reason do_things and do_things_differently work with doctest but not with blackdoc is that doctest executes the file, and python will concatenate any lines with trailing \ with the following line. That's also why do_other_things fail with doctest: the concatenated lines contain the prompt.

blackdoc does not know about \ (because I didn't teach it to), so any example that makes use of that will inevitably fail. I'm open to adding support for that since that's one of the things black will reformat, but it seems a bit tricky to implement and I'm also unlikely to have time for that anytime soon.

For new code I'd use parens: they don't have the issues \ has, and it's also what black will suggest using:

-    >>> g=[8,2,3]
-    >>> (sorted(g)
-    ...   .long_method_name1()
-    ...   .long_method_name2()
-    ...   .long_method_name3()
-    ...   .long_method_name4()
-    ...   .long_method_name5())
+    >>> g = [8, 2, 3]
+    >>> (
+    ...     sorted(g)
+    ...     .long_method_name1()
+    ...     .long_method_name2()
+    ...     .long_method_name3()
+    ...     .long_method_name4()
+    ...     .long_method_name5()
+    ... )
     8
    """

keewis avatar Mar 20 '23 13:03 keewis