diff --git a/_appmap/event.py b/_appmap/event.py index 4af73cbf..71f2222e 100644 --- a/_appmap/event.py +++ b/_appmap/event.py @@ -455,7 +455,11 @@ class FuncReturnEvent(ReturnEvent): def __init__(self, parent_id, elapsed, return_value): super().__init__(parent_id, elapsed) - self.return_value = describe_value(None, return_value) + # Import here to prevent circular dependency + # pylint: disable=import-outside-toplevel + from _appmap.instrument import recording_disabled # noqa: F401 + with recording_disabled(): + self.return_value = describe_value(None, return_value) class HttpResponseEvent(ReturnEvent): diff --git a/_appmap/instrument.py b/_appmap/instrument.py index e9fd050c..10410e2c 100644 --- a/_appmap/instrument.py +++ b/_appmap/instrument.py @@ -15,11 +15,12 @@ @contextmanager def recording_disabled(): tls = appmap_tls() + original_value = tls.get("instrumentation_disabled") tls["instrumentation_disabled"] = True try: yield finally: - tls["instrumentation_disabled"] = False + tls["instrumentation_disabled"] = original_value def is_instrumentation_disabled(): diff --git a/_appmap/test/data/example_class.py b/_appmap/test/data/example_class.py index c84f3fe1..3e61f7ee 100644 --- a/_appmap/test/data/example_class.py +++ b/_appmap/test/data/example_class.py @@ -110,6 +110,9 @@ def with_docstring(self): def with_comment(self): return True + def return_self(self): + return self + def modfunc(): return "Hello world!" diff --git a/_appmap/test/test_events.py b/_appmap/test/test_events.py index ba45a3af..4dba7cd8 100644 --- a/_appmap/test/test_events.py +++ b/_appmap/test/test_events.py @@ -105,3 +105,15 @@ def test_when_display_disabled(self, mocker): # MagicMock. (If it's broken, we may not get here at all, # because the assertion above may fail.) param.__repr__.assert_called_once_with() + + def test_describe_return_value_recursion_protection(self): + r = appmap.Recording() + with r: + # pylint: disable=import-outside-toplevel + from example_class import ExampleClass + + ExampleClass().return_self() + # There should be no event for method another_method which is called by __repr__. + assert [e.method_id for e in r.events if e.event == "call" and hasattr(e, "method_id")] == [ + "return_self" + ]