Script module nested import failure: unhelpful error message

The script module system cannot handle nested import failures. Importing module A with its own syntax error will give a useful error message. However, importing module B which itself is fine but imports module A will give “ImportError: Error loading module B: null”.

When dealing with simple code entirely within the script module system this isn’t normally much of a problem, as the script module editor catches most syntax errors. However, with more complex code or code that is outside of the script module (3rd-party modules using ScriptManager.addScriptModule()) it starts to become a real problem.

Example…

app.testA:

def foo():
		print 'fail' # deliberate syntax error (double indent)
	return 'foofunc'

app.testB:

import app
foo = app.testA.foo

In the script playground, running “print app.testA.foo()” correctly produces:

Traceback (most recent call last):
  File "<script playground>", line 2, in <module>
IndentationError: ('unindent does not match any outer indentation level', ('<module:testA>', 3, 1, "\treturn 'foofunc'\n"))

	at org.python.core.PyException.fillInStackTrace(PyException.java:70)
	...

However, running “print app.testB.foo()” gives:

Traceback (most recent call last):
  File "<script playground>", line 2, in <module>
ImportError: Error loading module testB: null

	at org.python.core.PyException.fillInStackTrace(PyException.java:70)
	at java.lang.Throwable.<init>(Throwable.java:181)
	at java.lang.Exception.<init>(Exception.java:29)
	at java.lang.RuntimeException.<init>(RuntimeException.java:32)
	at org.python.core.PyException.<init>(PyException.java:46)
	at org.python.core.PyException.<init>(PyException.java:43)
	at org.python.core.PyException.<init>(PyException.java:61)
	at org.python.core.Py.ImportError(Py.java:290)
	at com.inductiveautomation.ignition.common.script.ScriptModule.loadModule(ScriptModule.java:49)
	at com.inductiveautomation.ignition.common.script.ScriptPackage.__findattr_ex__(ScriptPackage.java:82)
	at org.python.core.PyObject.__getattr__(PyObject.java:923)
	at org.python.pycode._pyx118.f$0(<script playground>:2)
	at org.python.pycode._pyx118.call_function(<script playground>)
	at org.python.core.PyTableCode.call(PyTableCode.java:165)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1261)
	...

It would be really helpful if this could be fixed as soon as possible.

Thanks.

First things first - that error was unhelpful and the developer has fixed it. It should’ve said something to the effect of “app isn’t defined” and would’ve returned an error on line 1 of app.testB, and I’ll explain why.

The way the app package works is the modules are executed in order when being imported somewhere. This means that everything outside of a def is run in order to create the “app” package. So when you try to “import app” in testB, it fails out because “app” isn’t actually created yet. However, if you put code inside a def, it will not execute until something explicitly calls that function by name. So if you were to put that code in testB inside a def it should work:

app.testA:

def foo():
      print 'fail' # deliberate syntax error (double indent)
   return 'foofunc'

app.testB:

def bar():
	import app
	return app.testA.foo()

After some further testing I am forced to disagree with your explanation. The script module system does not execute modules in order. Importing a ‘package’ executes no Python code at all. It simply returns the ‘package’ object (which contains other packages and scripts) defined at that path. Importing a ‘script’ (either directly or from a package) will execute the code in that script to define it. If that script imports other scripts then they will also be run as part of their import process. But app is always defined, and importing it cannot fail, regardless of any syntax errors in the scripts it contains.

(For the record, I would much prefer it if the script module system supported init files and imported modules in order like standard Python, but that’s not how it works at the moment).

In any case, “app is not defined” would also be an unhelpful error message- I need to know why app isn’t defined yet. It’s failing because of a syntax error in testA, so the error message should give a SyntaxError for line 2 of app.testA.

As you say, putting testB’s initialisation code inside a function does produce the correct error message because it’s being run later, but that’s not the case I’m dealing with.

Please note that all of this works correctly without the syntax error. What I’m doing is perfectly legitimate and works well, it’s just that syntax errors aren’t handled correctly so it’s really hard to debug.

Thanks.

I was misguided about the import app part, you were right - your approach is valid. The developer has fixed this for the next beta.

I misguided James. :blush:

This was fixed by 7.5.5, I think in 7.5.3.

Thanks.