μHAL (v2.8.19)
Part of the IPbus software repository
Loading...
Searching...
No Matches
conftest.py
Go to the documentation of this file.
1"""pytest configuration
2
3Extends output capture as needed by pybind11: ignore constructors, optional unordered lines.
4Adds docstring and exceptions message sanitizers.
5"""
6
7import contextlib
8import difflib
9import gc
10import multiprocessing
11import os
12import re
13import textwrap
14import traceback
15
16import pytest
17
18# Early diagnostic for failed imports
19try:
20 import pybind11_tests
21except Exception:
22 # pytest does not show the traceback without this.
23 traceback.print_exc()
24 raise
25
26
27@pytest.fixture(scope="session", autouse=True)
29 if os.name == "nt":
30 return
31
32 # Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592
33 # In a nutshell: fork() after starting threads == flakiness in the form of deadlocks.
34 # It is actually a well-known pitfall, unfortunately without guard rails.
35 # "forkserver" is more performant than "spawn" (~9s vs ~13s for tests/test_gil_scoped.py,
36 # visit the issuecomment link above for details).
37 # Windows does not have fork() and the associated pitfall, therefore it is best left
38 # running with defaults.
39 multiprocessing.set_start_method("forkserver")
40
41
42_long_marker = re.compile(r"([0-9])L")
43_hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
44
45# Avoid collecting Python3 only files
46collect_ignore = []
47
48
50 """For triple-quote strings"""
51 return textwrap.dedent(s.lstrip("\n").rstrip())
52
53
54def _split_and_sort(s):
55 """For output which does not require specific line order"""
56 return sorted(_strip_and_dedent(s).splitlines())
57
58
59def _make_explanation(a, b):
60 """Explanation for a failed assert -- the a and b arguments are List[str]"""
61 return ["--- actual / +++ expected"] + [
62 line.strip("\n") for line in difflib.ndiff(a, b)
63 ]
64
65
66class Output:
67 """Basic output post-processing and comparison"""
68
69 def __init__(self, string):
70 self.string = string
71 self.explanation = []
72
73 def __str__(self):
74 return self.string
75
76 def __eq__(self, other):
77 # Ignore constructor/destructor output which is prefixed with "###"
78 a = [
79 line
80 for line in self.string.strip().splitlines()
81 if not line.startswith("###")
82 ]
83 b = _strip_and_dedent(other).splitlines()
84 if a == b:
85 return True
86 else:
88 return False
89
90
91class Unordered(Output):
92 """Custom comparison for output without strict line ordering"""
93
94 def __eq__(self, other):
95 a = _split_and_sort(self.string)
96 b = _split_and_sort(other)
97 if a == b:
98 return True
99 else:
101 return False
102
103
104class Capture:
105 def __init__(self, capfd):
106 self.capfd = capfd
107 self.out = ""
108 self.err = ""
109
110 def __enter__(self):
111 self.capfd.readouterr()
112 return self
113
114 def __exit__(self, *args):
115 self.out, self.err = self.capfd.readouterr()
116
117 def __eq__(self, other):
118 a = Output(self.out)
119 b = other
120 if a == b:
121 return True
122 else:
123 self.explanation = a.explanation
124 return False
125
126 def __str__(self):
127 return self.out
128
129 def __contains__(self, item):
130 return item in self.out
131
132 @property
133 def unordered(self):
134 return Unordered(self.out)
135
136 @property
137 def stderr(self):
138 return Output(self.err)
139
140
141@pytest.fixture
142def capture(capsys):
143 """Extended `capsys` with context manager and custom equality operators"""
144 return Capture(capsys)
145
146
147class SanitizedString:
148 def __init__(self, sanitizer):
149 self.sanitizer = sanitizer
150 self.string = ""
151 self.explanation = []
152
153 def __call__(self, thing):
154 self.string = self.sanitizer(thing)
155 return self
156
157 def __eq__(self, other):
158 a = self.string
159 b = _strip_and_dedent(other)
160 if a == b:
161 return True
162 else:
163 self.explanation = _make_explanation(a.splitlines(), b.splitlines())
164 return False
165
166
167def _sanitize_general(s):
168 s = s.strip()
169 s = s.replace("pybind11_tests.", "m.")
170 s = _long_marker.sub(r"\1", s)
171 return s
172
173
174def _sanitize_docstring(thing):
175 s = thing.__doc__
176 s = _sanitize_general(s)
177 return s
178
179
180@pytest.fixture
181def doc():
182 """Sanitize docstrings and add custom failure explanation"""
183 return SanitizedString(_sanitize_docstring)
184
185
186def _sanitize_message(thing):
187 s = str(thing)
188 s = _sanitize_general(s)
189 s = _hexadecimal.sub("0", s)
190 return s
191
192
193@pytest.fixture
194def msg():
195 """Sanitize messages and add custom failure explanation"""
196 return SanitizedString(_sanitize_message)
197
198
199# noinspection PyUnusedLocal
200def pytest_assertrepr_compare(op, left, right):
201 """Hook to insert custom failure explanation"""
202 if hasattr(left, "explanation"):
203 return left.explanation
204
205
206@contextlib.contextmanager
207def suppress(exception):
208 """Suppress the desired exception"""
209 try:
210 yield
211 except exception:
212 pass
213
214
215def gc_collect():
216 """Run the garbage collector twice (needed when running
217 reference counting tests with PyPy)"""
218 gc.collect()
219 gc.collect()
220
221
222def pytest_configure():
223 pytest.suppress = suppress
224 pytest.gc_collect = gc_collect
225
226
228 del config # Unused.
229 assert (
230 pybind11_tests.compiler_info is not None
231 ), "Please update pybind11_tests.cpp if this assert fails."
232 return (
233 "C++ Info:"
234 f" {pybind11_tests.compiler_info}"
235 f" {pybind11_tests.cpp_std}"
236 f" {pybind11_tests.PYBIND11_INTERNALS_ID}"
237 f" PYBIND11_SIMPLE_GIL_MANAGEMENT={pybind11_tests.PYBIND11_SIMPLE_GIL_MANAGEMENT}"
238 )
def unordered(self)
Definition: conftest.py:115
def stderr(self)
Definition: conftest.py:119
def __init__(self, capfd)
Definition: conftest.py:87
def __enter__(self)
Definition: conftest.py:92
def __contains__(self, item)
Definition: conftest.py:111
def __exit__(self, *args)
Definition: conftest.py:96
def __str__(self)
Definition: conftest.py:108
def __eq__(self, other)
Definition: conftest.py:99
def __str__(self)
Definition: conftest.py:55
def __init__(self, string)
Definition: conftest.py:51
def __eq__(self, other)
Definition: conftest.py:58
def __call__(self, thing)
Definition: conftest.py:135
def __init__(self, sanitizer)
Definition: conftest.py:130
def __eq__(self, other)
Definition: conftest.py:139
def __eq__(self, other)
Definition: conftest.py:76
Definition: pytypes.h:1200
bool hasattr(handle obj, handle name)
Definition: pytypes.h:517
def _sanitize_message(thing)
Definition: conftest.py:170
def pytest_assertrepr_compare(op, left, right)
Definition: conftest.py:184
def pytest_configure()
Definition: conftest.py:206
def suppress(exception)
Definition: conftest.py:191
def msg()
Definition: conftest.py:178
def _sanitize_general(s)
Definition: conftest.py:149
def always_forkserver_on_unix()
Definition: conftest.py:28
def _split_and_sort(s)
Definition: conftest.py:36
def _strip_and_dedent(s)
Definition: conftest.py:31
def _sanitize_docstring(thing)
Definition: conftest.py:158
def capture(capsys)
Definition: conftest.py:124
def doc()
Definition: conftest.py:165
def _make_explanation(a, b)
Definition: conftest.py:41
def gc_collect()
Definition: conftest.py:199
def pytest_report_header(config)
Definition: conftest.py:227