bpo-40884: Added defaults parameter for logging.Formatter (GH-20668)
Docs and tests are underway. Automerge-Triggered-By: @vsajip
This commit is contained in:
parent
ddbeb2f3e0
commit
8f192d12af
|
@ -529,7 +529,8 @@ The useful mapping keys in a :class:`LogRecord` are given in the section on
|
|||
:ref:`logrecord-attributes`.
|
||||
|
||||
|
||||
.. class:: Formatter(fmt=None, datefmt=None, style='%', validate=True)
|
||||
.. class:: Formatter(fmt=None, datefmt=None, style='%', validate=True, *,
|
||||
defaults=None)
|
||||
|
||||
Returns a new instance of the :class:`Formatter` class. The instance is
|
||||
initialized with a format string for the message as a whole, as well as a
|
||||
|
@ -545,6 +546,10 @@ The useful mapping keys in a :class:`LogRecord` are given in the section on
|
|||
:ref:`formatting-styles` for more information on using {- and $-formatting
|
||||
for log messages.
|
||||
|
||||
The *defaults* parameter can be a dictionary with default values to use in
|
||||
custom fields. For example:
|
||||
``logging.Formatter('%(ip)s %(message)s', defaults={"ip": None})``
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
The *style* parameter was added.
|
||||
|
||||
|
@ -553,6 +558,9 @@ The useful mapping keys in a :class:`LogRecord` are given in the section on
|
|||
will raise a ``ValueError``.
|
||||
For example: ``logging.Formatter('%(asctime)s - %(message)s', style='{')``.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
The *defaults* parameter was added.
|
||||
|
||||
.. method:: format(record)
|
||||
|
||||
The record's attribute dictionary is used as the operand to a string
|
||||
|
|
|
@ -411,8 +411,9 @@ class PercentStyle(object):
|
|||
asctime_search = '%(asctime)'
|
||||
validation_pattern = re.compile(r'%\(\w+\)[#0+ -]*(\*|\d+)?(\.(\*|\d+))?[diouxefgcrsa%]', re.I)
|
||||
|
||||
def __init__(self, fmt):
|
||||
def __init__(self, fmt, *, defaults=None):
|
||||
self._fmt = fmt or self.default_format
|
||||
self._defaults = defaults
|
||||
|
||||
def usesTime(self):
|
||||
return self._fmt.find(self.asctime_search) >= 0
|
||||
|
@ -423,7 +424,11 @@ class PercentStyle(object):
|
|||
raise ValueError("Invalid format '%s' for '%s' style" % (self._fmt, self.default_format[0]))
|
||||
|
||||
def _format(self, record):
|
||||
return self._fmt % record.__dict__
|
||||
if defaults := self._defaults:
|
||||
values = defaults | record.__dict__
|
||||
else:
|
||||
values = record.__dict__
|
||||
return self._fmt % values
|
||||
|
||||
def format(self, record):
|
||||
try:
|
||||
|
@ -441,7 +446,11 @@ class StrFormatStyle(PercentStyle):
|
|||
field_spec = re.compile(r'^(\d+|\w+)(\.\w+|\[[^]]+\])*$')
|
||||
|
||||
def _format(self, record):
|
||||
return self._fmt.format(**record.__dict__)
|
||||
if defaults := self._defaults:
|
||||
values = defaults | record.__dict__
|
||||
else:
|
||||
values = record.__dict__
|
||||
return self._fmt.format(**values)
|
||||
|
||||
def validate(self):
|
||||
"""Validate the input format, ensure it is the correct string formatting style"""
|
||||
|
@ -467,8 +476,8 @@ class StringTemplateStyle(PercentStyle):
|
|||
asctime_format = '${asctime}'
|
||||
asctime_search = '${asctime}'
|
||||
|
||||
def __init__(self, fmt):
|
||||
self._fmt = fmt or self.default_format
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._tpl = Template(self._fmt)
|
||||
|
||||
def usesTime(self):
|
||||
|
@ -490,7 +499,11 @@ class StringTemplateStyle(PercentStyle):
|
|||
raise ValueError('invalid format: no fields')
|
||||
|
||||
def _format(self, record):
|
||||
return self._tpl.substitute(**record.__dict__)
|
||||
if defaults := self._defaults:
|
||||
values = defaults | record.__dict__
|
||||
else:
|
||||
values = record.__dict__
|
||||
return self._tpl.substitute(**values)
|
||||
|
||||
|
||||
BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s"
|
||||
|
@ -546,7 +559,8 @@ class Formatter(object):
|
|||
|
||||
converter = time.localtime
|
||||
|
||||
def __init__(self, fmt=None, datefmt=None, style='%', validate=True):
|
||||
def __init__(self, fmt=None, datefmt=None, style='%', validate=True, *,
|
||||
defaults=None):
|
||||
"""
|
||||
Initialize the formatter with specified format strings.
|
||||
|
||||
|
@ -565,7 +579,7 @@ class Formatter(object):
|
|||
if style not in _STYLES:
|
||||
raise ValueError('Style must be one of: %s' % ','.join(
|
||||
_STYLES.keys()))
|
||||
self._style = _STYLES[style][0](fmt)
|
||||
self._style = _STYLES[style][0](fmt, defaults=defaults)
|
||||
if validate:
|
||||
self._style.validate()
|
||||
|
||||
|
|
|
@ -3710,6 +3710,9 @@ class FormatterTest(unittest.TestCase):
|
|||
'args': (2, 'placeholders'),
|
||||
}
|
||||
self.variants = {
|
||||
'custom': {
|
||||
'custom': 1234
|
||||
}
|
||||
}
|
||||
|
||||
def get_record(self, name=None):
|
||||
|
@ -3926,6 +3929,26 @@ class FormatterTest(unittest.TestCase):
|
|||
)
|
||||
self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
|
||||
|
||||
def test_defaults_parameter(self):
|
||||
fmts = ['%(custom)s %(message)s', '{custom} {message}', '$custom $message']
|
||||
styles = ['%', '{', '$']
|
||||
for fmt, style in zip(fmts, styles):
|
||||
f = logging.Formatter(fmt, style=style, defaults={'custom': 'Default'})
|
||||
r = self.get_record()
|
||||
self.assertEqual(f.format(r), 'Default Message with 2 placeholders')
|
||||
r = self.get_record("custom")
|
||||
self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
|
||||
|
||||
# Without default
|
||||
f = logging.Formatter(fmt, style=style)
|
||||
r = self.get_record()
|
||||
self.assertRaises(ValueError, f.format, r)
|
||||
|
||||
# Non-existing default is ignored
|
||||
f = logging.Formatter(fmt, style=style, defaults={'Non-existing': 'Default'})
|
||||
r = self.get_record("custom")
|
||||
self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
|
||||
|
||||
def test_invalid_style(self):
|
||||
self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Added a `defaults` parameter to :class:`logging.Formatter`, to allow
|
||||
specifying default values for custom fields. Patch by Asaf Alon and Bar
|
||||
Harel.
|
Loading…
Reference in New Issue