mypy icon indicating copy to clipboard operation
mypy copied to clipboard

sys.platform checks assigned to variables are not handled correctly

Open Dreamsorcerer opened this issue 2 years ago • 2 comments

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.

Dreamsorcerer avatar Feb 04 '23 13:02 Dreamsorcerer

hoping this could at least support Final variable

is_unix: typing.Final = sys.platform != "win32"

trim21 avatar Jul 06 '23 19:07 trim21

(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.)

finite-state-machine avatar Feb 16 '24 17:02 finite-state-machine