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.
You can use the 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.
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?
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.
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?
@PGriffith 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?
@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:
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.
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.)
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 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.
Can class objects be stored in system.util.getGlobals? Or do the objects need to be serialized? Based on @PGriffith code it looks like he is storing a class object.
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.
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.
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?
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)
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?
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