Search code examples
pythongetattrpython-class

python jedi, autocompletion for dynamic attribute


I'm trying to write a class. Objects of that class can take a label/value pair and store it in such a way that label can be accessed as an attribute, returning value: obj.label -> value

The main goal here is to get autocompletion in jupyter notebooks, so obj.<tab> should produce the list of labels as autocompletion suggestion. The below class accomplishes this:

class Autocompleter:
    def __init__(self, ):
        self._funcs = {}
    
    def add(self, label):
        self._funcs[label] = 1.
        
    def __dir__(self):
        return list(self._funcs.keys())
    
    def __getattr__(self, name):
        if name in dir(self):
            return self._funcs[name]

The problem: When accessing an invalid attribute, the __getattr__ simply returns None. I'd rather have it throw an exception. I can think of 2 ways to achieve this, but unfortunately both break the autocompletion:

Changing __getattr__ to:

def __getattr__(self, name):
    return self._funcs[name]

or

def __getattr__(self, name):
    if name in dir(self):
        return self._funcs[name]
    else:
        raise Exception('invalid name')

produces the desired exception, but breaks the autocompletion:

a = Autocompleter()
a.add('foo')

Now a.<tab> does not suggest foo for autocompletion, it simply does nothing. As far as I can tell, jedi is used by default for autompletion in jupyterlab. Question: Is there a way to get both the exception on invalid names and the autocomplete feature working?


Solution

  • I figured it out myself. The correct way to handle an invalid attribute name is to raise an AttributeError. This can then be understood by jedi.

    def __getattr__(self, name):
        if name in dir(self):
            return self._funcs[name]
        raise AttributeError(name)
    

    Note that the __dir__ method is still needed.