getting the exit code from a batch file that is run from a python program

Normally I like to keep titles short and simple, but this time I wanted to get all of the keywords up front so anyone else who has this problem can find it :) First an outline of the problem and how Chandler uses batch files. Chandler uses two batch files (and two bash scripts for OS X and Linux) to start Chandler and our own build of Python from the command line. The scripts make sure the environment is somewhat sane and preset the PATH and PYTHONPATH variables to avoid any local environment issues from "leaking" into the Chandler runtime environment.

To make sure that our values don't leak back out into the user's environment we use setlocal/endlocal to "wall off"our changes, but this comes with a price.

Once the endlocal boundary is crossed you lose the value of ERRORLEVEL and any variable you may have put ERRORLEVEL into for safe keeping, for example:

    set RC=
    setlocal
    somecommand.exe
    set RC=%ERRORLEVEL%
    echo %RC% %ERRORLEVEL%
    endlocal
    echo %RC% %ERRORLEVEL%

Assuming somecommand.exe returns 1 you will get this for your output:

    1 1
    0 0

Not exactly helpful is it? While working on the problem Andi from work discovered a possible solution that is pretty slick - take advantage of the command chaining of & and the delayed expansion of environment variables:

    set RC=
    setlocal
    somecommand.exe
    echo %ERRORLEVEL%
    endlocal & set RC=%ERRORLEVEL%
    echo %RC%

This will generate the proper result so combine that with exit /B and it generates the proper result.

    endlocal & exit /B %ERRORLEVEL%

Except for one small problem :) exit /B doesn't "transfer" properly when the batch file is called from a Python program using subprocess. I even worked on variations that involved shell=True and other attempts. It just would not take the return code and pass it on. After spinning my wheels for a while on this, dropping it for a couple days and then coming back and spinning more wheels I finally decided to look at some of the bug reports for Ant and Maven to see how they solved it.

It turns out that they really don't solve the problem of getting the exit code value but rather just get any value to be set to flag that something had happened. The reason for this is because they have a wider range of OS's to support: windows 95 on up to the current version. But while reading the bug comments I hit upon an idea that ended up working!

Yea for Open Source because otherwise the info would have been buried behind some corporate firewall. The variation I ended up with was this:

    set RC=
    setlocal
    somecommand.exe
    endlocal & set RC=%ERRORLEVEL%
    goto omega

    :returncode
    exit /B %RC%

    :omega
    call :returncode %RC%

This combines the exit /B trick with the & trick and adds a pretty slick wrinkle (if I do say so myself ;) -- by calling the subroutine I can use exit /B to set the errorlevel and because the call is the *last *command of the batch file that errorlevel is passed out to the caller just like you expect it to be.

So hopefully Google will find this and enable someone to spin their wheels for far less time than I ended up doing :)


Mentions