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

FIX: ExceptionFormatter infinite recursion traceback #1066

Closed
wants to merge 1 commit into from
Closed

FIX: ExceptionFormatter infinite recursion traceback #1066

wants to merge 1 commit into from

Conversation

ismagilli
Copy link

This is my attempt to fix #1044. I'm not sure how good the choice of variable names is, but I couldn't think of a better one. If there are any comments, I am ready to correct them.

@ismagilli
Copy link
Author

At the moment, there are several checks errors. I've fixed them, but I'd like to hear the comments on the implementation first and then prepare the commit.

@Delgan
Copy link
Owner

Delgan commented Jan 14, 2024

Hi @ismagilli, thanks a lot for your efforts to resolve this bug!

The solution you proposed is a clever way to circumvent the infinite recursion. However, I was able to craft a code example that exhibits an unexpected behavior:

from loguru import logger
import sys

if __name__ == "__main__":

    def sink(_):
        raise ValueError

    logger.remove()
    logger.add(sink, catch=False)

    catcher = logger.catch(reraise=False)

    class Foo:
        def __repr__(self):
            with catcher:
                raise ValueError

    print("Before: ", catcher._already_logging_exception)  # False (expected).

    try:
        foo = Foo()
        repr(foo)
    except Exception:
        pass

    print("After: ", catcher._already_logging_exception)  # True (unexpected).

    logger.remove()
    logger.add(sys.stderr)

    # The "ValueError" won't be captured by Loguru, instead of that
    # a "ExceptionFormatterRecursionError" will immediately be raised
    # and the program will exit.
    with catcher:
        raise ValueError

This is because if logger._log() raises an Exception, then self._already_logging_exception = False is never called.

You call easily fix it by adding a finally clause:

diff --git a/loguru/_logger.py b/loguru/_logger.py
index 84ac4cb..c26b6c5 100644
--- a/loguru/_logger.py
+++ b/loguru/_logger.py
@@ -1242,9 +1242,11 @@ class Logger:
 
                 if not self._already_logging_exception:
                     self._already_logging_exception = True
-                    catch_options = [(type_, value, traceback_), depth, True] + options
-                    logger._log(level, from_decorator, catch_options, message, (), {})
-                    self._already_logging_exception = False
+                    try:
+                        catch_options = [(type_, value, traceback_), depth, True] + options
+                        logger._log(level, from_decorator, catch_options, message, (), {})
+                    finally:
+                        self._already_logging_exception = False
                 else:
                     raise ExceptionFormatterRecursionError()

Unfortunately, there is another problem that can't be fixed as easily. The following code still causes infinite recursion:

from loguru import logger

if __name__ == "__main__":

    class Foo:
        def __repr__(self):
            with logger.catch(reraise=True):
                raise ValueError

    foo = Foo()
    repr(foo)

Since a new Catcher instance is created each time __repr__ is called, there is no way to detect the recursion with the suggested implementation. :/

There are also thread-safety concerns. I don't have a suitable solution in mind yet. It's a tricky problem to solve.

@ismagilli
Copy link
Author

I completely agree with you. It only comes to mind to try to identify recursion by looking for repetitions in the call stack. But this will lead to false positives and false negatives.

@ismagilli
Copy link
Author

Since the above code does not fix the existing problem and I have no idea how to fix the code, there is no need to leave this pull request open.

@ismagilli ismagilli closed this Jan 18, 2024
@Delgan
Copy link
Owner

Delgan commented Jan 20, 2024

Thanks for giving it a shot anyway. I may use your solution as inspiration for finalizing the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Loguru infinite recursion traceback loop
2 participants