Пусть mypy предупреждает о проверках на равенство переменных разных типов

mypy --strict допускает следующий минимальный пример без ошибок:

a: int = 1
b: str = '1'

if a == b:
    pass
else:
    pass

Есть ли возможность заставить его выдавать ошибку (или хотя бы предупреждение) о строке if a == b:?


person Tobias Hermann    schedule 01.09.2018    source источник


Ответы (1)


Редактировать: Мой исходный ответ ниже описывает, как реализовать это, написав собственный плагин mypy.

Однако начиная с mypy 0.700 теперь это можно сделать напрямую с помощью флага --strict-equality. Обратите внимание, что на момент написания этот флаг не включен по умолчанию с помощью флага --strict.

Например, запуск mypy в исходной программе выше приведет к следующей ошибке:

test.py:4: error: Non-overlapping equality check (left operand type: "int", right operand type: "str")

Вы можете найти более подробную информацию об этом флаге в нижней части Разное параметры строгости в командной строке mypy flags doc.


Это возможно с помощью (в настоящее время экспериментального и недокументированного) API плагинов.

Короче говоря, добавьте следующий файл где-нибудь в вашем проекте:

from typing import Callable, Optional, Type
from mypy.plugin import MethodContext, Plugin
from mypy.meet import is_overlapping_types

class StrictEqualityPlugin(Plugin):
    def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], Type]]:
        if fullname.endswith('__eq__') or fullname.endswith('__ne__'):
            return strict_check_callback

def strict_check_callback(ctx: MethodContext) -> Type:
    if len(ctx.arg_types) == 1 and len(ctx.arg_types[0]) == 1:
        # Note: Expressions of the form 'base_type == arg_type' get
        # translated into `base_type.__eq__(arg_type)`.
        base_type = ctx.type
        arg_type = ctx.arg_types[0][0]

        # Two types are overlapping if one of the types could potentially be the
        # same as or a subtype of the other.
        #
        # If you want something even stricter, add `from mypy.sametypes import is_same_type`
        # up at the top and call `is_same_type` instead of `is_overlapping_types`.
        if not is_overlapping_types(base_type, arg_type):
            ctx.api.msg.warn(
                "The left and right operands have disjoint types ({} and {})".format(
                    ctx.api.msg.format(base_type),
                    ctx.api.msg.format(arg_type),
                ), 
                ctx.context)
    return ctx.default_return_type

def plugin(mypy_version: str) -> Plugin:
    return StrictEqualityPlugin

Предположим, что имя этого файла strict_equality_plugins.py.

Затем на верхнем уровне проекта создайте файл mypy.ini. Файл должен, как минимум, содержать следующее:

[mypy]
plugins = ./path/to/strict_equality_plugins.py

Затем запуск mypy в вашем корневом проекте приведет к следующим ошибкам:

foo.py:1: предупреждение: левый и правый операнды имеют непересекающиеся типы ("int" и "str")

Отказ от ответственности: API подключаемого модуля проекта mypy является экспериментальным. Я не обещаю, что этот подключаемый модуль будет продолжать работать без изменений в будущих версиях mypy.

person Michael0x2a    schedule 01.09.2018