I'm using mkdocs & mkdocstring to build my documentation and including code examples in the docstrings. I'm also using doctest (via pytest --doctest-modules) to test all those examples.
Option 1 - format for documentation
If I format my docstring like this:
"""
Recursively flattens a nested iterable (including strings!) and returns all elements in order left to right.
Examples:
--------
```
>>> [x for x in flatten([1,2,[3,4,[5],6],7,[8,9]])]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
```
"""
Then it renders nicely in the documentation but doctest fails with the error:
Expected:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
```
Got:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
That makes sense as doctest treats everything until a blank line as expected output and aims to match is exactly
Option 2 - format for doctest
If I format the docstring for doctest without code blocks:
"""
Recursively flattens a nested iterable (including strings!) and returns all elements in order left to right.
Examples:
--------
>>> [x for x in flatten([1,2,[3,4,[5],6],7,[8,9]])]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
then doctest passes but the documentation renders
[x for x in flatten([1,2,[3,4,[5],6],7,[8,9]])][1, 2, 3, 4, 5, 6, 7, 8, 9]
Workaround? - add a blank line for doctest
If I format it with an extra blank line before the end of the codeblock:
"""
Recursively flattens a nested iterable (including strings!) and returns all elements in order left to right.
Examples:
--------
```
>>> [x for x in flatten([1,2,[3,4,[5],6],7,[8,9]])]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
```
"""
Then doctest passes but
- there is a blank line at the bottom of the example in the documentation (ugly)
- I need to remember to add a blank line at the end of each example (error prone and annoying)
Does anyone know of a better solution?
Patching the regex that doctest uses to identify codeblocks solved this problem. Documenting it here for those who stumble across this in the future ...
As this is not something I want to do regularly in projects(!), I created pytest-doctest-mkdocstrings as a pytest plugin to do this for me and included some additional sanity-checking, configuration options etc.
For those who are looking here for the answer in code to use yourself, the required change is:
Specifically I have included
(?![ ]*```) # Not end of a code blockin the identification of the "want"