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


gtsam
Author(s):
autogenerated on Sun Nov 10 2024 04:00:36