Skip to content

1.16 changed behavior of redefined variable to None #19280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
eli-schwartz opened this issue Jun 12, 2025 · 3 comments
Open

1.16 changed behavior of redefined variable to None #19280

eli-schwartz opened this issue Jun 12, 2025 · 3 comments
Labels
bug mypy got something wrong topic-strict-optional topic-type-narrowing Conditional type narrowing / binder

Comments

@eli-schwartz
Copy link
Contributor

https://github.com/mesonbuild/meson/blob/master/mesonbuild/cmake/fileapi.py

This file started triggering:

mesonbuild/cmake/fileapi.py:81:20: error: "None" has no attribute "__iter__" (not iterable)  [attr-defined]
mesonbuild/cmake/fileapi.py:82:20: error: Value of type "None" is not indexable  [index]
mesonbuild/cmake/fileapi.py:84:36: error: Value of type "None" is not indexable  [index]

due to reuse of for i in ......:

Reduction:

import typing as T
from pathlib import Path

for i in Path().iterdir():
    T.reveal_type(i)
    break

data = T.cast('T.Dict[str, T.Any]', {'foo': [{'kind': 1, 'b': 'two'}]})
for i in data['foo']:
    T.reveal_type(i)
    assert isinstance(i, dict)
    T.reveal_type(i)
    assert 'kind' in i

With mypy 1.16.0

$ mypy --no-strict-optional --warn-unreachable fileapi.py 
fileapi.py:5: note: Revealed type is "pathlib.Path"
fileapi.py:10: note: Revealed type is "Any"
fileapi.py:12: note: Revealed type is "None"
fileapi.py:13: error: "None" has no attribute "__iter__" (not iterable)  [attr-defined]

It starts off as Path, gets redefined as Any (which works?), but then asserting the Any is a dict results in it migrating to None. With mypy 1.15, it never redefined to Any at all, so it became unreachable:

$ pip install mypy==1.15.* 
$ mypy --no-strict-optional --warn-unreachable fileapi.py 
fileapi.py:5: note: Revealed type is "pathlib.Path"
fileapi.py:10: note: Revealed type is "pathlib.Path"
fileapi.py:11: error: Subclass of "Path" and "dict[Any, Any]" cannot exist: would have incompatible method signatures  [unreachable]
fileapi.py:12: error: Statement is unreachable  [unreachable]

I can't really say either behavior is useful. I want variables to be block-scoped and reset their expected value. But at least the 1.15 behavior was comprehensible to me. The new behavior only works if I delete the assert, which seems terribly counterproductive.

@eli-schwartz eli-schwartz added the bug mypy got something wrong label Jun 12, 2025
@brianschubert brianschubert added the topic-type-narrowing Conditional type narrowing / binder label Jun 12, 2025
@brianschubert
Copy link
Collaborator

Bisects to b50f3a1 (#18538), cc @ilevkivskyi

Without --no-strict-optional, i is narrowed to Never:

$ mypy --version
mypy 1.16.0+dev.b50f3a1a44038b5f6304f77263f6e08c157f9aa8 (compiled: no)

$ mypy --no-strict-optional fileapi.py
fileapi.py:5: note: Revealed type is "pathlib.Path"
fileapi.py:10: note: Revealed type is "Any"
fileapi.py:12: note: Revealed type is "None"
fileapi.py:13: error: "None" has no attribute "__iter__" (not iterable)  [attr-defined]
Found 1 error in 1 file (checked 1 source file)

$ mypy --no-strict-optional fileapi.py
fileapi.py.:5: note: Revealed type is "pathlib.Path"
fileapi.py:10: note: Revealed type is "Any"
fileapi.py:12: note: Revealed type is "Never"
fileapi.py:13: error: "Never" has no attribute "__iter__" (not iterable)  [attr-defined]
Found 1 error in 1 file (checked 1 source file)

After #18972, the behavior improves without --no-strict-optional

$ mypy --version
mypy 1.16.0+dev.c724a6a806655f94d0c705a7121e3d671eced96d (compiled: no)

$ mypy fileapi.py                        
fileapi.py:5: note: Revealed type is "pathlib.Path"
fileapi.py:10: note: Revealed type is "Any"
fileapi.py:12: note: Revealed type is "builtins.dict[Any, Any]"

$ mypy --no-strict-optional fileapi.py
fileapi.py:5: note: Revealed type is "pathlib.Path"
fileapi.py:10: note: Revealed type is "Any"
fileapi.py:12: note: Revealed type is "None"
fileapi.py:13: error: "None" has no attribute "__iter__" (not iterable)  [attr-defined]
Found 1 error in 1 file (checked 1 source file)

@hauntsaninja
Copy link
Collaborator

I guess this branch https://github.com/python/mypy/pull/18972/files#diff-42eda644ce5003e33ea0766d7601fcfb4934ad106429abfe115954a1fccddd07R6301 could also check for no strict optional and NoneType

@eli-schwartz your best unblock might be trying out --allow-redefinition-new in 1.16

@eli-schwartz
Copy link
Contributor Author

I'm not in an urgent hurry, we are still tangled up in python 3.7 compat :) but will probably jump straight to >=3.10 once November rolls around. Just locally exploring in the interest of being better prepared for November, actually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-strict-optional topic-type-narrowing Conditional type narrowing / binder
Projects
None yet
Development

No branches or pull requests

3 participants