Sys.exit() in Jython

Just noticed a big discrepancy with sys.exit(). Consider the following script:

import sys
sys.exit()

When running from the Script Editor, sys.exit() terminates the script (which is what I would expect from the docs), but if make it an IJ command by e.g., saving it as Fiji.app/plugins/Scripts/sys_exit_test.py, ImageJ will quit without any warning.

This happens if the script is called either through:

  1. Menu hierarchy: Plugins > Scripts > sys exit test
  2. Command Launcher
  3. A macro, e.g., run("sys exit test");

I’m guessing this is not intentional? It is quite an aggressive behavior. I’m imagining all sorts of scenarios where this could make someone loose all of its work inadvertently.

BTW, it does not seem to happen, through the IJ1 eval() macro function:

eval("python", "import sys" + "\n" +  "sys.exit()");

I think calls which exit the JVM should do so. What’s surprising to me is that sys.exit() does not quit ImageJ when run from the Script Editor. If you run e.g. System.exit(0); as a BeanShell script from the Script Editor, it certainly shuts down ImageJ.

Note that IJ1 supports BeanShell, Jython and JavaScript in a completely different way than the SciJava tools such as the Script Editor do. So discrepancies in behavior there are unsurprising.

I’m aware of the behavior of System.exit(0) from Groovy and BeanShell, and that is exactly what is confusing me: From the sys module documentation, it looks as if sys.exit() should just exit Jython, not the JVM.

Edit: I just realized that sys.exit() is pretty much the same as raise SystemExit, which also terminates the JVM when called from the menu bar. So I guess it is indeed calling java.lang.System.exit() (but I still don’t know why it exits the JVM only outside the Script Editor).

Then, my question now becomes: What do you guys recommend to terminate a python script, that does not shutdown the JVM?

I used to use the following snippet (that explores an IJ1 “feature”) to mimmic a sys.exit(0) call:

def exit(status=""):
    """Exits without displaying a stack trace if :status: is empty"""
    if not status:
        from java.lang import RuntimeException
        from ij import Macro
        raise RuntimeException(Macro.MACRO_CANCELED) #Ignored by IJ2's Console
    else:
        raise RuntimeError(status)

exit()

Is there a smarter way of accomplish this using SciJava / IJ2 tools?

3 Likes

The way I have seen it done in Python historically is with a main function, so that you can use the return statement as needed to terminate, instead of sys.exit everywhere. The great Guido himself even wrote about this back in 2003.

2 Likes

Thanks!
It would be nice to have a boilerplate template in the script editor that implemented good suggestions of such a main() function. If anyone more knowledgable has some ideas, please let me know.
Otherwise, I’ll look into it. Will post it here if I come across any hurdles.

1 Like

I agree. The Script Editor templates are woefully out of date and incomplete. PRs welcome there. See e.g.: