logging.debug("%s", f"Unexpected, got {got}")
because otherwise, if the format string is the first/only argument, and `got` unexpectedly contain `%`, an exception will be raised due to a missing format parameter.(Also, for the logging functions, you don't pass a tuple, just a normal series of arguments.)
(That's what I get for assuming consistent behavior :P)
import logging
got = "%d"
logging.warning(f"hi {got}")
logging.debug(f"hi {got}")
Output WARNING:root:hi %d
DEBUG:root:hi %dSo:
log.debug("Unexpected, got {got}".format(got=got))
Or, to just use the available locals to automatically fill that in for you: log.debug("Unexepected, got {got}".format(**locals()))
Or if you want both globals and locals with a preference for locals: log.debug("Unexepected, got {got}".format(**{**globals(), **locals()}))Also, shoutout to Template for untrusted strings. It’s surprisingly unknown. Most people suggest replace or a custom format function.
Limiting logging to `%`-formatting seems to be an inconsistency similar to supporting `%` but not `format` on `bytes` and `bytearray`.
the logging module predates the `.format` function.
You still miss out on the scoping of f-strings, and embedding function calls. But passing around what amounts to an "eval" expression is a footgun that should obviously be frowned upon.
There's also an option for $-style Template strings. But I can't recall when I've ever seen someone use that.
The style option only applies to the outer format applied by the formatter (timestamp, level, file, line, etc), it does not apply to the logging calls.
log.debug("Unexpected, got %r", (got,))
By doing it this way, it can defer rendering the string until it is actually needed, which is (assumed to be) a win when the logging module is normally just going to toss the low level messages away rather than emit them anywhere.
I think you lose this optimization if you use f-strings. I don't think rendering of the string can be lazy, deferred until actually needed.