Skip to content

Fix non-local partial None detection for non-literal None assignments - #22473

Open
Jahongir-Qurbonov wants to merge 1 commit into
python:masterfrom
Jahongir-Qurbonov:partial-none-non-literal
Open

Jahongir-Qurbonov wants to merge 1 commit into
python:masterfrom
Jahongir-Qurbonov:partial-none-non-literal

Conversation

@Jahongir-Qurbonov

Copy link
Copy Markdown
Contributor

#22437 changed which functions get processed together with the module top level, which is what makes non-local partial None types work. It now does this only for variables whose first assignment has a literal None rvalue. The checker, however, creates a partial None type for any inferred variable whose initial type is None, including variables that have no rvalue at all. As a result this now fails:

for v in [None]: pass

def k() -> None:
    global v
    v = 1  # error: Incompatible types in assignment (expression has type "int", variable has type "None")

Before #22437 this was accepted and v was inferred as int | None. The same regression happens for y = (z := None) (for z), for w = None if c else None, and for class attributes initialized this way and later assigned via self.a = ... in a method.

This PR treats binding forms that have no rvalue as possibly None, as they were before #22437. These are for loop indices, assignment expression targets, with/match/star targets and tuple items that can't be matched to an rvalue item. It also looks through conditional expressions and assignment expressions in the rvalue. All other rvalues still count as not None, so the speedup from #22437 is kept. This leaves one case unhandled on purpose: x = f() where f returns None. That assignment is already reported as a func-returns-value error.

Added testPartialNoneTypeNonLiteralNone to check-inference.test, next to the test added in #22437. It covers conditional expressions, walrus targets and for loop indices with global, and conditional expressions and walrus rvalues for class attributes assigned via self.

python#22437 made semantic analysis mark a function to be checked
together with the module top level (needed for non-local partial None
types) only when the variable's first assignment has a literal `None`
rvalue. However, the checker creates a partial None type for any
inferred variable whose initial type is None, so a for loop index
iterating over `[None]`, an assignment expression target, or
`x = None if c else None` no longer got completed in functions using
`global`/`nonlocal` or `self.x = ...`, and produced spurious errors
such as "Incompatible types in assignment (expression has type "int",
variable has type "None")".

Treat binding forms without an rvalue (for loop index, assignment
expression target, with/match/star targets, unmatched tuple items) as
possibly None, and also look through conditional expressions and
assignment expressions in the rvalue. Other rvalues are still treated
as not None to keep the speedup from python#22437. In particular,
`x = f()` where `f` returns None is intentionally not handled, since
it is already reported as a func-returns-value error.
@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant