1

I have a Python 3.9 class that uses the transition library. This lib create dynamic methods from local class setup like:

class experiment:
    # The states
    states=['solid', 'liquid', 'gas', 'plasma']
    
    # each trigger will create a dynamic method.
    transitions = [
        { 'trigger': 'melt', 'source': 'solid', 'dest': 'liquid' },
        { 'trigger': 'evaporate', 'source': 'liquid', 'dest': 'gas' },
        { 'trigger': 'sublimate', 'source': 'solid', 'dest': 'gas' },
        { 'trigger': 'ionize', 'source': 'gas', 'dest': 'plasma' }
    ]

    def __init__(self):
        self.machine = LHGMachine(model=self, states=self.states, transitions=self.transitions, initial=self.states[0])

In this example, dynamic methods will be created. like: self.melt(), self.evaporate() and other as stated at self.transitions.

Is it possible to document them so IDEs will get a hint of what methods are available?

michaelbn
  • 7,393
  • 3
  • 33
  • 46
  • 1
    You would have to implement type hints for PyCharm to show those methods in autocomplete. I don't see what part of the transition library implements magic methods (and your code lacks the imports so it's not an MRE). However, I don't think the transitions library automatically generates a stub file with the necessary type hints for the magic function signatures (if it's all dynamic the PyCharm linter may or may not be able to solve it to show autocomplete - my guess is it won't solve this case). If you just want to hint the collections see [this thread](https://stackoverflow.com/q/66782809). – bad_coder Nov 04 '21 at 07:48
  • @bad_coder, Maybe I am wrong of calling it `magic` but the method `self.melt()` is populated from the mentioned dictionary. This is the method I would like the IDE to autocomplete. Same as evaporate() and etc – michaelbn Nov 04 '21 at 10:14
  • I'm making an educated guess (beyond my previous comment) that if you want autocomplete working you should use [the Enumerations](https://github.com/pytransitions/transitions#enumerations). And yes [*"magic methods"*](https://docs.python.org/3/reference/datamodel.html#special-lookup) are dunders. – bad_coder Nov 04 '21 at 10:20
  • Will fix the title by replacing `magic` with `dynamic`. – michaelbn Nov 04 '21 at 10:32
  • Are you familiar with the `__doc__` attribute? – Karl Knechtel Nov 04 '21 at 10:56
  • @KarlKnechtel, yes, but not in a way that allow me to achieve what I wrote in the answer. can you elaborate on this? – michaelbn Nov 04 '21 at 15:11
  • If you set a `__doc__` attribute for the things that you're dynamically creating, then tools should be able to detect that. Certainly the built-in `help` does. – Karl Knechtel Nov 05 '21 at 13:26

2 Answers2

0

To solve this I created a .pyi file with content like this:

class Experiment:
    def melt(self): pass
    def evaporate(self): pass
    def sublimate(self): pass
    def ionize(self): pass

It seems like its part of PEP 484 and supported on several IDEs. I tested it on PyCharm.

Here is another example from JetBrains discussion thread.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
michaelbn
  • 7,393
  • 3
  • 33
  • 46
0

The best and most declarative solution I found so far was to state the dynamic methods as class properties with Callable type hint.

Using the above example it should look like this:

from typing import Callable

class experiment:
    # The states
    states=['solid', 'liquid', 'gas', 'plasma']
    
    # each trigger will create a dynamic method.
    transitions = [
        { 'trigger': 'melt', 'source': 'solid', 'dest': 'liquid' },
        { 'trigger': 'evaporate', 'source': 'liquid', 'dest': 'gas' },
        { 'trigger': 'sublimate', 'source': 'solid', 'dest': 'gas' },
        { 'trigger': 'ionize', 'source': 'gas', 'dest': 'plasma' }
    ]

    # here are the declerations for the puprose of readability/autocomplete
    melt: Callable
    evaporate: Callable
    sublimate: Callable
    ionize: Callable

    def __init__(self):
        self.machine = LHGMachine(model=self, states=self.states, transitions=self.transitions, initial=self.states[0])

Now the autocomplete will work as expected when calling obj->melt(), obj->evaporate and etc.

michaelbn
  • 7,393
  • 3
  • 33
  • 46