max-sixty / pytest-accept

A pytest plugin for automatically updating doctest outputs
Apache License 2.0
61 stars 3 forks source link

correcting linebreaks: `\n` --> `\\n` #32

Closed aaronspring closed 2 years ago

aaronspring commented 2 years ago

Assume a function with doctest where the return create a linebreak \n:

import xarray as xr

def func(a, b):
    """
    Example:
    >>> import xarray as xr
    >>> func(xr.DataArray([5], dims='a'), xr.DataArray([1], dims='a'))
    """
    c = a + b
    c = c.assign_attrs(a=a, b=b)
    return c

pytest --doctest-modules file.py --accept adds the expected:

import xarray as xr

def func(a, b):
    """
    Example:
    >>> import xarray as xr
    >>> func(xr.DataArray([5], dims='a'), xr.DataArray([1], dims='a'))
    <xarray.DataArray (a: 1)>
    array([6])
    Dimensions without coordinates: a
    Attributes:
        a:        <xarray.DataArray (a: 1)>\narray([5])\nDimensions without coord...
        b:        <xarray.DataArray (a: 1)>\narray([1])\nDimensions without coord...
    """
    c = a + b
    c = c.assign_attrs(a=a, b=b)
    return c

Usually I expect that I can run pytest --doctest-modules file.py --accept and all doctests pass. Now they dont because a single \ is present but docstrings somehow expect \\n. I get an error message:

――――――――――――――――――――――――――――――――――――――――――――――――― ERROR collecting file.py ――――――――――――――――――――――――――――――――――――――――――――――――――
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:939: in find
    self._find(tests, obj, name, module, source_lines, globs, {})
../../anaconda3/envs/climpred-dev/lib/python3.9/site-packages/_pytest/doctest.py:522: in _find
    doctest.DocTestFinder._find(  # type: ignore
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:1001: in _find
    self._find(tests, val, valname, module, source_lines,
../../anaconda3/envs/climpred-dev/lib/python3.9/site-packages/_pytest/doctest.py:522: in _find
    doctest.DocTestFinder._find(  # type: ignore
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:989: in _find
    test = self._get_test(obj, name, module, globs, source_lines)
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:1073: in _get_test
    return self._parser.get_doctest(docstring, globs, name,
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:675: in get_doctest
    return DocTest(self.get_examples(string, name), globs,
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:689: in get_examples
    return [x for x in self.parse(string, name)
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:651: in parse
    self._parse_example(m, name, lineno)
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:720: in _parse_example
    self._check_prefix(want_lines, ' '*indent, name,
../../anaconda3/envs/climpred-dev/lib/python3.9/doctest.py:805: in _check_prefix
    raise ValueError('line %r of the docstring for %s has '
E   ValueError: line 10 of the docstring for tes.func has inconsistent leading whitespace: 'array([5])'

Manually correcting \n to \\n lets tests pass:

import xarray as xr

def func(a, b):
    """
    Example:
    >>> import xarray as xr
    >>> func(xr.DataArray([5], dims='a'), xr.DataArray([1], dims='a'))
    <xarray.DataArray (a: 1)>
    array([6])
    Dimensions without coordinates: a
    Attributes:
        a:        <xarray.DataArray (a: 1)>\\narray([5])\\nDimensions without coord...
        b:        <xarray.DataArray (a: 1)>\\narray([1])\\nDimensions without coord...
    """
    c = a + b
    c = c.assign_attrs(a=a, b=b)
    return c

So would it be possible to add a small correction .replace("\n", "\\n") to pytest-accept? Probably \n is not the only issue here.

max-sixty commented 2 years ago

Thanks @aaronspring . I'll have a look.

One thing I've found works for these is to use r""" for docstrings. Let me see whether that works in this case. Either way, this should be a nicer experience than it currently is

max-sixty commented 2 years ago

FWIW this does fix the immediate problem:

diff --git a/examples/escapes.py b/examples/escapes.py
index 97f7d2f..17f8b9d 100644
--- a/examples/escapes.py
+++ b/examples/escapes.py
@@ -1,5 +1,5 @@
 def func(a, b):
-    """
+    r"""
     Example:
     >>> import xarray as xr
     >>> func(xr.DataArray([5], dims='a'), xr.DataArray([1], dims='a'))

We could at least check for the r / insert an r.

But would you actually prefer that the \n are newlines rather than \n characters? I could imagine that's clearer.

~I'm guessing this behavior is something to do with how we print the repr in xarray, since normally printing \n does give us an actual new line:~

    >>> print("testing\ntesting")
    testing
    testing

Edit: it's that the arrays are atrs so that part is up to xarray, and xarray is probably doing the correct thing for reprs within attrs

aaronspring commented 2 years ago

thanks for the r"""doc strings""" that works

aaronspring commented 2 years ago

But would you actually prefer that the \n are newlines rather than \n characters? I could imagine that's clearer.

I dont have an opinion