|
3 | 3 | """ |
4 | 4 | import doctest |
5 | 5 | import inspect |
| 6 | +import io |
6 | 7 | import os |
7 | 8 | import re |
8 | 9 | import sys |
9 | 10 | import types |
| 11 | +import unittest |
| 12 | +import warnings |
| 13 | + |
| 14 | + |
| 15 | +warnings.warn( |
| 16 | + 'zope.testing.doctestcase is deprecated and will be removed in a ' |
| 17 | + 'future release. Use plain doctest (e.g. doctest.DocTestSuite or ' |
| 18 | + 'doctest.DocFileSuite) instead.', |
| 19 | + DeprecationWarning, |
| 20 | + stacklevel=2) |
10 | 21 |
|
11 | 22 |
|
12 | 23 | __all__ = [ |
@@ -203,11 +214,38 @@ def name_from_path(path): |
203 | 214 |
|
204 | 215 | def _run_test(self, test, globs, name, path, |
205 | 216 | optionflags, checker, testname='self', lineno=0): |
| 217 | + # This deliberately avoids DocTestCase.run()/.runTest(), which (as of |
| 218 | + # Python 3.15) report each example as a separate subtest of the |
| 219 | + # enclosing unittest result, rather than raising on failure. We want |
| 220 | + # the whole doctest to be run in one go, with any failures raised |
| 221 | + # here so that they become a failure of *this* test. |
206 | 222 | globs.update(getattr(self, 'globs', ())) |
207 | 223 | globs[testname] = self |
208 | 224 | optionflags |= doctest.IGNORE_EXCEPTION_DETAIL |
209 | | - doctest.DocTestCase( |
210 | | - _parser.get_doctest(test, globs, name, path, lineno), |
211 | | - optionflags=optionflags, |
212 | | - checker=checker, |
213 | | - ).runTest() |
| 225 | + if not (optionflags & doctest.REPORTING_FLAGS): |
| 226 | + optionflags |= getattr(doctest, '_unittest_reportflags', 0) |
| 227 | + |
| 228 | + dtest = _parser.get_doctest(test, globs, name, path, lineno) |
| 229 | + runner = doctest.DocTestRunner( |
| 230 | + optionflags=optionflags, checker=checker, verbose=False) |
| 231 | + out = io.StringIO() |
| 232 | + old_stdout = sys.stdout |
| 233 | + try: |
| 234 | + runner.DIVIDER = "-" * 70 |
| 235 | + results = runner.run(dtest, out=out.write, clear_globs=False) |
| 236 | + # TestResults.skipped was only added in Python 3.13. |
| 237 | + if hasattr(results, 'skipped') and ( |
| 238 | + results.skipped == results.attempted): |
| 239 | + raise unittest.SkipTest("all examples were skipped") |
| 240 | + finally: |
| 241 | + sys.stdout = old_stdout |
| 242 | + |
| 243 | + if results.failed: |
| 244 | + lineno = ('unknown line number' if dtest.lineno is None |
| 245 | + else str(dtest.lineno)) |
| 246 | + lname = dtest.name.rsplit('.', 1)[-1] |
| 247 | + raise self.failureException( |
| 248 | + 'Failed doctest test for %s\n' |
| 249 | + ' File "%s", line %s, in %s\n\n%s' |
| 250 | + % (dtest.name, dtest.filename, lineno, lname, |
| 251 | + out.getvalue().rstrip('\n'))) |
0 commit comments