I have a timer event script that calls a project script. On saving the project, thus restarting the gateway scripts, I occasionally get this error in the log. Subsequent events seem to work as desired. Is this an artifact of restarting while a longer-running script is in the middle of running? It’s on a dedicated thread.
com.inductiveautomation.ignition.common.script.JythonExecException: TypeError: super(type, obj): obj must be an instance or subtype of type
at org.python.core.PySuper$exposed___new__.createOfType(Unknown Source)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.runAndReset(Unknown Source)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: org.python.core.PyException: TypeError: super(type, obj): obj must be an instance or subtype of type
I get this error pretty regularly when I save a project too.
The error is pointing at a problem with python inheritance but it all works fine.
It would be good to eliminate it.
Can anyone shed light onto what’s causing it and how to get rid of it?
I have been receiving this error for quite some time and have tried to ignore it, but it is becoming a nuisance. I read into it and it sounds like it has to do with caching of the script objects in Jython/Ignition memory, following a script change it can cause class referencing errors due to the cache not being reloaded appropriately. But that's as far as I have gotten.
I am also paranoid about jython scripting memory leaks. If you are handing a jython class to any platform java functions as listeners, or otherwise persisting jython code objects across project saves, you are likely leaking memory and screwing up your class hierarchy. Which would make this error plausible (when the class you are working with has a stale version stuck in memory and your fresh code is trying to use it).
Do you use much OO programming and inheritance? If not, you would probably never run into this issue. This seems to occur when I have multiple levels of inheritance and at least one of the inherited objects comes from a separate script, thus the desyncing on save, because I don't think Ignition/Jython dumps script classes from memory for scripts that weren't change. Which means you have a new class referencing an old one and for some reason this causes an isinstance error as mentioned here.
Per your comments on hierarchy and memory leaks, never had issues with hierarchy or memory leaks with classes. Except the one time I was caching high use object instances and didn't label them correctly.
Yes. A project save does not kill off any running threads, and does not replace the code within them. The memory of the entire old interpreter, with old objects, is stuck in process memory until the thread finishes.
If you are using system.util.getGlobals() and friends, or attaching jython classes to SDK APIs, old and new objects can mix. (And old objects can get stuck.)
Heads up, I found a solution to my version of this problem. I had a bunch of inherited and referential classes defined within a single file, when I broke them up across multiple files the error went away.
The source of the error appears to have been two locations, one class was storing references to the other classes in the same file in a class variable. When I moved that class to its own file and imported the other classes into it, the error went away.
The second was a multiple inheritance situation (importing 5 or 6 levels of inheritance from one class to boot). I believe moving it to its own file was the primary fix, but I also changed the classes from using multiple inheritance to a composition pattern.
I believe the cause in both cases is that defining and using classes in the same file can cause issues with load order of the classes, even if you define them in use-case order. This load order issue causes two or more instances of the same class with differing object IDs to be created and referenced depending on when/where the classes are used, which triggers the above error. This would explain why it only sometimes occurs, IE if the inherited/referenced classes are substantiated before the class that references/inherits them is substantiated there is no issue. The solution being, breaking up the file and importing the inherited/reference classes because that guarantees the load order and there is a better chance of not having multiple versions of a class in memory.
Are you talking about files in Ignition's site-packages hierarchy? Or are you talking about Ignition's own project scripting library? Because if that latter, and you are importing, you are screwing up. Ignition's project script libraries are not designed to be imported. Ignition cannot track references properly. Use fully qualified names of objects defined in project script libraries anywhere outside themselves.
If you must, for code readability purposes, you can assign a project library to a function local variable and use that within the scope of that function. Do not do this at the top level of a project script.
I am talking about importing other Jython file classes/functions within the Ignition Project library.
import somewhere.something as something
from somewhere import something <- I suggest this one and don't use *.
I do this extensively and haven't had issues, the only exception being circular referencing. You are correct, Ignition doesn't track references like a dedicated IDE, but it does work.
I am curious what a function local variable looks like to you? I re-use some classes dozens of times in the same script across multiple classes, as well as import multiple classes from the same script. I would be concerned with maintaining a bunch of extra lines of code and creating an additional layer of disconnect between classes and their function.
As long as somewhere.something is in site-packages, that's fine. If it is a project library script, you are setting yourself up for subtle bugs. Ignition project library scripts and packages, while placed in jython's module registry for corner cases, are not suitable as the target of import statements.
In a function local is fine. Like so:
something = somewhere.something
# use something in this function.
Don't import from Ignition's project script library. At all.
As noted, I use a fairly extensive class based architecture and the only import issue I have found is circular referencing, which I avoid or use referencing similar to what has been described to get around. Can we get more specific with the issues with this method? Is there a specific error or memory issue that is going to eventually creep in? It would be good to know ahead of time.
PS we have veered pretty heavily from the original question, so we might want to move this elsewhere.
It has been a long time since I researched this in depth, but it boils down to some race conditions in jython's module handling combined with Ignition's per-project script environments. The errors, when encountered, are subtle, and tend to be stale code captures.
The only safe approach is to always use fully-qualified paths to resources in project script libraries, with the one exception for time-of-use abbreviations in function or method scope.
Where you can, move your hierarchy to site-packages, which does not have this problem.