Mypy

来自百合仙子's Wiki
跳转到导航 跳转到搜索

用法

mypy --enable-error-code possibly-undefined xxx

可使用--allow-redefinition参数来允许更新变量的类型[1]

已知问题

type: ignore 必须位于同一行

当一行太长时,如:

conf = configparser.ConfigParser(dict_type=dict, allow_no_value=True)

要忽略第一个参数的报错而不使行过长,只能写成:

conf = configparser.ConfigParser( # type: ignore
  dict_type=dict, allow_no_value=True)

无法推断出联合类型

比如对于 strpathlib.Path 组成的列表,推断结果为 List[object] 然后被拒绝传入 subprocess.Popen

又如无法对 List[str]List[Union[str, os.PathLike]]相加:

error: Unsupported operand types for + ("List[str]" and "List[Union[str, _PathLike[Any]]]")

不支持变量名 shadowing

当变量名称被复用时,mypy 还认为是同一类型。尤其是传入可选值,然后内部处理掉 None 的情况时。使用不同的名称会占用大脑的处理空间。

不支持可选属性

当对象有可能有某个属性时,mypy 无法识别 hasattr 判断之后,该属性必然存在。

不识别 TypeError 异常处理

如下代码无法通过检查:

def t(x: Optional[str]) -> Optional[int]:
  try:
    return int(x)
  except (ValueError, TypeError):
    return None

max 函数的支持不好

如下代码能够通过检查:

def f(a: Union[int, str], b: Union[int, str]) -> Union[int, str]:
  return max(a, b)

max(int, str) 会返回 object 类型。

不支持为类添加属性

这导致无法将子类作为父类的属性,例如下边的代码,无论是在类定义里添加类型注解,还是定义好之后再赋值,均无法通过 mypy:

class BuildResult:
  def __init__(self) -> None:
    if __class__ is self.__class__:
      raise TypeError('use subclasses')

  def __bool__(self) -> bool:
    return self.__class__ in [self.successful, self.staged]

  def __init_subclass__(cls):
    setattr(__class__, cls.__name__, cls)

  def __repr__(self) -> str:
    name = self.__class__.__name__
    return f'<BuildResult.{name}>'

class successful(BuildResult):
  pass

class staged(BuildResult):
  pass

class failed(BuildResult):
  def __init__(self, exc: Exception) -> None:
    self.exc = exc

  def __repr__(self) -> str:
    name = self.__class__.__name__
    return f'<BuildResult.{name}: {self.exc!r}>'

class skipped(BuildResult):
  def __init__(self, reason: str) -> None:
    self.reason = reason

  def __repr__(self) -> str:
    name = self.__class__.__name__
    return f'<BuildResult.{name}: {self.reason!r}>'

del successful, staged, failed, skipped

在局部判定全局变量的类型

如下代码中的 assert 会被认为不成立从而通过检查并跳过 reveal_type[2]

from typing import Optional

class Global:
  attr: Optional[int] = None

G = Global()

def f() -> None:
  G.attr = 1

def t() -> None:
  G.attr = None
  f()
  assert G.attr is not None
  reveal_type(G)

不支持 LiteralString

参见

参考资料