Проблема, с которой вы здесь сталкиваетесь, заключается в том, что, поскольку с cython нет подсказок типов, к сожалению, неоднозначно, что именно должно означать выражение cython.int
, и, следовательно, неоднозначно, что должно означать Flags = cython.int
. .
В частности, это может быть случай, когда cython.int
должно быть значением , а не типом. В этом случае Flags = cython.int
будет обычным присваиванием переменной, а не псевдонимом типа.
Хотя mypy теоретически может попытаться проанализировать остальную часть вашей программы, чтобы устранить эту неоднозначность, это будет несколько дорого сделать , Поэтому вместо этого он несколько произвольно решает, что cython.int
должно быть значением (например, константой), что, в свою очередь, приводит к тому, что ваша функция difference
не может выполнить проверку типа.
Однако, если вы используете тип cython.int
непосредственно в сигнатуре типа, мы не получаем такой неоднозначности: в этом контексте это выражение, скорее всего, должно быть каким-то типом, поэтому mypy решает: интерпретировать выражение по-другому.
Итак, как вы обходите это? Ну, есть несколько вещей, которые вы можете попробовать, и я перечислю их в порядке убывания усилий (и в порядке увеличения хакерства).
Отправить запрос на получение поддержки в Mypy для поддержки PEP 613 . Этот PEP предназначен для того, чтобы дать пользователям возможность напрямую разрешить эту неоднозначность, позволяя им напрямую указывать, должен ли что-то быть псевдонимом типа.
Этот PEP принят; единственная причина, по которой mypy не поддерживает его, заключается в том, что пока никто не удосужился его реализовать.
Спросите у сопровождающих Cython, согласны ли они с отправкой заглушек для cython превращение их пакета в PEP 561 совместимый пакет - пакет, который поставляется в комплекте с подсказками типов.
Кажется, что Cython уже связывает некоторые подсказки типов в ограниченным образом и сделать их доступными для внешнего использования теоретически может быть так же просто, как проверить их актуальность и добавить файл py.typed
в пакет Cython.
Дополнительные сведения о типе подсказки в Cython можно найти здесь и здесь .
Mypy также планирует капитальный ремонт обработки импорта , так что вы можете по желанию использовать любой Связанные типы подсказок, даже если пакет не соответствует PEP 561 в течение следующих нескольких месяцев - вы также можете подождать, пока это произойдет.
Создайте свой собственный пакет-заглушку для Cython. Этот пакет может быть неполным и определять только int
и несколько других необходимых вам вещей. Например, вы можете создать файл «stubs / cython.pyi», который выглядит следующим образом:
from typing import Any
# Defining these two functions will tell mypy that this stub file
# is incomplete and to not complain if you try importing things other
# than 'int'.
def __getattr__(name: str) -> Any: ...
def __setattr__(name: str, value: Any) -> None: ...
class _int:
# Define relevant method stubs here
Затем укажите mypy на этот файл-заглушку в дополнение к вашему обычному коду. Затем Mypy поймет, что должен использовать этот файл-заглушку в качестве подсказки типа для модуля cython
. Это означает, что когда вы делаете cython.int
, mypy увидит, что это класс, который вы определили выше, и поэтому будет иметь достаточно информации, чтобы знать, что Flags = cython.int
, скорее всего, псевдоним типа.
Переопределить, что Flags
назначается при выполнении проверки только типа. Вы можете сделать это с помощью переменной typing.TYPE_CHECKING
:
from typing import TYPE_CHECKING
import cython
# The TYPE_CHECKING variable is always False at runtime, but is treated
# as being always True for the purposes of type checking
if TYPE_CHECKING:
# Hopefully this is a good enough approximation of cython.int?
Flags = int
else:
Flags = cython.int
def difference(f1: Flags, f2: Flags):
return bin(f1 ^ f2).count("1")
Одно предостережение в отношении этого подхода заключается в том, что я не уверен, в какой степени Cython поддерживает эти виды трюков PEP 484 и распознает ли он, что Flags
предназначен для псевдонима типа, если он заключен в оператор if, подобный этому.
Вместо того, чтобы Flags
псевдоним типа для cython.int
, сделайте его подклассом :
import cython
class Flags(cython.int): pass
def foo(a: Flags, b: Flags) -> Flags:
return a ^ b
Теперь вы используете cython.int
в контексте, в котором разумно предположить, что это тип, а mypy в итоге не сообщает об ошибке.
Конечно, это меняет семантику вашей программы и может также сделать Cython несчастным - я не очень знаком с тем, как работает Cython, но я подозреваю, что вы не должны создавать подкласс cython.int
.