Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/_pytest/doctest.py : 3%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""" discover and run doctests in modules and test files."""
2import bdb
3import inspect
4import platform
5import sys
6import traceback
7import types
8import warnings
9from contextlib import contextmanager
10from typing import Any
11from typing import Callable
12from typing import Dict
13from typing import Generator
14from typing import Iterable
15from typing import List
16from typing import Optional
17from typing import Pattern
18from typing import Sequence
19from typing import Tuple
20from typing import Union
22import py.path
24import pytest
25from _pytest import outcomes
26from _pytest._code.code import ExceptionInfo
27from _pytest._code.code import ReprFileLocation
28from _pytest._code.code import TerminalRepr
29from _pytest._io import TerminalWriter
30from _pytest.compat import safe_getattr
31from _pytest.compat import TYPE_CHECKING
32from _pytest.config import Config
33from _pytest.config.argparsing import Parser
34from _pytest.fixtures import FixtureRequest
35from _pytest.outcomes import OutcomeException
36from _pytest.pathlib import import_path
37from _pytest.python_api import approx
38from _pytest.warning_types import PytestWarning
40if TYPE_CHECKING:
41 import doctest
42 from typing import Type
44DOCTEST_REPORT_CHOICE_NONE = "none"
45DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
46DOCTEST_REPORT_CHOICE_NDIFF = "ndiff"
47DOCTEST_REPORT_CHOICE_UDIFF = "udiff"
48DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = "only_first_failure"
50DOCTEST_REPORT_CHOICES = (
51 DOCTEST_REPORT_CHOICE_NONE,
52 DOCTEST_REPORT_CHOICE_CDIFF,
53 DOCTEST_REPORT_CHOICE_NDIFF,
54 DOCTEST_REPORT_CHOICE_UDIFF,
55 DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE,
56)
58# Lazy definition of runner class
59RUNNER_CLASS = None
60# Lazy definition of output checker class
61CHECKER_CLASS = None # type: Optional[Type[doctest.OutputChecker]]
64def pytest_addoption(parser: Parser) -> None:
65 parser.addini(
66 "doctest_optionflags",
67 "option flags for doctests",
68 type="args",
69 default=["ELLIPSIS"],
70 )
71 parser.addini(
72 "doctest_encoding", "encoding used for doctest files", default="utf-8"
73 )
74 group = parser.getgroup("collect")
75 group.addoption(
76 "--doctest-modules",
77 action="store_true",
78 default=False,
79 help="run doctests in all .py modules",
80 dest="doctestmodules",
81 )
82 group.addoption(
83 "--doctest-report",
84 type=str.lower,
85 default="udiff",
86 help="choose another output format for diffs on doctest failure",
87 choices=DOCTEST_REPORT_CHOICES,
88 dest="doctestreport",
89 )
90 group.addoption(
91 "--doctest-glob",
92 action="append",
93 default=[],
94 metavar="pat",
95 help="doctests file matching pattern, default: test*.txt",
96 dest="doctestglob",
97 )
98 group.addoption(
99 "--doctest-ignore-import-errors",
100 action="store_true",
101 default=False,
102 help="ignore doctest ImportErrors",
103 dest="doctest_ignore_import_errors",
104 )
105 group.addoption(
106 "--doctest-continue-on-failure",
107 action="store_true",
108 default=False,
109 help="for a given doctest, continue to run after the first failure",
110 dest="doctest_continue_on_failure",
111 )
114def pytest_unconfigure() -> None:
115 global RUNNER_CLASS
117 RUNNER_CLASS = None
120def pytest_collect_file(
121 path: py.path.local, parent
122) -> Optional[Union["DoctestModule", "DoctestTextfile"]]:
123 config = parent.config
124 if path.ext == ".py":
125 if config.option.doctestmodules and not _is_setup_py(path):
126 mod = DoctestModule.from_parent(parent, fspath=path) # type: DoctestModule
127 return mod
128 elif _is_doctest(config, path, parent):
129 txt = DoctestTextfile.from_parent(parent, fspath=path) # type: DoctestTextfile
130 return txt
131 return None
134def _is_setup_py(path: py.path.local) -> bool:
135 if path.basename != "setup.py":
136 return False
137 contents = path.read_binary()
138 return b"setuptools" in contents or b"distutils" in contents
141def _is_doctest(config: Config, path: py.path.local, parent) -> bool:
142 if path.ext in (".txt", ".rst") and parent.session.isinitpath(path):
143 return True
144 globs = config.getoption("doctestglob") or ["test*.txt"]
145 for glob in globs:
146 if path.check(fnmatch=glob):
147 return True
148 return False
151class ReprFailDoctest(TerminalRepr):
152 def __init__(
153 self, reprlocation_lines: Sequence[Tuple[ReprFileLocation, Sequence[str]]]
154 ) -> None:
155 self.reprlocation_lines = reprlocation_lines
157 def toterminal(self, tw: TerminalWriter) -> None:
158 for reprlocation, lines in self.reprlocation_lines:
159 for line in lines:
160 tw.line(line)
161 reprlocation.toterminal(tw)
164class MultipleDoctestFailures(Exception):
165 def __init__(self, failures: "Sequence[doctest.DocTestFailure]") -> None:
166 super().__init__()
167 self.failures = failures
170def _init_runner_class() -> "Type[doctest.DocTestRunner]":
171 import doctest
173 class PytestDoctestRunner(doctest.DebugRunner):
174 """
175 Runner to collect failures. Note that the out variable in this case is
176 a list instead of a stdout-like object
177 """
179 def __init__(
180 self,
181 checker: Optional[doctest.OutputChecker] = None,
182 verbose: Optional[bool] = None,
183 optionflags: int = 0,
184 continue_on_failure: bool = True,
185 ) -> None:
186 doctest.DebugRunner.__init__(
187 self, checker=checker, verbose=verbose, optionflags=optionflags
188 )
189 self.continue_on_failure = continue_on_failure
191 def report_failure(
192 self, out, test: "doctest.DocTest", example: "doctest.Example", got: str,
193 ) -> None:
194 failure = doctest.DocTestFailure(test, example, got)
195 if self.continue_on_failure:
196 out.append(failure)
197 else:
198 raise failure
200 def report_unexpected_exception(
201 self,
202 out,
203 test: "doctest.DocTest",
204 example: "doctest.Example",
205 exc_info: "Tuple[Type[BaseException], BaseException, types.TracebackType]",
206 ) -> None:
207 if isinstance(exc_info[1], OutcomeException):
208 raise exc_info[1]
209 if isinstance(exc_info[1], bdb.BdbQuit):
210 outcomes.exit("Quitting debugger")
211 failure = doctest.UnexpectedException(test, example, exc_info)
212 if self.continue_on_failure:
213 out.append(failure)
214 else:
215 raise failure
217 return PytestDoctestRunner
220def _get_runner(
221 checker: Optional["doctest.OutputChecker"] = None,
222 verbose: Optional[bool] = None,
223 optionflags: int = 0,
224 continue_on_failure: bool = True,
225) -> "doctest.DocTestRunner":
226 # We need this in order to do a lazy import on doctest
227 global RUNNER_CLASS
228 if RUNNER_CLASS is None:
229 RUNNER_CLASS = _init_runner_class()
230 # Type ignored because the continue_on_failure argument is only defined on
231 # PytestDoctestRunner, which is lazily defined so can't be used as a type.
232 return RUNNER_CLASS( # type: ignore
233 checker=checker,
234 verbose=verbose,
235 optionflags=optionflags,
236 continue_on_failure=continue_on_failure,
237 )
240class DoctestItem(pytest.Item):
241 def __init__(
242 self,
243 name: str,
244 parent: "Union[DoctestTextfile, DoctestModule]",
245 runner: Optional["doctest.DocTestRunner"] = None,
246 dtest: Optional["doctest.DocTest"] = None,
247 ) -> None:
248 super().__init__(name, parent)
249 self.runner = runner
250 self.dtest = dtest
251 self.obj = None
252 self.fixture_request = None # type: Optional[FixtureRequest]
254 @classmethod
255 def from_parent( # type: ignore
256 cls,
257 parent: "Union[DoctestTextfile, DoctestModule]",
258 *,
259 name: str,
260 runner: "doctest.DocTestRunner",
261 dtest: "doctest.DocTest"
262 ):
263 # incompatible signature due to to imposed limits on sublcass
264 """
265 the public named constructor
266 """
267 return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest)
269 def setup(self) -> None:
270 if self.dtest is not None:
271 self.fixture_request = _setup_fixtures(self)
272 globs = dict(getfixture=self.fixture_request.getfixturevalue)
273 for name, value in self.fixture_request.getfixturevalue(
274 "doctest_namespace"
275 ).items():
276 globs[name] = value
277 self.dtest.globs.update(globs)
279 def runtest(self) -> None:
280 assert self.dtest is not None
281 assert self.runner is not None
282 _check_all_skipped(self.dtest)
283 self._disable_output_capturing_for_darwin()
284 failures = [] # type: List[doctest.DocTestFailure]
285 # Type ignored because we change the type of `out` from what
286 # doctest expects.
287 self.runner.run(self.dtest, out=failures) # type: ignore[arg-type]
288 if failures:
289 raise MultipleDoctestFailures(failures)
291 def _disable_output_capturing_for_darwin(self) -> None:
292 """
293 Disable output capturing. Otherwise, stdout is lost to doctest (#985)
294 """
295 if platform.system() != "Darwin":
296 return
297 capman = self.config.pluginmanager.getplugin("capturemanager")
298 if capman:
299 capman.suspend_global_capture(in_=True)
300 out, err = capman.read_global_capture()
301 sys.stdout.write(out)
302 sys.stderr.write(err)
304 # TODO: Type ignored -- breaks Liskov Substitution.
305 def repr_failure( # type: ignore[override]
306 self, excinfo: ExceptionInfo[BaseException],
307 ) -> Union[str, TerminalRepr]:
308 import doctest
310 failures = (
311 None
312 ) # type: Optional[Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]]]
313 if isinstance(
314 excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException)
315 ):
316 failures = [excinfo.value]
317 elif isinstance(excinfo.value, MultipleDoctestFailures):
318 failures = excinfo.value.failures
320 if failures is not None:
321 reprlocation_lines = []
322 for failure in failures:
323 example = failure.example
324 test = failure.test
325 filename = test.filename
326 if test.lineno is None:
327 lineno = None
328 else:
329 lineno = test.lineno + example.lineno + 1
330 message = type(failure).__name__
331 # TODO: ReprFileLocation doesn't expect a None lineno.
332 reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type]
333 checker = _get_checker()
334 report_choice = _get_report_choice(
335 self.config.getoption("doctestreport")
336 )
337 if lineno is not None:
338 assert failure.test.docstring is not None
339 lines = failure.test.docstring.splitlines(False)
340 # add line numbers to the left of the error message
341 assert test.lineno is not None
342 lines = [
343 "%03d %s" % (i + test.lineno + 1, x)
344 for (i, x) in enumerate(lines)
345 ]
346 # trim docstring error lines to 10
347 lines = lines[max(example.lineno - 9, 0) : example.lineno + 1]
348 else:
349 lines = [
350 "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example"
351 ]
352 indent = ">>>"
353 for line in example.source.splitlines():
354 lines.append("??? {} {}".format(indent, line))
355 indent = "..."
356 if isinstance(failure, doctest.DocTestFailure):
357 lines += checker.output_difference(
358 example, failure.got, report_choice
359 ).split("\n")
360 else:
361 inner_excinfo = ExceptionInfo(failure.exc_info)
362 lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)]
363 lines += [
364 x.strip("\n")
365 for x in traceback.format_exception(*failure.exc_info)
366 ]
367 reprlocation_lines.append((reprlocation, lines))
368 return ReprFailDoctest(reprlocation_lines)
369 else:
370 return super().repr_failure(excinfo)
372 def reportinfo(self):
373 assert self.dtest is not None
374 return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
377def _get_flag_lookup() -> Dict[str, int]:
378 import doctest
380 return dict(
381 DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1,
382 DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE,
383 NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE,
384 ELLIPSIS=doctest.ELLIPSIS,
385 IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL,
386 COMPARISON_FLAGS=doctest.COMPARISON_FLAGS,
387 ALLOW_UNICODE=_get_allow_unicode_flag(),
388 ALLOW_BYTES=_get_allow_bytes_flag(),
389 NUMBER=_get_number_flag(),
390 )
393def get_optionflags(parent):
394 optionflags_str = parent.config.getini("doctest_optionflags")
395 flag_lookup_table = _get_flag_lookup()
396 flag_acc = 0
397 for flag in optionflags_str:
398 flag_acc |= flag_lookup_table[flag]
399 return flag_acc
402def _get_continue_on_failure(config):
403 continue_on_failure = config.getvalue("doctest_continue_on_failure")
404 if continue_on_failure:
405 # We need to turn off this if we use pdb since we should stop at
406 # the first failure
407 if config.getvalue("usepdb"):
408 continue_on_failure = False
409 return continue_on_failure
412class DoctestTextfile(pytest.Module):
413 obj = None
415 def collect(self) -> Iterable[DoctestItem]:
416 import doctest
418 # inspired by doctest.testfile; ideally we would use it directly,
419 # but it doesn't support passing a custom checker
420 encoding = self.config.getini("doctest_encoding")
421 text = self.fspath.read_text(encoding)
422 filename = str(self.fspath)
423 name = self.fspath.basename
424 globs = {"__name__": "__main__"}
426 optionflags = get_optionflags(self)
428 runner = _get_runner(
429 verbose=False,
430 optionflags=optionflags,
431 checker=_get_checker(),
432 continue_on_failure=_get_continue_on_failure(self.config),
433 )
435 parser = doctest.DocTestParser()
436 test = parser.get_doctest(text, globs, name, filename, 0)
437 if test.examples:
438 yield DoctestItem.from_parent(
439 self, name=test.name, runner=runner, dtest=test
440 )
443def _check_all_skipped(test: "doctest.DocTest") -> None:
444 """raises pytest.skip() if all examples in the given DocTest have the SKIP
445 option set.
446 """
447 import doctest
449 all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples)
450 if all_skipped:
451 pytest.skip("all tests skipped by +SKIP option")
454def _is_mocked(obj: object) -> bool:
455 """
456 returns if a object is possibly a mock object by checking the existence of a highly improbable attribute
457 """
458 return (
459 safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None)
460 is not None
461 )
464@contextmanager
465def _patch_unwrap_mock_aware() -> Generator[None, None, None]:
466 """
467 contextmanager which replaces ``inspect.unwrap`` with a version
468 that's aware of mock objects and doesn't recurse on them
469 """
470 real_unwrap = inspect.unwrap
472 def _mock_aware_unwrap(
473 func: Callable[..., Any], *, stop: Optional[Callable[[Any], Any]] = None
474 ) -> Any:
475 try:
476 if stop is None or stop is _is_mocked:
477 return real_unwrap(func, stop=_is_mocked)
478 _stop = stop
479 return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func))
480 except Exception as e:
481 warnings.warn(
482 "Got %r when unwrapping %r. This is usually caused "
483 "by a violation of Python's object protocol; see e.g. "
484 "https://github.com/pytest-dev/pytest/issues/5080" % (e, func),
485 PytestWarning,
486 )
487 raise
489 inspect.unwrap = _mock_aware_unwrap
490 try:
491 yield
492 finally:
493 inspect.unwrap = real_unwrap
496class DoctestModule(pytest.Module):
497 def collect(self) -> Iterable[DoctestItem]:
498 import doctest
500 class MockAwareDocTestFinder(doctest.DocTestFinder):
501 """
502 a hackish doctest finder that overrides stdlib internals to fix a stdlib bug
504 https://github.com/pytest-dev/pytest/issues/3456
505 https://bugs.python.org/issue25532
506 """
508 def _find_lineno(self, obj, source_lines):
509 """
510 Doctest code does not take into account `@property`, this is a hackish way to fix it.
512 https://bugs.python.org/issue17446
513 """
514 if isinstance(obj, property):
515 obj = getattr(obj, "fget", obj)
516 # Type ignored because this is a private function.
517 return doctest.DocTestFinder._find_lineno( # type: ignore
518 self, obj, source_lines,
519 )
521 def _find(
522 self, tests, obj, name, module, source_lines, globs, seen
523 ) -> None:
524 if _is_mocked(obj):
525 return
526 with _patch_unwrap_mock_aware():
528 # Type ignored because this is a private function.
529 doctest.DocTestFinder._find( # type: ignore
530 self, tests, obj, name, module, source_lines, globs, seen
531 )
533 if self.fspath.basename == "conftest.py":
534 module = self.config.pluginmanager._importconftest(
535 self.fspath, self.config.getoption("importmode")
536 )
537 else:
538 try:
539 module = import_path(self.fspath)
540 except ImportError:
541 if self.config.getvalue("doctest_ignore_import_errors"):
542 pytest.skip("unable to import module %r" % self.fspath)
543 else:
544 raise
545 # uses internal doctest module parsing mechanism
546 finder = MockAwareDocTestFinder()
547 optionflags = get_optionflags(self)
548 runner = _get_runner(
549 verbose=False,
550 optionflags=optionflags,
551 checker=_get_checker(),
552 continue_on_failure=_get_continue_on_failure(self.config),
553 )
555 for test in finder.find(module, module.__name__):
556 if test.examples: # skip empty doctests
557 yield DoctestItem.from_parent(
558 self, name=test.name, runner=runner, dtest=test
559 )
562def _setup_fixtures(doctest_item: DoctestItem) -> FixtureRequest:
563 """
564 Used by DoctestTextfile and DoctestItem to setup fixture information.
565 """
567 def func() -> None:
568 pass
570 doctest_item.funcargs = {} # type: ignore[attr-defined]
571 fm = doctest_item.session._fixturemanager
572 doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined]
573 node=doctest_item, func=func, cls=None, funcargs=False
574 )
575 fixture_request = FixtureRequest(doctest_item)
576 fixture_request._fillfixtures()
577 return fixture_request
580def _init_checker_class() -> "Type[doctest.OutputChecker]":
581 import doctest
582 import re
584 class LiteralsOutputChecker(doctest.OutputChecker):
585 """
586 Based on doctest_nose_plugin.py from the nltk project
587 (https://github.com/nltk/nltk) and on the "numtest" doctest extension
588 by Sebastien Boisgerault (https://github.com/boisgera/numtest).
589 """
591 _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
592 _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE)
593 _number_re = re.compile(
594 r"""
595 (?P<number>
596 (?P<mantissa>
597 (?P<integer1> [+-]?\d*)\.(?P<fraction>\d+)
598 |
599 (?P<integer2> [+-]?\d+)\.
600 )
601 (?:
602 [Ee]
603 (?P<exponent1> [+-]?\d+)
604 )?
605 |
606 (?P<integer3> [+-]?\d+)
607 (?:
608 [Ee]
609 (?P<exponent2> [+-]?\d+)
610 )
611 )
612 """,
613 re.VERBOSE,
614 )
616 def check_output(self, want: str, got: str, optionflags: int) -> bool:
617 if doctest.OutputChecker.check_output(self, want, got, optionflags):
618 return True
620 allow_unicode = optionflags & _get_allow_unicode_flag()
621 allow_bytes = optionflags & _get_allow_bytes_flag()
622 allow_number = optionflags & _get_number_flag()
624 if not allow_unicode and not allow_bytes and not allow_number:
625 return False
627 def remove_prefixes(regex: Pattern[str], txt: str) -> str:
628 return re.sub(regex, r"\1\2", txt)
630 if allow_unicode:
631 want = remove_prefixes(self._unicode_literal_re, want)
632 got = remove_prefixes(self._unicode_literal_re, got)
634 if allow_bytes:
635 want = remove_prefixes(self._bytes_literal_re, want)
636 got = remove_prefixes(self._bytes_literal_re, got)
638 if allow_number:
639 got = self._remove_unwanted_precision(want, got)
641 return doctest.OutputChecker.check_output(self, want, got, optionflags)
643 def _remove_unwanted_precision(self, want: str, got: str) -> str:
644 wants = list(self._number_re.finditer(want))
645 gots = list(self._number_re.finditer(got))
646 if len(wants) != len(gots):
647 return got
648 offset = 0
649 for w, g in zip(wants, gots):
650 fraction = w.group("fraction")
651 exponent = w.group("exponent1")
652 if exponent is None:
653 exponent = w.group("exponent2")
654 if fraction is None:
655 precision = 0
656 else:
657 precision = len(fraction)
658 if exponent is not None:
659 precision -= int(exponent)
660 if float(w.group()) == approx(float(g.group()), abs=10 ** -precision):
661 # They're close enough. Replace the text we actually
662 # got with the text we want, so that it will match when we
663 # check the string literally.
664 got = (
665 got[: g.start() + offset] + w.group() + got[g.end() + offset :]
666 )
667 offset += w.end() - w.start() - (g.end() - g.start())
668 return got
670 return LiteralsOutputChecker
673def _get_checker() -> "doctest.OutputChecker":
674 """
675 Returns a doctest.OutputChecker subclass that supports some
676 additional options:
678 * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b''
679 prefixes (respectively) in string literals. Useful when the same
680 doctest should run in Python 2 and Python 3.
682 * NUMBER to ignore floating-point differences smaller than the
683 precision of the literal number in the doctest.
685 An inner class is used to avoid importing "doctest" at the module
686 level.
687 """
688 global CHECKER_CLASS
689 if CHECKER_CLASS is None:
690 CHECKER_CLASS = _init_checker_class()
691 return CHECKER_CLASS()
694def _get_allow_unicode_flag() -> int:
695 """
696 Registers and returns the ALLOW_UNICODE flag.
697 """
698 import doctest
700 return doctest.register_optionflag("ALLOW_UNICODE")
703def _get_allow_bytes_flag() -> int:
704 """
705 Registers and returns the ALLOW_BYTES flag.
706 """
707 import doctest
709 return doctest.register_optionflag("ALLOW_BYTES")
712def _get_number_flag() -> int:
713 """
714 Registers and returns the NUMBER flag.
715 """
716 import doctest
718 return doctest.register_optionflag("NUMBER")
721def _get_report_choice(key: str) -> int:
722 """
723 This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid
724 importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests.
725 """
726 import doctest
728 return {
729 DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF,
730 DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF,
731 DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF,
732 DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE,
733 DOCTEST_REPORT_CHOICE_NONE: 0,
734 }[key]
737@pytest.fixture(scope="session")
738def doctest_namespace() -> Dict[str, Any]:
739 """
740 Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests.
741 """
742 return dict()