From d3d4c4a9e264fdf1a7dbf84a198897b338b22165 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 20 Nov 2019 18:35:16 -0300 Subject: [PATCH] Handle 'mocker.patch' being used without source code Fix #169 --- CHANGELOG.rst | 8 ++++++++ src/pytest_mock/plugin.py | 3 +++ tests/test_pytest_mock.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c61172d..d40165b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,11 @@ +1.12.1 (2019-11-20) +------------------- + +* Fix error if ``mocker.patch`` is used in code where the source file + is not available, for example stale ``.pyc`` files (`#169`_). + +.. _#169: https://github.com/pytest-dev/pytest-mock/issues/169#issuecomment-555729265 + 1.12.0 (2019-11-19) ------------------- diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index 5ee170e..dda073a 100644 --- a/src/pytest_mock/plugin.py +++ b/src/pytest_mock/plugin.py @@ -160,6 +160,9 @@ def _enforce_no_with_context(self, stack): caller = stack[2] frame = caller[0] info = inspect.getframeinfo(frame) + if info.code_context is None: + # no source code available (#169) + return code_context = " ".join(info.code_context).strip() if code_context.startswith("with mocker."): diff --git a/tests/test_pytest_mock.py b/tests/test_pytest_mock.py index d1d8db9..dd8c3a9 100644 --- a/tests/test_pytest_mock.py +++ b/tests/test_pytest_mock.py @@ -753,3 +753,41 @@ def test_abort_patch_context_manager(mocker): ) assert str(excinfo.value) == expected_error_msg + + +def test_abort_patch_context_manager_with_stale_pyc(testdir): + """Ensure we don't trigger an error in case the frame where mocker.patch is being + used doesn't have a 'context' (#169)""" + import compileall + + py_fn = testdir.makepyfile( + c=""" + class C: + x = 1 + + def check(mocker): + mocker.patch.object(C, "x", 2) + assert C.x == 2 + """ + ) + testdir.syspathinsert() + + testdir.makepyfile( + """ + from c import check + def test_foo(mocker): + check(mocker) + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines("* 1 passed *") + + kwargs = {"legacy": True} if sys.version_info[0] >= 3 else {} + assert compileall.compile_file(str(py_fn), **kwargs) + + pyc_fn = str(py_fn) + "c" + assert os.path.isfile(pyc_fn) + + py_fn.remove() + result = testdir.runpytest() + result.stdout.fnmatch_lines("* 1 passed *")