Skip to content

Commit 3b9610a

Browse files
committed
Add native support for t-strings in the stdlib logging library
1 parent aadda87 commit 3b9610a

File tree

1 file changed

+32
-7
lines changed

1 file changed

+32
-7
lines changed

Lib/logging/__init__.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from types import GenericAlias
2929
from string import Template
3030
from string import Formatter as StrFormatter
31+
from string.templatelib import Template as TStringTemplate, Interpolation
3132

3233

3334
__all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
@@ -289,11 +290,14 @@ class LogRecord(object):
289290
290291
LogRecord instances are created every time something is logged. They
291292
contain all the information pertinent to the event being logged. The
292-
main information passed in is in msg and args, which are combined
293-
using str(msg) % args to create the message field of the record. The
294-
record also includes information such as when the record was created,
295-
the source line where the logging call was made, and any exception
296-
information to be logged.
293+
main information passed in is in msg and args. These are combined
294+
using str(msg) % args to create the message field of the record. A
295+
special case here is that if the msg is a t-string, it will be
296+
formatted as if it was an f-string without using the provided args.
297+
298+
The record also includes information such as when the record was
299+
created, the source line where the logging call was made, and any
300+
exception information to be logged.
297301
"""
298302
def __init__(self, name, level, pathname, lineno,
299303
msg, args, exc_info, func=None, sinfo=None, **kwargs):
@@ -303,7 +307,14 @@ def __init__(self, name, level, pathname, lineno,
303307
ct = time.time_ns()
304308
self.name = name
305309
self.msg = msg
306-
#
310+
311+
# When logging a t-string, pull the values out of it and prepend
312+
# them to any provided args
313+
if isinstance(msg, TStringTemplate):
314+
args = (
315+
{x.expression: x.value for x in msg if isinstance(x, Interpolation)},
316+
*args
317+
)
307318
# The following statement allows passing of a dictionary as a sole
308319
# argument, so that you can do something like
309320
# logging.debug("a %(a)d b %(b)s", {'a':1, 'b':2})
@@ -394,7 +405,21 @@ def getMessage(self):
394405
395406
Return the message for this LogRecord after merging any user-supplied
396407
arguments with the message.
397-
"""
408+
If the messsage is a t-string object it will be formatted in the same
409+
way as an f-string without any extra user-supplied arguments.
410+
"""
411+
if isinstance(self.msg, TStringTemplate):
412+
return "".join(
413+
(
414+
_str_formatter.format_field(
415+
_str_formatter.convert_field(x.value, x.conversion),
416+
x.format_spec
417+
)
418+
) if isinstance(x, Interpolation)
419+
else x
420+
for x in self.msg
421+
)
422+
398423
msg = str(self.msg)
399424
if self.args:
400425
msg = msg % self.args

0 commit comments

Comments
 (0)