Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

The first attempt to hande the format property during logging.Formatter initilization removes the . dictionary from the config #110875

Copy link
Copy link
Closed
@he-dev

Description

@he-dev
Issue body actions

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

stdlibPython modules in the Lib dirPython modules in the Lib dirtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Projects

Status

Done
Show more project fields

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.