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).