python_api.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import
  3. import math
  4. import pprint
  5. import sys
  6. import warnings
  7. from decimal import Decimal
  8. from numbers import Number
  9. from more_itertools.more import always_iterable
  10. from six.moves import filterfalse
  11. from six.moves import zip
  12. import _pytest._code
  13. from _pytest import deprecated
  14. from _pytest.compat import isclass
  15. from _pytest.compat import Iterable
  16. from _pytest.compat import Mapping
  17. from _pytest.compat import Sized
  18. from _pytest.compat import STRING_TYPES
  19. from _pytest.outcomes import fail
  20. BASE_TYPE = (type, STRING_TYPES)
  21. def _cmp_raises_type_error(self, other):
  22. """__cmp__ implementation which raises TypeError. Used
  23. by Approx base classes to implement only == and != and raise a
  24. TypeError for other comparisons.
  25. Needed in Python 2 only, Python 3 all it takes is not implementing the
  26. other operators at all.
  27. """
  28. __tracebackhide__ = True
  29. raise TypeError(
  30. "Comparison operators other than == and != not supported by approx objects"
  31. )
  32. def _non_numeric_type_error(value, at):
  33. at_str = " at {}".format(at) if at else ""
  34. return TypeError(
  35. "cannot make approximate comparisons to non-numeric values: {!r} {}".format(
  36. value, at_str
  37. )
  38. )
  39. # builtin pytest.approx helper
  40. class ApproxBase(object):
  41. """
  42. Provide shared utilities for making approximate comparisons between numbers
  43. or sequences of numbers.
  44. """
  45. # Tell numpy to use our `__eq__` operator instead of its.
  46. __array_ufunc__ = None
  47. __array_priority__ = 100
  48. def __init__(self, expected, rel=None, abs=None, nan_ok=False):
  49. __tracebackhide__ = True
  50. self.expected = expected
  51. self.abs = abs
  52. self.rel = rel
  53. self.nan_ok = nan_ok
  54. self._check_type()
  55. def __repr__(self):
  56. raise NotImplementedError
  57. def __eq__(self, actual):
  58. return all(
  59. a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual)
  60. )
  61. __hash__ = None
  62. def __ne__(self, actual):
  63. return not (actual == self)
  64. if sys.version_info[0] == 2:
  65. __cmp__ = _cmp_raises_type_error
  66. def _approx_scalar(self, x):
  67. return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
  68. def _yield_comparisons(self, actual):
  69. """
  70. Yield all the pairs of numbers to be compared. This is used to
  71. implement the `__eq__` method.
  72. """
  73. raise NotImplementedError
  74. def _check_type(self):
  75. """
  76. Raise a TypeError if the expected value is not a valid type.
  77. """
  78. # This is only a concern if the expected value is a sequence. In every
  79. # other case, the approx() function ensures that the expected value has
  80. # a numeric type. For this reason, the default is to do nothing. The
  81. # classes that deal with sequences should reimplement this method to
  82. # raise if there are any non-numeric elements in the sequence.
  83. pass
  84. def _recursive_list_map(f, x):
  85. if isinstance(x, list):
  86. return list(_recursive_list_map(f, xi) for xi in x)
  87. else:
  88. return f(x)
  89. class ApproxNumpy(ApproxBase):
  90. """
  91. Perform approximate comparisons where the expected value is numpy array.
  92. """
  93. def __repr__(self):
  94. list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
  95. return "approx({!r})".format(list_scalars)
  96. if sys.version_info[0] == 2:
  97. __cmp__ = _cmp_raises_type_error
  98. def __eq__(self, actual):
  99. import numpy as np
  100. # self.expected is supposed to always be an array here
  101. if not np.isscalar(actual):
  102. try:
  103. actual = np.asarray(actual)
  104. except: # noqa
  105. raise TypeError("cannot compare '{}' to numpy.ndarray".format(actual))
  106. if not np.isscalar(actual) and actual.shape != self.expected.shape:
  107. return False
  108. return ApproxBase.__eq__(self, actual)
  109. def _yield_comparisons(self, actual):
  110. import numpy as np
  111. # `actual` can either be a numpy array or a scalar, it is treated in
  112. # `__eq__` before being passed to `ApproxBase.__eq__`, which is the
  113. # only method that calls this one.
  114. if np.isscalar(actual):
  115. for i in np.ndindex(self.expected.shape):
  116. yield actual, self.expected[i].item()
  117. else:
  118. for i in np.ndindex(self.expected.shape):
  119. yield actual[i].item(), self.expected[i].item()
  120. class ApproxMapping(ApproxBase):
  121. """
  122. Perform approximate comparisons where the expected value is a mapping with
  123. numeric values (the keys can be anything).
  124. """
  125. def __repr__(self):
  126. return "approx({!r})".format(
  127. {k: self._approx_scalar(v) for k, v in self.expected.items()}
  128. )
  129. def __eq__(self, actual):
  130. if set(actual.keys()) != set(self.expected.keys()):
  131. return False
  132. return ApproxBase.__eq__(self, actual)
  133. def _yield_comparisons(self, actual):
  134. for k in self.expected.keys():
  135. yield actual[k], self.expected[k]
  136. def _check_type(self):
  137. __tracebackhide__ = True
  138. for key, value in self.expected.items():
  139. if isinstance(value, type(self.expected)):
  140. msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}"
  141. raise TypeError(msg.format(key, value, pprint.pformat(self.expected)))
  142. elif not isinstance(value, Number):
  143. raise _non_numeric_type_error(self.expected, at="key={!r}".format(key))
  144. class ApproxSequencelike(ApproxBase):
  145. """
  146. Perform approximate comparisons where the expected value is a sequence of
  147. numbers.
  148. """
  149. def __repr__(self):
  150. seq_type = type(self.expected)
  151. if seq_type not in (tuple, list, set):
  152. seq_type = list
  153. return "approx({!r})".format(
  154. seq_type(self._approx_scalar(x) for x in self.expected)
  155. )
  156. def __eq__(self, actual):
  157. if len(actual) != len(self.expected):
  158. return False
  159. return ApproxBase.__eq__(self, actual)
  160. def _yield_comparisons(self, actual):
  161. return zip(actual, self.expected)
  162. def _check_type(self):
  163. __tracebackhide__ = True
  164. for index, x in enumerate(self.expected):
  165. if isinstance(x, type(self.expected)):
  166. msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}"
  167. raise TypeError(msg.format(x, index, pprint.pformat(self.expected)))
  168. elif not isinstance(x, Number):
  169. raise _non_numeric_type_error(
  170. self.expected, at="index {}".format(index)
  171. )
  172. class ApproxScalar(ApproxBase):
  173. """
  174. Perform approximate comparisons where the expected value is a single number.
  175. """
  176. DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
  177. DEFAULT_RELATIVE_TOLERANCE = 1e-6
  178. def __repr__(self):
  179. """
  180. Return a string communicating both the expected value and the tolerance
  181. for the comparison being made, e.g. '1.0 +- 1e-6'. Use the unicode
  182. plus/minus symbol if this is python3 (it's too hard to get right for
  183. python2).
  184. """
  185. if isinstance(self.expected, complex):
  186. return str(self.expected)
  187. # Infinities aren't compared using tolerances, so don't show a
  188. # tolerance.
  189. if math.isinf(self.expected):
  190. return str(self.expected)
  191. # If a sensible tolerance can't be calculated, self.tolerance will
  192. # raise a ValueError. In this case, display '???'.
  193. try:
  194. vetted_tolerance = "{:.1e}".format(self.tolerance)
  195. except ValueError:
  196. vetted_tolerance = "???"
  197. if sys.version_info[0] == 2:
  198. return "{} +- {}".format(self.expected, vetted_tolerance)
  199. else:
  200. return u"{} \u00b1 {}".format(self.expected, vetted_tolerance)
  201. def __eq__(self, actual):
  202. """
  203. Return true if the given value is equal to the expected value within
  204. the pre-specified tolerance.
  205. """
  206. if _is_numpy_array(actual):
  207. # Call ``__eq__()`` manually to prevent infinite-recursion with
  208. # numpy<1.13. See #3748.
  209. return all(self.__eq__(a) for a in actual.flat)
  210. # Short-circuit exact equality.
  211. if actual == self.expected:
  212. return True
  213. # Allow the user to control whether NaNs are considered equal to each
  214. # other or not. The abs() calls are for compatibility with complex
  215. # numbers.
  216. if math.isnan(abs(self.expected)):
  217. return self.nan_ok and math.isnan(abs(actual))
  218. # Infinity shouldn't be approximately equal to anything but itself, but
  219. # if there's a relative tolerance, it will be infinite and infinity
  220. # will seem approximately equal to everything. The equal-to-itself
  221. # case would have been short circuited above, so here we can just
  222. # return false if the expected value is infinite. The abs() call is
  223. # for compatibility with complex numbers.
  224. if math.isinf(abs(self.expected)):
  225. return False
  226. # Return true if the two numbers are within the tolerance.
  227. return abs(self.expected - actual) <= self.tolerance
  228. __hash__ = None
  229. @property
  230. def tolerance(self):
  231. """
  232. Return the tolerance for the comparison. This could be either an
  233. absolute tolerance or a relative tolerance, depending on what the user
  234. specified or which would be larger.
  235. """
  236. def set_default(x, default):
  237. return x if x is not None else default
  238. # Figure out what the absolute tolerance should be. ``self.abs`` is
  239. # either None or a value specified by the user.
  240. absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE)
  241. if absolute_tolerance < 0:
  242. raise ValueError(
  243. "absolute tolerance can't be negative: {}".format(absolute_tolerance)
  244. )
  245. if math.isnan(absolute_tolerance):
  246. raise ValueError("absolute tolerance can't be NaN.")
  247. # If the user specified an absolute tolerance but not a relative one,
  248. # just return the absolute tolerance.
  249. if self.rel is None:
  250. if self.abs is not None:
  251. return absolute_tolerance
  252. # Figure out what the relative tolerance should be. ``self.rel`` is
  253. # either None or a value specified by the user. This is done after
  254. # we've made sure the user didn't ask for an absolute tolerance only,
  255. # because we don't want to raise errors about the relative tolerance if
  256. # we aren't even going to use it.
  257. relative_tolerance = set_default(
  258. self.rel, self.DEFAULT_RELATIVE_TOLERANCE
  259. ) * abs(self.expected)
  260. if relative_tolerance < 0:
  261. raise ValueError(
  262. "relative tolerance can't be negative: {}".format(absolute_tolerance)
  263. )
  264. if math.isnan(relative_tolerance):
  265. raise ValueError("relative tolerance can't be NaN.")
  266. # Return the larger of the relative and absolute tolerances.
  267. return max(relative_tolerance, absolute_tolerance)
  268. class ApproxDecimal(ApproxScalar):
  269. """
  270. Perform approximate comparisons where the expected value is a decimal.
  271. """
  272. DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12")
  273. DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6")
  274. def approx(expected, rel=None, abs=None, nan_ok=False):
  275. """
  276. Assert that two numbers (or two sets of numbers) are equal to each other
  277. within some tolerance.
  278. Due to the `intricacies of floating-point arithmetic`__, numbers that we
  279. would intuitively expect to be equal are not always so::
  280. >>> 0.1 + 0.2 == 0.3
  281. False
  282. __ https://docs.python.org/3/tutorial/floatingpoint.html
  283. This problem is commonly encountered when writing tests, e.g. when making
  284. sure that floating-point values are what you expect them to be. One way to
  285. deal with this problem is to assert that two floating-point numbers are
  286. equal to within some appropriate tolerance::
  287. >>> abs((0.1 + 0.2) - 0.3) < 1e-6
  288. True
  289. However, comparisons like this are tedious to write and difficult to
  290. understand. Furthermore, absolute comparisons like the one above are
  291. usually discouraged because there's no tolerance that works well for all
  292. situations. ``1e-6`` is good for numbers around ``1``, but too small for
  293. very big numbers and too big for very small ones. It's better to express
  294. the tolerance as a fraction of the expected value, but relative comparisons
  295. like that are even more difficult to write correctly and concisely.
  296. The ``approx`` class performs floating-point comparisons using a syntax
  297. that's as intuitive as possible::
  298. >>> from pytest import approx
  299. >>> 0.1 + 0.2 == approx(0.3)
  300. True
  301. The same syntax also works for sequences of numbers::
  302. >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6))
  303. True
  304. Dictionary *values*::
  305. >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6})
  306. True
  307. ``numpy`` arrays::
  308. >>> import numpy as np # doctest: +SKIP
  309. >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP
  310. True
  311. And for a ``numpy`` array against a scalar::
  312. >>> import numpy as np # doctest: +SKIP
  313. >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP
  314. True
  315. By default, ``approx`` considers numbers within a relative tolerance of
  316. ``1e-6`` (i.e. one part in a million) of its expected value to be equal.
  317. This treatment would lead to surprising results if the expected value was
  318. ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``.
  319. To handle this case less surprisingly, ``approx`` also considers numbers
  320. within an absolute tolerance of ``1e-12`` of its expected value to be
  321. equal. Infinity and NaN are special cases. Infinity is only considered
  322. equal to itself, regardless of the relative tolerance. NaN is not
  323. considered equal to anything by default, but you can make it be equal to
  324. itself by setting the ``nan_ok`` argument to True. (This is meant to
  325. facilitate comparing arrays that use NaN to mean "no data".)
  326. Both the relative and absolute tolerances can be changed by passing
  327. arguments to the ``approx`` constructor::
  328. >>> 1.0001 == approx(1)
  329. False
  330. >>> 1.0001 == approx(1, rel=1e-3)
  331. True
  332. >>> 1.0001 == approx(1, abs=1e-3)
  333. True
  334. If you specify ``abs`` but not ``rel``, the comparison will not consider
  335. the relative tolerance at all. In other words, two numbers that are within
  336. the default relative tolerance of ``1e-6`` will still be considered unequal
  337. if they exceed the specified absolute tolerance. If you specify both
  338. ``abs`` and ``rel``, the numbers will be considered equal if either
  339. tolerance is met::
  340. >>> 1 + 1e-8 == approx(1)
  341. True
  342. >>> 1 + 1e-8 == approx(1, abs=1e-12)
  343. False
  344. >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12)
  345. True
  346. If you're thinking about using ``approx``, then you might want to know how
  347. it compares to other good ways of comparing floating-point numbers. All of
  348. these algorithms are based on relative and absolute tolerances and should
  349. agree for the most part, but they do have meaningful differences:
  350. - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative
  351. tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute
  352. tolerance is met. Because the relative tolerance is calculated w.r.t.
  353. both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor
  354. ``b`` is a "reference value"). You have to specify an absolute tolerance
  355. if you want to compare to ``0.0`` because there is no tolerance by
  356. default. Only available in python>=3.5. `More information...`__
  357. __ https://docs.python.org/3/library/math.html#math.isclose
  358. - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference
  359. between ``a`` and ``b`` is less that the sum of the relative tolerance
  360. w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance
  361. is only calculated w.r.t. ``b``, this test is asymmetric and you can
  362. think of ``b`` as the reference value. Support for comparing sequences
  363. is provided by ``numpy.allclose``. `More information...`__
  364. __ http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.isclose.html
  365. - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b``
  366. are within an absolute tolerance of ``1e-7``. No relative tolerance is
  367. considered and the absolute tolerance cannot be changed, so this function
  368. is not appropriate for very large or very small numbers. Also, it's only
  369. available in subclasses of ``unittest.TestCase`` and it's ugly because it
  370. doesn't follow PEP8. `More information...`__
  371. __ https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertAlmostEqual
  372. - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative
  373. tolerance is met w.r.t. ``b`` or if the absolute tolerance is met.
  374. Because the relative tolerance is only calculated w.r.t. ``b``, this test
  375. is asymmetric and you can think of ``b`` as the reference value. In the
  376. special case that you explicitly specify an absolute tolerance but not a
  377. relative tolerance, only the absolute tolerance is considered.
  378. .. warning::
  379. .. versionchanged:: 3.2
  380. In order to avoid inconsistent behavior, ``TypeError`` is
  381. raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons.
  382. The example below illustrates the problem::
  383. assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10)
  384. assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10)
  385. In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)``
  386. to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to
  387. comparison. This is because the call hierarchy of rich comparisons
  388. follows a fixed behavior. `More information...`__
  389. __ https://docs.python.org/3/reference/datamodel.html#object.__ge__
  390. """
  391. # Delegate the comparison to a class that knows how to deal with the type
  392. # of the expected value (e.g. int, float, list, dict, numpy.array, etc).
  393. #
  394. # The primary responsibility of these classes is to implement ``__eq__()``
  395. # and ``__repr__()``. The former is used to actually check if some
  396. # "actual" value is equivalent to the given expected value within the
  397. # allowed tolerance. The latter is used to show the user the expected
  398. # value and tolerance, in the case that a test failed.
  399. #
  400. # The actual logic for making approximate comparisons can be found in
  401. # ApproxScalar, which is used to compare individual numbers. All of the
  402. # other Approx classes eventually delegate to this class. The ApproxBase
  403. # class provides some convenient methods and overloads, but isn't really
  404. # essential.
  405. __tracebackhide__ = True
  406. if isinstance(expected, Decimal):
  407. cls = ApproxDecimal
  408. elif isinstance(expected, Number):
  409. cls = ApproxScalar
  410. elif isinstance(expected, Mapping):
  411. cls = ApproxMapping
  412. elif _is_numpy_array(expected):
  413. cls = ApproxNumpy
  414. elif (
  415. isinstance(expected, Iterable)
  416. and isinstance(expected, Sized)
  417. and not isinstance(expected, STRING_TYPES)
  418. ):
  419. cls = ApproxSequencelike
  420. else:
  421. raise _non_numeric_type_error(expected, at=None)
  422. return cls(expected, rel, abs, nan_ok)
  423. def _is_numpy_array(obj):
  424. """
  425. Return true if the given object is a numpy array. Make a special effort to
  426. avoid importing numpy unless it's really necessary.
  427. """
  428. import sys
  429. np = sys.modules.get("numpy")
  430. if np is not None:
  431. return isinstance(obj, np.ndarray)
  432. return False
  433. # builtin pytest.raises helper
  434. def raises(expected_exception, *args, **kwargs):
  435. r"""
  436. Assert that a code block/function call raises ``expected_exception``
  437. or raise a failure exception otherwise.
  438. :kwparam match: if specified, a string containing a regular expression,
  439. or a regular expression object, that is tested against the string
  440. representation of the exception using ``re.search``. To match a literal
  441. string that may contain `special characters`__, the pattern can
  442. first be escaped with ``re.escape``.
  443. __ https://docs.python.org/3/library/re.html#regular-expression-syntax
  444. :kwparam message: **(deprecated since 4.1)** if specified, provides a custom failure message
  445. if the exception is not raised. See :ref:`the deprecation docs <raises message deprecated>` for a workaround.
  446. .. currentmodule:: _pytest._code
  447. Use ``pytest.raises`` as a context manager, which will capture the exception of the given
  448. type::
  449. >>> with raises(ZeroDivisionError):
  450. ... 1/0
  451. If the code block does not raise the expected exception (``ZeroDivisionError`` in the example
  452. above), or no exception at all, the check will fail instead.
  453. You can also use the keyword argument ``match`` to assert that the
  454. exception matches a text or regex::
  455. >>> with raises(ValueError, match='must be 0 or None'):
  456. ... raise ValueError("value must be 0 or None")
  457. >>> with raises(ValueError, match=r'must be \d+$'):
  458. ... raise ValueError("value must be 42")
  459. The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the
  460. details of the captured exception::
  461. >>> with raises(ValueError) as exc_info:
  462. ... raise ValueError("value must be 42")
  463. >>> assert exc_info.type is ValueError
  464. >>> assert exc_info.value.args[0] == "value must be 42"
  465. .. deprecated:: 4.1
  466. In the context manager form you may use the keyword argument
  467. ``message`` to specify a custom failure message that will be displayed
  468. in case the ``pytest.raises`` check fails. This has been deprecated as it
  469. is considered error prone as users often mean to use ``match`` instead.
  470. See :ref:`the deprecation docs <raises message deprecated>` for a workaround.
  471. .. note::
  472. When using ``pytest.raises`` as a context manager, it's worthwhile to
  473. note that normal context manager rules apply and that the exception
  474. raised *must* be the final line in the scope of the context manager.
  475. Lines of code after that, within the scope of the context manager will
  476. not be executed. For example::
  477. >>> value = 15
  478. >>> with raises(ValueError) as exc_info:
  479. ... if value > 10:
  480. ... raise ValueError("value must be <= 10")
  481. ... assert exc_info.type is ValueError # this will not execute
  482. Instead, the following approach must be taken (note the difference in
  483. scope)::
  484. >>> with raises(ValueError) as exc_info:
  485. ... if value > 10:
  486. ... raise ValueError("value must be <= 10")
  487. ...
  488. >>> assert exc_info.type is ValueError
  489. **Using with** ``pytest.mark.parametrize``
  490. When using :ref:`pytest.mark.parametrize ref`
  491. it is possible to parametrize tests such that
  492. some runs raise an exception and others do not.
  493. See :ref:`parametrizing_conditional_raising` for an example.
  494. **Legacy form**
  495. It is possible to specify a callable by passing a to-be-called lambda::
  496. >>> raises(ZeroDivisionError, lambda: 1/0)
  497. <ExceptionInfo ...>
  498. or you can specify an arbitrary callable with arguments::
  499. >>> def f(x): return 1/x
  500. ...
  501. >>> raises(ZeroDivisionError, f, 0)
  502. <ExceptionInfo ...>
  503. >>> raises(ZeroDivisionError, f, x=0)
  504. <ExceptionInfo ...>
  505. The form above is fully supported but discouraged for new code because the
  506. context manager form is regarded as more readable and less error-prone.
  507. .. note::
  508. Similar to caught exception objects in Python, explicitly clearing
  509. local references to returned ``ExceptionInfo`` objects can
  510. help the Python interpreter speed up its garbage collection.
  511. Clearing those references breaks a reference cycle
  512. (``ExceptionInfo`` --> caught exception --> frame stack raising
  513. the exception --> current frame stack --> local variables -->
  514. ``ExceptionInfo``) which makes Python keep all objects referenced
  515. from that cycle (including all local variables in the current
  516. frame) alive until the next cyclic garbage collection run. See the
  517. official Python ``try`` statement documentation for more detailed
  518. information.
  519. """
  520. __tracebackhide__ = True
  521. for exc in filterfalse(isclass, always_iterable(expected_exception, BASE_TYPE)):
  522. msg = (
  523. "exceptions must be old-style classes or"
  524. " derived from BaseException, not %s"
  525. )
  526. raise TypeError(msg % type(exc))
  527. message = "DID NOT RAISE {}".format(expected_exception)
  528. match_expr = None
  529. if not args:
  530. if "message" in kwargs:
  531. message = kwargs.pop("message")
  532. warnings.warn(deprecated.RAISES_MESSAGE_PARAMETER, stacklevel=2)
  533. if "match" in kwargs:
  534. match_expr = kwargs.pop("match")
  535. if kwargs:
  536. msg = "Unexpected keyword arguments passed to pytest.raises: "
  537. msg += ", ".join(sorted(kwargs))
  538. raise TypeError(msg)
  539. return RaisesContext(expected_exception, message, match_expr)
  540. elif isinstance(args[0], str):
  541. warnings.warn(deprecated.RAISES_EXEC, stacklevel=2)
  542. (code,) = args
  543. assert isinstance(code, str)
  544. frame = sys._getframe(1)
  545. loc = frame.f_locals.copy()
  546. loc.update(kwargs)
  547. # print "raises frame scope: %r" % frame.f_locals
  548. try:
  549. code = _pytest._code.Source(code).compile(_genframe=frame)
  550. exec(code, frame.f_globals, loc)
  551. # XXX didn't mean f_globals == f_locals something special?
  552. # this is destroyed here ...
  553. except expected_exception:
  554. return _pytest._code.ExceptionInfo.from_current()
  555. else:
  556. func = args[0]
  557. try:
  558. func(*args[1:], **kwargs)
  559. except expected_exception:
  560. return _pytest._code.ExceptionInfo.from_current()
  561. fail(message)
  562. raises.Exception = fail.Exception
  563. class RaisesContext(object):
  564. def __init__(self, expected_exception, message, match_expr):
  565. self.expected_exception = expected_exception
  566. self.message = message
  567. self.match_expr = match_expr
  568. self.excinfo = None
  569. def __enter__(self):
  570. self.excinfo = _pytest._code.ExceptionInfo.for_later()
  571. return self.excinfo
  572. def __exit__(self, *tp):
  573. __tracebackhide__ = True
  574. if tp[0] is None:
  575. fail(self.message)
  576. self.excinfo.__init__(tp)
  577. suppress_exception = issubclass(self.excinfo.type, self.expected_exception)
  578. if sys.version_info[0] == 2 and suppress_exception:
  579. sys.exc_clear()
  580. if self.match_expr is not None and suppress_exception:
  581. self.excinfo.match(self.match_expr)
  582. return suppress_exception