0

I'm attempting to use Python subprocess.Popen to run a batch file, which executes xfoil.exe and feeds it some parameters. Then it waits until xfoil.exe exits.

I've used the following code as a batch runner, which waits for the process to exit with a timeout. The process is killed if it times out.

def Bat_Runner(args, **kwargs):
    """
    Batch runner
    :param args:
    :return:
    """
    si = STARTUPINFO()
    si.dwFlags |= STARTF_USESHOWWINDOW
    process = Popen(args, stdin=PIPE, stdout=DEVNULL, bufsize=1, universal_newlines=True)
    try:
        process.wait(kwargs['timeout'])
    except KeyError:
        process.wait()
    except TimeoutExpired:
        process.kill()
        raise TimeoutError

However, sometimes xfoil.exe fails and hangs, in which case the batch file also hangs, and the main program gets stuck at process.wait(kwargs['timeout]). Even when the time limit is reached, the batch process is not killed. In this case, if I manually run the batch file by double clicking "XXX.bat", the console window also hangs.

What is the reason of this? I'm a bit puzzled regarding this since the Bat_Runner function has been tested and works well at killing the process in some cases, but not this case.

Also, if kill() is killing the batch process, is there any way to kill the xfoil.exe process that it invokes?

========================= Update Line ===============================

PS:

1 This Bat_Runner function will be called by this code:

def _solve_by_Xfoil(self, alpha_set, Mach_number, Raynolds_number, parameter, is_viscus=True, **kwargs):
    ...many unrelated codes...
    try:
        if kwargs.get('timeout'):
            Bat_Runner(batch_file__bat, timout=kwargs['timeout'])
        else:
            Bat_Runner(batch_file__bat, timeout=5)
    except TimeoutError:
        print(''.join((Print_Colors.FAIL, 'Warning!\n', Print_Colors.ENDC,
               'Current Airfoil {} Cannot get an Output: {}!')).format(self._name, parameter))  

The codes are relatively big so for simplisity I just show what matters.Theoritically it will print an fail information. The _solve_by_Xfoil will be called by following:

cl_str_list = self._solve_by_Xfoil(alpha_range, Mach_number, Raynolds_number, 'CL', is_viscus=is_viscus,timeout=10, **kwargs)  

The batch_file__bat(same as XXX.bat) is a file contain follows:

xfoil.exe < xfoil_batch_pid_11028.dat

2 For referrence the xfoil.exe is the this opensource program, I'm using its excutable exe file in this case, it will show a console window, just type some instructions to make it run, so I'm using batch file to run it.

3 Double click the batch file XXX.bat when xfoil.exe fails and hangs, the cmd window will stay and show following: enter image description here
the batch file will run and disappear automatically if xfoil.exe run successfuly.

4 I'm prety sure that the batch file did not been killed because when I mannualy delete the xfoil_batch_pid_11028.dat which feeds to xfoil.exe when it fails, a message show that operation could not been completed cause this file has been open by windos cmd.

Will
  • 101
  • 1
  • 11
  • The only reason I'd see is that the `process.wait`call is not raising the `TimeoutExpired` exception. Not knowing what "xfoil.exe" does is pretty difficult, however there is a worthy note in the [doc](https://docs.python.org/3/library/subprocess.html#subprocess.Popen.wait): "This will deadlock when using stdout=PIPE ". Can you try without declaring the subprocess with "stdout=PIPE"? – Adonis Jul 18 '17 at 09:48
  • Please provide the exact call so people can see the `timeout` value you're using. If the wait really has a timeout, then it should kill the CMD process that runs the batch file. As is, it *will not* kill the xfoil.exe process. – Eryk Sun Jul 18 '17 at 12:20
  • @asettouf Thanks for your suggestions though I've tried that but there's no difference – Will Jul 18 '17 at 14:41
  • @eryksun I've updated this question. I'm certainly sure that this timeout command will work in some cases, I've tested this code for a while and it will kill the batch most time when xfoil.exe run more than timelimit. However, I may guess that there's a certain circumstance that xfoil.exe will stuck and the cmd cannot be killed as usual. – Will Jul 18 '17 at 14:46
  • Unfortunately I did not manage yet to reproduce the problem on my end, but could you try the solution in [this post](https://stackoverflow.com/a/4791612/4121573)? – Adonis Jul 18 '17 at 15:15
  • If `xfoil.exe < xfoil_batch_pid_11028.dat` is the only line in the batch script, you should simplify your problem by running xfoil.exe directly via subprocess. You can open the .dat file for reading and pass it to `Popen` as `stdin`. – Eryk Sun Jul 18 '17 at 19:36
  • If you kill the CMD instance that's running the batch script, there's very little chance that it doesn't work. It calls `TerminateProcess`, which has the kernel abruptly kill a process, in which case there shouldn't be a problem with the .dat file still being open by cmd.exe. But you won't be able to delete it because xfoil.exe has a handle for it as its `StandardInput`. Also, the console remaining open doesn't mean cmd.exe is still running. The console window is separately hosted by conhost.exe. cmd.exe is a console client the same as python.exe, powershell.exe, or xfoil.exe. – Eryk Sun Jul 18 '17 at 19:39
  • Also, `Bat_Runner` doesn't use `stdin` or `stderr`, so it should set them to `DEVNULL` as well. This is more reliable when running in Windows 7 and earlier with a non-console Python process such as pythonw.exe. Otherwise `Popen` may fail if it tries to duplicate a standard handle from the current process that's invalid. This occurs on older versions of Windows, which inherit the standard handle values for a non-console process even though they're not valid, inherited handles in the child process. Also, using the `NUL` device is simpler, since its read and write operations never block. – Eryk Sun Jul 18 '17 at 19:55
  • @eryksun Many thanks for the detailed explanation, I've get little experience with subprocess. I'll turn to use subprocess to derectly call xfoil.exe instead. But I still get two questions: 1. if a console remaining while the cmd has finished running, how could I close that since it dosen't return a end run signal. 2.how could I kill xfoil.exe directly in batch file run cases? – Will Jul 19 '17 at 01:41
  • 1
    The console remains open because xfoil is still attached to it. It will close as soon as xfoil either exits (or is forcibly terminated) or detaches by calling `FreeConsole`. The latter isn't a factor in your case, so focus on killing xfoil. If you're attached to the same console, one option is to run the batch script with `creationflags=subprocess.CREATE_NEW_PROCESS_GROUP`. Then you can use `process.send_signal(signal.CTRL_BREAK_EVENT)`, which will send a Ctrl+Break to just that process group (i.e. CMD and xfoil), and this should kill xfoil. Then use `process.kill()` to kill CMD. – Eryk Sun Jul 19 '17 at 01:55
  • @eryksun I've attempting to use popen to directly run xfoil.exe and use stdin to send commands to it. But now I wonder how to make sure that xfoil.exe could receive next piece of commands every time it has just finished running last command? I cannot get any output if just use stdin.write() to loop instructions unless setting a time.sleep() to wait for a while, is there any advice? Thanks. – Will Jul 23 '17 at 02:37

0 Answers0