Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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...

like image 900
Germano Carella Avatar asked Jun 07 '16 18:06

Germano Carella


1 Answers

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.

like image 99
Matthias Fripp Avatar answered Oct 17 '22 22:10

Matthias Fripp