pythonautocompletepython-jedi

python jedi: how to retrieve methods of instances?


I built simple text editor with some accessibility feature for screen reading software. I'm using Python for .NET (pythonnet) to show a form containing a rich text box. When user press tab after a period it pop-ups a context menu with completions for selected element. Ok, it works fine with Python objects, but it doesn't work with .net live objects, there is no solution to this problem. Now, I want build a TreeView object with all names and definitions of module I'm editing.

So, for example I type:

import sys
import os
lst = list()

etc... If I use jedi.names of my source, I can retrieve os, sys and lst. For each name, I want retrieve sub definitions, such as functions for sys and os module, and methods for lst. I can't find a way to do this with jedi:

names = jedi.names(MySource)
names[0].defined_names() # works for sys
names[1].defined_names() # works for os
names[2].defined_names() # doesn't work for lst instance of list().

Any suggestions? I tried to use more and more editors, but accessibility support is very very bad...


Solution

  • This looks like a bug, where jedi.evaluate.representation.Instance.__getattr__() mistakenly blocks evaluation of .names_dict. I added a pull request to the jedi repository to fix this. In the mean time, you can either add 'names_dict' to the whitelist in Instance.__getattr__() in your copy of jedi/evaluate/representation.py, or use the code below to patch this method automatically for the current session.

    import jedi
    
    def patch_jedi():
    
        __old__getattr__ = jedi.evaluate.representation.Instance.__getattr__
    
        def __patched__getattr__(self, name):
            if name == 'names_dict':
                # do a simplified version of __old__getattr__, bypassing the name check
                return getattr(self.base, name)
            else:
                # use standard behavior
                return __old__getattr__(self, name)
    
        # test whether jedi has been updated to avoid the Instance.defined_names() bug
        try:
            jedi.names("lst = list()")[0].defined_names()
        except AttributeError as e:
            if e.args[0].startswith("Instance ") and e.args[0].endswith("Don't touch this (names_dict)!"):
                # patch jedi to avoid this error
                print "patching jedi"
                jedi.evaluate.representation.Instance.__getattr__ = __patched__getattr__
            else:
                # something else strange is going on
                raise
    
    patch_jedi()
    print jedi.names("lst = list()")[0].defined_names()
    # or: print jedi.Script("lst = list()").goto_definitions()[0].defined_names()
    

    I should note that I'm not familiar with jedi, so I don't know whether defined_names() is supposed to work for definitions that create instances. The code above won't fix references like jedi.names("lst = []")[0].defined_names(), and there's no obvious patch to do that. So there may be something deeper going on that I don't know about. Hopefully the developer will help set this straight in response to that pull request.