Skip to content

Do not lose the run when a warning's class cannot be resolved - #1387

Open
dprada wants to merge 1 commit into
pytest-dev:masterfrom
dprada:fix/guard-warning-class-resolution
Open

dprada wants to merge 1 commit into
pytest-dev:masterfrom
dprada:fix/guard-warning-class-resolution

Conversation

@dprada

@dprada dprada commented Sep 20, 2026 •

Copy link
Copy Markdown

Resolving a warning's class on the controller is an importlib.import_module and a getattr:

mod = importlib.import_module(data["message_module"])   # receiver thread, unguarded
cls = getattr(mod, data["message_class_name"])          # receiver thread, unguarded

Both run in the controller's receiver thread, and anything they raise ends the session: the node goes down mid-test and the run fails in the scheduler with KeyError: <WorkerController gwN> — a different subsystem, naming nothing that leads back to the import.

This is #404. Open since 2019, and its 2022 report names the getattr line and shows it arriving as an INTERNALERROR. test_warning_serialization_tweaked_module was added alongside that report and asserted pytest.raises(ModuleNotFoundError): it characterised the defect rather than fixing it. It asserts the degradation now, and a second test covers the AttributeError shape, which is the one that report actually hit.

Both resolutions degrade to the generic Warning this function already builds for a warning it cannot rebuild, and the fallback text carries the reason (class not resolved: ModuleNotFoundError: ...). A guard widened around an import is how a genuinely broken package turns into a warning nobody can explain.

The category resolution below it had no guard and no test either, and category is what pytest filters and reports on. It degrades to the message's own class rather than to None, because pytest renders a warning through warnings.formatwarning, which reads category.__name__ — so None would end the run just as surely as the unguarded getattr it replaces.

Field evidence. In uibcdf/molsysviewer#76, a cold import molsysmt raising inside the receiver thread discarded roughly 950 tests per occurrence, on about half of all -n 12 runs. It took three attempts to attribute, because the error names the scheduler and the cause is an import two steps away. A warning failing to deserialize should not be able to do that.

Tests

One each for an unimportable module, a class missing from its module, and an unresolvable category, plus the rewrite of the GH#404 test. Mutation-verified: removing the message-class guard fails 3 tests, the category guard 2.

Closes #404.


Split out of #1372, which keeps the separate question of transferring a warning's state — the two touch the same function but neither needs the other, and this half has the user-visible failure.

The two red py311-pytestmain checks are #1386 (--dist=loadgroup silently losing its grouping on pytest main): unrelated to this change, and red on every open PR in the repo since pytest-dev/pytest#14758 merged on 2026-09-06.

`unserialize_warning_message` resolved the message class with
`importlib.import_module` and a `getattr`, both outside every guard and both in
the controller's receiver thread. Anything they raised killed that thread: the
node went down mid-test and the run failed in the scheduler with
`KeyError: <WorkerController gwN>`, two steps away and naming nothing that led
back to the import.

This is GH#404, open since 2019. Its 2022 report names the `getattr` line and
shows it arriving as an `INTERNALERROR`. The test added alongside that report,
`test_warning_serialization_tweaked_module`, asserted
`pytest.raises(ModuleNotFoundError)`: it characterised the defect rather than
fixing it. It asserts the degradation now, and a second test covers the
`AttributeError` shape, which is the one that report actually hit.

Both resolutions degrade to the generic `Warning` this function already builds
for a warning it cannot rebuild, and the fallback text carries the reason. A
guard widened around an import is how a genuinely broken package turns into a
warning nobody can explain.

The category resolution below it had no guard and no test either, and `category`
is what pytest filters and reports on. It degrades to the message's own class
rather than to `None`, because pytest renders a warning through
`warnings.formatwarning`, which reads `category.__name__`.

Closes pytest-dev#404.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

'module' object has no attribute 'GtkWarning'

1 participant