aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py
diff options
context:
space:
mode:
authormonster <monster@ydb.tech>2022-07-07 14:41:37 +0300
committermonster <monster@ydb.tech>2022-07-07 14:41:37 +0300
commit06e5c21a835c0e923506c4ff27929f34e00761c2 (patch)
tree75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py
parent03f024c4412e3aa613bb543cf1660176320ba8f4 (diff)
downloadydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz
fix ya.make
Diffstat (limited to 'contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py')
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py860
1 files changed, 0 insertions, 860 deletions
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py b/contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py
deleted file mode 100644
index 809713d7c8..0000000000
--- a/contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py
+++ /dev/null
@@ -1,860 +0,0 @@
-# Based on Pytest doctest.py
-# Original license:
-# The MIT License (MIT)
-#
-# Copyright (c) 2004-2021 Holger Krekel and others
-"""Discover and run ipdoctests in modules and test files."""
-import builtins
-import bdb
-import inspect
-import os
-import platform
-import sys
-import traceback
-import types
-import warnings
-from contextlib import contextmanager
-from pathlib import Path
-from typing import Any
-from typing import Callable
-from typing import Dict
-from typing import Generator
-from typing import Iterable
-from typing import List
-from typing import Optional
-from typing import Pattern
-from typing import Sequence
-from typing import Tuple
-from typing import Type
-from typing import TYPE_CHECKING
-from typing import Union
-
-import pytest
-from _pytest import outcomes
-from _pytest._code.code import ExceptionInfo
-from _pytest._code.code import ReprFileLocation
-from _pytest._code.code import TerminalRepr
-from _pytest._io import TerminalWriter
-from _pytest.compat import safe_getattr
-from _pytest.config import Config
-from _pytest.config.argparsing import Parser
-from _pytest.fixtures import FixtureRequest
-from _pytest.nodes import Collector
-from _pytest.outcomes import OutcomeException
-from _pytest.pathlib import fnmatch_ex
-from _pytest.pathlib import import_path
-from _pytest.python_api import approx
-from _pytest.warning_types import PytestWarning
-
-if TYPE_CHECKING:
- import doctest
-
-DOCTEST_REPORT_CHOICE_NONE = "none"
-DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
-DOCTEST_REPORT_CHOICE_NDIFF = "ndiff"
-DOCTEST_REPORT_CHOICE_UDIFF = "udiff"
-DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = "only_first_failure"
-
-DOCTEST_REPORT_CHOICES = (
- DOCTEST_REPORT_CHOICE_NONE,
- DOCTEST_REPORT_CHOICE_CDIFF,
- DOCTEST_REPORT_CHOICE_NDIFF,
- DOCTEST_REPORT_CHOICE_UDIFF,
- DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE,
-)
-
-# Lazy definition of runner class
-RUNNER_CLASS = None
-# Lazy definition of output checker class
-CHECKER_CLASS: Optional[Type["IPDoctestOutputChecker"]] = None
-
-
-def pytest_addoption(parser: Parser) -> None:
- parser.addini(
- "ipdoctest_optionflags",
- "option flags for ipdoctests",
- type="args",
- default=["ELLIPSIS"],
- )
- parser.addini(
- "ipdoctest_encoding", "encoding used for ipdoctest files", default="utf-8"
- )
- group = parser.getgroup("collect")
- group.addoption(
- "--ipdoctest-modules",
- action="store_true",
- default=False,
- help="run ipdoctests in all .py modules",
- dest="ipdoctestmodules",
- )
- group.addoption(
- "--ipdoctest-report",
- type=str.lower,
- default="udiff",
- help="choose another output format for diffs on ipdoctest failure",
- choices=DOCTEST_REPORT_CHOICES,
- dest="ipdoctestreport",
- )
- group.addoption(
- "--ipdoctest-glob",
- action="append",
- default=[],
- metavar="pat",
- help="ipdoctests file matching pattern, default: test*.txt",
- dest="ipdoctestglob",
- )
- group.addoption(
- "--ipdoctest-ignore-import-errors",
- action="store_true",
- default=False,
- help="ignore ipdoctest ImportErrors",
- dest="ipdoctest_ignore_import_errors",
- )
- group.addoption(
- "--ipdoctest-continue-on-failure",
- action="store_true",
- default=False,
- help="for a given ipdoctest, continue to run after the first failure",
- dest="ipdoctest_continue_on_failure",
- )
-
-
-def pytest_unconfigure() -> None:
- global RUNNER_CLASS
-
- RUNNER_CLASS = None
-
-
-def pytest_collect_file(
- file_path: Path,
- parent: Collector,
-) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]:
- config = parent.config
- if file_path.suffix == ".py":
- if config.option.ipdoctestmodules and not any(
- (_is_setup_py(file_path), _is_main_py(file_path))
- ):
- mod: IPDoctestModule = IPDoctestModule.from_parent(parent, path=file_path)
- return mod
- elif _is_ipdoctest(config, file_path, parent):
- txt: IPDoctestTextfile = IPDoctestTextfile.from_parent(parent, path=file_path)
- return txt
- return None
-
-
-if int(pytest.__version__.split(".")[0]) < 7:
- _collect_file = pytest_collect_file
-
- def pytest_collect_file(
- path,
- parent: Collector,
- ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]:
- return _collect_file(Path(path), parent)
-
- _import_path = import_path
-
- def import_path(path, root):
- import py.path
-
- return _import_path(py.path.local(path))
-
-
-def _is_setup_py(path: Path) -> bool:
- if path.name != "setup.py":
- return False
- contents = path.read_bytes()
- return b"setuptools" in contents or b"distutils" in contents
-
-
-def _is_ipdoctest(config: Config, path: Path, parent: Collector) -> bool:
- if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path):
- return True
- globs = config.getoption("ipdoctestglob") or ["test*.txt"]
- return any(fnmatch_ex(glob, path) for glob in globs)
-
-
-def _is_main_py(path: Path) -> bool:
- return path.name == "__main__.py"
-
-
-class ReprFailDoctest(TerminalRepr):
- def __init__(
- self, reprlocation_lines: Sequence[Tuple[ReprFileLocation, Sequence[str]]]
- ) -> None:
- self.reprlocation_lines = reprlocation_lines
-
- def toterminal(self, tw: TerminalWriter) -> None:
- for reprlocation, lines in self.reprlocation_lines:
- for line in lines:
- tw.line(line)
- reprlocation.toterminal(tw)
-
-
-class MultipleDoctestFailures(Exception):
- def __init__(self, failures: Sequence["doctest.DocTestFailure"]) -> None:
- super().__init__()
- self.failures = failures
-
-
-def _init_runner_class() -> Type["IPDocTestRunner"]:
- import doctest
- from .ipdoctest import IPDocTestRunner
-
- class PytestDoctestRunner(IPDocTestRunner):
- """Runner to collect failures.
-
- Note that the out variable in this case is a list instead of a
- stdout-like object.
- """
-
- def __init__(
- self,
- checker: Optional["IPDoctestOutputChecker"] = None,
- verbose: Optional[bool] = None,
- optionflags: int = 0,
- continue_on_failure: bool = True,
- ) -> None:
- super().__init__(checker=checker, verbose=verbose, optionflags=optionflags)
- self.continue_on_failure = continue_on_failure
-
- def report_failure(
- self,
- out,
- test: "doctest.DocTest",
- example: "doctest.Example",
- got: str,
- ) -> None:
- failure = doctest.DocTestFailure(test, example, got)
- if self.continue_on_failure:
- out.append(failure)
- else:
- raise failure
-
- def report_unexpected_exception(
- self,
- out,
- test: "doctest.DocTest",
- example: "doctest.Example",
- exc_info: Tuple[Type[BaseException], BaseException, types.TracebackType],
- ) -> None:
- if isinstance(exc_info[1], OutcomeException):
- raise exc_info[1]
- if isinstance(exc_info[1], bdb.BdbQuit):
- outcomes.exit("Quitting debugger")
- failure = doctest.UnexpectedException(test, example, exc_info)
- if self.continue_on_failure:
- out.append(failure)
- else:
- raise failure
-
- return PytestDoctestRunner
-
-
-def _get_runner(
- checker: Optional["IPDoctestOutputChecker"] = None,
- verbose: Optional[bool] = None,
- optionflags: int = 0,
- continue_on_failure: bool = True,
-) -> "IPDocTestRunner":
- # We need this in order to do a lazy import on doctest
- global RUNNER_CLASS
- if RUNNER_CLASS is None:
- RUNNER_CLASS = _init_runner_class()
- # Type ignored because the continue_on_failure argument is only defined on
- # PytestDoctestRunner, which is lazily defined so can't be used as a type.
- return RUNNER_CLASS( # type: ignore
- checker=checker,
- verbose=verbose,
- optionflags=optionflags,
- continue_on_failure=continue_on_failure,
- )
-
-
-class IPDoctestItem(pytest.Item):
- def __init__(
- self,
- name: str,
- parent: "Union[IPDoctestTextfile, IPDoctestModule]",
- runner: Optional["IPDocTestRunner"] = None,
- dtest: Optional["doctest.DocTest"] = None,
- ) -> None:
- super().__init__(name, parent)
- self.runner = runner
- self.dtest = dtest
- self.obj = None
- self.fixture_request: Optional[FixtureRequest] = None
-
- @classmethod
- def from_parent( # type: ignore
- cls,
- parent: "Union[IPDoctestTextfile, IPDoctestModule]",
- *,
- name: str,
- runner: "IPDocTestRunner",
- dtest: "doctest.DocTest",
- ):
- # incompatible signature due to imposed limits on subclass
- """The public named constructor."""
- return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest)
-
- def setup(self) -> None:
- if self.dtest is not None:
- self.fixture_request = _setup_fixtures(self)
- globs = dict(getfixture=self.fixture_request.getfixturevalue)
- for name, value in self.fixture_request.getfixturevalue(
- "ipdoctest_namespace"
- ).items():
- globs[name] = value
- self.dtest.globs.update(globs)
-
- from .ipdoctest import IPExample
-
- if isinstance(self.dtest.examples[0], IPExample):
- # for IPython examples *only*, we swap the globals with the ipython
- # namespace, after updating it with the globals (which doctest
- # fills with the necessary info from the module being tested).
- self._user_ns_orig = {}
- self._user_ns_orig.update(_ip.user_ns)
- _ip.user_ns.update(self.dtest.globs)
- # We must remove the _ key in the namespace, so that Python's
- # doctest code sets it naturally
- _ip.user_ns.pop("_", None)
- _ip.user_ns["__builtins__"] = builtins
- self.dtest.globs = _ip.user_ns
-
- def teardown(self) -> None:
- from .ipdoctest import IPExample
-
- # Undo the test.globs reassignment we made
- if isinstance(self.dtest.examples[0], IPExample):
- self.dtest.globs = {}
- _ip.user_ns.clear()
- _ip.user_ns.update(self._user_ns_orig)
- del self._user_ns_orig
-
- self.dtest.globs.clear()
-
- def runtest(self) -> None:
- assert self.dtest is not None
- assert self.runner is not None
- _check_all_skipped(self.dtest)
- self._disable_output_capturing_for_darwin()
- failures: List["doctest.DocTestFailure"] = []
-
- # exec(compile(..., "single", ...), ...) puts result in builtins._
- had_underscore_value = hasattr(builtins, "_")
- underscore_original_value = getattr(builtins, "_", None)
-
- # Save our current directory and switch out to the one where the
- # test was originally created, in case another doctest did a
- # directory change. We'll restore this in the finally clause.
- curdir = os.getcwd()
- os.chdir(self.fspath.dirname)
- try:
- # Type ignored because we change the type of `out` from what
- # ipdoctest expects.
- self.runner.run(self.dtest, out=failures, clear_globs=False) # type: ignore[arg-type]
- finally:
- os.chdir(curdir)
- if had_underscore_value:
- setattr(builtins, "_", underscore_original_value)
- elif hasattr(builtins, "_"):
- delattr(builtins, "_")
-
- if failures:
- raise MultipleDoctestFailures(failures)
-
- def _disable_output_capturing_for_darwin(self) -> None:
- """Disable output capturing. Otherwise, stdout is lost to ipdoctest (pytest#985)."""
- if platform.system() != "Darwin":
- return
- capman = self.config.pluginmanager.getplugin("capturemanager")
- if capman:
- capman.suspend_global_capture(in_=True)
- out, err = capman.read_global_capture()
- sys.stdout.write(out)
- sys.stderr.write(err)
-
- # TODO: Type ignored -- breaks Liskov Substitution.
- def repr_failure( # type: ignore[override]
- self,
- excinfo: ExceptionInfo[BaseException],
- ) -> Union[str, TerminalRepr]:
- import doctest
-
- failures: Optional[
- Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]]
- ] = None
- if isinstance(
- excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException)
- ):
- failures = [excinfo.value]
- elif isinstance(excinfo.value, MultipleDoctestFailures):
- failures = excinfo.value.failures
-
- if failures is None:
- return super().repr_failure(excinfo)
-
- reprlocation_lines = []
- for failure in failures:
- example = failure.example
- test = failure.test
- filename = test.filename
- if test.lineno is None:
- lineno = None
- else:
- lineno = test.lineno + example.lineno + 1
- message = type(failure).__name__
- # TODO: ReprFileLocation doesn't expect a None lineno.
- reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type]
- checker = _get_checker()
- report_choice = _get_report_choice(self.config.getoption("ipdoctestreport"))
- if lineno is not None:
- assert failure.test.docstring is not None
- lines = failure.test.docstring.splitlines(False)
- # add line numbers to the left of the error message
- assert test.lineno is not None
- lines = [
- "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines)
- ]
- # trim docstring error lines to 10
- lines = lines[max(example.lineno - 9, 0) : example.lineno + 1]
- else:
- lines = [
- "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example"
- ]
- indent = ">>>"
- for line in example.source.splitlines():
- lines.append(f"??? {indent} {line}")
- indent = "..."
- if isinstance(failure, doctest.DocTestFailure):
- lines += checker.output_difference(
- example, failure.got, report_choice
- ).split("\n")
- else:
- inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info)
- lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)]
- lines += [
- x.strip("\n") for x in traceback.format_exception(*failure.exc_info)
- ]
- reprlocation_lines.append((reprlocation, lines))
- return ReprFailDoctest(reprlocation_lines)
-
- def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]:
- assert self.dtest is not None
- return self.path, self.dtest.lineno, "[ipdoctest] %s" % self.name
-
- if int(pytest.__version__.split(".")[0]) < 7:
-
- @property
- def path(self) -> Path:
- return Path(self.fspath)
-
-
-def _get_flag_lookup() -> Dict[str, int]:
- import doctest
-
- return dict(
- DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1,
- DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE,
- NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE,
- ELLIPSIS=doctest.ELLIPSIS,
- IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL,
- COMPARISON_FLAGS=doctest.COMPARISON_FLAGS,
- ALLOW_UNICODE=_get_allow_unicode_flag(),
- ALLOW_BYTES=_get_allow_bytes_flag(),
- NUMBER=_get_number_flag(),
- )
-
-
-def get_optionflags(parent):
- optionflags_str = parent.config.getini("ipdoctest_optionflags")
- flag_lookup_table = _get_flag_lookup()
- flag_acc = 0
- for flag in optionflags_str:
- flag_acc |= flag_lookup_table[flag]
- return flag_acc
-
-
-def _get_continue_on_failure(config):
- continue_on_failure = config.getvalue("ipdoctest_continue_on_failure")
- if continue_on_failure:
- # We need to turn off this if we use pdb since we should stop at
- # the first failure.
- if config.getvalue("usepdb"):
- continue_on_failure = False
- return continue_on_failure
-
-
-class IPDoctestTextfile(pytest.Module):
- obj = None
-
- def collect(self) -> Iterable[IPDoctestItem]:
- import doctest
- from .ipdoctest import IPDocTestParser
-
- # Inspired by doctest.testfile; ideally we would use it directly,
- # but it doesn't support passing a custom checker.
- encoding = self.config.getini("ipdoctest_encoding")
- text = self.path.read_text(encoding)
- filename = str(self.path)
- name = self.path.name
- globs = {"__name__": "__main__"}
-
- optionflags = get_optionflags(self)
-
- runner = _get_runner(
- verbose=False,
- optionflags=optionflags,
- checker=_get_checker(),
- continue_on_failure=_get_continue_on_failure(self.config),
- )
-
- parser = IPDocTestParser()
- test = parser.get_doctest(text, globs, name, filename, 0)
- if test.examples:
- yield IPDoctestItem.from_parent(
- self, name=test.name, runner=runner, dtest=test
- )
-
- if int(pytest.__version__.split(".")[0]) < 7:
-
- @property
- def path(self) -> Path:
- return Path(self.fspath)
-
- @classmethod
- def from_parent(
- cls,
- parent,
- *,
- fspath=None,
- path: Optional[Path] = None,
- **kw,
- ):
- if path is not None:
- import py.path
-
- fspath = py.path.local(path)
- return super().from_parent(parent=parent, fspath=fspath, **kw)
-
-
-def _check_all_skipped(test: "doctest.DocTest") -> None:
- """Raise pytest.skip() if all examples in the given DocTest have the SKIP
- option set."""
- import doctest
-
- all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples)
- if all_skipped:
- pytest.skip("all docstests skipped by +SKIP option")
-
-
-def _is_mocked(obj: object) -> bool:
- """Return if an object is possibly a mock object by checking the
- existence of a highly improbable attribute."""
- return (
- safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None)
- is not None
- )
-
-
-@contextmanager
-def _patch_unwrap_mock_aware() -> Generator[None, None, None]:
- """Context manager which replaces ``inspect.unwrap`` with a version
- that's aware of mock objects and doesn't recurse into them."""
- real_unwrap = inspect.unwrap
-
- def _mock_aware_unwrap(
- func: Callable[..., Any], *, stop: Optional[Callable[[Any], Any]] = None
- ) -> Any:
- try:
- if stop is None or stop is _is_mocked:
- return real_unwrap(func, stop=_is_mocked)
- _stop = stop
- return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func))
- except Exception as e:
- warnings.warn(
- "Got %r when unwrapping %r. This is usually caused "
- "by a violation of Python's object protocol; see e.g. "
- "https://github.com/pytest-dev/pytest/issues/5080" % (e, func),
- PytestWarning,
- )
- raise
-
- inspect.unwrap = _mock_aware_unwrap
- try:
- yield
- finally:
- inspect.unwrap = real_unwrap
-
-
-class IPDoctestModule(pytest.Module):
- def collect(self) -> Iterable[IPDoctestItem]:
- import doctest
- from .ipdoctest import DocTestFinder, IPDocTestParser
-
- class MockAwareDocTestFinder(DocTestFinder):
- """A hackish ipdoctest finder that overrides stdlib internals to fix a stdlib bug.
-
- https://github.com/pytest-dev/pytest/issues/3456
- https://bugs.python.org/issue25532
- """
-
- def _find_lineno(self, obj, source_lines):
- """Doctest code does not take into account `@property`, this
- is a hackish way to fix it. https://bugs.python.org/issue17446
-
- Wrapped Doctests will need to be unwrapped so the correct
- line number is returned. This will be reported upstream. #8796
- """
- if isinstance(obj, property):
- obj = getattr(obj, "fget", obj)
-
- if hasattr(obj, "__wrapped__"):
- # Get the main obj in case of it being wrapped
- obj = inspect.unwrap(obj)
-
- # Type ignored because this is a private function.
- return super()._find_lineno( # type:ignore[misc]
- obj,
- source_lines,
- )
-
- def _find(
- self, tests, obj, name, module, source_lines, globs, seen
- ) -> None:
- if _is_mocked(obj):
- return
- with _patch_unwrap_mock_aware():
-
- # Type ignored because this is a private function.
- super()._find( # type:ignore[misc]
- tests, obj, name, module, source_lines, globs, seen
- )
-
- if self.path.name == "conftest.py":
- if int(pytest.__version__.split(".")[0]) < 7:
- module = self.config.pluginmanager._importconftest(
- self.path,
- self.config.getoption("importmode"),
- )
- else:
- module = self.config.pluginmanager._importconftest(
- self.path,
- self.config.getoption("importmode"),
- rootpath=self.config.rootpath,
- )
- else:
- try:
- module = import_path(self.path, root=self.config.rootpath)
- except ImportError:
- if self.config.getvalue("ipdoctest_ignore_import_errors"):
- pytest.skip("unable to import module %r" % self.path)
- else:
- raise
- # Uses internal doctest module parsing mechanism.
- finder = MockAwareDocTestFinder(parser=IPDocTestParser())
- optionflags = get_optionflags(self)
- runner = _get_runner(
- verbose=False,
- optionflags=optionflags,
- checker=_get_checker(),
- continue_on_failure=_get_continue_on_failure(self.config),
- )
-
- for test in finder.find(module, module.__name__):
- if test.examples: # skip empty ipdoctests
- yield IPDoctestItem.from_parent(
- self, name=test.name, runner=runner, dtest=test
- )
-
- if int(pytest.__version__.split(".")[0]) < 7:
-
- @property
- def path(self) -> Path:
- return Path(self.fspath)
-
- @classmethod
- def from_parent(
- cls,
- parent,
- *,
- fspath=None,
- path: Optional[Path] = None,
- **kw,
- ):
- if path is not None:
- import py.path
-
- fspath = py.path.local(path)
- return super().from_parent(parent=parent, fspath=fspath, **kw)
-
-
-def _setup_fixtures(doctest_item: IPDoctestItem) -> FixtureRequest:
- """Used by IPDoctestTextfile and IPDoctestItem to setup fixture information."""
-
- def func() -> None:
- pass
-
- doctest_item.funcargs = {} # type: ignore[attr-defined]
- fm = doctest_item.session._fixturemanager
- doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined]
- node=doctest_item, func=func, cls=None, funcargs=False
- )
- fixture_request = FixtureRequest(doctest_item, _ispytest=True)
- fixture_request._fillfixtures()
- return fixture_request
-
-
-def _init_checker_class() -> Type["IPDoctestOutputChecker"]:
- import doctest
- import re
- from .ipdoctest import IPDoctestOutputChecker
-
- class LiteralsOutputChecker(IPDoctestOutputChecker):
- # Based on doctest_nose_plugin.py from the nltk project
- # (https://github.com/nltk/nltk) and on the "numtest" doctest extension
- # by Sebastien Boisgerault (https://github.com/boisgera/numtest).
-
- _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
- _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE)
- _number_re = re.compile(
- r"""
- (?P<number>
- (?P<mantissa>
- (?P<integer1> [+-]?\d*)\.(?P<fraction>\d+)
- |
- (?P<integer2> [+-]?\d+)\.
- )
- (?:
- [Ee]
- (?P<exponent1> [+-]?\d+)
- )?
- |
- (?P<integer3> [+-]?\d+)
- (?:
- [Ee]
- (?P<exponent2> [+-]?\d+)
- )
- )
- """,
- re.VERBOSE,
- )
-
- def check_output(self, want: str, got: str, optionflags: int) -> bool:
- if super().check_output(want, got, optionflags):
- return True
-
- allow_unicode = optionflags & _get_allow_unicode_flag()
- allow_bytes = optionflags & _get_allow_bytes_flag()
- allow_number = optionflags & _get_number_flag()
-
- if not allow_unicode and not allow_bytes and not allow_number:
- return False
-
- def remove_prefixes(regex: Pattern[str], txt: str) -> str:
- return re.sub(regex, r"\1\2", txt)
-
- if allow_unicode:
- want = remove_prefixes(self._unicode_literal_re, want)
- got = remove_prefixes(self._unicode_literal_re, got)
-
- if allow_bytes:
- want = remove_prefixes(self._bytes_literal_re, want)
- got = remove_prefixes(self._bytes_literal_re, got)
-
- if allow_number:
- got = self._remove_unwanted_precision(want, got)
-
- return super().check_output(want, got, optionflags)
-
- def _remove_unwanted_precision(self, want: str, got: str) -> str:
- wants = list(self._number_re.finditer(want))
- gots = list(self._number_re.finditer(got))
- if len(wants) != len(gots):
- return got
- offset = 0
- for w, g in zip(wants, gots):
- fraction: Optional[str] = w.group("fraction")
- exponent: Optional[str] = w.group("exponent1")
- if exponent is None:
- exponent = w.group("exponent2")
- precision = 0 if fraction is None else len(fraction)
- if exponent is not None:
- precision -= int(exponent)
- if float(w.group()) == approx(float(g.group()), abs=10 ** -precision):
- # They're close enough. Replace the text we actually
- # got with the text we want, so that it will match when we
- # check the string literally.
- got = (
- got[: g.start() + offset] + w.group() + got[g.end() + offset :]
- )
- offset += w.end() - w.start() - (g.end() - g.start())
- return got
-
- return LiteralsOutputChecker
-
-
-def _get_checker() -> "IPDoctestOutputChecker":
- """Return a IPDoctestOutputChecker subclass that supports some
- additional options:
-
- * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b''
- prefixes (respectively) in string literals. Useful when the same
- ipdoctest should run in Python 2 and Python 3.
-
- * NUMBER to ignore floating-point differences smaller than the
- precision of the literal number in the ipdoctest.
-
- An inner class is used to avoid importing "ipdoctest" at the module
- level.
- """
- global CHECKER_CLASS
- if CHECKER_CLASS is None:
- CHECKER_CLASS = _init_checker_class()
- return CHECKER_CLASS()
-
-
-def _get_allow_unicode_flag() -> int:
- """Register and return the ALLOW_UNICODE flag."""
- import doctest
-
- return doctest.register_optionflag("ALLOW_UNICODE")
-
-
-def _get_allow_bytes_flag() -> int:
- """Register and return the ALLOW_BYTES flag."""
- import doctest
-
- return doctest.register_optionflag("ALLOW_BYTES")
-
-
-def _get_number_flag() -> int:
- """Register and return the NUMBER flag."""
- import doctest
-
- return doctest.register_optionflag("NUMBER")
-
-
-def _get_report_choice(key: str) -> int:
- """Return the actual `ipdoctest` module flag value.
-
- We want to do it as late as possible to avoid importing `ipdoctest` and all
- its dependencies when parsing options, as it adds overhead and breaks tests.
- """
- import doctest
-
- return {
- DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF,
- DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF,
- DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF,
- DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE,
- DOCTEST_REPORT_CHOICE_NONE: 0,
- }[key]
-
-
-@pytest.fixture(scope="session")
-def ipdoctest_namespace() -> Dict[str, Any]:
- """Fixture that returns a :py:class:`dict` that will be injected into the
- namespace of ipdoctests."""
- return dict()