使用mypy检测含pysnmp-lextudio的Python项目时出现name-defined错误怎么办?
解决pysnmp-lextudio与mypy类型检查的
name-defined错误 在依赖pysnmp-lextudio的Python项目中运行mypy类型检查时,会出现大量name-defined错误,但代码实际可正常运行,且Pylance能识别相关定义。另外,必须给导入语句加# type: ignore,否则会触发import-untyped错误(提示模块缺失类型存根或py.typed标记)。
问题重现步骤
poetry new pysnmp-and-mypy cd ./pysnmp-and-mypy poetry add mypy poetry add pysnmp-lextudio touch ./pysnmp_and_mypy/test.py
将以下代码写入./pysnmp_and_mypy/test.py:
from pysnmp.hlapi import * # type: ignore from typing import Any def convert_snmp_type_to_python_type( snmp_value: Integer32 | Integer | Unsigned32 | Gauge32 | OctetString | Any, ) -> int | str: match snmp_value: case Integer32(): return int(snmp_value) case Integer(): return int(snmp_value) case Unsigned32(): return int(snmp_value) case Gauge32(): return int(snmp_value) case OctetString(): return str(snmp_value) case _: raise TypeError( "Only SNMP types of type integer and string are supported. Received type of {}".format( str(type(snmp_value)) ) ) def get_data(ip_address: str, object_identity: str) -> int | str: iterator = getCmd( SnmpEngine(), CommunityData("public", mpModel=0), UdpTransportTarget((ip_address, 161)), ContextData(), ObjectType(ObjectIdentity(object_identity)), ) error_indication, error_status, error_index, variable_bindings = next(iterator) if error_indication: raise RuntimeError(error_indication.prettyPrint()) elif error_status: raise RuntimeError(error_status.prettyPrint()) else: [variable_binding] = variable_bindings [_oid, value] = variable_binding return convert_snmp_type_to_python_type(value)
执行检查命令:
poetry install poetry run mypy ./pysnmp_and_mypy
错误信息
pysnmp_and_mypy\test.py:6: error: Name "Integer32" is not defined [name-defined] pysnmp_and_mypy\test.py:6: error: Name "Integer" is not defined [name-defined] pysnmp_and_mypy\test.py:6: error: Name "Unsigned32" is not defined [name-defined] pysnmp_and_mypy\test.py:6: error: Name "Gauge32" is not defined [name-defined] pysnmp_and_mypy\test.py:6: error: Name "OctetString" is not defined [name-defined] pysnmp_and_mypy\test.py:9: error: Name "Integer32" is not defined [name-defined] pysnmp_and_mypy\test.py:11: error: Name "Integer" is not defined [name-defined] pysnmp_and_mypy\test.py:13: error: Name "Unsigned32" is not defined [name-defined] pysnmp_and_mypy\test.py:15: error: Name "Gauge32" is not defined [name-defined] pysnmp_and_mypy\test.py:17: error: Name "OctetString" is not defined [name-defined] pysnmp_and_mypy\test.py:28: error: Name "getCmd" is not defined [name-defined] pysnmp_and_mypy\test.py:29: error: Name "SnmpEngine" is not defined [name-defined] pysnmp_and_mypy\test.py:30: error: Name "CommunityData" is not defined [name-defined] pysnmp_and_mypy\test.py:31: error: Name "UdpTransportTarget" is not defined [name-defined] pysnmp_and_mypy\test.py:32: error: Name "ContextData" is not defined [name-defined] pysnmp_and_mypy\test.py:33: error: Name "ObjectType" is not defined [name-defined] pysnmp_and_mypy\test.py:33: error: Name "ObjectIdentity" is not defined [name-defined]
问题原因
- pysnmp-lextudio未提供
py.typed标记或官方类型存根,mypy默认会跳过对这类无类型模块的分析。 - 你添加的
# type: ignore会让mypy完全忽略该导入语句的所有类型检查,导致它无法识别从pysnmp.hlapi导入的类和函数名称。 - Pylance依靠动态类型推断和内置的第三方库类型提示机制,因此能识别这些名称,不受类型存根缺失的影响。
解决方案
方案1:安装第三方类型存根(优先尝试)
查找社区维护的pysnmp类型存根包,比如types-pysnmp,通过poetry安装:
poetry add --dev types-pysnmp
安装后移除导入语句的# type: ignore,重新运行mypy检查即可。
方案2:手动编写项目级类型存根
如果没有合适的第三方类型存根,可以在项目中创建自定义类型存根:
- 在项目根目录创建
typings/pysnmp/hlapi.pyi文件,内容如下:
from typing import Iterator, Tuple, Type class Integer32: def __int__(self) -> int: ... class Integer: def __int__(self) -> int: ... class Unsigned32: def __int__(self) -> int: ... class Gauge32: def __int__(self) -> int: ... class OctetString: def __str__(self) -> str: ... class SnmpEngine: def __init__(self) -> None: ... class CommunityData: def __init__(self, community: str, mpModel: int) -> None: ... class UdpTransportTarget: def __init__(self, addr: Tuple[str, int]) -> None: ... class ContextData: def __init__(self) -> None: ... class ObjectIdentity: def __init__(self, oid: str) -> None: ... class ObjectType: def __init__(self, oid: ObjectIdentity) -> None: ... def getCmd( engine: SnmpEngine, data: CommunityData, transport: UdpTransportTarget, context: ContextData, obj_type: ObjectType ) -> Iterator[Tuple[object, object, object, object]]: ...
- 在项目的
pyproject.toml中添加mypy配置,指定类型存根路径:
[tool.mypy] mypypath = ["typings"] strict = true # 保持原有严格检查,根据需求调整
- 移除导入语句的
# type: ignore,重新运行mypy检查。
方案3:放宽mypy检查规则(不推荐)
如果以上方案都无法实施,可以通过mypy参数放宽检查,但会降低类型检查的有效性:
poetry run mypy --ignore-missing-imports --follow-imports=silent ./pysnmp_and_mypy
或者在pyproject.toml中配置:
[tool.mypy] ignore_missing_imports = true follow_imports = "silent"
最佳替代方案
如果不想编写完整的类型存根,可结合typing.TYPE_CHECKING和字符串类型注解,仅在类型检查阶段提供类型信息:
from pysnmp.hlapi import * # type: ignore[import-untyped] from typing import Any, TYPE_CHECKING if TYPE_CHECKING: # 仅在类型检查时导入,不影响运行时 from pysnmp.hlapi import ( Integer32, Integer, Unsigned32, Gauge32, OctetString, getCmd, SnmpEngine, CommunityData, UdpTransportTarget, ContextData, ObjectType, ObjectIdentity ) def convert_snmp_type_to_python_type( snmp_value: "Integer32 | Integer | Unsigned32 | Gauge32 | OctetString | Any", ) -> int | str: match snmp_value: case Integer32(): return int(snmp_value) case Integer(): return int(snmp_value) case Unsigned32(): return int(snmp_value) case Gauge32(): return int(snmp_value) case OctetString(): return str(snmp_value) case _: raise TypeError( "Only SNMP types of type integer and string are supported. Received type of {}".format( str(type(snmp_value)) ) ) # get_data函数保持不变
这种方式既能让mypy识别类型,又不影响代码运行,同时仅忽略特定的import-untyped错误,保留其他类型检查。
内容的提问来源于stack exchange,提问作者bmitc
相关产品推荐
相关产品推荐

