Description
Bug Report
I have a platform-specific script that uses libraries only available on one platform. For the sake of the bug report, I'll use this script:
import mslex
import winreg
from ctypes import WinError
windir = winreg.ExpandEnvironmentStrings("%windir%")
print(mslex.quote(windir))
raise WinError()
With the following requirements.txt
:
mslex==1.3.0 ; sys_platform=="win32"
mypy==1.16.1
The script needs three different kinds of Windows-specific functionality:
- A Windows-specific function from a cross-platform builtin module (
ctypes.WinError
) - A Windows-specific builtin module (
winreg
) - A thirdparty module from PyPI, that I'm only installing on Windows (
mslex
)
I'm currently working on macOS. If I run mypy on the script, the result is, as expected, an error:
mypy_repro % uv run --with-requirements requirements.txt mypy win.py
win.py:1: error: Cannot find implementation or library stub for module named "mslex" [import-not-found]
win.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
win.py:3: error: Module "ctypes" has no attribute "WinError" [attr-defined]
win.py:5: error: Module has no attribute "ExpandEnvironmentStrings" [attr-defined]
Found 3 errors in 1 file (checked 1 source file)
Interestingly, there's no error about the missing winreg
module.
If I wrap the script in a platform check, there are no errors:
import sys
if sys.platform=="win32":
import mslex
import winreg
from ctypes import WinError
windir = winreg.ExpandEnvironmentStrings("%windir%")
print(mslex.quote(windir))
raise WinError()
mypy_repro % uv run --with-requirements requirements.txt mypy win_if.py
Success: no issues found in 1 source file
However, indenting the whole script is not really elegant. Luckily, mypy's docs offer a solution:
As a special case, you can also use one of these checks in a top-level (unindented) assert; this makes mypy skip the rest of the file.
However, when I try it, it still produces an error, although only for the thirdparty mslex
module:
import sys
assert sys.platform=="win32"
import mslex
import winreg
from ctypes import WinError
windir = winreg.ExpandEnvironmentStrings("%windir%")
print(mslex.quote(windir))
raise WinError()
mypy_repro % uv run --with-requirements requirements.txt mypy win_assert.py
win_assert.py:4: error: Cannot find implementation or library stub for module named "mslex" [import-not-found]
win_assert.py:4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 1 source file)
It seems like even though the rest of the file after the assert
is skipped during type checking, the imports are still processed and error out if unavailable. This is not what I expected; I expected it to pass.
What's even more curious is that if I wrap the imports in an if
after the assert
, the error persists. It's as if the assert makes mypy skip the rest of the file, including the conditionals around the import statements, but not including the import statements themselves:
import sys
assert sys.platform=="win32"
if sys.platform=="win32":
import mslex
import winreg
from ctypes import WinError
windir = winreg.ExpandEnvironmentStrings("%windir%")
print(mslex.quote(windir))
raise WinError()
mypy_repro % uv run --with-requirements requirements.txt mypy win_assert_then_if.py
win_assert_then_if.py:5: error: Cannot find implementation or library stub for module named "mslex" [import-not-found]
win_assert_then_if.py:5: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 1 source file)
The only workaround I found is to add a platform check around the Windows-specific imports, and put an assert AFTER the imports:
import sys
if sys.platform=="win32":
import mslex
import winreg
from ctypes import WinError
assert sys.platform=="win32"
windir = winreg.ExpandEnvironmentStrings("%windir%")
print(mslex.quote(windir))
raise WinError()
mypy_repro % uv run --with-requirements requirements.txt mypy win_if_then_assert.py
Success: no issues found in 1 source file
To summarize the results:
Testcase | Expected behaviour | Actual behaviour | OK/NOK |
---|---|---|---|
No platform check | Error for all 3 imports | Error for ctypes and mslex imports, but not the winreg import. Also, an error for calling a function from winreg. |
OK (kinda) |
Platform if around entire script |
No errors | No errors | OK |
Top-level platform assert |
No errors | Error on mslex import |
NOK |
Top-level platform assert , then platform if around imports |
No errors (if should be redundant) |
Error on mslex import |
NOK |
Platform if around imports, then top-level platform assert |
No errors | No errors | OK |
Am I missing something? Or is there a bug in mypy's handling of the top-level platform asserts?
** Environment**
- Mypy version used: 1.16.1
- Mypy command-line flags: none
- Mypy configuration options from
mypy.ini
(and other config files): none - Python version used: 3.12.4
- OS version: macOS 15.5 Sequoia
The issue is not specific to type-checking Windows-specific script on macOS, the same results can be reproduced on Windows for a macOS-specific script. I haven't checked whether conditions using the python interpreter version misbehave the same way as platform checks.