Fix mypy type narrowing false positive in cfg.py#190
Conversation
…dentity comparison Agent-Logs-Url: https://github.com/MatthieuDartiailh/bytecode/sessions/b7904bd0-bbc8-412f-8f14-de2ed29696a1 Co-authored-by: MatthieuDartiailh <4481740+MatthieuDartiailh@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #190 +/- ##
=======================================
Coverage 95.16% 95.16%
=======================================
Files 7 7
Lines 2028 2028
Branches 449 449
=======================================
Hits 1930 1930
Misses 54 54
Partials 44 44 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| # through a particular jump. So we are lenient here. | ||
| if ( | ||
| te := self.block.get_trailing_try_end(i) | ||
| ) and te.entry is self._current_try_begin: |
There was a problem hiding this comment.
TryEnd.entry cannot be None so the error from Mypy is weird. Could you try flipping the test around self._current_try_begin is te.entry to see if it satisfies Mypy ?
There was a problem hiding this comment.
Tried flipping to self._current_try_begin is te.entry — it produces the same 3 errors. The root cause is mypy's identity-narrowing behavior: when evaluating x is y where y is Optional[T], mypy narrows x into T | None in the truthy branch (reasoning: if y were None and x is y is True, then x must also be None). This applies regardless of which side the optional is on.
You're right that TryEnd.entry can never actually be None — but mypy doesn't exploit that to suppress the narrowing. The explicit is not None guard is the minimal fix that teaches mypy self._current_try_begin is non-None before the identity check, preventing the spurious narrowing of te.entry.
There was a problem hiding this comment.
@copilot Did you try of just assume the behavior ? Also be mindful of the formatting which is currently erroring in CI
There was a problem hiding this comment.
I did try it — flipped to self._current_try_begin is te.entry produces the same errors. Mypy's identity narrowing widens the non-optional operand to include None regardless of which side of is it's on, so the is not None guard is required.
The formatting issue has been fixed in commit 7702d3e — black now reformats the walrus-operator if condition and the ternary expression to use the parenthesised multi-line style it expects.
MatthieuDartiailh
left a comment
There was a problem hiding this comment.
Address the formatting issue identified by the CI (lint job)
|
@copilot fix the formatting issue |
Agent-Logs-Url: https://github.com/MatthieuDartiailh/bytecode/sessions/8ed3b0c7-8cc3-4d4c-9421-7a291774fbd9 Co-authored-by: MatthieuDartiailh <4481740+MatthieuDartiailh@users.noreply.github.com>
Fixed in commit
Done in commit |
mypy 1.20.0 CI lint job fails due to type narrowing behavior: when evaluating
te.entry is self._current_try_begin, mypy narrowste.entryfromTryBegintoTryBegin | Nonebecause the RHS (self._current_try_begin) isOptional[TryBegin].Change
src/bytecode/cfg.py: Add explicitself._current_try_begin is not Noneguard before the identity comparison, giving mypy enough information to narrow both sides correctly:Semantically equivalent — the identity check can never be
Truewhenself._current_try_beginisNone.