functools.partial objects cannot be used as `Dynamics.StateMachine.State` methods (onset, ongoing, etc.)

Issue #41 resolved
Scott Mooney created an issue

Creating a basic functools.partial object and assigning it to a State method, like:

def my_func(argument):
    print(argument)

sm.AddState("NewState", onset=partial(my_func, "new_state_onset"))

produces an error:

AttributeError: 'functools.partial' object has no attribute '__get__'

Comments (3)

  1. Jeremy Hill

    I cannot replicate this with Shady 1.13.1 (git 79355e0 2020-12-02) on Python 3.7.3 for macOS 10.13 or on Python 3.7.6 for Windows 10.

    In [1]: from Shady.Dynamics import StateMachine; sm = StateMachine(); from functools import partial
    
    In [2]: paste
    def my_func(argument):
        print(argument)
    
    sm.AddState("NewState", onset=partial(my_func, "new_state_onset"))
    
    ## -- End pasted text --
    Out[2]: <Shady.Dynamics.StateMachine.State at 0x10c347cf8>
    
    In [3]: sm(0)
    new_state_onset
    Out[3]: <Shady.Dynamics.StateMachine.State at 0x10c347cf8>
    

    Although it is true, on these Python versions, that the partial object has no __get__ :

    In [4]: sm.current.onset
    Out[4]: functools.partial(<function my_func at 0x10c2e8510>, 'new_state_onset')
    
    In [5]: _.__get__
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    <ipython-input-5-02173c58bbdf> in <module>
    ----> 1 _.__get__
    
    AttributeError: 'functools.partial' object has no attribute '__get__'
    
    In [6]:
    

    So Shady 1.13.1 must be handling this contingency. I cannot replicate this problem even by rolling back to 1.8.x (early 2019). Double-check your python -m Shady --versions

  2. Scott Mooney reporter

    Clarification: it only happens when partial is used on a bound class method. My bad, I didn’t actually test the basic example I initially gave.

    This does produce the error:

    import Shady
    from functools import partial
    
    
    class MyClass:
    
        def __init__(self, message_prefix):
            self.message_prefix = message_prefix
    
        def flexible_onset(self, message):
            print(self.message_prefix + message)
    
        def add_to_state_machine(self, state_machine, state_name, message):
            state_machine.AddState(state_name, onset=partial(self.flexible_onset, message))
    
    
    if __name__ == '__main__':
        c = MyClass("Prefix: ")
        sm = Shady.StateMachine()
        c.add_to_state_machine(sm, "MyState", "Suffix")
        sm.AddState("MyState2", onset=partial(c.flexible_onset, "Suffix"))  # ALSO ERROR
    

  3. Log in to comment