Python Doctests optionflags working under Python2 but not Python3

304 views Asked by At

I have a particular Doctest that is working correctly in python 2.7 but not in python 3.4.

"""
Trying to build a cyclic network (should fail):

    >>> buildCyclicNetwork(False)
    Traceback (most recent call last):
        ...
    NetworkConstructionException: Loop in network graph.
"""

if __name__ == "__main__":
    runModuleTestSuite(__import__('__main__'))

The testsuite is compiled here, with the options

def runModuleTestSuite(module):
    """Runs a test suite for all local tests."""
    suite = TestSuite([TestLoader().loadTestsFromModule(module)])

    # Add local doctests
    optionflags = ELLIPSIS | NORMALIZE_WHITESPACE | REPORT_ONLY_FIRST_FAILURE 

    try:
        suite.addTest(DocTestSuite(module, optionflags=optionflags))
    except ValueError:
        # No tests have been found in that module.
        pass

    TextTestRunner().run(suite)

I've tried to use # doctest: +ELLIPSIS in the docstring itself, but this does not solve anything. I'm puzzled as to why this works under 2.x but not 3.x. The particular problem here is an ellipses eliding the path in the traceback. When the test fails it outputs:

Expected:
   Traceback (most recent call last):
      ...
   NetworkConstructionException: Loop in network graph.
Got:
   Traceback (most recent call last):
   File "/usr......complete trace path"
   networks.network.NetworkConstructionException: Loop in network graph.
2

There are 2 answers

2
abarnert On BEST ANSWER

This is covered in the documentation. Although the ELLIPSIS docs don't directly explain it, the next section on IGNORE_EXCEPTION_DETAIL says:

It will also ignore the module name used in Python 3 doctest reports. Hence both of these variations will work with the flag specified, regardless of whether the test is run under Python 2.7 or Python 3.2 (or later versions) …

Note that ELLIPSIS can also be used to ignore the details of the exception message, but such a test may still fail based on whether or not the module details are printed as part of the exception name. Using IGNORE_EXCEPTION_DETAIL and the details from Python 2.3 is also the only clear way to write a doctest that doesn’t care about the exception detail yet continues to pass under Python 2.3 or earlier (those releases do not support doctest directives and ignore them as irrelevant comments).

In other words, the problem is that ... will not skip over the networks.network. part that 3.x tracebacks print out, this is a known limitation in doctest, and there's no way around it short of using IGNORE_EXCEPTION_DETAIL instead.

If all you want to check is the type, this is actually better—it's simpler, more readable, and harder to get wrong. But what if you want to check the exception value, not just the types? There are hacky workarounds to do that (e.g., replace builtins.isinstance with a function that calls an __instancehook__ so you can define a class that's a superclass of only if the value matches), but I don't think those are worth doing. If you really need to distinguish a specific exception, it should probably be a distinct subtype in the first place.

0
Kevin On

You need to enable the IGNORE_EXCEPTION_DETAIL flag. Right now, the exception is failing to match because of the networks.network. garbage at the front of the exception name.