pythonpython-typingpyrightpyalex

Pylance doesn't like calling `[]` on a dict-inherited class


PyAlex has the class OpenAlexEntity:

class OpenAlexEntity(dict):
    """Base class for OpenAlex entities."""

    pass

and Institution:

class Institution(OpenAlexEntity):
    """Class representing an institution entity in OpenAlex."""

    pass

So the following code correctly works

from pyalex import Institutions

insts = Institutions().search("MIT").get()
inst = insts[0]
print(type(inst)) # prints "Institution"
print(inst["display_name"]) # prints "Massachusetts Institute of Technology"

However, PyLance underlines it in red showing the following error:

No overloads for "__getitem__" match the provided argumentsPylancereportCallIssue
builtins.pyi(1062, 9): Overload 2 is the closest match

enter image description here

How can I fix this?


Solution

  • The problem is that pyAlex is not typed, hence the type checker can only infer the types. This might create unions and well as Any | Unknown that one with knowledge of the package could easily infer. Missing overload for multiple one vs. many return types like for Institutions.get creates a few problems.

    insts returned by Institutions.get is a tuple or union of options: Taking the first item allows for only a small selection of options. In your case inst is at best inferred as a OpenAlexResponseList, which is a list. You cannot index a list with a string, hence you get the error you are reporting

    To be precise:

    insts: tuple[OpenAlexResponseList | Any, Unknown | Any | None] | OpenAlexResponseList | Any
    inst = insts[0]
    inst: OpenAlexResponseList | Any | Unknown
    inst["some_string"] # -> error no overload for list.__get__(self, str)
    

    How to fix; as you know the correct type do a cast, assert isinstance, or if isinstance:

    # Option 1
    from pyalex import Institution
    inst = cast("Institution", insts[0])
    
    # Option 2
    if isinstance(inst, Institution):
        print("Found institution:", inst["display_name"])
        print(type(inst))  # prints "Institution"
        print(inst["display_name"])  # prints "Massachusetts Institute of Technology"
    
    # Option 3
    assert isinstance(inst, Institution)
    print("Found institution:", inst["display_name"])
    print(type(inst))  # prints "Institution"
    print(inst["display_name"])  # prints "Massachusetts Institute of Technology"