# Passing python functions and storing via message handlers in Perspective

**URL:** <https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610>\
**Category:** Ignition\
**Tags:** perspective, ignition81\
**Created:** [May 12, 2021, 6:29am UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610 "2021-05-12T06:29:47Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 12, 2021, 6:29am UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/1 "2021-05-12T06:29:47Z")

</div>

Is it possible to pass python functions via message handlers?

I’d like to be able to pass ad-hoc functions to a popup which would then be called on buttons in the popup. I used to be able to do this in Vision for example for custom confirmation popups where you might want to do a number of things when the user presses “confirm” or “cancel”. To make it generic, I made it so that you could pass functions with specific names to the popup which the buttons called (there were defaults in case none were passed in).

I’ve confirmed that I am able to send the functions, but I just can’t store them anywhere on the component, so i’m unable to call it again when I need it. I can however call it straight away:  
`payload['onConfirmFunction']()`

I’ve tried this:

**Button to send message**  
onClick Script:

```auto
def fn():
	system.perspective.print('Hello!')
payload={'function': fn}
system.perspective.sendMessage('mh', payload=payload, scope='view')

```

**Button with message handler to call function**  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/3/a/3af6cd2d1c99bd15c356842f2cf880df229295f3.png)  
onClick Script:

```auto
self.onConfirm()

```

Message Handler ‘mh’:

```auto
self.onConfirm = payload['function'] ### <--- Line 3

```

However when I try to send the message, I get the error:

```auto
com.inductiveautomation.ignition.common.GenericTransferrableException: Traceback (most recent call last):
  File "<function:onMessageReceived>", line 3, in onMessageReceived
AttributeError: 'com.inductiveautomation.perspective.gateway.script' object has no attribute 'onConfirm'

```

I can call the function right in the message handler script using:

```auto
payload['function']()

```

I have also tried writing the function to a custom prop, but it just writes the memory address of the function into it 🙂

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 12, 2021, 6:49am UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/2 "2021-05-12T06:49:50Z")

</div>

~~I got it, I can use`system.util.getGlobals()` to store the function 🙂~~

~~Although it’s a little bit dodgy in that the function(s) remain after closing the popup… I’ll need to clear them out upon close~~

Don’t do this… thanks @pturmel for the caution

---

<div class="post-metadata">

**Author:** ![ryanjmclaughlin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryanjmclaughlin/32/4318_2.png) [@ryanjmclaughlin](https://forum.inductiveautomation.com/u/ryanjmclaughlin)\
**Post date:** [May 12, 2021, 12:17pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/3 "2021-05-12T12:17:19Z")

</div>

I think there is a simpler way… I extended this resource from the exchange ([Ignition Exchange | Inductive Automation](https://inductiveautomation.com/exchange/142/overview))

The buttons on the view are set to fire the corresponding message handler tied to the view params. I either have a listener somewhere on the page or in the session to match to the events. Didn’t have to deal with passing around functions, although some of the message handlers just turn around and call project scripts.

Turned out to be an extremely flexible and reusable popup that I used all over the place especially to replicate the systyem.gui.\* popup functions from vision.

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/4/9/490702ba6b16482115a8b566a5fac715d5058cba.png)

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/3/d/3d37e7226cc73f8e70ef0e5207f902ef4520eed6.png)

```auto
	# Set the message and payload
	messageType = self.view.params.btnActionPrimary	
	payload = perspective.toDict(self.view.params.btnPayloadPrimary)
	
	# Append the ID of the popup
	payload['id'] = self.view.params.id
	
	# Send the message to perform cool actions!
	system.perspective.sendMessage(messageType, scope = "session", payload = payload)
	system.perspective.print('Send Message | %s | %s' % (messageType, payload))

```

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [May 12, 2021, 12:33pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/4 "2021-05-12T12:33:08Z")

</div>

Generally speaking functions are `first class` in python and yes you can pass them. It’s what allows you to make decorators in general. You can do stuff like

```auto
def timer_function(func, args, kwargs):
    start_time = time.time()
    result = func(*args, **kwargs)
    end_time = time.time()
    print end_time - start_time

```

and then call this on any generic function to time it.

You can store functions in a list and then evaluate them in a certain order and store a list of results

```auto
my_funcs = [someModule.foo, module.bar, foo.bar]
results = [func() for func in my_funcs]

```

You can assign them to a variable (though use case wise I don’t have anything for this)

```auto
x = someModule.foo
x()

```

You most likely don’t want to just be able to send back any function via message handler to be run on the gateway as if someone is able to spoof a client and knows just a little ignition or say some of your functions that relate to deleting/updating your database, then you could be in a world of hurt.

Heres a [post](http://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915) I made about it a while back, but my method was passing a string of the function name and then importing it with importlib. However to make sure I was safe I would need to white list what functions would be acceptable for a client to call and I was able to do that with string comparisons. If you’re passing a function object back directly I am not sure how you would have a whitelist.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 12, 2021, 12:39pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/5 "2021-05-12T12:39:07Z")

</div>

> [@bkarabinchak.psi](#):
>
> You can assign them to a variable

I use this for my large number of json manipulation functions that do various things parsing json, where most of the time they will need to call themselves recursively. Instead of having to call the function name itself every time, at the top I just set a variable to the function's name and then call that instead. It means I can copy and paste any single function that has a similar structure to what I need for a new function, and just mod the top variable to set it to the new name.

E.g.

```python
def foo():
    this_function = foo
    for i in range(999999):
        this_function()

```

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 12, 2021, 12:50pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/6 "2021-05-12T12:50:51Z")

</div>

Perspective properties are limited to the documented types in order to yield something javascript compatible for the front end. Which is why your attempt to store a function on the view’s properties stringified it instead of retaining its true nature. You theoretically could cache items in getGlobals(), but that requires strict control over lifetimes, especially since you want to store **code** in there. Even the slightest error in lifetime management will yield huge memory leaks. (The code will hold entire old interpreter environments in memory after script restarts…)

I like @ryanjmclaughlin’s approach.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 12, 2021, 1:05pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/7 "2021-05-12T13:05:12Z")

</div>

A question for better understanding:  
How would I “destroy” a function or code once i’m done with it if it _was_ stored inside getGlobals?

That being said, I’m going to go with @ryanjmclaughlin’s way, much cleaner! Thanks

---

<div class="post-metadata">

**Author:** ![ryanjmclaughlin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryanjmclaughlin/32/4318_2.png) [@ryanjmclaughlin](https://forum.inductiveautomation.com/u/ryanjmclaughlin)\
**Post date:** [May 12, 2021, 1:25pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/8 "2021-05-12T13:25:40Z")

</div>

To be fair, most of the idea is originally @ray resource on the exchange. I just added the payload stuff 😛

BTW… in the button code, here is the code to the `perspective.toDict()` function

```auto
def toDict(perspective_object):
    """Formats a perspective object as Dict

    Adapted from http://forum.inductiveautomation.com/t/ideal-way-to-display-objects-on-a-screen/41575

    Args:
        perspective_object: A perspective property object
    """
    from com.inductiveautomation.ignition.common import TypeUtilities

    # Convert the Perspective property object to Gson and back to a Py Object
    return TypeUtilities.gsonToPy(TypeUtilities.pyToGson(perspective_object))

```

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 12, 2021, 1:36pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/9 "2021-05-12T13:36:46Z")

</div>

I wondered about that function, cheers!

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 12, 2021, 6:43pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/10 "2021-05-12T18:43:53Z")

</div>

> [@nminchin](#):
>
> How would I “destroy” a ....

Basic python `del` statement as used with any dictionary:

```auto
gg = system.util.getGlobals()

del gg['someKey']

```

---

<div class="post-metadata">

**Author:** ![ray](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ray/32/11451_2.png) [@ray](https://forum.inductiveautomation.com/u/ray)\
**Post date:** [June 2, 2021, 9:38pm UTC](https://forum.inductiveautomation.com/t/passing-python-functions-and-storing-via-message-handlers-in-perspective/46610/11 "2021-06-02T21:38:20Z")

</div>

Really neat addition to the component, thanks for sharing! I’ll look into adding this to the exchange resource itself as a V2 improvement.
