Maya failure to exit

At my company we’ve recently encountered a few issues which require forcing Maya to close in order to end the process.

Context:
Start Maya on the command line passing a MEL script which forwards to a Python file.
start_maya.mel > start_maya.py (examples below)

// Example - start_maya.mel
proc main() {
    python ("import start_maya; start_maya.main()");
}

main();
# Example - start_maya.py
import sys

def main():
    load_plugins()
    run_postscript()


def load_plugins():
    # loading plugins
    return


def run_postscript():
    # running custom scripts
    sys.exit(0)   # <--- This is where the problem manifests


if __name__ is "__main__":
    main()

In the start_maya.py process when the application encounters the sys.exit(0) Maya simply reports a traceback for the SystemExit: 0 error and returns to the REPL

When this sort of thing happens on the farm, the process remains open while the job idles in a “processing” / “rendering” state.

Taking hints from other sources, I’ve tried to use the following with mixed success:
cmds.quit(exitCode=0, force=True) - Does not work
cmds.quit(exitCode=0, abort=True) - Does not work
mel.eval('''evalDeferred "quit -f"''') - Works sometimes

For the non-deferred commands, I suspect that this is due to Maya continuing to process and will not close the application during this processing.

With little other success, the most reliable method I’ve found so far is to use a taskkill command.
Note: We use Windows 10 OS

os.system("taskkill /PID %s /F" % os.getpid())

My problem with running this taskkill command is that I’m not able to control the exit code for the process and may run into issues where the farm management software could flag the job as failed even though it successfully completed.

Questions:
Have you encountered this issue?
Have you found any indication of what could cause this problem? (plugins / 3rd party C++ DLL’s / Python libraries)
Any alternative suggestions for how to exit Maya’s process reliably, cleanly, and with an exit code of 0?

Cheers,
David

There’s a common issue in mayapys where they don’t properly kill one thread. In the past I’ve handled it with an atexit handler:

You might also try to throw a maya.standalone.unitialize() into the atexit, I’ve seen that used too

1 Like

So, did some testing with atexit handling.

The behavior of them is really interesting and I think it’s a great way to do cleanup on your file or pipeline during a “normal” application close action.

Here’s what I understood the approach should be:

# start_maya.py
import os
import sys

def get_out_of_maya():
    print 'Getting out of Maya - atexit...'
    try:
        import maya.cmds as cmds
        cmds.file(new=True, force=True)
    except:
        pass
    os._exit(22)  # note underscore

import atexit
atexit.register(get_out_of_maya)

sys.exit(0)

Result: Process does not terminate as expected
atexit function processes after control has been returned to the user and a manual quit() from the REPR has been entered.

C:\Program Files\Autodesk\Maya2017\bin>mayapy.exe -i D:/Temp/maya/exit/start_maya.py
Traceback (most recent call last):
  File "D:/Temp/maya/exit/start_maya.py", line 16, in <module>
    sys.exit(0)
SystemExit: 0
>>> quit()
Getting out of Maya - atexit...

C:\Program Files\Autodesk\Maya2017\bin>echo %ERRORLEVEL%
22

Now, after some experimentation… I have found that the direct: sys.exit produces the error as expected. But when combined with os._exit setting the exit code prior to calling sys.exit - the application closes as expected.

With the side-effect that the chosen exit code passed to sys.exit is ignored in favor of the one passed to os._exit

# start_maya.py
import os
import sys

def get_out_of_maya():
    print 'Getting out of Maya - atexit...'
    try:
        import maya.cmds as cmds
        cmds.file(new=True, force=True)
    except:
        pass
    os._exit(22)  # note underscore

import atexit
atexit.register(get_out_of_maya)

os._exit(33)
sys.exit(0)
C:\Program Files\Autodesk\Maya2017\bin>mayapy.exe -i D:/Temp/maya/exit/start_maya.py

C:\Program Files\Autodesk\Maya2017\bin>echo %ERRORLEVEL%
33

Unfortunately, when the sys.exit is processed, it’ll bypass any of the atexit registered handlers :roll_eyes:


Thank you @Theodox! - you put me on the trail of another interesting dive into “Why”
(and gave me another tool for my toolbox)

1 Like

I just realized in looking back at this that I’m using the mayapy.exe -i flag to make the process session interactive and by excluding this, it may function “as expected” without the need for these hoops.

That being said - We’ve been working on standardizing our application launch process where the -i flag will be supplied to all non-GUI sessions and the same startup code will be used for both interactive and non-interactive launches. So it’s good to know how to approach this problem even if the -i flag is provided to the executable.

If you want to use -i , what about cmds.quit()?

I know at some point I had to add cmds.quit to atexit because of some plugin not unloading properly. But that was years back.

@Theodox - I did give this an attempt, and I believe it works when Maya’s processes are idle, though as part of a larger procedure without adding to the command queue through evalDeferred I didn’t have much success

So this seems to work for me without -i:

   subprocess.check_call( ''c:/program files/autodesk/maya2021/bin/mayapy.exe', '-c', 'import maya.standalone; maya.standalone.initialize(); import sys; sys.exit(123)')
   subprocess.CalledProcessError: Command '['c:/program files/autodesk/maya2021/bin/mayapy.exe', '-c', 'import maya.standalone; maya.standalone.initialize(); import sys; sys.exit(123)']' returned non-zero exit status 123.

I guess I’d just avoid using -i for unattended operation. Mayapy is a weird beast – the exe is python with Maya running as, effectlvely, a dll rather than being a regular Maya, so a number of things based on the QT event pump don’t work (I suspect that evalDeferred is at best a nullop under Mayapy)

One thing you can do is to add a simple switch into your startup routine that detects if sys.executable is mayapy rather than Maya, and use that to go down the correct path. rather than telling mayapy you want it to be interactive and then actually running with no humans involved.