Dependent Script Modules are not updated

When using the standard Ignition Script Module system, dependent scripts do not update correctly. I suspect the client is not reloading them.

Given two scripts A and B, where A imports B:

Changes to B do not propagate to clients correctly unless A is also changed. Even changing A does not always cause B to be updated.

Ignition: 7.5.4
Client: Windows XP
Server: Windows Server 2008

Are you creating static functions or classes?

I’m using both. So far I’ve only noticed the problem with functions, but that’s probably because that’s the more obvious case.

I don’t get that problem with static functions but I do get the problem with classes. It seems the Python interpreter caches the class when you instantiate it for the first time. You have to restart the designer or client to get the new changes. I will see what we can do.

Does it work correctly when you restart the designer or client?

Restarting the client completely (logging out is not sufficient) causes it to reload the code correctly.

A simple test with a few functions appears to work correctly. I’m starting to think there is something else going on here.

Test for one case where this does not work correctly (I suspect there are more)…

# app.tests.dependencyB
def test():
	print 'B1'
# app.tests.dependencyA
def test():
	from app.tests import dependencyB as foo1
	import app.tests.dependencyB as foo2
	foo1.test()
	foo2.test()
>>> app.tests.dependencyA()
B1
B1

Change dependencyB to print B2…

>>> app.tests.dependencyA()
B1
B2

The ‘from’ import version has not updated.

You are right, it has to do with the import when using from. That doesn’t seem to update correctly in the client. I don’t know if there is much we can do right now since it is similar to the class problem.

In the meantime, it is safe to always use the following:import app app.test.dependencyB.test()

Replicated, fixed for app module imports, currently investigating whether this bug affects the system.* imports as well.

Don’t forget about scripts added with ScriptManager.addScriptModule()! That said, I’ve been using it for a while and not encountered any problems, even with code that uses classes and all sorts of other things. I suspect the app resource is doing something weird, possibly some unnecessary optimisation?

Thanks.

The problem lies in an inconsistency in Jython’s import processing logic.

‘from x import y [as z]’ takes a different path than ‘import x [as y]’. Whatever. The point is that PySystemState.modules is a map that contains, among other things, our package names ‘app’, ‘system’, ‘fpmi’, as keys with the corresponding package/module as the value.

While processing regular imports PySystemState.modules is basically left untouched. When processing the from-style imports Jython ends up adding another entry for your packages. In the example case here it adds an ‘app.testing’ key that points to the same package that you would resolve by getting ‘app’, then getting ‘testing’ from ‘app’.

The problem is that ScriptManager’s lifecycle in the client is basically the same as the client itself - it’s never restarted. Changes a project’s script modules are project resources. When they change, we wipe out everything the ‘app’ package points to and rebuild it. This happens in ScriptManager#clearAppModule().

You can see how that additional entry in PySystemState.modules is now a problem - it points to an outdated package while if you traversed ‘app’ to ‘testing’ to whatever module you’d have the up-to-date code.

So the quick fix was to just search for any package/module starting with ‘app.’ in PySystemState.modules during ScriptManager#clearAppModule() and remove them, which causes Jython to traverse the module/package hierarchy next time it processes imports (and add the extra package names back to PySystemState.modules).