cppyabm  1.0.17
An agent-based library to integrate C++ and Python
conftest.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 """pytest configuration
3 
4 Extends output capture as needed by pybind11: ignore constructors, optional unordered lines.
5 Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences.
6 """
7 
8 import contextlib
9 import difflib
10 import gc
11 import re
12 import textwrap
13 
14 import pytest
15 
16 import env
17 
18 # Early diagnostic for failed imports
19 import pybind11_tests # noqa: F401
20 
21 _unicode_marker = re.compile(r"u(\'[^\']*\')")
22 _long_marker = re.compile(r"([0-9])L")
23 _hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
24 
25 # Avoid collecting Python3 only files
26 collect_ignore = []
27 if env.PY2:
28  collect_ignore.append("test_async.py")
29 
30 
31 def _strip_and_dedent(s):
32  """For triple-quote strings"""
33  return textwrap.dedent(s.lstrip("\n").rstrip())
34 
35 
36 def _split_and_sort(s):
37  """For output which does not require specific line order"""
38  return sorted(_strip_and_dedent(s).splitlines())
39 
40 
41 def _make_explanation(a, b):
42  """Explanation for a failed assert -- the a and b arguments are List[str]"""
43  return ["--- actual / +++ expected"] + [
44  line.strip("\n") for line in difflib.ndiff(a, b)
45  ]
46 
47 
48 class Output(object):
49  """Basic output post-processing and comparison"""
50 
51  def __init__(self, string):
52  self.string = string
53  self.explanation = []
54 
55  def __str__(self):
56  return self.string
57 
58  def __eq__(self, other):
59  # Ignore constructor/destructor output which is prefixed with "###"
60  a = [
61  line
62  for line in self.string.strip().splitlines()
63  if not line.startswith("###")
64  ]
65  b = _strip_and_dedent(other).splitlines()
66  if a == b:
67  return True
68  else:
69  self.explanation = _make_explanation(a, b)
70  return False
71 
72 
74  """Custom comparison for output without strict line ordering"""
75 
76  def __eq__(self, other):
77  a = _split_and_sort(self.string)
78  b = _split_and_sort(other)
79  if a == b:
80  return True
81  else:
82  self.explanation = _make_explanation(a, b)
83  return False
84 
85 
86 class Capture(object):
87  def __init__(self, capfd):
88  self.capfd = capfd
89  self.out = ""
90  self.err = ""
91 
92  def __enter__(self):
93  self.capfd.readouterr()
94  return self
95 
96  def __exit__(self, *args):
97  self.out, self.err = self.capfd.readouterr()
98 
99  def __eq__(self, other):
100  a = Output(self.out)
101  b = other
102  if a == b:
103  return True
104  else:
105  self.explanation = a.explanation
106  return False
107 
108  def __str__(self):
109  return self.out
110 
111  def __contains__(self, item):
112  return item in self.out
113 
114  @property
115  def unordered(self):
116  return Unordered(self.out)
117 
118  @property
119  def stderr(self):
120  return Output(self.err)
121 
122 
123 @pytest.fixture
124 def capture(capsys):
125  """Extended `capsys` with context manager and custom equality operators"""
126  return Capture(capsys)
127 
128 
130  def __init__(self, sanitizer):
131  self.sanitizer = sanitizer
132  self.string = ""
133  self.explanation = []
134 
135  def __call__(self, thing):
136  self.string = self.sanitizer(thing)
137  return self
138 
139  def __eq__(self, other):
140  a = self.string
141  b = _strip_and_dedent(other)
142  if a == b:
143  return True
144  else:
145  self.explanation = _make_explanation(a.splitlines(), b.splitlines())
146  return False
147 
148 
149 def _sanitize_general(s):
150  s = s.strip()
151  s = s.replace("pybind11_tests.", "m.")
152  s = s.replace("unicode", "str")
153  s = _long_marker.sub(r"\1", s)
154  s = _unicode_marker.sub(r"\1", s)
155  return s
156 
157 
158 def _sanitize_docstring(thing):
159  s = thing.__doc__
160  s = _sanitize_general(s)
161  return s
162 
163 
164 @pytest.fixture
165 def doc():
166  """Sanitize docstrings and add custom failure explanation"""
167  return SanitizedString(_sanitize_docstring)
168 
169 
170 def _sanitize_message(thing):
171  s = str(thing)
172  s = _sanitize_general(s)
173  s = _hexadecimal.sub("0", s)
174  return s
175 
176 
177 @pytest.fixture
178 def msg():
179  """Sanitize messages and add custom failure explanation"""
180  return SanitizedString(_sanitize_message)
181 
182 
183 # noinspection PyUnusedLocal
184 def pytest_assertrepr_compare(op, left, right):
185  """Hook to insert custom failure explanation"""
186  if hasattr(left, "explanation"):
187  return left.explanation
188 
189 
190 @contextlib.contextmanager
191 def suppress(exception):
192  """Suppress the desired exception"""
193  try:
194  yield
195  except exception:
196  pass
197 
198 
200  """Run the garbage collector twice (needed when running
201  reference counting tests with PyPy)"""
202  gc.collect()
203  gc.collect()
204 
205 
207  pytest.suppress = suppress
208  pytest.gc_collect = gc_collect
conftest.msg
def msg()
Definition: conftest.py:178
conftest.Capture.__str__
def __str__(self)
Definition: conftest.py:108
conftest.SanitizedString.explanation
explanation
Definition: conftest.py:133
conftest.Capture.explanation
explanation
Definition: conftest.py:105
conftest.Output.__eq__
def __eq__(self, other)
Definition: conftest.py:58
conftest.Output.__init__
def __init__(self, string)
Definition: conftest.py:51
conftest.SanitizedString.__call__
def __call__(self, thing)
Definition: conftest.py:135
conftest.Capture.__contains__
def __contains__(self, item)
Definition: conftest.py:111
hasattr
bool hasattr(handle obj, handle name)
Definition: pytypes.h:405
conftest.pytest_configure
def pytest_configure()
Definition: conftest.py:206
conftest.Capture.err
err
Definition: conftest.py:90
conftest.Output.explanation
explanation
Definition: conftest.py:53
conftest.Capture.out
out
Definition: conftest.py:89
conftest.Unordered.__eq__
def __eq__(self, other)
Definition: conftest.py:76
conftest.Capture.__eq__
def __eq__(self, other)
Definition: conftest.py:99
conftest.suppress
def suppress(exception)
Definition: conftest.py:191
object
Definition: pytypes.h:232
conftest.Capture.unordered
def unordered(self)
Definition: conftest.py:115
conftest.Capture.__init__
def __init__(self, capfd)
Definition: conftest.py:87
conftest.Capture.stderr
def stderr(self)
Definition: conftest.py:119
conftest.capture
def capture(capsys)
Definition: conftest.py:124
conftest.Capture.__exit__
def __exit__(self, *args)
Definition: conftest.py:96
conftest.Unordered
Definition: conftest.py:73
conftest.Capture
Definition: conftest.py:86
str
Definition: pytypes.h:946
conftest.Output.string
string
Definition: conftest.py:52
conftest.Output.__str__
def __str__(self)
Definition: conftest.py:55
conftest.SanitizedString.__eq__
def __eq__(self, other)
Definition: conftest.py:139
conftest.Output
Definition: conftest.py:48
conftest.SanitizedString.sanitizer
sanitizer
Definition: conftest.py:131
conftest.SanitizedString
Definition: conftest.py:129
conftest.pytest_assertrepr_compare
def pytest_assertrepr_compare(op, left, right)
Definition: conftest.py:184
conftest.SanitizedString.__init__
def __init__(self, sanitizer)
Definition: conftest.py:130
conftest.doc
def doc()
Definition: conftest.py:165
conftest.Capture.capfd
capfd
Definition: conftest.py:88
conftest.SanitizedString.string
string
Definition: conftest.py:132
conftest.gc_collect
def gc_collect()
Definition: conftest.py:199
conftest.Capture.__enter__
def __enter__(self)
Definition: conftest.py:92