plugin.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. import asyncio
  2. import builtins
  3. import functools
  4. import inspect
  5. import unittest.mock
  6. import warnings
  7. from dataclasses import dataclass
  8. from dataclasses import field
  9. from typing import Any
  10. from typing import Callable
  11. from typing import Dict
  12. from typing import Generator
  13. from typing import Iterable
  14. from typing import Iterator
  15. from typing import List
  16. from typing import Mapping
  17. from typing import Optional
  18. from typing import Tuple
  19. from typing import Type
  20. from typing import TypeVar
  21. from typing import Union
  22. from typing import cast
  23. from typing import overload
  24. import pytest
  25. from ._util import get_mock_module
  26. from ._util import parse_ini_boolean
  27. _T = TypeVar("_T")
  28. AsyncMockType = unittest.mock.AsyncMock
  29. MockType = Union[
  30. unittest.mock.MagicMock,
  31. unittest.mock.AsyncMock,
  32. unittest.mock.NonCallableMagicMock,
  33. ]
  34. class PytestMockWarning(UserWarning):
  35. """Base class for all warnings emitted by pytest-mock."""
  36. @dataclass
  37. class MockCacheItem:
  38. mock: MockType
  39. patch: Optional[Any] = None
  40. @dataclass
  41. class MockCache:
  42. """
  43. Cache MagicMock and Patcher instances so we can undo them later.
  44. """
  45. cache: List[MockCacheItem] = field(default_factory=list)
  46. def _find(self, mock: MockType) -> MockCacheItem:
  47. for mock_item in self.cache:
  48. if mock_item.mock is mock:
  49. return mock_item
  50. raise ValueError("This mock object is not registered")
  51. def add(self, mock: MockType, **kwargs: Any) -> MockCacheItem:
  52. self.cache.append(MockCacheItem(mock=mock, **kwargs))
  53. return self.cache[-1]
  54. def remove(self, mock: MockType) -> None:
  55. mock_item = self._find(mock)
  56. if mock_item.patch:
  57. mock_item.patch.stop()
  58. self.cache.remove(mock_item)
  59. def clear(self) -> None:
  60. for mock_item in reversed(self.cache):
  61. if mock_item.patch is not None:
  62. mock_item.patch.stop()
  63. self.cache.clear()
  64. def __iter__(self) -> Iterator[MockCacheItem]:
  65. return iter(self.cache)
  66. class MockerFixture:
  67. """
  68. Fixture that provides the same interface to functions in the mock module,
  69. ensuring that they are uninstalled at the end of each test.
  70. """
  71. def __init__(self, config: Any) -> None:
  72. self._mock_cache: MockCache = MockCache()
  73. self.mock_module = mock_module = get_mock_module(config)
  74. self.patch = self._Patcher(self._mock_cache, mock_module) # type: MockerFixture._Patcher
  75. # aliases for convenience
  76. self.Mock = mock_module.Mock
  77. self.MagicMock = mock_module.MagicMock
  78. self.NonCallableMock = mock_module.NonCallableMock
  79. self.NonCallableMagicMock = mock_module.NonCallableMagicMock
  80. self.PropertyMock = mock_module.PropertyMock
  81. if hasattr(mock_module, "AsyncMock"):
  82. self.AsyncMock = mock_module.AsyncMock
  83. self.call = mock_module.call
  84. self.ANY = mock_module.ANY
  85. self.DEFAULT = mock_module.DEFAULT
  86. self.sentinel = mock_module.sentinel
  87. self.mock_open = mock_module.mock_open
  88. if hasattr(mock_module, "seal"):
  89. self.seal = mock_module.seal
  90. def create_autospec(
  91. self, spec: Any, spec_set: bool = False, instance: bool = False, **kwargs: Any
  92. ) -> MockType:
  93. m: MockType = self.mock_module.create_autospec(
  94. spec, spec_set, instance, **kwargs
  95. )
  96. self._mock_cache.add(m)
  97. return m
  98. def resetall(
  99. self, *, return_value: bool = False, side_effect: bool = False
  100. ) -> None:
  101. """
  102. Call reset_mock() on all patchers started by this fixture.
  103. :param bool return_value: Reset the return_value of mocks.
  104. :param bool side_effect: Reset the side_effect of mocks.
  105. """
  106. supports_reset_mock_with_args: Tuple[Type[Any], ...]
  107. if hasattr(self, "AsyncMock"):
  108. supports_reset_mock_with_args = (self.Mock, self.AsyncMock)
  109. else:
  110. supports_reset_mock_with_args = (self.Mock,)
  111. for mock_item in self._mock_cache:
  112. # See issue #237.
  113. if not hasattr(mock_item.mock, "reset_mock"):
  114. continue
  115. # NOTE: The mock may be a dictionary
  116. if hasattr(mock_item.mock, "spy_return_list"):
  117. mock_item.mock.spy_return_list = []
  118. if isinstance(mock_item.mock, supports_reset_mock_with_args):
  119. mock_item.mock.reset_mock(
  120. return_value=return_value, side_effect=side_effect
  121. )
  122. else:
  123. mock_item.mock.reset_mock()
  124. def stopall(self) -> None:
  125. """
  126. Stop all patchers started by this fixture. Can be safely called multiple
  127. times.
  128. """
  129. self._mock_cache.clear()
  130. def stop(self, mock: unittest.mock.MagicMock) -> None:
  131. """
  132. Stops a previous patch or spy call by passing the ``MagicMock`` object
  133. returned by it.
  134. """
  135. self._mock_cache.remove(mock)
  136. def spy(self, obj: object, name: str) -> MockType:
  137. """
  138. Create a spy of method. It will run method normally, but it is now
  139. possible to use `mock` call features with it, like call count.
  140. :param obj: An object.
  141. :param name: A method in object.
  142. :return: Spy object.
  143. """
  144. method = getattr(obj, name)
  145. def wrapper(*args, **kwargs):
  146. spy_obj.spy_return = None
  147. spy_obj.spy_exception = None
  148. try:
  149. r = method(*args, **kwargs)
  150. except BaseException as e:
  151. spy_obj.spy_exception = e
  152. raise
  153. else:
  154. spy_obj.spy_return = r
  155. spy_obj.spy_return_list.append(r)
  156. return r
  157. async def async_wrapper(*args, **kwargs):
  158. spy_obj.spy_return = None
  159. spy_obj.spy_exception = None
  160. try:
  161. r = await method(*args, **kwargs)
  162. except BaseException as e:
  163. spy_obj.spy_exception = e
  164. raise
  165. else:
  166. spy_obj.spy_return = r
  167. spy_obj.spy_return_list.append(r)
  168. return r
  169. if asyncio.iscoroutinefunction(method):
  170. wrapped = functools.update_wrapper(async_wrapper, method)
  171. else:
  172. wrapped = functools.update_wrapper(wrapper, method)
  173. autospec = inspect.ismethod(method) or inspect.isfunction(method)
  174. spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec)
  175. spy_obj.spy_return = None
  176. spy_obj.spy_return_list = []
  177. spy_obj.spy_exception = None
  178. return spy_obj
  179. def stub(self, name: Optional[str] = None) -> unittest.mock.MagicMock:
  180. """
  181. Create a stub method. It accepts any arguments. Ideal to register to
  182. callbacks in tests.
  183. :param name: the constructed stub's name as used in repr
  184. :return: Stub object.
  185. """
  186. return cast(
  187. unittest.mock.MagicMock,
  188. self.mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name),
  189. )
  190. def async_stub(self, name: Optional[str] = None) -> AsyncMockType:
  191. """
  192. Create a async stub method. It accepts any arguments. Ideal to register to
  193. callbacks in tests.
  194. :param name: the constructed stub's name as used in repr
  195. :return: Stub object.
  196. """
  197. return cast(
  198. AsyncMockType,
  199. self.mock_module.AsyncMock(spec=lambda *args, **kwargs: None, name=name),
  200. )
  201. class _Patcher:
  202. """
  203. Object to provide the same interface as mock.patch, mock.patch.object,
  204. etc. We need this indirection to keep the same API of the mock package.
  205. """
  206. DEFAULT = object()
  207. def __init__(self, mock_cache, mock_module):
  208. self.__mock_cache = mock_cache
  209. self.mock_module = mock_module
  210. def _start_patch(
  211. self, mock_func: Any, warn_on_mock_enter: bool, *args: Any, **kwargs: Any
  212. ) -> MockType:
  213. """Patches something by calling the given function from the mock
  214. module, registering the patch to stop it later and returns the
  215. mock object resulting from the mock call.
  216. """
  217. p = mock_func(*args, **kwargs)
  218. mocked: MockType = p.start()
  219. self.__mock_cache.add(mock=mocked, patch=p)
  220. if hasattr(mocked, "reset_mock"):
  221. # check if `mocked` is actually a mock object, as depending on autospec or target
  222. # parameters `mocked` can be anything
  223. if hasattr(mocked, "__enter__") and warn_on_mock_enter:
  224. mocked.__enter__.side_effect = lambda: warnings.warn(
  225. "Mocks returned by pytest-mock do not need to be used as context managers. "
  226. "The mocker fixture automatically undoes mocking at the end of a test. "
  227. "This warning can be ignored if it was triggered by mocking a context manager. "
  228. "https://pytest-mock.readthedocs.io/en/latest/remarks.html#usage-as-context-manager",
  229. PytestMockWarning,
  230. stacklevel=5,
  231. )
  232. return mocked
  233. def object(
  234. self,
  235. target: object,
  236. attribute: str,
  237. new: object = DEFAULT,
  238. spec: Optional[object] = None,
  239. create: bool = False,
  240. spec_set: Optional[object] = None,
  241. autospec: Optional[object] = None,
  242. new_callable: object = None,
  243. **kwargs: Any,
  244. ) -> MockType:
  245. """API to mock.patch.object"""
  246. if new is self.DEFAULT:
  247. new = self.mock_module.DEFAULT
  248. return self._start_patch(
  249. self.mock_module.patch.object,
  250. True,
  251. target,
  252. attribute,
  253. new=new,
  254. spec=spec,
  255. create=create,
  256. spec_set=spec_set,
  257. autospec=autospec,
  258. new_callable=new_callable,
  259. **kwargs,
  260. )
  261. def context_manager(
  262. self,
  263. target: builtins.object,
  264. attribute: str,
  265. new: builtins.object = DEFAULT,
  266. spec: Optional[builtins.object] = None,
  267. create: bool = False,
  268. spec_set: Optional[builtins.object] = None,
  269. autospec: Optional[builtins.object] = None,
  270. new_callable: builtins.object = None,
  271. **kwargs: Any,
  272. ) -> MockType:
  273. """This is equivalent to mock.patch.object except that the returned mock
  274. does not issue a warning when used as a context manager."""
  275. if new is self.DEFAULT:
  276. new = self.mock_module.DEFAULT
  277. return self._start_patch(
  278. self.mock_module.patch.object,
  279. False,
  280. target,
  281. attribute,
  282. new=new,
  283. spec=spec,
  284. create=create,
  285. spec_set=spec_set,
  286. autospec=autospec,
  287. new_callable=new_callable,
  288. **kwargs,
  289. )
  290. def multiple(
  291. self,
  292. target: builtins.object,
  293. spec: Optional[builtins.object] = None,
  294. create: bool = False,
  295. spec_set: Optional[builtins.object] = None,
  296. autospec: Optional[builtins.object] = None,
  297. new_callable: Optional[builtins.object] = None,
  298. **kwargs: Any,
  299. ) -> Dict[str, MockType]:
  300. """API to mock.patch.multiple"""
  301. return self._start_patch(
  302. self.mock_module.patch.multiple,
  303. True,
  304. target,
  305. spec=spec,
  306. create=create,
  307. spec_set=spec_set,
  308. autospec=autospec,
  309. new_callable=new_callable,
  310. **kwargs,
  311. )
  312. def dict(
  313. self,
  314. in_dict: Union[Mapping[Any, Any], str],
  315. values: Union[Mapping[Any, Any], Iterable[Tuple[Any, Any]]] = (),
  316. clear: bool = False,
  317. **kwargs: Any,
  318. ) -> Any:
  319. """API to mock.patch.dict"""
  320. return self._start_patch(
  321. self.mock_module.patch.dict,
  322. True,
  323. in_dict,
  324. values=values,
  325. clear=clear,
  326. **kwargs,
  327. )
  328. @overload
  329. def __call__(
  330. self,
  331. target: str,
  332. new: None = ...,
  333. spec: Optional[builtins.object] = ...,
  334. create: bool = ...,
  335. spec_set: Optional[builtins.object] = ...,
  336. autospec: Optional[builtins.object] = ...,
  337. new_callable: None = ...,
  338. **kwargs: Any,
  339. ) -> MockType: ...
  340. @overload
  341. def __call__(
  342. self,
  343. target: str,
  344. new: _T,
  345. spec: Optional[builtins.object] = ...,
  346. create: bool = ...,
  347. spec_set: Optional[builtins.object] = ...,
  348. autospec: Optional[builtins.object] = ...,
  349. new_callable: None = ...,
  350. **kwargs: Any,
  351. ) -> _T: ...
  352. @overload
  353. def __call__(
  354. self,
  355. target: str,
  356. new: None,
  357. spec: Optional[builtins.object],
  358. create: bool,
  359. spec_set: Optional[builtins.object],
  360. autospec: Optional[builtins.object],
  361. new_callable: Callable[[], _T],
  362. **kwargs: Any,
  363. ) -> _T: ...
  364. @overload
  365. def __call__(
  366. self,
  367. target: str,
  368. new: None = ...,
  369. spec: Optional[builtins.object] = ...,
  370. create: bool = ...,
  371. spec_set: Optional[builtins.object] = ...,
  372. autospec: Optional[builtins.object] = ...,
  373. *,
  374. new_callable: Callable[[], _T],
  375. **kwargs: Any,
  376. ) -> _T: ...
  377. def __call__(
  378. self,
  379. target: str,
  380. new: builtins.object = DEFAULT,
  381. spec: Optional[builtins.object] = None,
  382. create: bool = False,
  383. spec_set: Optional[builtins.object] = None,
  384. autospec: Optional[builtins.object] = None,
  385. new_callable: Optional[Callable[[], Any]] = None,
  386. **kwargs: Any,
  387. ) -> Any:
  388. """API to mock.patch"""
  389. if new is self.DEFAULT:
  390. new = self.mock_module.DEFAULT
  391. return self._start_patch(
  392. self.mock_module.patch,
  393. True,
  394. target,
  395. new=new,
  396. spec=spec,
  397. create=create,
  398. spec_set=spec_set,
  399. autospec=autospec,
  400. new_callable=new_callable,
  401. **kwargs,
  402. )
  403. def _mocker(pytestconfig: Any) -> Generator[MockerFixture, None, None]:
  404. """
  405. Return an object that has the same interface to the `mock` module, but
  406. takes care of automatically undoing all patches after each test method.
  407. """
  408. result = MockerFixture(pytestconfig)
  409. yield result
  410. result.stopall()
  411. mocker = pytest.fixture()(_mocker) # default scope is function
  412. class_mocker = pytest.fixture(scope="class")(_mocker)
  413. module_mocker = pytest.fixture(scope="module")(_mocker)
  414. package_mocker = pytest.fixture(scope="package")(_mocker)
  415. session_mocker = pytest.fixture(scope="session")(_mocker)
  416. _mock_module_patches = [] # type: List[Any]
  417. _mock_module_originals = {} # type: Dict[str, Any]
  418. def assert_wrapper(
  419. __wrapped_mock_method__: Callable[..., Any], *args: Any, **kwargs: Any
  420. ) -> None:
  421. __tracebackhide__ = True
  422. try:
  423. __wrapped_mock_method__(*args, **kwargs)
  424. return
  425. except AssertionError as e:
  426. if getattr(e, "_mock_introspection_applied", 0):
  427. msg = str(e)
  428. else:
  429. __mock_self = args[0]
  430. msg = str(e)
  431. if __mock_self.call_args is not None:
  432. actual_args, actual_kwargs = __mock_self.call_args
  433. introspection = ""
  434. try:
  435. assert actual_args == args[1:]
  436. except AssertionError as e_args:
  437. introspection += "\nArgs:\n" + str(e_args)
  438. try:
  439. assert actual_kwargs == kwargs
  440. except AssertionError as e_kwargs:
  441. introspection += "\nKwargs:\n" + str(e_kwargs)
  442. if introspection:
  443. msg += "\n\npytest introspection follows:\n" + introspection
  444. e = AssertionError(msg)
  445. e._mock_introspection_applied = True # type:ignore[attr-defined]
  446. raise e
  447. def assert_has_calls_wrapper(
  448. __wrapped_mock_method__: Callable[..., Any], *args: Any, **kwargs: Any
  449. ) -> None:
  450. __tracebackhide__ = True
  451. try:
  452. __wrapped_mock_method__(*args, **kwargs)
  453. return
  454. except AssertionError as e:
  455. any_order = kwargs.get("any_order", False)
  456. if getattr(e, "_mock_introspection_applied", 0) or any_order:
  457. msg = str(e)
  458. else:
  459. __mock_self = args[0]
  460. msg = str(e)
  461. if __mock_self.call_args_list is not None:
  462. actual_calls = list(__mock_self.call_args_list)
  463. expect_calls = args[1]
  464. introspection = ""
  465. from itertools import zip_longest
  466. for actual_call, expect_call in zip_longest(actual_calls, expect_calls):
  467. if actual_call is not None:
  468. actual_args, actual_kwargs = actual_call
  469. else:
  470. actual_args = tuple()
  471. actual_kwargs = {}
  472. if expect_call is not None:
  473. _, expect_args, expect_kwargs = expect_call
  474. else:
  475. expect_args = tuple()
  476. expect_kwargs = {}
  477. try:
  478. assert actual_args == expect_args
  479. except AssertionError as e_args:
  480. introspection += "\nArgs:\n" + str(e_args)
  481. try:
  482. assert actual_kwargs == expect_kwargs
  483. except AssertionError as e_kwargs:
  484. introspection += "\nKwargs:\n" + str(e_kwargs)
  485. if introspection:
  486. msg += "\n\npytest introspection follows:\n" + introspection
  487. e = AssertionError(msg)
  488. e._mock_introspection_applied = True # type:ignore[attr-defined]
  489. raise e
  490. def wrap_assert_not_called(*args: Any, **kwargs: Any) -> None:
  491. __tracebackhide__ = True
  492. assert_wrapper(_mock_module_originals["assert_not_called"], *args, **kwargs)
  493. def wrap_assert_called_with(*args: Any, **kwargs: Any) -> None:
  494. __tracebackhide__ = True
  495. assert_wrapper(_mock_module_originals["assert_called_with"], *args, **kwargs)
  496. def wrap_assert_called_once(*args: Any, **kwargs: Any) -> None:
  497. __tracebackhide__ = True
  498. assert_wrapper(_mock_module_originals["assert_called_once"], *args, **kwargs)
  499. def wrap_assert_called_once_with(*args: Any, **kwargs: Any) -> None:
  500. __tracebackhide__ = True
  501. assert_wrapper(_mock_module_originals["assert_called_once_with"], *args, **kwargs)
  502. def wrap_assert_has_calls(*args: Any, **kwargs: Any) -> None:
  503. __tracebackhide__ = True
  504. assert_has_calls_wrapper(
  505. _mock_module_originals["assert_has_calls"], *args, **kwargs
  506. )
  507. def wrap_assert_any_call(*args: Any, **kwargs: Any) -> None:
  508. __tracebackhide__ = True
  509. assert_wrapper(_mock_module_originals["assert_any_call"], *args, **kwargs)
  510. def wrap_assert_called(*args: Any, **kwargs: Any) -> None:
  511. __tracebackhide__ = True
  512. assert_wrapper(_mock_module_originals["assert_called"], *args, **kwargs)
  513. def wrap_assert_not_awaited(*args: Any, **kwargs: Any) -> None:
  514. __tracebackhide__ = True
  515. assert_wrapper(_mock_module_originals["assert_not_awaited"], *args, **kwargs)
  516. def wrap_assert_awaited_with(*args: Any, **kwargs: Any) -> None:
  517. __tracebackhide__ = True
  518. assert_wrapper(_mock_module_originals["assert_awaited_with"], *args, **kwargs)
  519. def wrap_assert_awaited_once(*args: Any, **kwargs: Any) -> None:
  520. __tracebackhide__ = True
  521. assert_wrapper(_mock_module_originals["assert_awaited_once"], *args, **kwargs)
  522. def wrap_assert_awaited_once_with(*args: Any, **kwargs: Any) -> None:
  523. __tracebackhide__ = True
  524. assert_wrapper(_mock_module_originals["assert_awaited_once_with"], *args, **kwargs)
  525. def wrap_assert_has_awaits(*args: Any, **kwargs: Any) -> None:
  526. __tracebackhide__ = True
  527. assert_wrapper(_mock_module_originals["assert_has_awaits"], *args, **kwargs)
  528. def wrap_assert_any_await(*args: Any, **kwargs: Any) -> None:
  529. __tracebackhide__ = True
  530. assert_wrapper(_mock_module_originals["assert_any_await"], *args, **kwargs)
  531. def wrap_assert_awaited(*args: Any, **kwargs: Any) -> None:
  532. __tracebackhide__ = True
  533. assert_wrapper(_mock_module_originals["assert_awaited"], *args, **kwargs)
  534. def wrap_assert_methods(config: Any) -> None:
  535. """
  536. Wrap assert methods of mock module so we can hide their traceback and
  537. add introspection information to specified argument asserts.
  538. """
  539. # Make sure we only do this once
  540. if _mock_module_originals:
  541. return
  542. mock_module = get_mock_module(config)
  543. wrappers = {
  544. "assert_called": wrap_assert_called,
  545. "assert_called_once": wrap_assert_called_once,
  546. "assert_called_with": wrap_assert_called_with,
  547. "assert_called_once_with": wrap_assert_called_once_with,
  548. "assert_any_call": wrap_assert_any_call,
  549. "assert_has_calls": wrap_assert_has_calls,
  550. "assert_not_called": wrap_assert_not_called,
  551. }
  552. for method, wrapper in wrappers.items():
  553. try:
  554. original = getattr(mock_module.NonCallableMock, method)
  555. except AttributeError: # pragma: no cover
  556. continue
  557. _mock_module_originals[method] = original
  558. patcher = mock_module.patch.object(mock_module.NonCallableMock, method, wrapper)
  559. patcher.start()
  560. _mock_module_patches.append(patcher)
  561. if hasattr(mock_module, "AsyncMock"):
  562. async_wrappers = {
  563. "assert_awaited": wrap_assert_awaited,
  564. "assert_awaited_once": wrap_assert_awaited_once,
  565. "assert_awaited_with": wrap_assert_awaited_with,
  566. "assert_awaited_once_with": wrap_assert_awaited_once_with,
  567. "assert_any_await": wrap_assert_any_await,
  568. "assert_has_awaits": wrap_assert_has_awaits,
  569. "assert_not_awaited": wrap_assert_not_awaited,
  570. }
  571. for method, wrapper in async_wrappers.items():
  572. try:
  573. original = getattr(mock_module.AsyncMock, method)
  574. except AttributeError: # pragma: no cover
  575. continue
  576. _mock_module_originals[method] = original
  577. patcher = mock_module.patch.object(mock_module.AsyncMock, method, wrapper)
  578. patcher.start()
  579. _mock_module_patches.append(patcher)
  580. config.add_cleanup(unwrap_assert_methods)
  581. def unwrap_assert_methods() -> None:
  582. for patcher in _mock_module_patches:
  583. try:
  584. patcher.stop()
  585. except RuntimeError as e:
  586. # a patcher might have been stopped by user code (#137)
  587. # so we need to catch this error here and ignore it;
  588. # unfortunately there's no public API to check if a patch
  589. # has been started, so catching the error it is
  590. if str(e) == "stop called on unstarted patcher":
  591. pass
  592. else:
  593. raise
  594. _mock_module_patches[:] = []
  595. _mock_module_originals.clear()
  596. def pytest_addoption(parser: Any) -> None:
  597. parser.addini(
  598. "mock_traceback_monkeypatch",
  599. "Monkeypatch the mock library to improve reporting of the "
  600. "assert_called_... methods",
  601. default=True,
  602. )
  603. parser.addini(
  604. "mock_use_standalone_module",
  605. 'Use standalone "mock" (from PyPI) instead of builtin "unittest.mock" '
  606. "on Python 3",
  607. default=False,
  608. )
  609. def pytest_configure(config: Any) -> None:
  610. tb = config.getoption("--tb", default="auto")
  611. if (
  612. parse_ini_boolean(config.getini("mock_traceback_monkeypatch"))
  613. and tb != "native"
  614. ):
  615. wrap_assert_methods(config)