Skip to content

Nested caplog.filtering usage may remove filters early #14189

@ncoghlan

Description

@ncoghlan
  • a detailed description of the bug or problem you are having
  • output of pip list from the virtual environment you are using (N/A, design bug)
  • pytest and operating system versions (N/A, design bug)
  • minimal example if possible

I had a baffling case where a caplog filter set up by a yield fixture with caplog.filtering was gone by the time the test case using the fixture was executed. I eventually traced the problem down to a separate non-yielding fixture that used caplog.filtering with the same filtering function around an immediate setup operation.

The issue is that the standard library's addFilter/removeFilter log handler methods work by value, so

def filtering(self, filter_: logging.Filter) -> Generator[None]:
will eagerly remove the filter when leaving the context manager, even if it was already present when the context manager was entered.

Minimal example:

def test_caplog_fixture(caplog):
    import logging
    def no_capture_filter(log_record):
        return False

    with caplog.filtering(no_capture_filter):
        logging.warning("Will not be captured")
        with caplog.filtering(no_capture_filter):
            logging.warning("Will also not be captured")
        logging.warning("Will incorrectly be captured")

    assert caplog.records == []  # Fails

There are two ways I see to allow nested invocations to work without side effects:

  • add an external check to see if the given filter is already in self.handler.filters and skip the add/remove pair entirely in that case (it's unfortunate that addFilter doesn't return a boolean value to indicate if the filter list changed or not); or
  • save a full snapshot of the filter list before calling addFilter and restore that instead of using removeFilter

The former is presumably the better option, since it's just an extra scan of the filter list rather than making two full copies of the list (one to take the snapshot, one to insert the snapshot back into the original filter list).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions