# \[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:** 1

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [December 6, 2019, 5:37pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/1 "2019-12-06T17:37:15Z")

</div>

I am working in Perspective, but this probably applies to other modules as well. I have created user defined classes in the project scripts and I am instantiating them from a view in Perspective. Is there a place I can persist the instance of the object for use between events by the user?

Right now I have it serializing to a property on the view, but when I want to work with the object again I have to de-serialize the object in order to work with it again. Having to write custom de-serialize method seems like extra work if there is a way to persist the object for the client.

---

<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:** [December 6, 2019, 5:50pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/2 "2019-12-06T17:50:40Z")

</div>

You can use the [`system.util.getGlobals()`](https://docs.inductiveautomation.com/display/DOC80/system.util.getGlobals) function to get access to a persistent, dictionary. Be _careful_ with this - it’s pretty easy to end up with persistent objects that consume memory and never release it. Also, keep in mind that since this object is getting created _per-scope_, it’s going to be the same object (on your gateway server) between Perspective sessions - you’ll need to use a unique identifier for your session as the key.

---

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [December 6, 2019, 6:10pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/3 "2019-12-06T18:10:58Z")

</div>

That sounds risky. Would you say that writing a de-serialized method would be the preferred way to handle this in Ignition/Perspective?

Would this be a good use case for Pickle? [https://docs.python.org/2/library/pickle.html](https://docs.python.org/2/library/pickle.html)

---

<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:** [December 6, 2019, 6:51pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/4 "2019-12-06T18:51:20Z")

</div>

Well, it heavily depends on what you’re actually doing with these objects.  
Also, FWIW - I have no idea how well pickle works inside the Jython environment. It’s _probably_ fine, but there may be issues if you end up with Java objects mixed into your Python stuff - such as when working with Ignition dates or Perspective’s object wrappers.

What’s the use case? I understand if you’re cagey about doing something proprietary, but maybe there’s a different method to entirely avoid doing whatever you’re doing?

---

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [December 6, 2019, 7:13pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/5 "2019-12-06T19:13:14Z")

</div>

I have an object which holds the state of the view. When the user interacts with the view I want to call a method on that state object that will, depending on the action the user took, update and return the new state object.

Right now on the views startup I have it initialize an object, lets call the object “state”, then the startup script serializes the object “state” to a custom property on the root of the view. Now when the user clicks a button I want to take the object “state” and call the method “buttonClicked” like so state.buttonClicked(). But before I can do that I need to make a instance method that will take the json representation of “state” and turn it back into a python class object before I can call the method buttonClicked.

---

<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:** [February 11, 2021, 2:00am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/6 "2021-02-11T02:00:12Z")

</div>

Wondering if there is any updated best practices on this. I have a class I built that interacts with our MES. Not sure what classifies as a large class, but its doing TCP communication, read/write xml, usually the request/response is fairly small, but could be up to a meg or more. I re-wrote all the code recently to be class based as before it was all single functions and was growing way to unmanageable for even a minor edit. Additionally when the class is initialized we pass the perspective session so we can gather auth info and write back results from the mes to session.custom.

I was hoping on view load, I could just load the class into a view.custom, but it looks like it is just storing the str representation. So that wont work.

I can init the class on each event (binding/script/etc) that needs to use the class, but that just seems crazy to keep re-initializing each time something needs to happen. But maybe this is the safest as regular GC will clean up after me?

@paul-griffith I’m a bit worried about how these are very persistent. If I scope each instance of the class to a perspective session id, then just run a cleanup once an hour or so to clear out any orphaned classes, would this keep things safe?

---

<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 11, 2021, 5:11pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/7 "2021-02-11T17:11:24Z")

</div>

@pturmel will correct me if I’m wrong, but I think as of 8.1.x it’s ‘safe’ to use `system.util.getGlobals` - I would probably keep a dictionary of `sessionId: handlerObjects` in a `handlers` key under `getGlobals`; then create a project script that works something like this:

```auto
def getHandler(sessionId):
    return system.util.globals["handlers"].setdefault(sessionId, __createHandler())

def __createHandler():
    return HandlerClass()

```

So, in your script(s) you just always use `projectLibrary.getHandler(self.sessionId)`, and at the use site you don’t have to care if it’s initialized or not - but it will be automatically (lazily) created when needed. You can make a best effort to clean it up on session shutdown via the script hook, but a periodic cleanup is probably also a good idea.

---

<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 11, 2021, 6:37pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/8 "2021-02-11T18:37:37Z")

</div>

> [@paul-griffith](#):
>
> as of 8.1.x it’s ‘safe’ to use `system.util.getGlobals`

Not so much that it's safe, but that it persists like v7.9. I temporarily patched that up in v8.0 with my ~~now-obsolete~~ LifeCycle module. (Not so obsolete, [after all](http://forum.inductiveautomation.com/t/system-util-getglobals-not-persisted-across-designer-save/33869/14).)

The question of safety is not the lack of persistence, but the persistence of old code, and persistence of cached objects for discarded sessions.

To avoid keeping (leaking) old code, the script module that contains the class definition should have top-level code that retrieves the cache from getGlobals() and replaces all instances with new ones. The class's ` __init__ ` method should have a form that copies state from another (old) instance.

To avoid keeping instances for dead sessions, the cache itself should be based on a [weak-keyed dictionary](https://docs.python.org/2.7/library/weakref.html#weakref.WeakKeyDictionary) where each key is the session object itself (is `session` really that?). When that is garbage collected, the associated class object will drop out of the cache automatically.

---

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [February 23, 2021, 5:39pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/9 "2021-02-23T17:39:15Z")

</div>

Can class objects be stored in system.util.getGlobals? Or do the objects need to be serialized? Based on @paul-griffith code it looks like he is storing a class object.

---

<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 23, 2021, 5:42pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/10 "2021-02-23T17:42:48Z")

</div>

Nothing in globals needs to be serializable; it’s local to the lifetime of the current JVM. You only need to worry about serialization if you’re handing things between client/designer/gateway.

---

<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 23, 2021, 5:53pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/11 "2021-02-23T17:53:36Z")

</div>

Jython class objects are code, so the comment above about replacing instances in .getGlobals() when script modules reload does apply. I can’t emphasize enough how important this is to avoid huge memory leaks.

---

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [February 23, 2021, 6:15pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/12 "2021-02-23T18:15:20Z")

</div>

Are you saying there should be code outside of the class but inside the script file that goes through the global variables and looks for instances of the class and instantiates new objects based on the old ones. Then replace the old instances with the new instances in the system.util.globals?

something like this?

```auto
class TestClass(object):	
	def __init__ (self,
		param1,
		param2
	):
		self.param1=param1
		self.param2=param2
		
	@classmethod
	def reInit(cls,
		testClassObject	
	):
		return cls(
			param1=testClassObject.param1,
			param2=testClassObject.param2
		)
		
dictGlobals = system.util.getGlobals()
if("testObjects" in dictGlobals.keys()):
	dictTestObjects = dictGlobals["testObjects"]
					
	for key, value in dictTestObjects.items():
		dictTestObjects[key] = TestClass.reInit(value)

```

---

<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 23, 2021, 6:33pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/13 "2021-02-23T18:33:29Z")

</div>

Precisely.

---

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [February 23, 2021, 6:35pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/14 "2021-02-23T18:35:39Z")

</div>

Does system.util.globals synchronize across a redundant pair of gateways?

---

<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 23, 2021, 6:43pm UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/15 "2021-02-23T18:43:37Z")

</div>

No.

---

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

</div>

Technically when doing this with `self.session` on a button or something, it actually gives you a wrapper to the session, and somehow still with `self.session.getSession()` you still just get another wrapper.

Since that wrapper gets destroyed, you lose the data in the weak-keyed ref, any idea how to get a reference to the actual session object for this?

---

<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, 12:28am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/17 "2022-02-17T00:28:41Z")

</div>

Hmmm. Experiments are in order.

---

<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, 12:31am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/18 "2022-02-17T00:31:37Z")

</div>

I have been trying each of the values in `dir()`, and amazingly the ones for `getSession()` and `session` gives you a new wrapper object each time, with a different place in memory.

Not sure if there is a function somewhere in a script that could be called with this as a parameter to extrapolate the actual content of the wrapper

> **Contents of dir for reference:**
>
> ```auto
> " __class__",
> " __copy__",
> " __deepcopy__",
> " __delattr__",
> " __doc__",
> " __ensure_finalizer__",
> " __eq__",
> " __format__",
> " __getattribute__",
> " __hash__",
> " __init__",
> " __ne__",
> " __new__",
> " __reduce__",
> " __reduce_ex__",
> " __repr__",
> " __setattr__",
> " __str__",
> " __subclasshook__",
> " __unicode__",
> "class",
> "close",
> "equals",
> "getClass",
> "getInfo",
> "getName",
> "getPage",
> "getPages",
> "getProject",
> "getProjectInfo",
> "getPropertyTreeOf",
> "getSession",
> "getView",
> "hashCode",
> "info",
> "name",
> "notify",
> "notifyAll",
> "page",
> "pages",
> "print",
> "project",
> "projectInfo",
> "refreshBinding",
> "session",
> "toString",
> "view",
> "wait"
> 
> ```

---

<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:36am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/19 "2022-02-17T00:36:54Z")

</div>

There’s a lot of trickery going on to prevent the scripting system from getting unwrapped session objects. Don’t fight it.

---

<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, 12:42am UTC](https://forum.inductiveautomation.com/t/ign-5244-persist-python-class-object/30498/20 "2022-02-17T00:42:18Z")

</div>

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?

I hate to add more erroneous scripting on timers, but if its the only way than it is what it is.

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