Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a39c571

Browse files
committed
logging: Added style option to Formatter to allow %, {} or himBHformatting.
1 parent 7e9065c commit a39c571

4 files changed

Lines changed: 103 additions & 12 deletions

File tree

Doc/library/logging.rst

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -301,17 +301,29 @@ Formatters
301301
Formatter objects configure the final order, structure, and contents of the log
302302
message. Unlike the base :class:`logging.Handler` class, application code may
303303
instantiate formatter classes, although you could likely subclass the formatter
304-
if your application needs special behavior. The constructor takes two optional
305-
arguments: a message format string and a date format string. If there is no
306-
message format string, the default is to use the raw message. If there is no
307-
date format string, the default date format is::
304+
if your application needs special behavior. The constructor takes three
305+
optional arguments -- a message format string, a date format string and a style
306+
indicator.
307+
308+
.. method:: logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
309+
310+
If there is no message format string, the default is to use the
311+
raw message. If there is no date format string, the default date format is::
308312

309313
%Y-%m-%d %H:%M:%S
310314

311-
with the milliseconds tacked on at the end.
315+
with the milliseconds tacked on at the end. The ``style`` is one of `%`, '{'
316+
or '$'. If one of these is not specified, then '%' will be used.
312317

313-
The message format string uses ``%(<dictionary key>)s`` styled string
314-
substitution; the possible keys are documented in :ref:`formatter-objects`.
318+
If the ``style`` is '%', the message format string uses
319+
``%(<dictionary key>)s`` styled string substitution; the possible keys are
320+
documented in :ref:`formatter-objects`. If the style is '{', the message format
321+
string is assumed to be compatible with :meth:`str.format` (using keyword
322+
arguments), while if the style is '$' then the message format string should
323+
conform to what is expected by :meth:`string.Template.substitute`.
324+
325+
.. versionchanged:: 3.2
326+
Added the ``style`` parameter.
315327

316328
The following message format string will log the time in a human-readable
317329
format, the severity of the message, and the contents of the message, in that

Lib/logging/__init__.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -395,18 +395,33 @@ class Formatter(object):
395395

396396
converter = time.localtime
397397

398-
def __init__(self, fmt=None, datefmt=None):
398+
def __init__(self, fmt=None, datefmt=None, style='%'):
399399
"""
400400
Initialize the formatter with specified format strings.
401401
402402
Initialize the formatter either with the specified format string, or a
403403
default as described above. Allow for specialized date formatting with
404404
the optional datefmt argument (if omitted, you get the ISO8601 format).
405+
406+
Use a style parameter of '%', '{' or '$' to specify that you want to
407+
use one of %-formatting, :meth:`str.format` (``{}``) formatting or
408+
:class:`string.Template` formatting in your format string.
409+
410+
.. versionchanged: 3.2
411+
Added the ``style`` parameter.
405412
"""
413+
if style not in ('%', '$', '{'):
414+
style = '%'
415+
self._style = style
406416
if fmt:
407417
self._fmt = fmt
408418
else:
409-
self._fmt = "%(message)s"
419+
if style == '%':
420+
self._fmt = "%(message)s"
421+
elif style == '{':
422+
self._fmt = '{message}'
423+
else:
424+
self._fmt = '${message}'
410425
self.datefmt = datefmt
411426

412427
def formatTime(self, record, datefmt=None):
@@ -432,7 +447,7 @@ def formatTime(self, record, datefmt=None):
432447
s = time.strftime(datefmt, ct)
433448
else:
434449
t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
435-
s = "%s,%03d" % (t, record.msecs)
450+
s = "%s,%03d" % (t, record.msecs) # the use of % here is internal
436451
return s
437452

438453
def formatException(self, ei):
@@ -458,7 +473,14 @@ def usesTime(self):
458473
"""
459474
Check if the format uses the creation time of the record.
460475
"""
461-
return self._fmt.find("%(asctime)") >= 0
476+
if self._style == '%':
477+
result = self._fmt.find("%(asctime)") >= 0
478+
elif self._style == '$':
479+
result = self._fmt.find("{asctime}") >= 0
480+
else:
481+
result = self._fmt.find("$asctime") >= 0 or \
482+
self._fmt.find("${asctime}") >= 0
483+
return result
462484

463485
def format(self, record):
464486
"""
@@ -476,7 +498,14 @@ def format(self, record):
476498
record.message = record.getMessage()
477499
if self.usesTime():
478500
record.asctime = self.formatTime(record, self.datefmt)
479-
s = self._fmt % record.__dict__
501+
style = self._style
502+
if style == '%':
503+
s = self._fmt % record.__dict__
504+
elif style == '{':
505+
s = self._fmt.format(**record.__dict__)
506+
else:
507+
from string import Template
508+
s = Template(self._fmt).substitute(**record.__dict__)
480509
if record.exc_info:
481510
# Cache the traceback text to avoid converting it multiple times
482511
# (it's constant anyway)

Lib/test/test_logging.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,53 @@ def test_queue_handler(self):
18631863
self.assertEqual(data.name, self.que_logger.name)
18641864
self.assertEqual((data.msg, data.args), (msg, None))
18651865

1866+
class FormatterTest(unittest.TestCase):
1867+
def setUp(self):
1868+
self.common = {
1869+
'name': 'formatter.test',
1870+
'level': logging.DEBUG,
1871+
'pathname': os.path.join('path', 'to', 'dummy.ext'),
1872+
'lineno': 42,
1873+
'exc_info': None,
1874+
'func': None,
1875+
'msg': 'Message with %d %s',
1876+
'args': (2, 'placeholders'),
1877+
}
1878+
self.variants = {
1879+
}
1880+
1881+
def get_record(self, name=None):
1882+
result = dict(self.common)
1883+
if name is not None:
1884+
result.update(self.variants[name])
1885+
return logging.makeLogRecord(result)
1886+
1887+
def test_percent(self):
1888+
"Test %-formatting"
1889+
r = self.get_record()
1890+
f = logging.Formatter('${%(message)s}')
1891+
self.assertEqual(f.format(r), '${Message with 2 placeholders}')
1892+
f = logging.Formatter('%(random)s')
1893+
self.assertRaises(KeyError, f.format, r)
1894+
1895+
def test_braces(self):
1896+
"Test {}-formatting"
1897+
r = self.get_record()
1898+
f = logging.Formatter('$%{message}%$', style='{')
1899+
self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
1900+
f = logging.Formatter('{random}', style='{')
1901+
self.assertRaises(KeyError, f.format, r)
1902+
1903+
def test_dollars(self):
1904+
"Test $-formatting"
1905+
r = self.get_record()
1906+
f = logging.Formatter('$message', style='$')
1907+
self.assertEqual(f.format(r), 'Message with 2 placeholders')
1908+
f = logging.Formatter('$$%${message}%$$', style='$')
1909+
self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
1910+
f = logging.Formatter('${random}', style='$')
1911+
self.assertRaises(KeyError, f.format, r)
1912+
18661913
class BaseFileTest(BaseTest):
18671914
"Base class for handler tests that write log files"
18681915

@@ -1945,6 +1992,7 @@ def test_main():
19451992
CustomLevelsAndFiltersTest, MemoryHandlerTest,
19461993
ConfigFileTest, SocketHandlerTest, MemoryTest,
19471994
EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
1995+
FormatterTest,
19481996
LogRecordClassTest, ChildLoggerTest, QueueHandlerTest,
19491997
RotatingFileHandlerTest,
19501998
#TimedRotatingFileHandlerTest

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ Core and Builtins
5151
Library
5252
-------
5353

54+
- logging: Added style option to Formatter to allow %, {} or $-formatting.
55+
5456
- Issue #5178: Added tempfile.TemporaryDirectory class that can be used
5557
as a context manager.
5658

0 commit comments

Comments
 (0)