The Bug That Hid Behind Its Own Comment: Fixing Inconsistent Inference in astroid

2026年8月22日1 次浏览来源:Dev.to阅读原文

This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry.

Project Overview astroid is the static-analysis engine that powers pylint — one of the most widely used linters in the Python ecosystem.

Instead of running your code, astroid builds a model of what your code would do (a process called "inference") so pylint can catch real bugs before you ever hit run.

That means astroid's inference logic has to be extremely consistent: if it gets confused about what a piece of code returns, pylint either misses real bugs, or — as in this case — flags perfectly correct code as broken.

Bug Fix or Performance Improvement I picked up astroid issue #3077: identical expressions were being inferred differently depending only on how the surrounding call was written — even when the code was structurally symmetric.

In a class like this: Both and do the exact same thing at runtime — I verified this by actually running the file.

But pylint only flagged one of them: The explicit path got a false positive; the equivalent implicit path did not, even though is a plain in both cases at runtime.

Code PR: https://github.com/pylint-dev/astroid/pull/3242 My Improvements Ruling out the obvious suspect My first hypothesis was , the function that handles itself — it seemed like the natural place for a cast-related inconsistency to live.

Tested in isolation, though, it behaves identically for both call styles.

Dead end — but a useful one, because it told me the bug lived somewhere upstream of entirely.

Tracing the real divergence I live-patched pylint's own inference calls with a small monkey-patching script (rather than editing installed files directly, so I could observe astroid's real behavior without risking my environment) and found the two call styles actually go through completely different astroid code paths: resolves to a , which walks normally into 's body and evaluates correctly. resolves to the itself, routed through — the code path specifically responsible for resolving implicit dunder calls.

Finding the actual bug Inside (), there's an optional first step that tries to resolve the call as if it were a plain attribute lookup on the callee: For , that first branch tries to look up an attribute literally named — on the instance, which obviously has no such attribute.

That lookup raises an .

Because this is a generator function, an unhandled exception anywhere inside it terminates the entire function immediately — including the second loop just below, which is the code that actually resolves correctly.

The comment right there in the source literally says "Otherwise we infer the call to the dunder normally" — but the code never got the chance to reach it.

The bug was hiding directly behind its own explanation.

The fix A small, surgical change: wrap that first branch in , so a failed attribute lookup no longer aborts the whole function — it simply falls through to the resolution below, exactly as the existing comment always promised.

Result — both call styles now consistently resolve the same way: (As the original issue notes, isn't necessarily the most precise answer could give — but consistency is what the bug was actually about, and this fix delivers it cleanly.) Testing I added a regression test, , reproducing the minimal case directly in astroid's own suite (), and ran the entire existing test suite (2,000+ tests) to confirm nothing else broke.

The only failures present were pre-existing, unrelated Windows-environment issues (symlink permissions, missing fixtures) and one unrelated failure — confirmed via to also occur on unmodified , ruling out any regression from this change.

分享
Baike.dev

baike.dev helps you discover great languages, frameworks, databases, DevOps and cloud-native tools.

Quick links

About

Contribute

Found a great developer tool? Share it with the community.

Submit a tool
© 2026 baike.dev Developer EncyclopediaUpdated daily · Discover great developer tools