# \[IGN-5244\]Persist Python Class Object

**URL:** <https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498>\
**Category:** Ignition\
**Created:** [December 6, 2019, 5:37pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498 "2019-12-06T17:37:15Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [February 17, 2022, 12:44am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/21 "2022-02-17T00:44:16Z")

</div>

Umm… sure… I don’t actually know what’s going on here or what you’re trying to do, I didn’t really pay much attention. Just got curious about why the session was wrapped and it’s very intentional.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [February 17, 2022, 12:57am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/22 "2022-02-17T00:57:10Z")

</div>

> [@kgamble](#):
>
> Would you say that the easiest way here would be to use sessionId’s and then add a garbage collection script to remove old sessionIds?

Yes, that's definitely going to be the easiest. Couldn't you do cleanup in a session shutdown event script?  
[https://docs.inductiveautomation.com/display/DOC81/Perspective+Session+Event+Scripts#PerspectiveSessionEventScripts-StartupandShutdownEventScripts](https://docs.inductiveautomation.com/display/DOC81/Perspective+Session+Event+Scripts#PerspectiveSessionEventScripts-StartupandShutdownEventScripts)

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [February 17, 2022, 1:06am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/23 "2022-02-17T01:06:11Z")

</div>

I didn’t even realize there was a session shutdown event, how convenient!

This should work then. In general this premise makes OOP work in Perspective much easier.

It would be nice if there was an easier way to pass class references into views without them being converted into their text references, I recognize we’re talking about going from Jython to JavaScript and back, and I’m not sure how possible that even is, but this could ease a ton of projects that rely on lots of scripting.

---

<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:** [February 17, 2022, 1:16am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/24 "2022-02-17T01:16:55Z")

</div>

> [@Kevin.Herron](#):
>
> There’s a lot of trickery going on to prevent the scripting system from getting unwrapped session objects. Don’t fight it.

Awww!

So, A `PyObjectAdapter` registered to wrap all `Session` objects with the `SessionScriptWrapper$SafetyWrapper`, I presume? Defeating that would require a 3rd-party module that can use `getSession()` and then provide an alternate wrapper. (Jython can't do it because a PyObjectAdapter kicks in to rewrap it wherever it pops out.)

Maybe I'll create a module function that combines `.getSession()` and a `WeakRef`. Would still be safe because dereferencing in jython would auto-wrap back to the SafetyWrapper. Hmmm.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [February 17, 2022, 1:20am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/25 "2022-02-17T01:20:12Z")

</div>

> [@kgamble](#):
>
> It would be nice if there was an easier way to pass class references into views without them being converted into their text references

This isn't a dismissal - what do you (and @pturmel, for that matter) plan to do with this? I could see us adding something akin to Vision/Swing's `putClientProperty` mechanism to Perspective; it would obviously stay entirely on the backend model, but it would be easy enough to add. But compelling use cases would definitely make it an easier sell.

---

<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:** [February 17, 2022, 1:28am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/26 "2022-02-17T01:28:55Z")

</div>

> [@paul-griffith](#):
>
> I could see us adding something akin to Vision/Swing’s `putClientProperty` mechanism to Perspective

That would entirely supplant what I was thinking, yes. Please do. Separate ones for Session, Page, and View would be glorious. Life cycle when scripts restart would be tricky if not restricted to python native objects.

The whole idea is to have class models that carry pure back-end data that can't survive a json round trip. Preferably tolerating jython custom class instances. A design built around weak references saved in a python global can have its objects updated by project script top level actions upon script restart. Yet remain "findable" by scripts in the Perspective session, per session.

Jython collections and lists are fundamentally thread-safe, so the weakref approach provides a way to provide state to sessions that are simultaneously available to non-perspective events.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [February 17, 2022, 1:32am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/27 "2022-02-17T01:32:55Z")

</div>

Hah. Such a good idea we already talked about it a few months ago:

> [@\[IGN-5244\]Using Class Instance as View Custom Object Property](https://forum.inductiveautomation.com/t/using-class-instance-as-view-custom-object-property/53676/3):
>
> Yeah, as Phil said, this isn’t going to work as-is. We could in theory implement something like this (it is convenient in Vision to be able to get/put arbitrary objects at runtime), but I wouldn’t expect it anytime soon, so you’ll have to work around it somehow.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [February 17, 2022, 1:33am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/28 "2022-02-17T01:33:09Z")

</div>

> [@pturmel](#):
>
> The whole idea is to have class models that carry pure back-end data that can’t survive a json round trip.

This is exactly the issue, if I spend a lot of "compute" building a class with a lot of custom data and methods, etc. Than rebuilding it every time a user interacts with the screen can be really expensive to the clients.

I am currently working through an issue where I am writing a class that manages a dynamic array of events, and needs to ensure that each event is processed prior to saving some data. Without a class it requires me to pass a json serializable object back and forth from script to view, changing some of the metadata inside the object through each iteration of code. If I was able to use a class, than I could either store the class on the session, or in that page, and just have the popups call a "next\_step" type method that exists on that class.

In this case I am going to need to store my "event manager" in the globals, and then have my view tell it to perform each "next step" through the method we have described above. It is fully do-able this way, but not super easy to implement and manage

---

<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:** [February 17, 2022, 1:34am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/29 "2022-02-17T01:34:25Z")

</div>

Another possibility: define “Private” access for custom properties to mean whatever a script/binding assigns retains its native object identity. That would be clean and intuitive all around.

Edit: except for the memory leakage hazard I mentioned in the other discussion. A weak-key dictionary keyed on sessions but stored persistently and managed by a project script is the only safe way I can think of to do this kind of code. Nobody is doing this because it really can’t yet be done. (A bug in a session shutdown script would block cleanup when weak references aren’t used.)

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [February 17, 2022, 1:57am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/30 "2022-02-17T01:57:18Z")

</div>

Is there any possible way to identify shutdown at the page level by chance?

If I wanted to have a specific instance of my class per page open, only thing I can think of is storing it with the session id and page id in a compound key, and then to clean it up I would have to iterate through the pages shown in the results from `system.perspective.getSessionInfo()`, but once again that would have me going through a timer, not a conveniently placed shutdown script

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [September 24, 2022, 3:45pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/31 "2022-09-24T15:45:58Z")

</div>

Was curious if there was ever any internal traction on this feature request?

It’s definitely an advanced user feature requirement, but it would enable advanced users to do immensely powerful things.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [September 26, 2022, 3:28pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/32 "2022-09-26T15:28:54Z")

</div>

No movement on it yet.

---

<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:** [September 26, 2022, 3:32pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/33 "2022-09-26T15:32:44Z")

</div>

A per-session python dictionary safely established with weak references is on my todo list after I push out a beta driver that’s had me preoccupied lately.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [September 26, 2022, 3:43pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/34 "2022-09-26T15:43:12Z")

</div>

I figured, just had to remind you guys about how much the advanced users want it 😁

---

<div class="post-metadata">

**Author:** ![Adam\_Lyon](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/adam_lyon/32/41432_2.png) [@Adam\_Lyon](https://forum.inductiveautomation.com/u/Adam_Lyon)\
**Post date:** [December 1, 2022, 7:20pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/35 "2022-12-01T19:20:31Z")

</div>

I read through this thread and I want to make sure I'm implementing this correctly.

Here is the flow. It's all happening in 1 script

When another script wants the data it goes through a getter method

```auto
def get_device_list():
	device_list = None
	try:
		device_list = system.util.getGlobals()[key_device_list]
	except KeyError:
		build_device_info()
		device_list = system.util.getGlobals()[key_device_list]
	except:
		raise
	return device_list

```

SIDE QUESTION: is catching the KeyError the best way? Or is it better to run...

```auto
dictGlobals = system.util.getGlobals()
if key_device_list in dictGlobals.keys():

```

```auto
def build_device_info():
    device_list = do.some.stuff.to.build.a.list()
    set_device_list(device_list)

```

```auto
def set_device_list(device_list):
    if device_list == None:
        del system.util.getGlobals()[key_device_list]
    else:
        system.util.getGlobals()[key_device_list] = device_list

```

Then when the data is no longer needed...

```auto
def clear_globals():
	set_device_list(None)

```

Is this the correct way to do this to prevent memory leaks. I would prefer to avoid using getGlobals() but I am having a bear of time trying to move information around in a clean way. Another reason I am leaning towards using getGlobals() is that the build\_device\_info() is a big process and ideally it only happens at Vision startup, or when needed.

Thanks!

---

<div class="post-metadata">

**Author:** ![jlandwerlen](https://avatars.discourse-cdn.com/v4/letter/j/bc8723/32.png) [@jlandwerlen](https://forum.inductiveautomation.com/u/jlandwerlen)\
**Post date:** [December 1, 2022, 7:22pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/36 "2022-12-01T19:22:07Z")

</div>

@Adam_Lyon please post code using the preformatted text buttton \</\>

---

<div class="post-metadata">

**Author:** ![Adam\_Lyon](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/adam_lyon/32/41432_2.png) [@Adam\_Lyon](https://forum.inductiveautomation.com/u/Adam_Lyon)\
**Post date:** [December 1, 2022, 7:26pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/37 "2022-12-01T19:26:02Z")

</div>

Thanks! I was trying to figure out how to do that

---

<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:** [December 1, 2022, 7:42pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/38 "2022-12-01T19:42:07Z")

</div>

You might find the techniques in this topic helpful. The same life cycle considerations for background threads apply to any kind of jython class objects you try to persist. Be aware that you **cannot** leave a jython class instance in any global scope through a scripting restart. That leaks a big chunk of memory, and your class object **keeps running the old code**.

> [@Managing multiple asynchronous threads](https://forum.inductiveautomation.com/t/managing-multiple-asynchronous-threads/37185):
>
> I've followed this post for monitoring a directory for file changes and I think I have created more than one watch service in different threads. How do I monitor, manage, and delete these threads?

---

<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:** [December 1, 2022, 7:47pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/39 "2022-12-01T19:47:04Z")

</div>

I strongly recommend you **not** place your class object directly into a persistent dictionary, whether from `.getGlobals()` or my own `.persistent()`. Instead, segregate the class's data model to store all dynamic data, including nested data, in a standard python dictionary or list or combination thereof. Make your class **initialize** with the global dictionary, and keep that dictionary updated. In a scripting restart, you simply **initialize** again with that dictionary.

---

<div class="post-metadata">

**Author:** ![Adam\_Lyon](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/adam_lyon/32/41432_2.png) [@Adam\_Lyon](https://forum.inductiveautomation.com/u/Adam_Lyon)\
**Post date:** [December 1, 2022, 8:19pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/40 "2022-12-01T20:19:29Z")

</div>

I think this is starting to make sense. One of the data types of the class object is an ignition dataset. Is that safe to store in `.persistent()` (inside of a dict) or is it best to only store primitives and use that to build any objects when the class is instantiated?

[Previous page](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498.md?page=1)

[Next page](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498.md?page=3)
