Search code examples
pythonintellisensestatic-analysistooling

Intellisense for returned python object's methods


I am new to Python and I love this language much. But I encountered one annoying issue recently when working with PyDev in eclipse.

Some method returned an instance of some class. But I cannot get intellisense for the instance's methods.

For example:

import openpyxl
from openpyxl.reader.excel import load_workbook
from openpyxl.worksheet import Worksheet


xlsFile='hello.xlsx'
wbook = load_workbook(xlsFile)

wsheet1=wbook.get_sheet_by_name('mysheet')
wsheet1.cell('A9').hyperlink=r'\\sharefolder'

wsheet2=Worksheet()
wsheet2.cell('A1').hyperlink=r'\\sharefolder'

In this code, I can get the prompt for method cell() with wsheet2, but not with wsheet1. Though they are both of Worksheet type which I have already imported. It seems python or PyDev cannot properly detect the type of the returned object.

Is this a language limitation? Or is there something I did wrong? For now, I have to dig into the source code and see what the real type of the return value is. And then check the methods defined in that type. It's very tedious.

I wrote a small test to repro this issue. Strange, the intellisense seems working.

enter image description here


Solution

  • It's a consequence of the fact that Python is dynamically typed.

    In a statically-typed language such as C#, methods are annotated with their type signatures. (Aside: in some systems types can be inferred by the type checker.) The compiler knows the return type of the function, and the types the arguments are meant to have, without running your code, because you wrote the types down! This enables your tooling to not only check the types of your programs, but also to build up metadata about the methods in your program and their types; Intellisense works by querying this metadata harvested from the text of your program.


    Python has no static type system built in to the language. This makes it much harder for tooling to give you hints without running the code. For example, what is the return type of this function?

    def spam(eggs):
        if eggs:
            return "ham"
        return 42
    

    Sometimes spam returns a string; sometimes it returns an integer. What methods should Intellisense display on the return value of a call to spam?

    What are the available attributes on this class?

    class Spam:
        def __getattr__(self, name):
            if len(name) > 5:
                return "foo"
            return super().__getattr__(name)
    

    Spam sometimes dynamically generates attributes: what should Intellisense display for an instance of Spam?

    In these cases there is no correct answer. You might be able to volunteer some guesses (for example, you could show a list containing both str and int's methods on the return value of spam), but you can't give suggestions that will be right all the time.


    So Intellisense tooling for Python is reduced to best-guesses. In the example you give, your IDE doesn't know enough about the return type of get_sheet_by_name to give you information about wsheet1. However, it does know the type of wsheet2 because you just instantiated it to a Worksheet. In your second example, Intellisense is simply making a (correct) guess about the return type of f1 by inspecting its source code.

    Incidentally, auto-completion in an interactive shell like IPython is more reliable. This is because IPython actually runs the code you type. It can tell what the runtime type of an object is because the analysis is happening at runtime.