μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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