Search code examples
pythonmypypython-typingpython-poetrypysnmp

Running `mypy` on a project with `pysnmp-lextudio` package dependency returns `named-defined` errors


To recreate the issue I am having:

poetry new pysnmp-and-mypy
cd ./pysnmp-and-mypy
poetry add mypy
poetry add pysnmp-lextudio
touch ./pysnmp_and_mypy/test.py

Put the following code into ./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)

Run:

poetry install
poetry run mypy ./pysnmp_and_mypy

Now observe that the following errors are returned:

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]

How do I get rid of these errors in the appropriate way? Why isn't MyPy finding the definitions? They are definitely there, as the code runs just fine and Pylance doesn't have an issue finding them. If there isn't a good way, what is the best workaround?

Note that I had to do from pysnmp.hlapi import * # type: ignore because I otherwise get the error error: Skipping analyzing "pysnmp.hlapi": module is installed, but missing library stubs or py.typed marker [import-untyped].


Solution

  • One possible solution, garnered from the helpful comments in the original question, is to explicitly import all used types and functions. In this case, the following allows MyPy to pass.

    from pysnmp.hlapi import (  # type: ignore
        Integer32,
        Integer,
        Unsigned32,
        Gauge32,
        OctetString,
        Integer32,
        Integer,
        Unsigned32,
        Gauge32,
        OctetString,
        SnmpEngine,
        ObjectType,
        ObjectIdentity,
        SnmpEngine,
        ObjectType,
        ObjectIdentity,
        Integer,
        getCmd,
        setCmd,
        CommunityData,
        UdpTransportTarget,
        ContextData,
    )
    

    Note that I still needed to provide the ignore comment for the first line to get rid of the error:

    error: Skipping analyzing "pysnmp.hlapi": module is installed, but missing library stubs or py.typed marker  [import-untyped]
    note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
    

    It appears this ignore comment is explicitly the line that contains it, as placing it after the import "block" (on the line with the closing parentheses does not work and additionally yields an error: Unused "type: ignore" comment [unused-ignore]".