Spaces:
Runtime error
Runtime error
# -*- coding: utf-8 -*- | |
"""pytest configuration | |
Extends output capture as needed by pybind11: ignore constructors, optional unordered lines. | |
Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences. | |
""" | |
import contextlib | |
import difflib | |
import gc | |
import re | |
import textwrap | |
import pytest | |
import env | |
# Early diagnostic for failed imports | |
import pybind11_tests # noqa: F401 | |
_unicode_marker = re.compile(r'u(\'[^\']*\')') | |
_long_marker = re.compile(r'([0-9])L') | |
_hexadecimal = re.compile(r'0x[0-9a-fA-F]+') | |
# Avoid collecting Python3 only files | |
collect_ignore = [] | |
if env.PY2: | |
collect_ignore.append("test_async.py") | |
def _strip_and_dedent(s): | |
"""For triple-quote strings""" | |
return textwrap.dedent(s.lstrip('\n').rstrip()) | |
def _split_and_sort(s): | |
"""For output which does not require specific line order""" | |
return sorted(_strip_and_dedent(s).splitlines()) | |
def _make_explanation(a, b): | |
"""Explanation for a failed assert -- the a and b arguments are List[str]""" | |
return ["--- actual / +++ expected"] + [line.strip('\n') for line in difflib.ndiff(a, b)] | |
class Output(object): | |
"""Basic output post-processing and comparison""" | |
def __init__(self, string): | |
self.string = string | |
self.explanation = [] | |
def __str__(self): | |
return self.string | |
def __eq__(self, other): | |
# Ignore constructor/destructor output which is prefixed with "###" | |
a = [line for line in self.string.strip().splitlines() if not line.startswith("###")] | |
b = _strip_and_dedent(other).splitlines() | |
if a == b: | |
return True | |
else: | |
self.explanation = _make_explanation(a, b) | |
return False | |
class Unordered(Output): | |
"""Custom comparison for output without strict line ordering""" | |
def __eq__(self, other): | |
a = _split_and_sort(self.string) | |
b = _split_and_sort(other) | |
if a == b: | |
return True | |
else: | |
self.explanation = _make_explanation(a, b) | |
return False | |
class Capture(object): | |
def __init__(self, capfd): | |
self.capfd = capfd | |
self.out = "" | |
self.err = "" | |
def __enter__(self): | |
self.capfd.readouterr() | |
return self | |
def __exit__(self, *args): | |
self.out, self.err = self.capfd.readouterr() | |
def __eq__(self, other): | |
a = Output(self.out) | |
b = other | |
if a == b: | |
return True | |
else: | |
self.explanation = a.explanation | |
return False | |
def __str__(self): | |
return self.out | |
def __contains__(self, item): | |
return item in self.out | |
def unordered(self): | |
return Unordered(self.out) | |
def stderr(self): | |
return Output(self.err) | |
def capture(capsys): | |
"""Extended `capsys` with context manager and custom equality operators""" | |
return Capture(capsys) | |
class SanitizedString(object): | |
def __init__(self, sanitizer): | |
self.sanitizer = sanitizer | |
self.string = "" | |
self.explanation = [] | |
def __call__(self, thing): | |
self.string = self.sanitizer(thing) | |
return self | |
def __eq__(self, other): | |
a = self.string | |
b = _strip_and_dedent(other) | |
if a == b: | |
return True | |
else: | |
self.explanation = _make_explanation(a.splitlines(), b.splitlines()) | |
return False | |
def _sanitize_general(s): | |
s = s.strip() | |
s = s.replace("pybind11_tests.", "m.") | |
s = s.replace("unicode", "str") | |
s = _long_marker.sub(r"\1", s) | |
s = _unicode_marker.sub(r"\1", s) | |
return s | |
def _sanitize_docstring(thing): | |
s = thing.__doc__ | |
s = _sanitize_general(s) | |
return s | |
def doc(): | |
"""Sanitize docstrings and add custom failure explanation""" | |
return SanitizedString(_sanitize_docstring) | |
def _sanitize_message(thing): | |
s = str(thing) | |
s = _sanitize_general(s) | |
s = _hexadecimal.sub("0", s) | |
return s | |
def msg(): | |
"""Sanitize messages and add custom failure explanation""" | |
return SanitizedString(_sanitize_message) | |
# noinspection PyUnusedLocal | |
def pytest_assertrepr_compare(op, left, right): | |
"""Hook to insert custom failure explanation""" | |
if hasattr(left, 'explanation'): | |
return left.explanation | |
def suppress(exception): | |
"""Suppress the desired exception""" | |
try: | |
yield | |
except exception: | |
pass | |
def gc_collect(): | |
''' Run the garbage collector twice (needed when running | |
reference counting tests with PyPy) ''' | |
gc.collect() | |
gc.collect() | |
def pytest_configure(): | |
pytest.suppress = suppress | |
pytest.gc_collect = gc_collect | |