fix: format traceback inline for loguru compatibility#622
Conversation
Stop passing `exc_info=` as a kwarg to `logger.log()`. Standard `logging.Logger` treats this specially (appends traceback), but loguru interprets all kwargs as message format parameters, causing a KeyError. Instead, format the traceback into the message string directly using `traceback.format_exception()`. This works with all loggers that implement `LoggerProtocol` (logging, loguru, structlog, etc.). Also tighten `LoggerProtocol.log()` by removing `**kwargs` since we no longer pass any keyword arguments. Fixes #420 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Change-Id: Icc145539d443c83f09266e25b9819bd2516aeddc
|
Hi @jd. Thanks for considering updating The change looks good. I tested it locally and I confirmed that the |
Merge Queue StatusRule:
This pull request spent 11 seconds in the queue, including 1 second running CI. Required conditions to merge |
There was a problem hiding this comment.
Pull request overview
This PR addresses issue #420 by fixing a KeyError that occurred when using loguru (or similar loggers) with before_sleep_log(). The root cause was that the old code passed exc_info= as a keyword argument to logger.log(), which standard logging.Logger handles specially (appending a traceback), but loguru treats as a message format parameter, causing a KeyError. The fix embeds the traceback directly into the message string using traceback.format_exception(), making the behavior portable across all loggers conforming to LoggerProtocol.
Changes:
- The traceback is now formatted inline into the log message string using
traceback.format_exception()instead of being passed asexc_info=kwarg tologger.log() - The
local_exc_infointermediate variable and theexc_info=kwarg inlogger.log()calls are removed LoggerProtocol.log()has**kwargsremoved from its signature
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
tenacity/before_sleep.py |
Replaces exc_info= kwarg in logger.log() with inline traceback formatting into the message string |
tenacity/_utils.py |
Removes **kwargs from LoggerProtocol.log() since it is no longer called with keyword arguments |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ) | ||
|
|
||
| if exc_info and retry_state.outcome.failed: | ||
| ex = retry_state.outcome.exception() |
There was a problem hiding this comment.
The ex variable is already assigned at line 46 when retry_state.outcome.failed is True. Since the guard condition on line 58 (if exc_info and retry_state.outcome.failed) ensures we are in the same failed branch, the re-assignment ex = retry_state.outcome.exception() on line 59 is a redundant duplicate call. The previously set ex from line 46 can be reused directly, eliminating the extra method call.
| ex = retry_state.outcome.exception() |
| def log( | ||
| self, level: int, msg: str, *args: typing.Any, **kwargs: typing.Any | ||
| ) -> typing.Any: ... | ||
| def log(self, level: int, msg: str, *args: typing.Any) -> typing.Any: ... |
There was a problem hiding this comment.
Removing **kwargs from LoggerProtocol.log() is a breaking API change for any users who have custom logger implementations typed against this protocol. The previous signature was def log(self, level: int, msg: str, *args: Any, **kwargs: Any) -> Any, which matched the broader logging.Logger.log signature that accepts stacklevel, extra, etc. as keyword arguments. Users who previously passed such keyword arguments through a LoggerProtocol-typed reference will now get a static type error. Consider whether a more conservative change (keeping **kwargs in the protocol even if tenacity itself no longer passes kwargs) would be preferable for backward compatibility.
| def log(self, level: int, msg: str, *args: typing.Any) -> typing.Any: ... | |
| def log( | |
| self, level: int, msg: str, *args: typing.Any, **kwargs: typing.Any | |
| ) -> typing.Any: ... |
Stop passing
exc_info=as a kwarg tologger.log(). Standardlogging.Loggertreats this specially (appends traceback), but loguruinterprets all kwargs as message format parameters, causing a KeyError.
Instead, format the traceback into the message string directly using
traceback.format_exception(). This works with all loggers thatimplement
LoggerProtocol(logging, loguru, structlog, etc.).Also tighten
LoggerProtocol.log()by removing**kwargssince weno longer pass any keyword arguments.
Fixes #420
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com