sys.platform checks assigned to variables are not handled correctly
When doing a sys.platform check, mypy seems to only recognise this when the check is done directly in the if statement, but not when the check is assigned to a variable.
e.g. https://github.com/kuwv/invoke/blob/typing/invoke/terminals.py#L33 This produces:
invoke/terminals.py:33:5: error: Module "ctypes" has no attribute "windll" [attr-defined]
from ctypes import (
But, if you change if WINDOWS to if sys.platform == "win32", then the error disappears.
I'm pretty sure I've seen mypy work correctly when sys.version_info checks are assigned to a variable, so not sure why sys.platform checks would be any different.
Tested with 0.991.
hoping this could at least support Final variable
is_unix: typing.Final = sys.platform != "win32"
(A11y: see note at end.)
This applies to sys.version_info as well; used directly it works as expected (completely ignoring code that fails the check); but when a Final variable is set to the result of the check (either directly or via an if/else block), mypy continues to process both branches of dependent if statements [mypy-play.net]:
from __future__ import annotations
import sys
from typing import *
# paragraph 'A1': this alternative to paragraph 'A2' doesn't help:
#
# HAVE_310: Final = sys.version_info >= (3, 10)
# Mypy's output, by version:
#┌───────────────────┬──────────────────┬────┐
#│ Python 3.9 │ Python 3.10 │ │
#├───────────────────┼──────────────────┼────┤
# paragraph 'A2': #│ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅ │
if sys.version_info < (3, 10): #│ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅ │
HAVE_310: Final = False #├───────────────────┼──────────────────┼────┤
reveal_type(HAVE_310) #│ Literal[False]? │ (silent) │ OK │
else: #├───────────────────┼──────────────────┼────┤
HAVE_310: Final = True #├───────────────────┼──────────────────┼────┤
reveal_type(HAVE_310) #│ (silent) │ Literal[True]? │ OK │
#├───────────────────┼──────────────────┼────┤
# reachability: #│ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅ │
if not HAVE_310: #├───────────────────┴──────────────────┼────┤
reveal_type(HAVE_310) #│ Literal[False] │ R1 │
else: #├──────────────────────────────────────┼────┤
reveal_type(HAVE_310) #│ Literal[True] │ R2 │
#├───────────────────┬──────────────────┼────┤
# dependent 'Final' declarations: #│ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅ │
if not HAVE_310: #│ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅ │
SHINY: Final = False #│ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ │ ⋅⋅ │
else: #├───────────────────┴──────────────────┼────┤
SHINY: Final = True #│ "Cannot redefine an existing name as │ R3 │
#│ final" [misc] │ │
#├──────────────────────────────────────┼────┤
reveal_type(SHINY) #│ Literal[False]? │ R4 │
#└──────────────────────────────────────┴────┘
# Expected behavior (where different):
#
# R1: for Python 3.10, this should be silent
# R2: for Python 3.9, this should be silent
# R3: only one definition of 'SHINY' should occur, there should be no
# error (just as there is no error defining 'HAVE_310' using
# 'Final' in paragraph 'A2')
# R4: for Python 3.10, this should read ~'Literal[True]'
(A11y: The embedded source has a table in comments to the right of the code, the rows of which pertain to the line of code with which they share a line. If an alternate format would be helpful, I would be happy to prepare one.)