PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/Lib/site-packages/_pytest/recwarn.py

https://gitlab.com/phongphans61/machine-learning-tictactoe
Python | 296 lines | 258 code | 21 blank | 17 comment | 23 complexity | 866737ebe500a62b795f11574bd8f76d MD5 | raw file
  1. """Record warnings during test function execution."""
  2. import re
  3. import warnings
  4. from types import TracebackType
  5. from typing import Any
  6. from typing import Callable
  7. from typing import Generator
  8. from typing import Iterator
  9. from typing import List
  10. from typing import Optional
  11. from typing import overload
  12. from typing import Pattern
  13. from typing import Tuple
  14. from typing import Type
  15. from typing import TypeVar
  16. from typing import Union
  17. from _pytest.compat import final
  18. from _pytest.deprecated import check_ispytest
  19. from _pytest.fixtures import fixture
  20. from _pytest.outcomes import fail
  21. T = TypeVar("T")
  22. @fixture
  23. def recwarn() -> Generator["WarningsRecorder", None, None]:
  24. """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
  25. See http://docs.python.org/library/warnings.html for information
  26. on warning categories.
  27. """
  28. wrec = WarningsRecorder(_ispytest=True)
  29. with wrec:
  30. warnings.simplefilter("default")
  31. yield wrec
  32. @overload
  33. def deprecated_call(
  34. *, match: Optional[Union[str, Pattern[str]]] = ...
  35. ) -> "WarningsRecorder":
  36. ...
  37. @overload
  38. def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
  39. ...
  40. def deprecated_call(
  41. func: Optional[Callable[..., Any]] = None, *args: Any, **kwargs: Any
  42. ) -> Union["WarningsRecorder", Any]:
  43. """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning``.
  44. This function can be used as a context manager::
  45. >>> import warnings
  46. >>> def api_call_v2():
  47. ... warnings.warn('use v3 of this api', DeprecationWarning)
  48. ... return 200
  49. >>> import pytest
  50. >>> with pytest.deprecated_call():
  51. ... assert api_call_v2() == 200
  52. It can also be used by passing a function and ``*args`` and ``**kwargs``,
  53. in which case it will ensure calling ``func(*args, **kwargs)`` produces one of
  54. the warnings types above. The return value is the return value of the function.
  55. In the context manager form you may use the keyword argument ``match`` to assert
  56. that the warning matches a text or regex.
  57. The context manager produces a list of :class:`warnings.WarningMessage` objects,
  58. one for each warning raised.
  59. """
  60. __tracebackhide__ = True
  61. if func is not None:
  62. args = (func,) + args
  63. return warns((DeprecationWarning, PendingDeprecationWarning), *args, **kwargs)
  64. @overload
  65. def warns(
  66. expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]],
  67. *,
  68. match: Optional[Union[str, Pattern[str]]] = ...,
  69. ) -> "WarningsChecker":
  70. ...
  71. @overload
  72. def warns(
  73. expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]],
  74. func: Callable[..., T],
  75. *args: Any,
  76. **kwargs: Any,
  77. ) -> T:
  78. ...
  79. def warns(
  80. expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]],
  81. *args: Any,
  82. match: Optional[Union[str, Pattern[str]]] = None,
  83. **kwargs: Any,
  84. ) -> Union["WarningsChecker", Any]:
  85. r"""Assert that code raises a particular class of warning.
  86. Specifically, the parameter ``expected_warning`` can be a warning class or
  87. sequence of warning classes, and the inside the ``with`` block must issue a warning of that class or
  88. classes.
  89. This helper produces a list of :class:`warnings.WarningMessage` objects,
  90. one for each warning raised.
  91. This function can be used as a context manager, or any of the other ways
  92. :func:`pytest.raises` can be used::
  93. >>> import pytest
  94. >>> with pytest.warns(RuntimeWarning):
  95. ... warnings.warn("my warning", RuntimeWarning)
  96. In the context manager form you may use the keyword argument ``match`` to assert
  97. that the warning matches a text or regex::
  98. >>> with pytest.warns(UserWarning, match='must be 0 or None'):
  99. ... warnings.warn("value must be 0 or None", UserWarning)
  100. >>> with pytest.warns(UserWarning, match=r'must be \d+$'):
  101. ... warnings.warn("value must be 42", UserWarning)
  102. >>> with pytest.warns(UserWarning, match=r'must be \d+$'):
  103. ... warnings.warn("this is not here", UserWarning)
  104. Traceback (most recent call last):
  105. ...
  106. Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted...
  107. """
  108. __tracebackhide__ = True
  109. if not args:
  110. if kwargs:
  111. msg = "Unexpected keyword arguments passed to pytest.warns: "
  112. msg += ", ".join(sorted(kwargs))
  113. msg += "\nUse context-manager form instead?"
  114. raise TypeError(msg)
  115. return WarningsChecker(expected_warning, match_expr=match, _ispytest=True)
  116. else:
  117. func = args[0]
  118. if not callable(func):
  119. raise TypeError(
  120. "{!r} object (type: {}) must be callable".format(func, type(func))
  121. )
  122. with WarningsChecker(expected_warning, _ispytest=True):
  123. return func(*args[1:], **kwargs)
  124. class WarningsRecorder(warnings.catch_warnings):
  125. """A context manager to record raised warnings.
  126. Adapted from `warnings.catch_warnings`.
  127. """
  128. def __init__(self, *, _ispytest: bool = False) -> None:
  129. check_ispytest(_ispytest)
  130. # Type ignored due to the way typeshed handles warnings.catch_warnings.
  131. super().__init__(record=True) # type: ignore[call-arg]
  132. self._entered = False
  133. self._list: List[warnings.WarningMessage] = []
  134. @property
  135. def list(self) -> List["warnings.WarningMessage"]:
  136. """The list of recorded warnings."""
  137. return self._list
  138. def __getitem__(self, i: int) -> "warnings.WarningMessage":
  139. """Get a recorded warning by index."""
  140. return self._list[i]
  141. def __iter__(self) -> Iterator["warnings.WarningMessage"]:
  142. """Iterate through the recorded warnings."""
  143. return iter(self._list)
  144. def __len__(self) -> int:
  145. """The number of recorded warnings."""
  146. return len(self._list)
  147. def pop(self, cls: Type[Warning] = Warning) -> "warnings.WarningMessage":
  148. """Pop the first recorded warning, raise exception if not exists."""
  149. for i, w in enumerate(self._list):
  150. if issubclass(w.category, cls):
  151. return self._list.pop(i)
  152. __tracebackhide__ = True
  153. raise AssertionError("%r not found in warning list" % cls)
  154. def clear(self) -> None:
  155. """Clear the list of recorded warnings."""
  156. self._list[:] = []
  157. # Type ignored because it doesn't exactly warnings.catch_warnings.__enter__
  158. # -- it returns a List but we only emulate one.
  159. def __enter__(self) -> "WarningsRecorder": # type: ignore
  160. if self._entered:
  161. __tracebackhide__ = True
  162. raise RuntimeError("Cannot enter %r twice" % self)
  163. _list = super().__enter__()
  164. # record=True means it's None.
  165. assert _list is not None
  166. self._list = _list
  167. warnings.simplefilter("always")
  168. return self
  169. def __exit__(
  170. self,
  171. exc_type: Optional[Type[BaseException]],
  172. exc_val: Optional[BaseException],
  173. exc_tb: Optional[TracebackType],
  174. ) -> None:
  175. if not self._entered:
  176. __tracebackhide__ = True
  177. raise RuntimeError("Cannot exit %r without entering first" % self)
  178. super().__exit__(exc_type, exc_val, exc_tb)
  179. # Built-in catch_warnings does not reset entered state so we do it
  180. # manually here for this context manager to become reusable.
  181. self._entered = False
  182. @final
  183. class WarningsChecker(WarningsRecorder):
  184. def __init__(
  185. self,
  186. expected_warning: Optional[
  187. Union[Type[Warning], Tuple[Type[Warning], ...]]
  188. ] = None,
  189. match_expr: Optional[Union[str, Pattern[str]]] = None,
  190. *,
  191. _ispytest: bool = False,
  192. ) -> None:
  193. check_ispytest(_ispytest)
  194. super().__init__(_ispytest=True)
  195. msg = "exceptions must be derived from Warning, not %s"
  196. if expected_warning is None:
  197. expected_warning_tup = None
  198. elif isinstance(expected_warning, tuple):
  199. for exc in expected_warning:
  200. if not issubclass(exc, Warning):
  201. raise TypeError(msg % type(exc))
  202. expected_warning_tup = expected_warning
  203. elif issubclass(expected_warning, Warning):
  204. expected_warning_tup = (expected_warning,)
  205. else:
  206. raise TypeError(msg % type(expected_warning))
  207. self.expected_warning = expected_warning_tup
  208. self.match_expr = match_expr
  209. def __exit__(
  210. self,
  211. exc_type: Optional[Type[BaseException]],
  212. exc_val: Optional[BaseException],
  213. exc_tb: Optional[TracebackType],
  214. ) -> None:
  215. super().__exit__(exc_type, exc_val, exc_tb)
  216. __tracebackhide__ = True
  217. # only check if we're not currently handling an exception
  218. if exc_type is None and exc_val is None and exc_tb is None:
  219. if self.expected_warning is not None:
  220. if not any(issubclass(r.category, self.expected_warning) for r in self):
  221. __tracebackhide__ = True
  222. fail(
  223. "DID NOT WARN. No warnings of type {} was emitted. "
  224. "The list of emitted warnings is: {}.".format(
  225. self.expected_warning, [each.message for each in self]
  226. )
  227. )
  228. elif self.match_expr is not None:
  229. for r in self:
  230. if issubclass(r.category, self.expected_warning):
  231. if re.compile(self.match_expr).search(str(r.message)):
  232. break
  233. else:
  234. fail(
  235. "DID NOT WARN. No warnings of type {} matching"
  236. " ('{}') was emitted. The list of emitted warnings"
  237. " is: {}.".format(
  238. self.expected_warning,
  239. self.match_expr,
  240. [each.message for each in self],
  241. )
  242. )