16

Are there any tricks I can employ to get IDEs to offer code completion for dynamically generated class attributes? For instance


class A:
    
    def __init__(self):
        setattr(self, "a", 5)

This code will set the class attribute of A called a to the value of 5. But IDEs do not know about a and therefore you do not get code completion for it. I've read that the __dir__ method can be hooked, but the suggestion made in that answer has not worked for me. Does anybody have any ideas?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
CiaranWelsh
  • 7,014
  • 10
  • 53
  • 106
  • 2
    I'm not so sure on the downvote either. My gut instinct is that the answer to your question is "no" because static analysis wouldn't see anything here but, hey, JIT is a thing and maybe there's some smart IDE magic that can do this – roganjosh Aug 16 '20 at 11:58
  • My thoughts too, but it would be very nice if there was a hack to get this working. Perhaps there is a Python wizard out there who can help me :) – CiaranWelsh Aug 16 '20 at 12:00
  • Which IDEs? What other constraints? (For example can you use type annotations like `a: int`? Docstrings?) – jonrsharpe Aug 16 '20 at 16:09
  • I use Pycharm, but would be interesting to get this working with others as well. Yes, type annotations are allowed... In fact, i'm interested in any and all ideas. I've had this problem a few times and would like to know if *any* strategy exist, regardless of which tools (like type annotations) are required. Thanks for responding. – CiaranWelsh Aug 21 '20 at 18:54

3 Answers3

13

I believe you will find the answer here. In short, Pycharm currently does not (and probably in the observable future will not) support dynamic attributes. That's because it analyzes code statically, and can't "see" what attributes a class might have. On the other hand, when you run your code in (say) iPython, once an instance of such class is created, the editor can get the attributes of the specific instance that resides in the memory, and thus show them in autocomplete. In such cases __dir__ helps. I assume that would be the case also with other IDEs.

So if you really need the autocompletion feature, you may want to try using Jupyter notebook. Still, you will have to instantiate your variable prior to getting autocomplete.

Another possible solution is to exploit that IDE supports .pyi files (Python interface file stub). You can add to your code a little snippet that instantiate the class with dynamic attributes, and writes down a class interface file (.pyi). Then IDE will use it for autocompletion. Obviously, this solution will have a "one run delay", but as in PyCharm you can switch to such file just by clicking on the asterisk near the class name, you can manually update it when having massive changes.

igrinis
  • 12,398
  • 20
  • 45
  • 1
    How would you accomplish this `writes down a class interface` programmatically? mypy has the stubgen tool, but I don't think this can be imported and run against a dynamically create class like this. Is there a way to generate a `pyi` from an instantiated class? – red888 Feb 10 '23 at 04:36
  • Literally inside __init__() create a `.pyi` file and write into it all you need. – igrinis Feb 12 '23 at 10:14
2

For this, I had to use Union type to get all the possible autocomplete options. It takes some extra time to write it, but when my program is huge, it is worth it.

from typing import Union


class dynamic1(object):
    def __init__(self):
        self.b = 5
        self.c = 0


class dynamic2(dynamic1):
    def __init__(self):
        self.e = 10
        self.d = 11


class dynamic3:
    def __init__(self):
        self.f = 12


def func(li):
    return li[1]


def func() -> Union[dynamic1, dynamic2, dynamic3]:
    a = [dynamic1(), dynamic2(), dynamic3()]
    b = func(a)
    return b

Code in action

Yansh
  • 112
  • 2
  • 7
0

Spyder gives autosuggestion option rather than autocompletion. Kite integration might help setting up code faster. Kite is actually a autocompletion plugin. You can also use it with Visual studio code.

nagln
  • 1
  • 2