pythonpython-3.xpython-3.8doctest

doctest ignore the front of line after an ellipsis


The docs don't seem to be very clear on how to address the following ...

def test():
    """
    >>> import doctest
    >>> doctest.ELLIPSIS_MARKER = '<ignore>'
    >>> import pandas as pd
    >>> raise pd.errors.InvalidIndexError # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
    Traceback (most recent call last):
     <ignore>
    <ignore>InvalidIndexError
    """

import doctest
doctest.run_docstring_examples(test, globals())

This will work fine but is not addressing the wildcard at the front of <ignore>InvalidIndexError

def test():
    """
    >>> import doctest
    >>> doctest.ELLIPSIS_MARKER = '<ignore>'
    >>> import pandas as pd
    >>> raise pd.errors.InvalidIndexError # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
    Traceback (most recent call last):
     <ignore>
    pandas.errors.InvalidIndexError
    """

import doctest
doctest.run_docstring_examples(test, globals())

Note pandas version is 1.1.3

References


Solution

  • doctest requires exceptions to look a certain way. From the docs:

    Each line of the traceback stack (if present) must be indented further than the first line of the example, or start with a non-alphanumeric character. The first line following the traceback header indented the same and starting with an alphanumeric is taken to be the start of the exception detail.

    (added bold)

    This means if you make the ELLIPSIS_MARKER start with an alphanumeric, it'll work properly. Here's an example using re.error:

    def test():
        """
        >>> import doctest
        >>> doctest.ELLIPSIS_MARKER = 'MODULE.'
        >>> import re
        >>> raise re.error(None) # doctest: +ELLIPSIS
        Traceback (most recent call last):
            ...
        MODULE.error: None
        """
    

    For context, here's an example with no exception that uses two ellipses:

    def test():
        r"""
        >>> print('foo\nbar\nbaz') # doctest: +ELLIPSIS
        foo
        ...
        ...
        """
    

    That said, IGNORE_EXCEPTION_DETAIL may be a better solution. (I just learned about it myself.)

    When specified, an example that expects an exception passes if an exception of the expected type is raised, even if the exception detail does not match. For example, an example expecting ValueError: 42 will pass if the actual exception raised is ValueError: 3*14, but will fail, e.g., if TypeError is raised.

    It will also ignore the module name used in Python 3 doctest reports.

    (added bold)

    For example:

    def test():
        """
        >>> import re
        >>> raise re.error(None) # doctest: +IGNORE_EXCEPTION_DETAIL
        Traceback (most recent call last):
            ...
        error: foobar
        """
    

    Note that both the exception module and exception details are ignored in this example. That's on purpose, to show a side-effect of this solution.


    Or, depending on the actual code/test, it might make more sense to catch the error, say for example:

    def test():
        """
        >>> import re
        >>> try:
        ...     re.compile('*')
        ... except re.error as e:
        ...     print(e)
        nothing to repeat at position 0
        """