Description
Bug report
Bug description:
When the library tries to initialize a formatter and comes across the old format
property, it falls back to an error handler, but before it does that, it pops the .
dicationary from the config
making it impossible to process it later during the second call to self.configure_custom(config)
https://github.com/python/cpython/blob/main/Lib/logging/config.py#L480
This is whrere configure_custom
calls props = config.pop('.', None)
, but it does that before result = c(**kwargs)
which throws an exception when it finds the format
property.
def configure_custom(self, config):
"""Configure an object with a user-supplied factory."""
c = config.pop('()')
if not callable(c):
c = self.resolve(c)
props = config.pop('.', None) # <-- '.' gets removed on first try and is gone on the second attempt
# Check for valid identifiers
kwargs = {k: config[k] for k in config if valid_ident(k)}
result = c(**kwargs) # <-- throws an error when `format` property is used
# props = config.pop('.', None) # <-- this is where it probably needs to get called so that '.' remains in 'config'
if props:
for name, value in props.items():
setattr(result, name, value)
return result
Then then initialization continues here inside the except
that calls configure_custom
for the second time, but this time without the .
in the config
so it's skipped.
https://github.com/python/cpython/blob/main/Lib/logging/config.py#L670
def configure_formatter(self, config):
"""Configure a formatter from a dictionary."""
if '()' in config:
factory = config['()'] # for use in exception handler
try:
result = self.configure_custom(config)
except TypeError as te:
if "'format'" not in str(te):
raise
#Name of parameter changed from fmt to format.
#Retry with old name.
#This is so that code can be used with older Python versions
#(e.g. by Django)
config['fmt'] = config.pop('format')
config['()'] = factory
result = self.configure_custom(config)
I guess the function configure_custom
should call props = config.pop('.', None)
after result = c(**kwargs)
so that the .
remains in the config
for the second call in case an exception is thrown during the first try.
Example
This config won't initialize the custom_property
of MyFormatter
:
"custom_formatter": {
"()": MyFormatter,
"style": "{",
"datefmt": "%Y-%m-%d %H:%M:%S",
"format": "<custom-format>", # <-- when removed or changed to `fmt` then the '.' works
".": {
"custom_property": "value"
}
}
The formatter is implemented like this:
class MyFormatter(logging.Formatter):
custom_property: str = "."
def format(self, record: logging.LogRecord) -> str:
# ...
return super().format(record)
CPython versions tested on:
3.10
Operating systems tested on:
Windows
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status