Index: Doc/library/doctest.rst =================================================================== --- Doc/library/doctest.rst (revision 76753) +++ Doc/library/doctest.rst (working copy) @@ -534,10 +534,20 @@ example, an example expecting ``ValueError: 42`` will pass if the actual exception raised is ``ValueError: 3*14``, but will fail, e.g., if :exc:`TypeError` is raised. - - Note that a similar effect can be obtained using :const:`ELLIPSIS`, and - :const:`IGNORE_EXCEPTION_DETAIL` may go away when Python releases prior to 2.4 - become uninteresting. Until then, :const:`IGNORE_EXCEPTION_DETAIL` is the only + + It will also ignore the module name used in Python 3 doctest reports. Hence + both these variations will work: + + >>> raise ValueError('message') #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ValueError: message + + >>> raise ValueError('message') #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + builtin.ValueError: message + + Note that a similar effect can be obtained using :const:`ELLIPSIS`, but + using :const:`IGNORE_EXCEPTION_DETAIL` is often clearer. It is also the only clear way to write a doctest that doesn't care about the exception detail yet continues to pass under Python releases prior to 2.4 (doctest directives appear to be comments to them). For example, :: Index: Lib/doctest.py =================================================================== --- Lib/doctest.py (revision 76753) +++ Lib/doctest.py (working copy) @@ -1276,9 +1276,9 @@ # Another chance if they didn't care about the detail. elif self.optionflags & IGNORE_EXCEPTION_DETAIL: - m1 = re.match(r'[^:]*:', example.exc_msg) - m2 = re.match(r'[^:]*:', exc_msg) - if m1 and m2 and check(m1.group(0), m2.group(0), + m1 = re.match(r'(?:[^:]*\.)?([^:]*:)', example.exc_msg) + m2 = re.match(r'(?:[^:]*\.)?([^:]*:)', exc_msg) + if m1 and m2 and check(m1.group(1), m2.group(1), self.optionflags): outcome = SUCCESS Index: Lib/test/test_doctest.py =================================================================== --- Lib/test/test_doctest.py (revision 76753) +++ Lib/test/test_doctest.py (working copy) @@ -863,7 +863,78 @@ >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) TestResults(failed=0, attempted=1) + +IGNORE_EXCEPTION_DETAIL also ignores difference in exception formatting +between Python versions. For example, in Python 3.x, the module path of +the exception is in the output: + >>> def f(x): + ... r''' + ... >>> from http.client import HTTPException + ... >>> raise HTTPException('message') + ... Traceback (most recent call last): + ... http.client.HTTPException: message + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + TestResults(failed=0, attempted=2) + +But in Python 2 the module path is not included, an therefore a test must look +like the following test to succeed in Python 2. But that test will fail under +Python 3. + + >>> def f(x): + ... r''' + ... >>> from http.client import HTTPException + ... >>> raise HTTPException('message') + ... Traceback (most recent call last): + ... HTTPException: message + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + ... # doctest: +ELLIPSIS + ********************************************************************** + File ..., line 4, in f + Failed example: + raise HTTPException('message') + Expected: + Traceback (most recent call last): + HTTPException: message + Got: + Traceback (most recent call last): + ... + http.client.HTTPException: message + TestResults(failed=1, attempted=2) + +However, with IGNORE_EXCEPTION_DETAIL, the module name of the exception +(if any) will be ignored: + + >>> def f(x): + ... r''' + ... >>> from http.client import HTTPException + ... >>> raise HTTPException('message') #doctest: +IGNORE_EXCEPTION_DETAIL + ... Traceback (most recent call last): + ... HTTPException: message + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + TestResults(failed=0, attempted=2) + +The module path will be completely ignored, so two different module paths will +still pass if IGNORE_EXCEPTION_DETAIL is given. This is intentional, so it can +be used when exceptions have changed module. + + >>> def f(x): + ... r''' + ... >>> from http.client import HTTPException + ... >>> raise HTTPException('message') #doctest: +IGNORE_EXCEPTION_DETAIL + ... Traceback (most recent call last): + ... foo.bar.HTTPException: message + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> doctest.DocTestRunner(verbose=False).run(test) + TestResults(failed=0, attempted=2) + But IGNORE_EXCEPTION_DETAIL does not allow a mismatch in the exception type: >>> def f(x):