python / python/cpython

mock.seal has infinite recursion with mutually recursive class references

Open
#91,710 5 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

stdlib type-bug
Dominant language
Python
Stars
77.2k
Forks
36k
PR merge metrics
PR metrics pending

Description

Bug report

This code seems to cause infinite recursion in unittest.mock.seal:

from unittest import mock
class Foo:
  pass
class Bar:
  foo = Foo
Foo.bar = Bar
foo = mock.create_autospec(Foo)
mock.seal(foo)
$ python3 foo.py 
Traceback (most recent call last):
  File "/tmp/tmp.yKOznvlUIG/foo.py", line 8, in <module>
    mock.seal(foo)
  File "/usr/lib/python3.9/unittest/mock.py", line 2874, in seal
    seal(m)
  File "/usr/lib/python3.9/unittest/mock.py", line 2874, in seal
    seal(m)
  File "/usr/lib/python3.9/unittest/mock.py", line 2874, in seal
    seal(m)
  [Previous line repeated 973 more times]
  File "/usr/lib/python3.9/unittest/mock.py", line 2866, in seal
    m = getattr(mock, attr)
  File "/usr/lib/python3.9/unittest/mock.py", line 655, in __getattr__
    result = create_autospec(
  File "/usr/lib/python3.9/unittest/mock.py", line 2627, in create_autospec
    mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name,
  File "/usr/lib/python3.9/unittest/mock.py", line 2034, in __init__
    _safe_super(MagicMixin, self).__init__(*args, **kw)
  File "/usr/lib/python3.9/unittest/mock.py", line 1074, in __init__
    _safe_super(CallableMixin, self).__init__(
  File "/usr/lib/python3.9/unittest/mock.py", line 437, in __init__
    self._mock_add_spec(spec, spec_set, _spec_as_instance, _eat_self)
  File "/usr/lib/python3.9/unittest/mock.py", line 500, in _mock_add_spec
    res = _get_signature_object(spec,
  File "/usr/lib/python3.9/unittest/mock.py", line 107, in _get_signature_object
    return func, inspect.signature(sig_func)
  File "/usr/lib/python3.9/inspect.py", line 3113, in signature
    return Signature.from_callable(obj, follow_wrapped=follow_wrapped)
  File "/usr/lib/python3.9/inspect.py", line 2862, in from_callable
    return _signature_from_callable(obj, sigcls=cls,
  File "/usr/lib/python3.9/inspect.py", line 2333, in _signature_from_callable
    wrapped_sig = _get_signature_of(obj.func)
  File "/usr/lib/python3.9/inspect.py", line 2329, in _signature_from_callable
    return _signature_from_builtin(sigcls, obj,
  File "/usr/lib/python3.9/inspect.py", line 2149, in _signature_from_builtin
    return _signature_fromstr(cls, func, s, skip_bound_arg)
  File "/usr/lib/python3.9/inspect.py", line 2009, in _signature_fromstr
    _signature_strip_non_python_syntax(s)
  File "/usr/lib/python3.9/inspect.py", line 1962, in _signature_strip_non_python_syntax
    for t in token_stream:
  File "/usr/lib/python3.9/tokenize.py", line 525, in _tokenize
    pseudomatch = _compile(PseudoToken).match(line, pos)
  File "/usr/lib/python3.9/tokenize.py", line 99, in _compile
    return re.compile(expr, re.UNICODE)
  File "/usr/lib/python3.9/re.py", line 252, in compile
    return _compile(pattern, flags)
  File "/usr/lib/python3.9/re.py", line 292, in _compile
    flags = flags.value
  File "/usr/lib/python3.9/types.py", line 178, in __get__
    return self.fget(instance)
RecursionError: maximum recursion depth exceeded

Your environment

  • CPython versions tested on: 3.9.9
  • Operating system and architecture: Debian testing, amd64

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with unittest.mock.seal and create_autospec in unittest/mock.py, using the provided Foo/Bar reproducer to trace the recursive path. Done means the mutually recursive class references no longer cause infinite recursion or a RecursionError when mock.seal(foo) runs.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.