Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RecursionError: maximum recursion depth exceeded while calling a Python object #321

Closed
dustinbyrne opened this issue May 25, 2024 · 4 comments · Fixed by #335
Closed

RecursionError: maximum recursion depth exceeded while calling a Python object #321

dustinbyrne opened this issue May 25, 2024 · 4 comments · Fixed by #335
Assignees
Labels
bug Something isn't working

Comments

@dustinbyrne
Copy link
Contributor

dustinbyrne commented May 25, 2024

Found in sympy v1.0 test cases.

 Traceback (most recent call last):
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/utilities/runtests.py", line 1130, in wrapper
    test_func()
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/calculus/tests/test_util.py", line 258, in test_AccumBounds_pow
    assert AccumBounds(-oo, -1)**(S(1)/3) == AccumBounds(-oo, -1)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/decorators.py", line 91, in __sympifyit_wrapper
    return func(a, b)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/calculus/util.py", line 949, in __pow__
    return AccumBounds(real_root(self.min, den),
  File "/home/db/dev/applandinc/appmap-python/_appmap/wrapt/wrappers.py", line 622, in __call__
    return self._self_wrapper(self.__wrapped__, self._self_instance,
  File "/home/db/dev/applandinc/appmap-python/_appmap/instrument.py", line 129, in instrumented_fn
    return call_instrumented(f, instance, args, kwargs)
  File "/home/db/dev/applandinc/appmap-python/_appmap/instrument.py", line 92, in call_instrumented
    ret = f.fn(*args, **kwargs)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/functions/elementary/miscellaneous.py", line 299, in real_root
    rv = root(arg, n)
  File "/home/db/dev/applandinc/appmap-python/_appmap/wrapt/wrappers.py", line 622, in __call__
    return self._self_wrapper(self.__wrapped__, self._self_instance,
  File "/home/db/dev/applandinc/appmap-python/_appmap/instrument.py", line 129, in instrumented_fn
    return call_instrumented(f, instance, args, kwargs)
  File "/home/db/dev/applandinc/appmap-python/_appmap/instrument.py", line 92, in call_instrumented
    ret = f.fn(*args, **kwargs)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/functions/elementary/miscellaneous.py", line 255, in root
    return Pow(arg, 1/n)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/power.py", line 227, in __new__
    obj = b._eval_power(e)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/numbers.py", line 3044, in _eval_power
    return S.NegativeOne**expt*S.Infinity**expt
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/decorators.py", line 91, in __sympifyit_wrapper
    return func(a, b)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/decorators.py", line 132, in binary_op_wrapper
    return func(self, other)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/expr.py", line 140, in __mul__
    return Mul(self, other)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/operations.py", line 41, in __new__
    c_part, nc_part, order_symbols = cls.flatten(args)
  File "/home/db/dev/applandinc/appmap-python/_appmap/wrapt/wrappers.py", line 731, in __call__
    return self._self_wrapper(self.__wrapped__, instance, args,
  File "/home/db/dev/applandinc/appmap-python/_appmap/instrument.py", line 129, in instrumented_fn
    return call_instrumented(f, instance, args, kwargs)
  File "/home/db/dev/applandinc/appmap-python/_appmap/instrument.py", line 92, in call_instrumented
    ret = f.fn(*args, **kwargs)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/mul.py", line 571, in flatten
    c_part, coeff_sign = _handle_for_oo(c_part, 1)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/mul.py", line 564, in _handle_for_oo
    if t.is_positive:
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 247, in getit
    return _ask(fact, self)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 302, in _ask
    _ask(pk, obj)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 302, in _ask
    _ask(pk, obj)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 290, in _ask
    a = evaluate(obj)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/power.py", line 551, in _eval_is_prime
    return self.doit().is_prime
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 247, in getit
    return _ask(fact, self)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 290, in _ask
    a = evaluate(obj)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/power.py", line 551, in _eval_is_prime
    return self.doit().is_prime
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 247, in getit
    return _ask(fact, self)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 290, in _ask
    a = evaluate(obj)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/power.py", line 551, in _eval_is_prime
    return self.doit().is_prime
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 247, in getit
    return _ask(fact, self)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 290, in _ask
    a = evaluate(obj)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/power.py", line 551, in _eval_is_prime
    return self.doit().is_prime
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 247, in getit
    return _ask(fact, self)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 290, in _ask
    a = evaluate(obj)

  ... TRUNCATED  ...

  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 247, in getit
    return _ask(fact, self)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 290, in _ask
    a = evaluate(obj)
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/power.py", line 551, in _eval_is_prime
    return self.doit().is_prime
  File "/tmp/tmpz56qw_4w/sympy__sympy__1.0/sympy/core/assumptions.py", line 246, in getit
    self._assumptions = self.default_assumptions.copy()
  File "/home/db/dev/applandinc/appmap-python/_appmap/wrapt/wrappers.py", line 712, in __call__
    return self._self_wrapper(self.__wrapped__, self._self_instance,
  File "/home/db/dev/applandinc/appmap-python/_appmap/instrument.py", line 129, in instrumented_fn
    return call_instrumented(f, instance, args, kwargs)
  File "/home/db/dev/applandinc/appmap-python/_appmap/instrument.py", line 86, in call_instrumented
    params = CallEvent.set_params(f.params, instance, args, kwargs)
  File "/home/db/dev/applandinc/appmap-python/_appmap/event.py", line 268, in set_params
    ret.append(p.to_dict(value))
  File "/home/db/dev/applandinc/appmap-python/_appmap/event.py", line 171, in to_dict
    ret.update(describe_value(self.name, value))
  File "/home/db/dev/applandinc/appmap-python/_appmap/event.py", line 112, in describe_value
    "value": display_string(val),
  File "/home/db/dev/applandinc/appmap-python/_appmap/event.py", line 53, in display_string
    if Env.current.display_params:
  File "/home/db/dev/applandinc/appmap-python/_appmap/env.py", line 131, in display_params
    return self.get("APPMAP_DISPLAY_PARAMS", "true").lower() == "true"
  File "/home/db/dev/applandinc/appmap-python/_appmap/env.py", line 64, in get
    return self._env.get(name, default)
RecursionError: maximum recursion depth exceeded while calling a Python object
@dustinbyrne dustinbyrne added the bug Something isn't working label May 25, 2024
@dustinbyrne dustinbyrne changed the title Instrumentation may call endlessly recursive method in user code RecursionError: maximum recursion depth exceeded while calling a Python object May 25, 2024
@zermelo-wisen zermelo-wisen self-assigned this May 28, 2024
@apotterri
Copy link
Collaborator

The best option here is just to set APPMAP_DISPLAY_PARAMS=false.

@zermelo-wisen
Copy link
Collaborator

zermelo-wisen commented May 30, 2024

I think I made the fix with ensuring describe_value calls in event.py are made within with recording_disabled(). We are doing a similar thing in appmap-node as well.

For example, sympy/calculus/tests/test_util.py::test_not_empty_in test does not error with the fix, and it errors without the fix. However, as far as I was able to investigate, I wasn't able to create a minimal test with some kind of circular structure as in sympy project which would lead to the same error. Usual suspects are StrPrinter and Printer classes in sympy project.

@kgilpin
Copy link
Contributor

kgilpin commented May 30, 2024

AppMap Ruby also turns off recoding for the thread while it is stringifying. If I recall correctly.

@apotterri
Copy link
Collaborator

Ok, given how much of a problem this is turning out to be, I've come around on making this fix.

The lack of a way to test it was bothering me, but I realized it's actually not necessary to reproduce this infinite recursion.

Instead, add a test of an object with a __repr__ implementation that calls another instrumented method is enough. If the test confirms that no events get generated for that call, that shows that the recursion can't happen.

@zermelo-wisen Please make this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants