Open ctrueden opened 10 years ago
See also the relevant discussion in #fiji-devel.
Here's the email I was ready to send to the mailing list:
I have some macros that I'm running in ImageJ in headless mode. After running I was checking the exit status to make sure that no error happened. However, I came to notice that there errors but ImageJ has always been issuing a exit status of zero. How to reproduce
echo "this is bad syntax" > macro.ijm && java -jar .../jars/ij-1.48t.jar -batch ./macro.ijm && echo $?
This always prints "0" but if there was an error, shouldn't the exit status be non-zero? I couldn't find anything on the builtin functions to specify a specific value, the closest I saw was the "exit ()" command which accepts an optional error message [1]
echo "exit ('bad exit');" > macro.ijm && java -jar ../../Applications/Fiji-32bit/jars/ij-1.48t.jar -batch ./macro.ijm && echo $?
Which prints the error message "bad exit", but still returns 0 for exit value.
[1] http://rsbweb.nih.gov/ij/developer/macro/functions.html#exit
Pointed out by @dscho: ImageJ does support executing more than one macro at a time this way, so it's a little tricky deciding which return code to give back if the multiple macros had different errors. My first cut at that would probably be something like "return the error code with the greatest magnitude" so that if any macro failed, you get a failure code.
We have a similar problem with ImageJ not returning anything other than 0 on errors
@fredricj do you really mean scripts? Or macros?
I ask because in scripts you can easily call System.exit(1);
yourself. It's a bit harsh, not letting any service dispose cleanly, but since you are running headlessly anyway...
I'm not sure if this is the right place to be reporting this, but using sys.exit does not in fact seem to work, at least in Python scripts- inside the FIJI docker, running a script sysexit1.py
that's literally just
import sys
print('sys exit 1')
sys.exit(1)
and executing it with
Fiji.app/ImageJ-linux64 --ij2 --headless --console --run "Fiji.app/plugins/sysexit1.py"
Gives the following output:
OpenJDK 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
OpenJDK 64-Bit Server VM warning: Using incremental CMS is deprecated and will likely be removed in a future release
sys exit 1
[ERROR] null
Traceback (most recent call last):
File "Fiji.app/plugins/sysexit1.py", line 4, in <module>
sys.exit(1)
SystemExit: 1
at org.python.core.PySystemState.exit(PySystemState.java:1505)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:206)
at org.python.core.PyObject.__call__(PyObject.java:497)
at org.python.core.PyObject.__call__(PyObject.java:501)
at org.python.core.PyMethod.__call__(PyMethod.java:141)
at org.python.pycode._pyx0.f$0(Fiji.app/plugins/sysexit1.py:4)
at org.python.pycode._pyx0.call_function(Fiji.app/plugins/sysexit1.py)
at org.python.core.PyTableCode.call(PyTableCode.java:171)
at org.python.core.PyCode.call(PyCode.java:18)
at org.python.core.Py.runCode(Py.java:1614)
at org.python.core.__builtin__.eval(__builtin__.java:497)
at org.python.core.__builtin__.eval(__builtin__.java:501)
at org.python.util.PythonInterpreter.eval(PythonInterpreter.java:259)
at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:57)
at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:31)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
at org.scijava.script.ScriptModule.run(ScriptModule.java:160)
at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168)
at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127)
at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66)
at org.scijava.thread.DefaultThreadService$3.call(DefaultThreadService.java:238)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
fiji@518285d69ae9:/opt/fiji$ echo $?
0
I confirm that you can use sys.exit(1)
without docker and with any script outside the scripts directory. The issue is that it requires the script to call sys.exit
so you effectively need to wrap the entire script in a try/catch block. Note that entire even includes importing because you never know where the error that causes the script to fail may come from:
import something
# if the import fails, then imagej still exits with error code 0.
try:
something.do_it()
except:
sys.exit(1)
$ Fiji.app/ImageJ-linux64 --ij2 --headless --console --run "./fail-python.py"
[...]
[ERROR] Traceback (most recent call last):
File "/home/carandraug/fail-python.py", line 1, in <module>
import something
ImportError: No module named something
[...]
$ echo $?
0
And this even assuming that there's no syntax error or your script.
No, even if my script hits an except loop and exits there, FIJI still returns an exit code of 0.
import sys
print('sys exit 1')
try:
import pathlib
except:
print('in the except')
sys.exit(1)
fiji@518285d69ae9:/opt/fiji$ Fiji.app/ImageJ-linux64 --ij2 --headless --console --run "Fiji.app/plugins/sysexit1.py"
OpenJDK 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
OpenJDK 64-Bit Server VM warning: Using incremental CMS is deprecated and will likely be removed in a future release
sys exit 1
in the except
[ERROR] null
Traceback (most recent call last):
File "Fiji.app/plugins/sysexit1.py", line 8, in <module>
sys.exit(1)
SystemExit: 1
at org.python.core.PySystemState.exit(PySystemState.java:1505)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:206)
at org.python.core.PyObject.__call__(PyObject.java:497)
at org.python.core.PyObject.__call__(PyObject.java:501)
at org.python.core.PyMethod.__call__(PyMethod.java:141)
at org.python.pycode._pyx0.f$0(Fiji.app/plugins/sysexit1.py:8)
at org.python.pycode._pyx0.call_function(Fiji.app/plugins/sysexit1.py)
at org.python.core.PyTableCode.call(PyTableCode.java:171)
at org.python.core.PyCode.call(PyCode.java:18)
at org.python.core.Py.runCode(Py.java:1614)
at org.python.core.__builtin__.eval(__builtin__.java:497)
at org.python.core.__builtin__.eval(__builtin__.java:501)
at org.python.util.PythonInterpreter.eval(PythonInterpreter.java:259)
at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:57)
at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:31)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
at org.scijava.script.ScriptModule.run(ScriptModule.java:160)
at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168)
at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127)
at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66)
at org.scijava.thread.DefaultThreadService$3.call(DefaultThreadService.java:238)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
fiji@518285d69ae9:/opt/fiji$ echo $?
0
No, even if my script hits an except loop and exits there, FIJI still returns an exit code of 0.
You're right, I tested it wrong. Using sys.exit
does not work to cause ImageJ to exist with another error code.
We are also running into this issue. A workaround we use is to parse the STDOUT stream and detect keywords like "Exception" and "Macro Error", but it's a lot of extra housekeeping code anytime you want to run Fiji, and it's just a heuristic. It often has false positives because there are many benign exceptions that happen in headless mode.
To allow running ImageJ in batch mode robustly, if any user macro throws an exception and doesn't complete, the return code should be non-zero.
When executing ImageJ scripts, plugins or macros headlessly, it would be nice if the process could give a non-zero return code in case of error. Currently, ImageJ1 does not do this.
Added by @luxigo: