classification
Title: [mock] Recursion on mocking inspect.isfunction
Type: behavior Stage: resolved
Components: Versions: Python 3.8
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: cjw296, lisroach, mariocj89, michael.foord, stanislavlevin, xtreak
Priority: normal Keywords:

Created on 2020-10-06 07:12 by stanislavlevin, last changed 2020-10-20 06:03 by stanislavlevin. This issue is now closed.

Messages (4)
msg378094 - (view) Author: Stanislav Levin (stanislavlevin) Date: 2020-10-06 07:12
With Python 3.8 the mocking of `inspect.isfunction` results in recursion.

Please, consider a code snippet:

```python
from unittest import TestCase
from unittest.mock import patch

import inspect

class TestClass(TestCase):
    def setUp(self):
        patcher = patch('inspect.isfunction')
        self.addCleanup(patcher.stop)
        self.patched_func = patcher.start()

    def test_m(self):
        def f():
           pass

        inspect.isfunction(f)
```

Output:
```
ERROR: test_m (test.TestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/src/test.py", line 16, in test_m
    inspect.isfunction(f)
  File "/usr/lib64/python3.8/unittest/mock.py", line 1081, in __call__
    return self._mock_call(*args, **kwargs)
  File "/usr/lib64/python3.8/unittest/mock.py", line 1085, in _mock_call
    return self._execute_mock_call(*args, **kwargs)
  File "/usr/lib64/python3.8/unittest/mock.py", line 1157, in _execute_mock_call
    return self.return_value
  File "/usr/lib64/python3.8/unittest/mock.py", line 526, in __get_return_value
    ret = self._get_child_mock(
  File "/usr/lib64/python3.8/unittest/mock.py", line 1025, in _get_child_mock
    return klass(**kw)
  File "/usr/lib64/python3.8/unittest/mock.py", line 408, in __new__
    sig = inspect.signature(NonCallableMock.__init__)
  File "/usr/lib64/python3.8/inspect.py", line 3093, in signature
    return Signature.from_callable(obj, follow_wrapped=follow_wrapped)
  File "/usr/lib64/python3.8/inspect.py", line 2842, in from_callable
    return _signature_from_callable(obj, sigcls=cls,
  File "/usr/lib64/python3.8/inspect.py", line 2289, in _signature_from_callable
    if isfunction(obj) or _signature_is_functionlike(obj):
  File "/usr/lib64/python3.8/unittest/mock.py", line 1081, in __call__
    return self._mock_call(*args, **kwargs)
  File "/usr/lib64/python3.8/unittest/mock.py", line 1085, in _mock_call
    return self._execute_mock_call(*args, **kwargs)
  File "/usr/lib64/python3.8/unittest/mock.py", line 1157, in _execute_mock_call
    return self.return_value
  File "/usr/lib64/python3.8/unittest/mock.py", line 526, in __get_return_value
    ret = self._get_child_mock(
  File "/usr/lib64/python3.8/unittest/mock.py", line 1025, in _get_child_mock
    return klass(**kw)
...

    return _signature_from_callable(obj, sigcls=cls,
RecursionError: maximum recursion depth exceeded
```

breaking commit: https://github.com/python/cpython/commit/77b3b7701a34ecf6316469e05b79bb91de2addfa

The pre-77b3b7701a34ecf6316469e05b79bb91de2addfa state of `Lib/unittest/mock.py` works as expected.
msg378116 - (view) Author: Mario Corchero (mariocj89) * (Python triager) Date: 2020-10-06 11:53
Oh, well spotted. We could add some guards around the code in mock to still work when key stdlib functionality is mocked out, but this might make the mock code quite messy. Specially as we will have to make sure that not only the function we call are safe to call (which is easy by just doing import from), but that all dependencies of those function are also safe to call.

I'd instead suggest for users to patch in the module where they are using the function instead.

I think that once any part of the stdlib is patched by mock, there are no guarantees that anything will work unless the patch provides a functioning implmentation.
msg378991 - (view) Author: Lisa Roach (lisroach) * (Python committer) Date: 2020-10-19 19:15
Oof yeah, sorry my commit broke this but it is pretty hard to avoid. Mario is right in that it would be messy and challenging to ensure anything mocked from the stdlib doesn't also break mock itself.  

I want to mark this as `wont fix`, unless there is a compelling reason for the stdlib's `inspect.isfunction` to be mockable. But I agree with Mario again here, it would be better to mock the module that is using the function rather than the built-in function itself.
msg379090 - (view) Author: Stanislav Levin (stanislavlevin) Date: 2020-10-20 06:03
I see, thank you for your time.
History
Date User Action Args
2020-10-20 06:03:12stanislavlevinsetstatus: open -> closed
resolution: wont fix
messages: + msg379090

stage: resolved
2020-10-19 19:15:55lisroachsetmessages: + msg378991
2020-10-06 11:53:46mariocj89setmessages: + msg378116
2020-10-06 07:15:48xtreaksetnosy: + cjw296, michael.foord, lisroach, mariocj89, xtreak
2020-10-06 07:12:22stanislavlevincreate