As I can see, whenever I save&publish my project, Python scripts get reinitialized (both shared and project ones) - if I put a print "init" just at the beginning, that line is executed again after save&publish.
My problem is as follows: I use a couple of global Python variables to save references to java objects from a JAR library I developed. If I save&publish my project I see that those variable are reinitialized to None, but the resources they allocated have not been freed! Moreover if I dump the heap of the client process with jmap, I see that the old references are still there. To solve the problem, whenever I save&publish the project, I need to ask my customer to restart the Ignition client - which is not that nice!
Is there any possibility to listen to save&publish or whatever, so that I can dispose the objects referenced by the global variables and then have a clean restart? Or do you have a cleverer solution where to put the object references I now save in global Python variables?
Use the dictionary obtained from system.util.getGlobals() to hold such objects. In your script modules, where you would initialize global variables, instead attempt to retrieve from this dictionary. Only if you don’t get the resource you need would you re-initialize it. Or, if the resource includes jython code (that needs to be replaced), close those resources before replacing them with the new versions.
Hi pturmel,
thanks for your suggestion. As of my tests when script restarts, the global namespace is empty and I cannot recall the instances I created before save&publish. Therefore I’m forced to create new instances of my objects and they collide with the old ones: the old ones are still managing resources, mainly a COM port which is connected to a device I must read from - one of my instance variables is the implementation of a java interface, a listener which gets called when data comes in from the device: what I see is that after save&publish the old object still manages data coming in.
It would be nice for me, if I could catch sort of before_savePublish_event, so that I can dispose of my old references.
Phil isn’t talking about the global namespace. The global namespace is in the Python scope, and the entire Python scope gets reset when reloading scripts.
Instead, he’s talking about the system.utils.getGlobals() method. This method calls into the Java part of Ignition, and allows you to store more persistent data (only gets cleared on a gateway restart IIRC).
However, the problem here seems that the object keeps listening, so the old script remains active but inaccessible.
A more robust option would be to reset the connection from time to time. This will give the garbage collector a chance to clean up the old objects, and minimize the need for persistent state.
Hi Sanderd17,
thank you for your reply. One question: you say:
This method calls into the Java part of Ignition, and allows you to store more persistent data (only gets cleared on a gateway restart IIRC)
I thought that system.utils.getGlobals() was a dictionary of all the variables defined in the script scope (i.e. not in a function scope). So: how can I populate the dictionary that is returned by getGlobals? Is there anything like setGlobals?
However, the problem here seems that the object keeps listening, so the old script remains active but inaccessible.
Yes, you are right. To give GC a chance to collect unreferenced objects, I programmed a Python function where I remove listeners, close ports and set variables to None. If only I could call that function, I would be happy: after calling that function a heap dump with jmap shows that memory is clean, as far as my objects are concerned. The problem is: I have no chance to call that function and calling it from time to time is not so good, in my opinion.
Since the device data are needed only on one page or two, I could allocate/deallocate those objects depending on the page where the operator is. But if by chance the operator is just on those pages that need the device when I issue a save&publish, then ... BOOM!!
To be more specific, the function system.util.getGlobals() is named that way because it is the global scope dictionary for legacy scripts. Each modern script module has its own global scope, more accurately called module scope.
Thanks pturmel and Sanderd17, you are right! I didn’t try to write to that dictionary: by using it, my instance variables now outlive the save&publish.