Skip to content

The documented pattern for parametrizing conditional raising leaks a lot of memory #13410

Open
@emontnemery

Description

@emontnemery

The documented pattern for parametrizing conditional raising leaks a lot of memory.

The problem is that the pytest.raises context manager is mutated with a reference to the raised exception and traceback when exiting the context manager:

exc_info = cast(Tuple[Type[E], E, TracebackType], (exc_type, exc_val, exc_tb))
self.excinfo.fill_unfilled(exc_info)

This causes the traceback to be kept in memory until the test session terminates.

If this is indeed the correct way for this kind of parametrization, pytest should do some automatic clean up after the test finishes.

Otherwise, the documented parametrization should be adjusted to not pass in mutable objects, for example:

@pytest.mark.parametrize(
    "example_input,expectation",
    [
        (3, lambda: nullcontext(2)),
        (2, lambda: nullcontext(3)),
        (1, lambda: nullcontext(6)),
        (0, lambda: pytest.raises(ZeroDivisionError)),
    ],
)
def test_division(example_input, expectation):
    """Test how much I know division."""
    with expectation() as e:
        assert (6 / example_input) == e

The same problem happens if the parametrization involves exception objects which are raised during the test, the objects are mutated by Python which adds a __traceback__ to them:

@pytest.mark.parametrize("error", [Exception("Boom!"),])
def test_division(error):
    """Blow up."""
    raise error

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: performanceperformance or memory problem/improvement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions